Decompiled source of PenNib v0.1.3

plugins/MuckPenNib.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;
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: InternalsVisibleTo("MuckPenNib.Tests")]
[assembly: AssemblyCompany("MuckPenNib")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds Pen Nib, a White powerup that multiplies every Nth attack on an enemy.")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+879fc7ac66fec639f2f84b4b7a65d5c61c9ed734")]
[assembly: AssemblyProduct("PenNib")]
[assembly: AssemblyTitle("MuckPenNib")]
[assembly: AssemblyVersion("0.1.3.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckPenNib
{
	internal readonly struct HudBox
	{
		public readonly float PlateX;

		public readonly float PlateY;

		public readonly float PlateWidth;

		public readonly float PlateHeight;

		public readonly float TextX;

		public readonly float TextY;

		public readonly float TextWidth;

		public readonly float TextHeight;

		public readonly float Shadow;

		public HudBox(float plateX, float plateY, float plateWidth, float plateHeight, float pad, float textWidth, float textHeight, float shadow)
		{
			PlateX = plateX;
			PlateY = plateY;
			PlateWidth = plateWidth;
			PlateHeight = plateHeight;
			TextX = plateX + pad;
			TextY = plateY + pad;
			TextWidth = textWidth;
			TextHeight = textHeight;
			Shadow = shadow;
		}
	}
	internal static class CounterLayout
	{
		public const int BaseFontSize = 20;

		public const double TopFraction = 0.7;

		private const float Margin = 20f;

		private const float Pad = 6f;

		private const float Gap = 8f;

		public static float Scale(int screenHeight)
		{
			return Math.Max(1f, (float)screenHeight / 1080f);
		}

		public static int FontSize(int screenHeight)
		{
			return (int)Math.Round(20f * Scale(screenHeight), MidpointRounding.AwayFromZero);
		}

		public static HudBox For(int screenWidth, int screenHeight, float textWidth, float textHeight)
		{
			float num = Scale(screenHeight);
			float num2 = (float)Math.Round(6f * num);
			float num3 = (float)Math.Ceiling(textWidth) + 2f * num2;
			float num4 = (float)Math.Ceiling(textHeight) + 2f * num2;
			float plateX = Math.Max(0f, (float)screenWidth - (float)Math.Round(20f * num) - num3);
			float val = (float)(Math.Round((double)screenHeight * 0.7) + Math.Round(8f * num));
			return new HudBox(plateY: Math.Max(0f, Math.Min(val, (float)screenHeight - num4)), shadow: Math.Max(1f, (float)Math.Round(num)), plateX: plateX, plateWidth: num3, plateHeight: num4, pad: num2, textWidth: (float)Math.Ceiling(textWidth), textHeight: (float)Math.Ceiling(textHeight));
		}
	}
	internal static class HitGate
	{
		public static bool ShouldCount(int weaponType, int damage, int targetHp, int stacks)
		{
			if (weaponType >= 0 && damage > 0 && targetHp > 0)
			{
				return stacks > 0;
			}
			return false;
		}
	}
	internal static class HudGate
	{
		public static bool Hidden(bool notPlaying, bool otherUi, bool inventoryOpen, bool dead)
		{
			return notPlaying || otherUi || inventoryOpen || dead;
		}
	}
	internal sealed class PenNibConfig
	{
		public readonly ConfigEntry<int> BaseInterval;

		public readonly ConfigEntry<int> IntervalReductionPerStack;

		public readonly ConfigEntry<int> MinInterval;

		public readonly ConfigEntry<float> DamageMultiplier;

		public readonly ConfigEntry<bool> ShowCounter;

		public readonly ConfigEntry<bool> MarkChargedHits;

		public readonly ConfigEntry<KeyboardShortcut> GivePenNibKey;

		public readonly ConfigEntry<bool> VerboseLogging;

		internal static int CardRefreshCount;

		internal static string LastCardText;

		public PenNibConfig(ConfigFile cfg)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			BaseInterval = cfg.Bind<int>("Pen Nib", "BaseInterval", PenNibRules.Default.BaseInterval, new ConfigDescription("Attacks that must connect before the next one deals multiplied damage, with one stack. 10 is the Slay the Spire interval. Not synced. Use the same value on every machine.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			IntervalReductionPerStack = cfg.Bind<int>("Pen Nib", "IntervalReductionPerStack", PenNibRules.Default.IntervalReductionPerStack, new ConfigDescription("How many attacks sooner each extra stack makes the charged hit. This stacking rule belongs to this mod. Not synced. Use the same value on every machine.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
			MinInterval = cfg.Bind<int>("Pen Nib", "MinInterval", PenNibRules.Default.MinInterval, new ConfigDescription("Smallest attack interval, however many stacks you hold. Not synced. Use the same value on every machine.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			DamageMultiplier = cfg.Bind<float>("Pen Nib", "DamageMultiplier", PenNibRules.Default.DamageMultiplier, new ConfigDescription("Damage multiplier on the charged attack. 2 is Slay the Spire's double damage. The result rounds half away from zero. Not synced. Use the same value on every machine.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			ShowCounter = cfg.Bind<bool>("Visuals", "ShowCounter", true, "Draw a local attack counter. Other players do not see it.");
			MarkChargedHits = cfg.Bind<bool>("Visuals", "MarkChargedHits", true, "Show a charged hit as a big hit number. Display only.");
			GivePenNibKey = cfg.Bind<KeyboardShortcut>("Debug", "GivePenNibKey", KeyboardShortcut.Empty, "Press in-game to give yourself one Pen Nib stack (local test helper). Empty = disabled.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Logs every charged attack.");
			cfg.SettingChanged += OnSettingChanged;
		}

		public PenNibRules Rules()
		{
			return new PenNibRules(BaseInterval.Value, IntervalReductionPerStack.Value, MinInterval.Value, DamageMultiplier.Value);
		}

		internal static bool UpdatesCard(string section, string key)
		{
			if (section == "Pen Nib")
			{
				switch (key)
				{
				default:
					return key == "DamageMultiplier";
				case "BaseInterval":
				case "IntervalReductionPerStack":
				case "MinInterval":
					return true;
				}
			}
			return false;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			ConfigDefinition definition = args.ChangedSetting.Definition;
			if (UpdatesCard(definition.Section, definition.Key))
			{
				string text = (LastCardText = Rules().Description());
				CardRefreshCount++;
				PenNibPowerup.ApplyDescription(text);
			}
		}
	}
	internal sealed class PenNibCounter
	{
		public const int RangedWeaponType = 1;

		private int _lastFrame = int.MinValue;

		private int _lastWeapon = int.MinValue;

		private bool _lastCharged;

		public int Count { get; private set; }

		public bool Register(int weaponType, int frame, int interval)
		{
			if (weaponType < 0 || interval <= 0)
			{
				return false;
			}
			if (weaponType != 1 && frame == _lastFrame && weaponType == _lastWeapon)
			{
				return _lastCharged;
			}
			_lastFrame = frame;
			_lastWeapon = weaponType;
			Count++;
			_lastCharged = Count >= interval;
			if (_lastCharged)
			{
				Count = 0;
			}
			return _lastCharged;
		}

		public int AttacksUntilCharged(int interval)
		{
			if (interval <= 0)
			{
				return 0;
			}
			return Math.Max(1, interval - Count);
		}

		public void Reset()
		{
			Count = 0;
			_lastFrame = int.MinValue;
			_lastWeapon = int.MinValue;
			_lastCharged = false;
		}
	}
	[HarmonyPatch(typeof(HitableMob), "Hit", new Type[]
	{
		typeof(int),
		typeof(float),
		typeof(int),
		typeof(Vector3),
		typeof(int)
	})]
	internal static class PenNibHitPatch
	{
		private static bool _reportedFailure;

		[HarmonyPriority(200)]
		[HarmonyBefore(new string[] { "evan.muck.crowbar" })]
		private static void Prefix(HitableMob __instance, ref int __0, ref int __2, int __4)
		{
			try
			{
				int num = PenNibPowerup.LocalStacks();
				if (!HitGate.ShouldCount(__4, __0, ((Hitable)__instance).hp, num))
				{
					return;
				}
				PenNibRules penNibRules = PenNibPlugin.Cfg.Rules();
				int num2 = penNibRules.IntervalFor(num);
				if (PenNibState.Counter.Register(__4, Time.frameCount, num2))
				{
					int num3 = __0;
					__0 = penNibRules.ChargedDamage(__0);
					if (PenNibPlugin.Cfg.MarkChargedHits.Value && __2 == 0)
					{
						__2 = 2;
					}
					if (PenNibPlugin.Cfg.VerboseLogging.Value)
					{
						PenNibPlugin.Log.LogInfo((object)$"Pen Nib: charged attack {num3} -> {__0} ({num} stacks, interval {num2})");
					}
				}
			}
			catch (Exception arg)
			{
				if (!_reportedFailure)
				{
					_reportedFailure = true;
					PenNibPlugin.Log.LogError((object)$"Pen Nib on-hit failed (further failures are not logged): {arg}");
				}
			}
		}
	}
	[BepInPlugin("evan.muck.pennib", "Pen Nib", "0.1.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PenNibPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.pennib";

		public const string Name = "Pen Nib";

		public const string Version = "0.1.3";

		internal static ManualLogSource Log;

		internal static PenNibConfig Cfg;

		private void Awake()
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new PenNibConfig(((BaseUnityPlugin)this).Config);
			PenNibRules penNibRules = Cfg.Rules();
			ManualLogSource log = Log;
			string text = string.Format("{0} {1} config: interval {2} (-{3} per stack, min {4}), ", "Pen Nib", "0.1.3", penNibRules.BaseInterval, penNibRules.IntervalReductionPerStack, penNibRules.MinInterval);
			float damageMultiplier = penNibRules.DamageMultiplier;
			log.LogInfo((object)(text + "x" + damageMultiplier.ToString("0.##", CultureInfo.InvariantCulture) + " damage. Config is per player and not synced."));
			Harmony harmony = new Harmony("evan.muck.pennib");
			if (!TryPatch(harmony, typeof(PenNibHitPatch)))
			{
				Log.LogError((object)"Pen Nib hit patch failed. The item will not be added to the drop pool.");
				return;
			}
			bool flag = TryPatch(harmony, typeof(UnnumberPenNibPatch));
			bool flag2 = TryPatch(harmony, typeof(RegisterPenNibPatch));
			if (!flag || !flag2)
			{
				Log.LogError((object)"Pen Nib registration patch failed. The hit patch stays installed and does nothing until the item is registered.");
			}
			PenNibPowerup.Create();
			PenNibRunner.Ensure();
			Log.LogInfo((object)"Pen Nib 0.1.3 loaded. Every player needs this mod, the same other powerup mods, and the same gameplay config.");
		}

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

		private static bool _stacksFailure;

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

		public static Powerup Definition { get; private set; }

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

		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 = "Pen Nib";
			((Object)obj).name = "Pen Nib";
			obj.description = PenNibPlugin.Cfg.Rules().Description();
			obj.tier = (PowerTier)0;
			obj.sprite = LoadIcon();
			((Object)obj).hideFlags = (HideFlags)32;
			Definition = obj;
		}

		public static void ApplyDescription(string text)
		{
			if ((Object)(object)Definition != (Object)null)
			{
				Definition.description = text;
			}
		}

		public static int LocalStacks()
		{
			if (Id < 0 || (Object)(object)PowerupInventory.Instance == (Object)null || _stacksFailure)
			{
				return 0;
			}
			try
			{
				return PowerupInventory.Instance.GetAmount("Pen Nib");
			}
			catch (Exception arg)
			{
				_stacksFailure = true;
				PenNibPlugin.Log.LogError((object)$"Pen Nib could not read stacks (further failures are not logged): {arg}");
				return 0;
			}
		}

		public static void GiveOneLocal()
		{
			PowerupInventory instance = PowerupInventory.Instance;
			if ((Object)(object)instance == (Object)null || Id < 0)
			{
				PenNibPlugin.Log.LogWarning((object)"Can't give Pen Nib: 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)
			{
				PenNibPlugin.Log.LogWarning((object)"Can't give Pen Nib: the powerup UI or player status is not ready.");
				return;
			}
			int[] array;
			try
			{
				if (_counts == null)
				{
					_counts = AccessTools.FieldRefAccess<PowerupInventory, int[]>("powerups");
				}
				array = _counts.Invoke(instance);
			}
			catch (Exception arg)
			{
				PenNibPlugin.Log.LogError((object)$"Can't give Pen Nib: failed to read the powerup counts. {arg}");
				return;
			}
			if (array == null || Id >= array.Length)
			{
				PenNibPlugin.Log.LogWarning((object)"Can't give Pen Nib: the powerup count array is missing or too short.");
				return;
			}
			array[Id]++;
			PowerupUI.Instance.AddPowerup(Id);
			PlayerStatus.Instance.UpdateStats();
			PenNibPlugin.Log.LogInfo((object)$"Debug: Pen Nib stacks now {array[Id]} (local only; the host's copy of your powerups is not updated).");
		}

		private static Sprite LoadIcon()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Assembly assembly = typeof(PenNibPowerup).Assembly;
				string name = assembly.GetName().Name + ".icon.png";
				using Stream stream = assembly.GetManifestResourceStream(name);
				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 = "PenNibIcon";
				((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 = "PenNibIcon";
				((Object)obj).hideFlags = (HideFlags)32;
				return obj;
			}
			catch (Exception arg)
			{
				PenNibPlugin.Log.LogError((object)$"Failed to load Pen Nib icon: {arg}");
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class UnnumberPenNibPatch
	{
		[HarmonyPriority(600)]
		private static void Prefix(ItemManager __instance)
		{
			if (!((Object)(object)PenNibPowerup.Definition == (Object)null))
			{
				__instance.powerupsWhite = PowerupPool.Without(__instance.powerupsWhite, PenNibPowerup.Definition);
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class RegisterPenNibPatch
	{
		[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", "evan.muck.shapedglass", "evan.muck.diosbestfriend", "evan.muck.waxquail" })]
		private static void Postfix(ItemManager __instance)
		{
			Powerup definition = PenNibPowerup.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 || __instance.powerupsWhite == null)
			{
				PenNibPlugin.Log.LogWarning((object)"Pen Nib registration skipped: the powerup table is not ready.");
				return;
			}
			int value;
			bool flag = stringToPowerupId.TryGetValue("Pen Nib", out value);
			Powerup value2;
			bool existingIsOurs = flag && allPowerups.TryGetValue(value, out value2) && (Object)(object)value2 == (Object)(object)definition;
			PowerupClaim powerupClaim = PowerupClaim.Resolve(flag, existingIsOurs, value, allPowerups.Keys);
			if (!powerupClaim.Claimed)
			{
				PenNibPlugin.Log.LogError((object)$"The name Pen Nib is already powerup id {value}. This mod will not add another pool entry.");
				LogTable(allPowerups);
				return;
			}
			PickupArt.Apply(definition, __instance.powerupsWhite);
			if (!flag)
			{
				allPowerups.Add(powerupClaim.Id, definition);
				stringToPowerupId.Add("Pen Nib", powerupClaim.Id);
			}
			definition.id = powerupClaim.Id;
			if (!__instance.powerupsWhite.Contains(definition))
			{
				__instance.powerupsWhite = CollectionExtensions.AddToArray<Powerup>(__instance.powerupsWhite, definition);
			}
			PenNibPowerup.Id = powerupClaim.Id;
			PenNibPlugin.Log.LogInfo((object)$"Registered Pen Nib as White powerup id {powerupClaim.Id} ({allPowerups.Count} total, {__instance.powerupsWhite.Length} in the White pool).");
			LogTable(allPowerups);
		}

		private static void LogTable(Dictionary<int, Powerup> all)
		{
			PenNibPlugin.Log.LogInfo((object)("Powerup table: " + PowerupTable.Format(all.Select((KeyValuePair<int, Powerup> kv) => new KeyValuePair<int, string>(kv.Key, kv.Value.name)))));
		}
	}
	internal readonly struct PenNibRules
	{
		public static readonly PenNibRules Default = new PenNibRules(10, 2, 4, 2f);

		public readonly int BaseInterval;

		public readonly int IntervalReductionPerStack;

		public readonly int MinInterval;

		public readonly float DamageMultiplier;

		public PenNibRules(int baseInterval, int intervalReductionPerStack, int minInterval, float damageMultiplier)
		{
			BaseInterval = Clamp(baseInterval, 1, 100);
			IntervalReductionPerStack = Clamp(intervalReductionPerStack, 0, 50);
			MinInterval = Clamp(minInterval, 1, 100);
			if (!(damageMultiplier >= 1f))
			{
				DamageMultiplier = 1f;
			}
			else if (damageMultiplier > 10f)
			{
				DamageMultiplier = 10f;
			}
			else
			{
				DamageMultiplier = damageMultiplier;
			}
		}

		public int IntervalFor(int stacks)
		{
			if (stacks <= 0)
			{
				return 0;
			}
			int num = Math.Min(MinInterval, BaseInterval);
			long val = BaseInterval - IntervalReductionPerStack * ((long)stacks - 1L);
			return (int)Math.Max(num, val);
		}

		public int ChargedDamage(int damage)
		{
			if (damage <= 0)
			{
				return damage;
			}
			double num = Math.Round((double)damage * (double)DamageMultiplier, MidpointRounding.AwayFromZero);
			if (num >= 2147483647.0)
			{
				return int.MaxValue;
			}
			return (int)num;
		}

		public string Description()
		{
			float damageMultiplier = DamageMultiplier;
			string text = damageMultiplier.ToString("0.##", CultureInfo.InvariantCulture);
			int num = Math.Min(MinInterval, BaseInterval);
			string text2 = "Every " + Ordinal(BaseInterval) + " attack on an enemy deals " + text + "x damage.";
			if (IntervalReductionPerStack == 0 || num == BaseInterval)
			{
				return text2;
			}
			return text2 + $" Each extra stack makes it {IntervalReductionPerStack} attacks sooner, down to every {Ordinal(num)}.";
		}

		public static string Ordinal(int n)
		{
			int num = n % 100;
			if (num >= 11 && num <= 13)
			{
				return n + "th";
			}
			return (n % 10) switch
			{
				1 => n + "st", 
				2 => n + "nd", 
				3 => n + "rd", 
				_ => n + "th", 
			};
		}

		public static string CounterLabel(int count, int interval)
		{
			if (interval <= 0)
			{
				return "";
			}
			if (interval - count <= 1)
			{
				return "Pen Nib: READY";
			}
			return $"Pen Nib: {count}/{interval}";
		}

		private static int Clamp(int value, int min, int max)
		{
			return Math.Max(min, Math.Min(max, value));
		}
	}
	internal sealed class PenNibRunner : MonoBehaviour
	{
		private static PenNibRunner _instance;

		private int _stacks;

		private int _interval;

		private int _count;

		private string _label = "";

		private GUIStyle _style;

		private string _measured;

		private Vector2 _textSize;

		private bool _loggedGate;

		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("MuckPenNib")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<PenNibRunner>();
				SceneManager.activeSceneChanged += delegate
				{
					PenNibState.Counter.Reset();
				};
			}
		}

		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 = PenNibPlugin.Cfg.GivePenNibKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				PenNibPowerup.GiveOneLocal();
			}
			_stacks = PenNibPowerup.LocalStacks();
			int num = PenNibPlugin.Cfg.Rules().IntervalFor(_stacks);
			int count = PenNibState.Counter.Count;
			if (count != _count || num != _interval)
			{
				_count = count;
				_interval = num;
				_label = PenNibRules.CounterLabel(count, num);
			}
		}

		private void OnGUI()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_006a: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00b5: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Event.current.type == 7 && PenNibPlugin.Cfg.ShowCounter.Value && _stacks > 0 && !string.IsNullOrEmpty(_label) && !Covered())
			{
				int num = CounterLayout.FontSize(Screen.height);
				if (_style == null || _style.fontSize != num)
				{
					_style = new GUIStyle(GUI.skin.label)
					{
						fontSize = num,
						fontStyle = (FontStyle)1,
						alignment = (TextAnchor)3,
						wordWrap = false,
						clipping = (TextClipping)0,
						padding = new RectOffset(0, 0, 0, 0),
						margin = new RectOffset(0, 0, 0, 0)
					};
					_style.normal.textColor = Color.white;
					_measured = null;
				}
				if ((object)_measured != _label)
				{
					_measured = _label;
					_textSize = _style.CalcSize(new GUIContent(_label));
				}
				DrawHud(CounterLayout.For(Screen.width, Screen.height, _textSize.x, _textSize.y), _label, _style);
			}
		}

		private bool Covered()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			try
			{
				OtherInput instance = OtherInput.Instance;
				PlayerStatus instance2 = PlayerStatus.Instance;
				bool flag = (int)GameManager.state != 1;
				return HudGate.Hidden(flag, !flag && (Object)(object)instance != (Object)null && instance.OtherUiActive(), !flag && (Object)(object)instance != (Object)null && instance.IsAnyMenuOpen(), (Object)(object)instance2 != (Object)null && instance2.IsPlayerDead());
			}
			catch (Exception arg)
			{
				if (!_loggedGate)
				{
					_loggedGate = true;
					PenNibPlugin.Log.LogError((object)$"Pen Nib: reading Muck's menu state for the counter failed. The counter stays visible. {arg}");
				}
				return false;
			}
		}

		private static void DrawHud(HudBox box, string text, GUIStyle style)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			GUI.color = new Color(0f, 0f, 0f, 0.45f);
			GUI.DrawTexture(new Rect(box.PlateX, box.PlateY, box.PlateWidth, box.PlateHeight), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = new Color(0f, 0f, 0f, 0.9f);
			GUI.Label(new Rect(box.TextX + box.Shadow, box.TextY + box.Shadow, box.TextWidth, box.TextHeight), text, style);
			GUI.color = Color.white;
			GUI.Label(new Rect(box.TextX, box.TextY, box.TextWidth, box.TextHeight), text, style);
			GUI.color = color;
		}
	}
	internal static class PenNibState
	{
		internal static readonly PenNibCounter Counter = new PenNibCounter();
	}
	internal static class PickupArt
	{
		public const string ObjResource = "MuckPenNib.pickup.obj";

		public const string JsonResource = "MuckPenNib.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("MuckPenNib.pickup.obj"), Read("MuckPenNib.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)
			{
				PenNibPlugin.Log.LogWarning((object)("Pen Nib 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
				{
					PenNibPlugin.Log.LogWarning((object)"No White powerup to borrow a pickup model from; dropped Pen Nibs 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 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));
		}
	}
	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 static class PowerupPool
	{
		public static T[] Without<T>(T[] pool, T item) where T : class
		{
			if (pool == null)
			{
				return null;
			}
			int num = 0;
			for (int i = 0; i < pool.Length; i++)
			{
				if (pool[i] != item)
				{
					num++;
				}
			}
			if (num == pool.Length)
			{
				return pool;
			}
			T[] array = new T[num];
			int num2 = 0;
			for (int j = 0; j < pool.Length; j++)
			{
				if (pool[j] != item)
				{
					array[num2++] = pool[j];
				}
			}
			return array;
		}
	}
	internal static class PowerupTable
	{
		public static string Format(IEnumerable<KeyValuePair<int, string>> entries)
		{
			return string.Join(", ", from pair in entries
				orderby pair.Key
				select pair.Key + "=" + (pair.Value ?? "?"));
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckPenNib";

		public const string PLUGIN_NAME = "PenNib";

		public const string PLUGIN_VERSION = "0.1.3";
	}
}