Decompiled source of SquidPolyp v0.2.2

plugins/MuckSquidPolyp.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.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckSquidPolyp")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds Squid Polyp as a Blue powerup. Activating an interactable spawns a stationary turret.")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2+879fc7ac66fec639f2f84b4b7a65d5c61c9ed734")]
[assembly: AssemblyProduct("Squid Polyp")]
[assembly: AssemblyTitle("MuckSquidPolyp")]
[assembly: InternalsVisibleTo("MuckSquidPolyp.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 MuckSquidPolyp
{
	[HarmonyPatch(typeof(LootContainerInteract), "LocalExecute")]
	internal static class LootChestPatch
	{
		private static void Postfix(LootContainerInteract __instance)
		{
			SquidPolypRunner.Notify(InteractKind.LootChest, ((Object)__instance).GetInstanceID(), opening: true);
		}
	}
	[HarmonyPatch(typeof(ShrineInteractable), "Interact")]
	internal static class BattleShrinePatch
	{
		private static void Postfix(ShrineInteractable __instance)
		{
			SquidPolypRunner.Notify(InteractKind.BattleShrine, ((Object)__instance).GetInstanceID(), !__instance.started);
		}
	}
	[HarmonyPatch(typeof(RepairInteract), "LocalExecute")]
	internal static class RepairPatch
	{
		private static void Postfix(RepairInteract __instance)
		{
			SquidPolypRunner.Notify(InteractKind.Repair, ((Object)__instance).GetInstanceID(), opening: true);
		}
	}
	[HarmonyPatch(typeof(CraftingInteract), "Interact")]
	internal static class CraftingStationPatch
	{
		private static void Postfix(CraftingInteract __instance)
		{
			bool opening = (Object)(object)InventoryUI.Instance != (Object)null && ((Component)InventoryUI.Instance).gameObject.activeInHierarchy;
			SquidPolypRunner.Notify(InteractKind.CraftingStation, ((Object)__instance).GetInstanceID(), opening);
		}
	}
	[HarmonyPatch(typeof(ClientHandle), "OpenChest")]
	internal static class StorageStationPatch
	{
		private static void Prefix(ref Chest __state)
		{
			__state = (((Object)(object)OtherInput.Instance != (Object)null) ? OtherInput.Instance.currentChest : null);
		}

		private static void Postfix(Chest __state)
		{
			Chest val = (((Object)(object)OtherInput.Instance != (Object)null) ? OtherInput.Instance.currentChest : null);
			bool flag = (Object)(object)val != (Object)null && val != __state;
			SquidPolypRunner.Notify(InteractKind.StorageStation, flag ? ((Object)val).GetInstanceID() : 0, flag);
		}
	}
	[HarmonyPatch(typeof(TradeUi), "BuySell")]
	internal static class TraderPatch
	{
		private static void Prefix(ref int __state)
		{
			__state = (((Object)(object)InventoryUI.Instance != (Object)null) ? InventoryUI.Instance.GetMoney() : (-1));
		}

		private static void Postfix(TradeUi __instance, int __state)
		{
			if (__state >= 0 && !((Object)(object)InventoryUI.Instance == (Object)null) && Activation.TradePaid(__state, InventoryUI.Instance.GetMoney()))
			{
				SquidPolypRunner.Notify(InteractKind.Trader, ((Object)__instance).GetInstanceID(), opening: true);
			}
		}
	}
	[HarmonyPatch(typeof(AchievementManager), "ReviveTeammate")]
	internal static class RevivePatch
	{
		private static void Postfix()
		{
			SquidPolypRunner.Notify(InteractKind.Revive, 1, opening: true);
		}
	}
	[HarmonyPatch(typeof(PickupInteract), "LocalExecute")]
	internal static class GroundPickupPatch
	{
		private static void Postfix(PickupInteract __instance)
		{
			SquidPolypRunner.Notify(InteractKind.GroundPickup, ((Object)__instance).GetInstanceID(), opening: true);
		}
	}
	[HarmonyPatch(typeof(DoorInteractable), "LocalExecute")]
	internal static class DoorPatch
	{
		private static void Postfix(DoorInteractable __instance)
		{
			SquidPolypRunner.Notify(InteractKind.Door, ((Object)__instance).GetInstanceID(), opening: true);
		}
	}
	internal static class ModdedShrinePatch
	{
		public static bool TryApply(Harmony harmony)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0063: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("MuckShrineOfChance.ChanceShrine");
			if (type == null)
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "LocalExecute", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ModdedShrinePatch), "Prefix", (Type[])null), new HarmonyMethod(typeof(ModdedShrinePatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			SquidPolypPlugin.Log.LogInfo((object)"Squid Polyp will spawn turrets from Shrine of Chance.");
			return true;
		}

		private static void Prefix(object __instance, ref bool __state)
		{
			__state = Activation.ModdedPay(Offering(__instance));
		}

		private static void Postfix(object __instance, bool __state)
		{
			if (__state && __instance != null)
			{
				Component val = (Component)((__instance is Component) ? __instance : null);
				int instanceId = ((val != null) ? ((Object)val).GetInstanceID() : 0);
				SquidPolypRunner.Notify(InteractKind.ModdedInteractable, instanceId, opening: true);
			}
		}

		private static bool Offering(object shrine)
		{
			try
			{
				FieldInfo field = shrine.GetType().GetField("exchange", BindingFlags.Instance | BindingFlags.NonPublic);
				object obj = ((field != null) ? field.GetValue(shrine) : null);
				if (obj == null)
				{
					return false;
				}
				PropertyInfo property = obj.GetType().GetProperty("Offering");
				bool flag = default(bool);
				int num;
				if (property != null)
				{
					object value = property.GetValue(obj);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				SquidPolypPlugin.Log.LogWarning((object)("Squid Polyp could not read the Shrine of Chance offer. " + ex.Message));
				return false;
			}
		}
	}
	internal enum InteractKind
	{
		LootChest,
		BattleShrine,
		Repair,
		CraftingStation,
		StorageStation,
		Trader,
		Revive,
		ModdedInteractable,
		GroundPickup,
		Door,
		LeaveIsland,
		Wishlist
	}
	internal static class Activation
	{
		public static bool Counts(InteractKind kind, PolypRules rules, bool opening)
		{
			if (!opening)
			{
				return false;
			}
			switch (kind)
			{
			case InteractKind.LootChest:
				return rules.CountChests;
			case InteractKind.BattleShrine:
				return rules.CountShrines;
			case InteractKind.Repair:
				return rules.CountRepairs;
			case InteractKind.CraftingStation:
			case InteractKind.StorageStation:
				return rules.CountFreeStations;
			case InteractKind.Trader:
				return rules.CountTraderBuys;
			case InteractKind.Revive:
				return rules.CountRevives;
			case InteractKind.ModdedInteractable:
				return rules.CountModded;
			case InteractKind.GroundPickup:
				return rules.CountPickups;
			case InteractKind.Door:
				return rules.CountDoors;
			default:
				return false;
			}
		}

		public static bool UsesStationDebounce(InteractKind kind)
		{
			if (kind != InteractKind.CraftingStation)
			{
				return kind == InteractKind.StorageStation;
			}
			return true;
		}

		public static bool TradePaid(int coinsBefore, int coinsAfter)
		{
			return coinsAfter < coinsBefore;
		}

		public static bool ModdedPay(bool offeringBefore)
		{
			return offeringBefore;
		}
	}
	internal static class AimYaw
	{
		public static float Towards(float fromX, float fromZ, float toX, float toZ)
		{
			float num = toX - fromX;
			float num2 = toZ - fromZ;
			if (num * num + num2 * num2 < 1E-08f)
			{
				return float.NaN;
			}
			return (float)(Math.Atan2(num, num2) * (180.0 / Math.PI));
		}

		public static float Step(float current, float target, float maxDegrees)
		{
			if (float.IsNaN(current))
			{
				current = 0f;
			}
			if (float.IsNaN(target))
			{
				return current;
			}
			if (maxDegrees < 0f || float.IsNaN(maxDegrees))
			{
				maxDegrees = 0f;
			}
			float value = Wrap(target - current);
			if (Math.Abs(value) <= maxDegrees)
			{
				return Wrap(target);
			}
			return Wrap(current + (float)Math.Sign(value) * maxDegrees);
		}

		public static float Wrap(float degrees)
		{
			float num = degrees % 360f;
			if (num >= 180f)
			{
				num -= 360f;
			}
			if (num < -180f)
			{
				num += 360f;
			}
			return num;
		}
	}
	internal static class HostDamage
	{
		public static int AfterDefense(int damage, float defense)
		{
			float num = defense;
			if (num < 0f)
			{
				num = 0f;
			}
			else if (num > 100f)
			{
				num = 100f;
			}
			float num2 = (float)((100.0 - (double)num) / 100.0);
			if ((double)num2 < 0.019999999552965164)
			{
				num2 = 0.02f;
			}
			return (int)((double)damage * (double)num2);
		}
	}
	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 sealed class LookCache<T> where T : class
	{
		private readonly Dictionary<string, T> made = new Dictionary<string, T>();

		private readonly Func<T, bool> alive;

		public LookCache(Func<T, bool> alive)
		{
			this.alive = alive;
		}

		public T Get(string key, Func<string, T> make)
		{
			if (made.TryGetValue(key, out var value) && alive(value))
			{
				return value;
			}
			value = make(key);
			if (value == null)
			{
				made.Remove(key);
			}
			else
			{
				made[key] = value;
			}
			return value;
		}

		public T[] For(PartMesh part, Func<string, T> make)
		{
			T[] array = new T[part.SubMeshes.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Get(part.SubMeshes[i].Material, make);
			}
			return array;
		}
	}
	internal readonly struct PolypRules
	{
		public const float AbsoluteMinInterval = 0.05f;

		public readonly float BaseInterval;

		public readonly float MinInterval;

		public readonly float Duration;

		public readonly float Range;

		public readonly float DamageFraction;

		public readonly int MinimumWeaponDamage;

		public readonly float SpawnDistance;

		public readonly float SpawnSpreadDegrees;

		public readonly int MaxTurrets;

		public readonly float StationDebounce;

		public readonly float RetriggerSeconds;

		public readonly bool CountChests;

		public readonly bool CountShrines;

		public readonly bool CountRepairs;

		public readonly bool CountFreeStations;

		public readonly bool CountTraderBuys;

		public readonly bool CountRevives;

		public readonly bool CountModded;

		public readonly bool CountPickups;

		public readonly bool CountDoors;

		public readonly bool HitNeutral;

		public static PolypRules Defaults => new PolypRules(0.75f, 0.08f, 30f, 20f, 1f, 10, 2.5f, 28f, 4, 1f, 0.25f, countChests: true, countShrines: true, countRepairs: true, countFreeStations: false, countTraderBuys: true, countRevives: true, countModded: true, countPickups: false, countDoors: false, hitNeutral: false);

		public PolypRules(float baseInterval, float minInterval, float duration, float range, float damageFraction, int minimumWeaponDamage, float spawnDistance, float spawnSpreadDegrees, int maxTurrets, float stationDebounce, float retriggerSeconds, bool countChests, bool countShrines, bool countRepairs, bool countFreeStations, bool countTraderBuys, bool countRevives, bool countModded, bool countPickups, bool countDoors, bool hitNeutral)
		{
			BaseInterval = ((baseInterval > 0f && !float.IsNaN(baseInterval)) ? baseInterval : 0.75f);
			MinInterval = ((minInterval > 0f && !float.IsNaN(minInterval)) ? minInterval : 0.05f);
			Duration = ((duration >= 0f && !float.IsNaN(duration)) ? duration : 30f);
			Range = ((range >= 0f && !float.IsNaN(range)) ? range : 0f);
			DamageFraction = ((damageFraction >= 0f && !float.IsNaN(damageFraction)) ? damageFraction : 0f);
			MinimumWeaponDamage = ((minimumWeaponDamage >= 0) ? minimumWeaponDamage : 0);
			SpawnDistance = ((spawnDistance >= 0f && !float.IsNaN(spawnDistance)) ? spawnDistance : 0f);
			SpawnSpreadDegrees = ((spawnSpreadDegrees >= 0f && !float.IsNaN(spawnSpreadDegrees)) ? spawnSpreadDegrees : 0f);
			MaxTurrets = ((maxTurrets >= 0) ? maxTurrets : 0);
			StationDebounce = ((stationDebounce >= 0f && !float.IsNaN(stationDebounce)) ? stationDebounce : 0f);
			RetriggerSeconds = ((retriggerSeconds >= 0f && !float.IsNaN(retriggerSeconds)) ? retriggerSeconds : 0f);
			CountChests = countChests;
			CountShrines = countShrines;
			CountRepairs = countRepairs;
			CountFreeStations = countFreeStations;
			CountTraderBuys = countTraderBuys;
			CountRevives = countRevives;
			CountModded = countModded;
			CountPickups = countPickups;
			CountDoors = countDoors;
			HitNeutral = hitNeutral;
		}

		public float AttackSpeedFor(int stacks)
		{
			if (stacks > 0)
			{
				return stacks;
			}
			return 0f;
		}

		public float IntervalFor(int stacks)
		{
			float num = AttackSpeedFor(stacks);
			if (num <= 0f)
			{
				return BaseInterval;
			}
			float val = BaseInterval / num;
			return Math.Max(Math.Max(0.05f, Math.Min(MinInterval, BaseInterval)), val);
		}

		public int ShotDamage(int heldAttack, bool heldIsBow, int arrowAttack, float strength)
		{
			if (DamageFraction <= 0f)
			{
				return 0;
			}
			int num = (heldIsBow ? arrowAttack : heldAttack);
			if (num < MinimumWeaponDamage)
			{
				num = MinimumWeaponDamage;
			}
			if (num < 1)
			{
				num = 1;
			}
			float num2 = ((strength > 0f && !float.IsNaN(strength)) ? strength : 1f);
			return Math.Max(1, RoundToInt((float)num * num2 * DamageFraction));
		}

		public string Description()
		{
			return string.Format(CultureInfo.InvariantCulture, "Activating an interactable spawns a Squid Turret for {0:0.#}s. It shoots the nearest enemy every {1:0.##}s for {2:0.#}% of your weapon damage. Each extra stack adds +100% attack speed.", Duration, BaseInterval, DamageFraction * 100f);
		}

		private static int RoundToInt(float value)
		{
			return (int)Math.Round(value, MidpointRounding.ToEven);
		}
	}
	internal enum RegistrationDecision
	{
		Add,
		AlreadyOurs,
		NameTaken,
		IdGap
	}
	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;
		}

		public static RegistrationDecision Decide(bool nameExists, bool existingIsOurs, int id, int countBefore)
		{
			if (!nameExists)
			{
				if (id > countBefore)
				{
					return RegistrationDecision.IdGap;
				}
				return RegistrationDecision.Add;
			}
			if (!existingIsOurs)
			{
				return RegistrationDecision.NameTaken;
			}
			return RegistrationDecision.AlreadyOurs;
		}
	}
	internal static class PowerupNames
	{
		public static T[] Without<T>(T[] items, T remove) where T : class
		{
			if (items == null || remove == null)
			{
				return items;
			}
			int num = 0;
			for (int i = 0; i < items.Length; i++)
			{
				if (items[i] != remove)
				{
					num++;
				}
			}
			if (num == items.Length)
			{
				return items;
			}
			T[] array = new T[num];
			int num2 = 0;
			for (int j = 0; j < items.Length; j++)
			{
				if (items[j] != remove)
				{
					array[num2++] = items[j];
				}
			}
			return array;
		}

		public static T[] With<T>(T[] items, T add) where T : class
		{
			if (add == null)
			{
				return items;
			}
			if (items == null)
			{
				return new T[1] { add };
			}
			for (int i = 0; i < items.Length; i++)
			{
				if (items[i] == add)
				{
					return items;
				}
			}
			T[] array = new T[items.Length + 1];
			for (int j = 0; j < items.Length; j++)
			{
				array[j] = items[j];
			}
			array[items.Length] = add;
			return array;
		}
	}
	internal sealed class Retrigger
	{
		private readonly Dictionary<long, float> last = new Dictionary<long, float>();

		private readonly HashSet<int> claimed = new HashSet<int>();

		public bool Allow(InteractKind kind, int instanceId, float now, float stationDebounce, float retriggerSeconds)
		{
			float num = (Activation.UsesStationDebounce(kind) ? stationDebounce : retriggerSeconds);
			long key = ((long)kind << 32) ^ (uint)instanceId;
			if (last.TryGetValue(key, out var value) && now - value < num)
			{
				return false;
			}
			last[key] = now;
			return true;
		}

		public bool ClaimOnce(int instanceId)
		{
			return claimed.Add(instanceId);
		}

		public void Clear()
		{
			last.Clear();
			claimed.Clear();
		}
	}
	internal static class Roster
	{
		public static int EvictIndex(int count, int max)
		{
			if (max <= 0 || count < max)
			{
				return -1;
			}
			return 0;
		}
	}
	internal static class ShotOrder
	{
		public static bool TryFire(TurretCandidate[] candidates, float originX, float originY, float originZ, float range, bool hitNeutral, Func<int> damage, out int targetIndex, out int shotDamage)
		{
			targetIndex = -1;
			shotDamage = 0;
			if (candidates == null || damage == null)
			{
				return false;
			}
			targetIndex = TargetPick.Pick(candidates, originX, originY, originZ, range, hitNeutral);
			if (targetIndex < 0)
			{
				return false;
			}
			int num = damage();
			if (num <= 0)
			{
				targetIndex = -1;
				return false;
			}
			if (HostDamage.AfterDefense(num, candidates[targetIndex].Defense) <= 0)
			{
				TurretCandidate[] array = new TurretCandidate[candidates.Length];
				for (int i = 0; i < candidates.Length; i++)
				{
					TurretCandidate turretCandidate = candidates[i];
					if (HostDamage.AfterDefense(num, turretCandidate.Defense) <= 0)
					{
						turretCandidate = new TurretCandidate(turretCandidate.Id, turretCandidate.X, turretCandidate.Y, turretCandidate.Z, alive: false, turretCandidate.Neutral, turretCandidate.Defense);
					}
					array[i] = turretCandidate;
				}
				targetIndex = TargetPick.Pick(array, originX, originY, originZ, range, hitNeutral);
				if (targetIndex < 0)
				{
					return false;
				}
			}
			shotDamage = num;
			return true;
		}
	}
	internal static class SpawnRing
	{
		public const float FrontHalfDegrees = 60f;

		public const int PlacementAttempts = 5;

		public static void Offset(float forwardX, float forwardZ, int index, float distance, float spreadDegrees, out float x, out float z)
		{
			if (index < 0)
			{
				index = 0;
			}
			if (distance < 0f || float.IsNaN(distance))
			{
				distance = 0f;
			}
			if (spreadDegrees < 0f || float.IsNaN(spreadDegrees))
			{
				spreadDegrees = 0f;
			}
			float num = (float)Math.Sqrt(forwardX * forwardX + forwardZ * forwardZ);
			if (num < 0.0001f || float.IsNaN(num))
			{
				forwardX = 0f;
				forwardZ = 1f;
				num = 1f;
			}
			forwardX /= num;
			forwardZ /= num;
			float num2 = 0f;
			if (index > 0)
			{
				int num3 = (index + 1) / 2;
				num2 = (float)(((index % 2 == 1) ? 1 : (-1)) * num3) * spreadDegrees;
			}
			float num4 = num2 * (MathF.PI / 180f);
			float num5 = (float)Math.Cos(num4);
			float num6 = (float)Math.Sin(num4);
			x = (forwardX * num5 - forwardZ * num6) * distance;
			z = (forwardX * num6 + forwardZ * num5) * distance;
		}

		public static int SlotsInFront(float spreadDegrees)
		{
			if (!(spreadDegrees > 0f) || float.IsNaN(spreadDegrees))
			{
				return 1;
			}
			int num = (int)Math.Floor(60f / spreadDegrees);
			if (num < 1)
			{
				return 1;
			}
			return num * 2 + 1;
		}

		public static int FanWidth(float spreadDegrees, int maxTurrets)
		{
			int num = SlotsInFront(spreadDegrees);
			if (maxTurrets <= 0)
			{
				return num;
			}
			if (maxTurrets >= num)
			{
				return num;
			}
			return maxTurrets;
		}

		public static int NextSlot(IReadOnlyList<int> occupied, int maxTurrets, float spreadDegrees)
		{
			int num = FanWidth(spreadDegrees, maxTurrets);
			if (num < 1)
			{
				num = 1;
			}
			int[] array = new int[num];
			if (occupied != null)
			{
				for (int i = 0; i < occupied.Count; i++)
				{
					int num2 = occupied[i];
					if ((uint)num2 < (uint)num)
					{
						array[num2]++;
					}
				}
			}
			int num3 = 0;
			for (int j = 1; j < num; j++)
			{
				if (array[j] < array[num3])
				{
					num3 = j;
				}
			}
			return num3;
		}

		public static void Candidate(float forwardX, float forwardZ, int slot, float distance, float spreadDegrees, int attempt, out float x, out float z)
		{
			Offset(forwardX, forwardZ, slot, distance, spreadDegrees, out x, out z);
			float num = (float)Math.Sqrt(forwardX * forwardX + forwardZ * forwardZ);
			float num2;
			float num3;
			if (num < 0.0001f || float.IsNaN(num))
			{
				num2 = 0f;
				num3 = 1f;
			}
			else
			{
				num2 = forwardX / num;
				num3 = forwardZ / num;
			}
			float num4 = 0f;
			float num5 = 0f;
			if (attempt == 1)
			{
				num4 = 1.5f;
			}
			else if (attempt == 2)
			{
				num5 = 1.25f;
			}
			else if (attempt == 3)
			{
				num5 = -1.25f;
			}
			else if (attempt >= 4)
			{
				num4 = 1.5f;
				num5 = 1.25f;
			}
			x += num2 * num4 - num3 * num5;
			z += num3 * num4 + num2 * num5;
		}

		public static int FirstOpen(bool[] onGround, bool[] blocked)
		{
			if (onGround == null || blocked == null)
			{
				return -1;
			}
			int num = ((onGround.Length < blocked.Length) ? onGround.Length : blocked.Length);
			for (int i = 0; i < num; i++)
			{
				if (onGround[i] && !blocked[i])
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal static class StackCount
	{
		public static int Read(int[] counts, int id)
		{
			if (counts == null || id < 0 || id >= counts.Length)
			{
				return 0;
			}
			return Math.Max(0, counts[id]);
		}
	}
	internal readonly struct TurretCandidate
	{
		public readonly int Id;

		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly bool Alive;

		public readonly bool Neutral;

		public readonly float Defense;

		public TurretCandidate(int id, float x, float y, float z, bool alive, bool neutral, float defense = 0f)
		{
			Id = id;
			X = x;
			Y = y;
			Z = z;
			Alive = alive;
			Neutral = neutral;
			Defense = defense;
		}
	}
	internal static class TargetPick
	{
		public static int Pick(TurretCandidate[] candidates, float originX, float originY, float originZ, float range, bool hitNeutral)
		{
			if (candidates == null || range < 0f)
			{
				return -1;
			}
			float num = range * range;
			int num2 = -1;
			float num3 = 0f;
			int num4 = 0;
			for (int i = 0; i < candidates.Length; i++)
			{
				TurretCandidate turretCandidate = candidates[i];
				if (turretCandidate.Alive && (!turretCandidate.Neutral || hitNeutral))
				{
					float num5 = turretCandidate.X - originX;
					float num6 = turretCandidate.Y - originY;
					float num7 = turretCandidate.Z - originZ;
					float num8 = num5 * num5 + num6 * num6 + num7 * num7;
					if (!(num8 > num) && (num2 < 0 || (!(num8 > num3) && (num8 != num3 || turretCandidate.Id < num4))))
					{
						num2 = i;
						num3 = num8;
						num4 = turretCandidate.Id;
					}
				}
			}
			return num2;
		}
	}
	internal enum TurretPhase
	{
		Idle,
		Fire,
		Expired
	}
	internal struct TurretClock
	{
		public float Age;

		public float UntilShot;

		public TurretPhase Advance(float dt, float interval, float duration)
		{
			if (dt < 0f || float.IsNaN(dt))
			{
				dt = 0f;
			}
			if (interval < 0.01f || float.IsNaN(interval))
			{
				interval = 0.01f;
			}
			if (duration < 0f || float.IsNaN(duration))
			{
				duration = 0f;
			}
			Age += dt;
			if (Age >= duration)
			{
				return TurretPhase.Expired;
			}
			UntilShot -= dt;
			if (UntilShot > 0f)
			{
				return TurretPhase.Idle;
			}
			UntilShot += interval;
			if (UntilShot < 0f)
			{
				UntilShot = interval;
			}
			return TurretPhase.Fire;
		}
	}
	internal static class TurretLook
	{
		public const float BodyScale = 1.42f;

		public const float BoltSeconds = 0.18f;

		public const float BoltThickness = 0.16f;

		public const float FlashSeconds = 0.12f;

		public const float FlashSize = 0.4f;

		public const float ExpireSeconds = 0.22f;

		public const float YawDegreesPerSecond = 420f;

		public static float StandingHeight(float meshHeight)
		{
			return meshHeight * 1.42f;
		}
	}
	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);
		}

		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 PartMesh
	{
		public readonly string Object;

		public readonly Vec3 Pivot;

		public readonly Vec3[] Positions;

		public readonly Vec3[] Normals;

		public readonly IReadOnlyList<SubMesh> SubMeshes;

		public int TriangleCount => Positions.Length / 3;

		public PartMesh(string obj, Vec3 pivot, Vec3[] positions, Vec3[] normals, IReadOnlyList<SubMesh> subMeshes)
		{
			Object = obj;
			Pivot = pivot;
			Positions = positions;
			Normals = normals;
			SubMeshes = subMeshes;
		}

		public Vec3 Centroid()
		{
			if (Positions.Length == 0)
			{
				return Pivot;
			}
			double num = 0.0;
			double num2 = 0.0;
			double num3 = 0.0;
			for (int i = 0; i < Positions.Length; i++)
			{
				num += (double)Positions[i].X;
				num2 += (double)Positions[i].Y;
				num3 += (double)Positions[i].Z;
			}
			float num4 = Positions.Length;
			return new Vec3((float)(num / (double)num4), (float)(num2 / (double)num4), (float)(num3 / (double)num4)) + Pivot;
		}
	}
	internal sealed class TurretModel
	{
		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>>();
		}

		private struct PartEntry
		{
			public string Object;

			public Vec3 Pivot;

			public int Tris;
		}

		public readonly string Name;

		public readonly IReadOnlyList<PartMesh> Parts;

		public readonly IReadOnlyDictionary<string, MaterialSpec> Materials;

		public int TriangleCount
		{
			get
			{
				int num = 0;
				foreach (PartMesh part in Parts)
				{
					num += part.TriangleCount;
				}
				return num;
			}
		}

		public TurretModel(string name, IReadOnlyList<PartMesh> parts, IReadOnlyDictionary<string, MaterialSpec> materials)
		{
			Name = name;
			Parts = parts;
			Materials = materials;
		}

		public PartMesh Find(string objectName)
		{
			foreach (PartMesh part in Parts)
			{
				if (part.Object == objectName)
				{
					return part;
				}
			}
			return null;
		}

		public static Vec3 ObjToModel(Vec3 v)
		{
			return new Vec3(0f - v.X, v.Y, v.Z);
		}

		public static TurretModel Parse(string objText, string partsJson)
		{
			if (!(JsonReader.Parse(partsJson) is Dictionary<string, object> obj))
			{
				throw new FormatException("parts.json: top level is not an object.");
			}
			string text = Str(obj, "name");
			Dictionary<string, MaterialSpec> materials = ReadMaterials(Obj(obj, "materials"));
			List<PartEntry> list = new List<PartEntry>();
			foreach (object item in Arr(obj, "parts"))
			{
				list.Add(ReadPart(item));
			}
			Dictionary<string, ObjObject> dictionary = ReadObj(objText, text);
			List<PartMesh> list2 = new List<PartMesh>();
			HashSet<string> hashSet = new HashSet<string>();
			foreach (PartEntry item2 in list)
			{
				if (!dictionary.TryGetValue(item2.Object, out var value))
				{
					throw new FormatException(text + ": parts.json part " + item2.Object + " is missing from the OBJ.");
				}
				hashSet.Add(item2.Object);
				list2.Add(BuildPart(text, item2, value, materials));
			}
			foreach (string key in dictionary.Keys)
			{
				if (!hashSet.Contains(key))
				{
					throw new FormatException(text + ": OBJ object " + key + " is not in parts.json.");
				}
			}
			TurretModel turretModel = new TurretModel(text, list2, materials);
			int num = (int)Num(obj, "tris");
			if (turretModel.TriangleCount != num)
			{
				throw new FormatException($"{text}: OBJ has {turretModel.TriangleCount} triangles, parts.json says {num}.");
			}
			return turretModel;
		}

		private static PartMesh BuildPart(string name, PartEntry entry, ObjObject obj, Dictionary<string, MaterialSpec> materials)
		{
			int num = obj.Positions.Count / 3;
			if (num != entry.Tris)
			{
				throw new FormatException($"{name}: part {entry.Object} has {num} triangles, parts.json says {entry.Tris}.");
			}
			Vec3[] array = new Vec3[obj.Positions.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = obj.Positions[i] - entry.Pivot;
			}
			List<SubMesh> list = new List<SubMesh>();
			foreach (string item in obj.MaterialOrder)
			{
				if (!materials.ContainsKey(item))
				{
					throw new FormatException(name + ": part " + entry.Object + " uses undefined material '" + item + "'.");
				}
				list.Add(new SubMesh(item, obj.Triangles[item].ToArray()));
			}
			return new PartMesh(entry.Object, entry.Pivot, array, obj.Normals.ToArray(), list);
		}

		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":
					list.Add(ObjToModel(ReadVec(array2, name, i)));
					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("parts.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 PartEntry ReadPart(object raw)
		{
			if (!(raw is Dictionary<string, object> obj))
			{
				throw new FormatException("parts.json: a part is not an object.");
			}
			return new PartEntry
			{
				Object = Str(obj, "object"),
				Pivot = ReadVec3(obj, "pivot_unity"),
				Tris = (int)Num(obj, "tris")
			};
		}

		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("parts.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("parts.json: missing '" + key + "'.");
			}
			return value;
		}

		private static string Str(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as string) ?? throw new FormatException("parts.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("parts.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("parts.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("parts.json: '" + key + "' is not an object.");
		}
	}
	internal static class PickupArt
	{
		public const string ObjResource = "MuckSquidPolyp.pickup.obj";

		public const string JsonResource = "MuckSquidPolyp.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("MuckSquidPolyp.pickup.obj"), Read("MuckSquidPolyp.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)
			{
				SquidPolypPlugin.Log.LogWarning((object)("Squid Polyp 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
				{
					SquidPolypPlugin.Log.LogWarning((object)"No Blue powerup to borrow a pickup model from; dropped Squid Polyps 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 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 static class SquidPolypArt
	{
		public const string ObjResource = "MuckSquidPolyp.turret.obj";

		public const string PartsResource = "MuckSquidPolyp.turret.parts.json";

		private static TurretModel _model;

		private static bool _failed;

		public static TurretModel Model
		{
			get
			{
				if (_model == null && !_failed)
				{
					try
					{
						_model = TurretModel.Parse(Read("MuckSquidPolyp.turret.obj"), Read("MuckSquidPolyp.turret.parts.json"));
					}
					catch (Exception ex)
					{
						_failed = true;
						SquidPolypPlugin.Log.LogError((object)("Squid Polyp could not read the turret model. A sphere is used instead. " + ex.Message));
					}
				}
				return _model;
			}
		}

		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();
		}
	}
	internal sealed class SquidPolypConfig
	{
		public readonly ConfigEntry<float> BaseInterval;

		public readonly ConfigEntry<float> MinInterval;

		public readonly ConfigEntry<float> Duration;

		public readonly ConfigEntry<float> Range;

		public readonly ConfigEntry<float> DamageFraction;

		public readonly ConfigEntry<int> MinimumWeaponDamage;

		public readonly ConfigEntry<float> SpawnDistance;

		public readonly ConfigEntry<float> SpawnSpreadDegrees;

		public readonly ConfigEntry<int> MaxTurrets;

		public readonly ConfigEntry<float> StationDebounce;

		public readonly ConfigEntry<float> RetriggerSeconds;

		public readonly ConfigEntry<bool> CountChests;

		public readonly ConfigEntry<bool> CountShrines;

		public readonly ConfigEntry<bool> CountRepairs;

		public readonly ConfigEntry<bool> CountFreeStations;

		public readonly ConfigEntry<bool> CountTraderBuys;

		public readonly ConfigEntry<bool> CountRevives;

		public readonly ConfigEntry<bool> CountModded;

		public readonly ConfigEntry<bool> CountPickups;

		public readonly ConfigEntry<bool> CountDoors;

		public readonly ConfigEntry<bool> HitNeutralMobs;

		public readonly ConfigEntry<bool> ShowTurret;

		public readonly ConfigEntry<bool> ShowBolt;

		public readonly ConfigEntry<KeyboardShortcut> GiveSquidPolypKey;

		public readonly ConfigEntry<KeyboardShortcut> SpawnTurretKey;

		public readonly ConfigEntry<bool> VerboseLogging;

		public SquidPolypConfig(ConfigFile cfg)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected O, but got Unknown
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Expected O, but got Unknown
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Expected O, but got Unknown
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Expected O, but got Unknown
			//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			BaseInterval = cfg.Bind<float>("Squid Polyp", "BaseInterval", 0.75f, new ConfigDescription("Seconds between shots at 1 stack (100% attack speed). 0.75 is the Risk of Rain 2 turret. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>()));
			MinInterval = cfg.Bind<float>("Squid Polyp", "MinInterval", 0.08f, new ConfigDescription("Shortest seconds between shots, however many stacks you hold. Stops a tall stack from sending a packet every frame. Not from RoR2. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			Duration = cfg.Bind<float>("Squid Polyp", "Duration", 30f, new ConfigDescription("Seconds a turret stays. 30 is RoR2. Stacks do not add time. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), Array.Empty<object>()));
			Range = cfg.Bind<float>("Squid Polyp", "Range", 20f, new ConfigDescription("Meters. The turret shoots the nearest living enemy inside this range. A Muck choice. RoR2 uses a projectile. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 80f), Array.Empty<object>()));
			DamageFraction = cfg.Bind<float>("Squid Polyp", "DamageFraction", 1f, new ConfigDescription("Shot damage as a multiple of the held weapon (a bow uses the arrow), after strength. 1 is 100% of that swing. RoR2's shot is 500% of the turret's own 4 base damage, which Muck has no equivalent for. Set this to 5 to chase that coefficient. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			MinimumWeaponDamage = cfg.Bind<int>("Squid Polyp", "MinimumWeaponDamage", 10, new ConfigDescription("Weapon damage used when the held attack is lower. An empty hand still shoots. Applied before strength. A Muck choice. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 500), Array.Empty<object>()));
			SpawnDistance = cfg.Bind<float>("Squid Polyp", "SpawnDistance", 2.5f, new ConfigDescription("Meters in front of the player. The turret is then dropped to the ground. A Muck choice. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 12f), Array.Empty<object>()));
			SpawnSpreadDegrees = cfg.Bind<float>("Squid Polyp", "SpawnSpreadDegrees", 28f, new ConfigDescription("Degrees between neighboring turrets. The fan stays within 60 degrees of where you are looking. A Muck choice. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 90f), Array.Empty<object>()));
			MaxTurrets = cfg.Bind<int>("Squid Polyp", "MaxTurrets", 4, new ConfigDescription("Most turrets this machine keeps. The oldest one is removed when a new one would pass the cap. 0 means no cap. A Muck choice, for the packet budget. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>()));
			StationDebounce = cfg.Bind<float>("Squid Polyp", "StationDebounce", 1f, new ConfigDescription("Seconds before the same workbench, furnace, or storage chest can spawn another turret. Default 1. Used only when CountFreeStations is on. A Muck choice. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			RetriggerSeconds = cfg.Bind<float>("Squid Polyp", "RetriggerSeconds", 0.25f, new ConfigDescription("Seconds before the same chest, shrine, repair, or trade row can spawn a second turret. Default 0.25. Guards a double callback on one machine. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			CountChests = cfg.Bind<bool>("What counts", "CountChests", true, "Loot chests (LootContainerInteract). The open runs once on the player who paid. Not synced.");
			CountShrines = cfg.Bind<bool>("What counts", "CountShrines", true, "Battle totems (ShrineInteractable). One turret per totem per player who starts it. Not synced.");
			CountRepairs = cfg.Bind<bool>("What counts", "CountRepairs", true, "Repair stations (RepairInteract), once the repair is accepted. Not synced.");
			CountFreeStations = cfg.Bind<bool>("What counts", "CountFreeStations", false, "Opening a workbench, anvil, fletching table, furnace, cauldron, or storage chest. Default false. These cost nothing, so on means a turret every StationDebounce seconds. Not synced.");
			CountTraderBuys = cfg.Bind<bool>("What counts", "CountTraderBuys", true, "Buying from a woodman trader (TradeUi.BuySell), when coins are spent. Default true. Opening the trade window and selling do not count. Not synced.");
			CountRevives = cfg.Bind<bool>("What counts", "CountRevives", true, "A successful grave revive or respawn-totem revive, on the reviver's machine. Not synced.");
			CountModded = cfg.Bind<bool>("What counts", "CountModded", true, "Shrine of Chance, when that mod is installed and the roll is actually paid. Not synced.");
			CountPickups = cfg.Bind<bool>("What counts", "CountPickups", false, "Ground items (PickupInteract). Off. Those are pickups, not RoR2 interactables. Not synced.");
			CountDoors = cfg.Bind<bool>("What counts", "CountDoors", false, "Doors. Off. A door toggle is not a purchase. Not synced.");
			HitNeutralMobs = cfg.Bind<bool>("What counts", "HitNeutralMobs", false, "Also shoot cows and other neutral animals. Not synced.");
			ShowTurret = cfg.Bind<bool>("Visuals", "ShowTurret", true, "Draw the turret on this machine. Other players do not see it. Not synced.");
			ShowBolt = cfg.Bind<bool>("Visuals", "ShowBolt", true, "Draw a short local bolt from the eye to the target. Damage is instant. Not synced.");
			GiveSquidPolypKey = cfg.Bind<KeyboardShortcut>("Debug", "GiveSquidPolypKey", KeyboardShortcut.Empty, "Gives one local stack. For example F8. Empty means off.");
			SpawnTurretKey = cfg.Bind<KeyboardShortcut>("Debug", "SpawnTurretKey", KeyboardShortcut.Empty, "Spawns one turret in front of you, if you hold at least one stack. Empty means off.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each spawn and each shot.");
			cfg.SettingChanged += delegate(object _, SettingChangedEventArgs args)
			{
				if (UpdatesCard(args.ChangedSetting.Definition.Section, args.ChangedSetting.Definition.Key))
				{
					SquidPolypPowerup.ApplyDescription(Rules().Description());
				}
			};
		}

		public PolypRules Rules()
		{
			return new PolypRules(BaseInterval.Value, MinInterval.Value, Duration.Value, Range.Value, DamageFraction.Value, MinimumWeaponDamage.Value, SpawnDistance.Value, SpawnSpreadDegrees.Value, MaxTurrets.Value, StationDebounce.Value, RetriggerSeconds.Value, CountChests.Value, CountShrines.Value, CountRepairs.Value, CountFreeStations.Value, CountTraderBuys.Value, CountRevives.Value, CountModded.Value, CountPickups.Value, CountDoors.Value, HitNeutralMobs.Value);
		}

		public string Summary()
		{
			PolypRules polypRules = Rules();
			return string.Format(CultureInfo.InvariantCulture, "BaseInterval={0} MinInterval={1} Duration={2} Range={3} DamageFraction={4} MinimumWeaponDamage={5} SpawnDistance={6} MaxTurrets={7} Chests={8} Shrines={9} Repairs={10} FreeStations={11} TraderBuys={12} Revives={13} Modded={14} Pickups={15} Doors={16}", Num(polypRules.BaseInterval), Num(polypRules.MinInterval), Num(polypRules.Duration), Num(polypRules.Range), Num(polypRules.DamageFraction), polypRules.MinimumWeaponDamage, Num(polypRules.SpawnDistance), polypRules.MaxTurrets, polypRules.CountChests, polypRules.CountShrines, polypRules.CountRepairs, polypRules.CountFreeStations, polypRules.CountTraderBuys, polypRules.CountRevives, polypRules.CountModded, polypRules.CountPickups, polypRules.CountDoors);
		}

		internal static bool UpdatesCard(string section, string key)
		{
			if (section == "Squid Polyp")
			{
				if (!(key == "BaseInterval") && !(key == "Duration"))
				{
					return key == "DamageFraction";
				}
				return true;
			}
			return false;
		}

		private static string Num(float value)
		{
			return value.ToString("0.###", CultureInfo.InvariantCulture);
		}
	}
	[BepInPlugin("evan.muck.squidpolyp", "Squid Polyp", "0.2.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class SquidPolypPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.squidpolyp";

		public const string Name = "Squid Polyp";

		public const string Version = "0.2.2";

		internal static ManualLogSource Log;

		internal static SquidPolypConfig Cfg;

		private void Awake()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new SquidPolypConfig(((BaseUnityPlugin)this).Config);
			try
			{
				SquidPolypPowerup.Create();
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("{0} disabled: could not create the powerup. The powerup will not drop. {1}", "Squid Polyp", arg));
				return;
			}
			Harmony harmony = new Harmony("evan.muck.squidpolyp");
			if (!TryPatch(harmony, typeof(UnnumberSquidPolypPatch)) || !TryPatch(harmony, typeof(RegisterSquidPolypPatch)))
			{
				Log.LogError((object)"Squid Polyp did not register. Registration failed, so the powerup will not drop.");
				return;
			}
			TryPatch(harmony, typeof(LootChestPatch));
			TryPatch(harmony, typeof(BattleShrinePatch));
			TryPatch(harmony, typeof(RepairPatch));
			TryPatch(harmony, typeof(CraftingStationPatch));
			TryPatch(harmony, typeof(StorageStationPatch));
			TryPatch(harmony, typeof(TraderPatch));
			TryPatch(harmony, typeof(RevivePatch));
			TryPatch(harmony, typeof(GroundPickupPatch));
			TryPatch(harmony, typeof(DoorPatch));
			if (!ModdedShrinePatch.TryApply(harmony))
			{
				Log.LogInfo((object)"Squid Polyp did not find Shrine of Chance. That shrine will not spawn turrets.");
			}
			SquidPolypRunner.Ensure();
			Log.LogInfo((object)("Squid Polyp 0.2.2 loaded. Every player needs this mod and the same other powerup mods. The turret is simulated on the player who activated the interactable. Config: " + Cfg.Summary()));
		}

		private static bool TryPatch(Harmony harmony, Type patchClass)
		{
			try
			{
				harmony.CreateClassProcessor(patchClass).Patch();
				return true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("{0} failed to patch {1}: {2}", "Squid Polyp", patchClass.Name, arg));
				return false;
			}
		}
	}
	internal static class SquidPolypPowerup
	{
		public const string PowerupName = "Squid Polyp";

		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 = "Squid Polyp";
			((Object)obj).name = "Squid Polyp";
			obj.description = SquidPolypPlugin.Cfg.Rules().Description();
			obj.tier = (PowerTier)1;
			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
			{
				if (_counts == null)
				{
					_counts = AccessTools.FieldRefAccess<PowerupInventory, int[]>("powerups");
				}
				return StackCount.Read(_counts.Invoke(PowerupInventory.Instance), Id);
			}
			catch (Exception arg)
			{
				_stacksFailure = true;
				SquidPolypPlugin.Log.LogError((object)$"Squid Polyp 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)
			{
				SquidPolypPlugin.Log.LogWarning((object)"Can't give Squid Polyp: 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)
			{
				SquidPolypPlugin.Log.LogWarning((object)"Can't give Squid Polyp: 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)
			{
				SquidPolypPlugin.Log.LogError((object)$"Can't give Squid Polyp: failed to read the powerup counts. {arg}");
				return;
			}
			if (array == null || Id >= array.Length)
			{
				SquidPolypPlugin.Log.LogWarning((object)"Can't give Squid Polyp: the powerup count array is missing or too short.");
				return;
			}
			array[Id]++;
			PowerupUI.Instance.AddPowerup(Id);
			PlayerStatus.Instance.UpdateStats();
			SquidPolypPlugin.Log.LogInfo((object)$"Debug: Squid Polyp stacks now {array[Id]} (local only).");
		}

		private static Sprite LoadIcon()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Assembly assembly = typeof(SquidPolypPowerup).Assembly;
				using Stream stream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".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 = "SquidPolypIcon";
				((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 = "SquidPolypIcon";
				((Object)obj).hideFlags = (HideFlags)32;
				return obj;
			}
			catch (Exception arg)
			{
				SquidPolypPlugin.Log.LogError((object)$"Failed to load Squid Polyp icon: {arg}");
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class UnnumberSquidPolypPatch
	{
		[HarmonyPriority(600)]
		private static void Prefix(ItemManager __instance)
		{
			if (!((Object)(object)SquidPolypPowerup.Definition == (Object)null))
			{
				__instance.powerupsBlue = PowerupNames.Without(__instance.powerupsBlue, SquidPolypPowerup.Definition);
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class RegisterSquidPolypPatch
	{
		[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",
			"evan.muck.pennib", "evan.muck.garlic", "evan.muck.bustlingfungus"
		})]
		private static void Postfix(ItemManager __instance)
		{
			try
			{
				Register(__instance);
			}
			catch (Exception arg)
			{
				SquidPolypPowerup.Id = -1;
				SquidPolypPlugin.Log.LogError((object)$"Squid Polyp could not register; turrets will not spawn. {arg}");
			}
		}

		private static void Register(ItemManager manager)
		{
			SquidPolypPowerup.Id = -1;
			Powerup definition = SquidPolypPowerup.Definition;
			if ((Object)(object)definition == (Object)null)
			{
				return;
			}
			Dictionary<int, Powerup> allPowerups = manager.allPowerups;
			Dictionary<string, int> stringToPowerupId = manager.stringToPowerupId;
			if (allPowerups == null || stringToPowerupId == null || manager.powerupsBlue == null)
			{
				SquidPolypPlugin.Log.LogWarning((object)"Squid Polyp registration skipped: the powerup table is not ready.");
				return;
			}
			PickupArt.Apply(definition, manager.powerupsBlue);
			int value;
			bool flag = stringToPowerupId.TryGetValue("Squid Polyp", out value);
			Powerup value2;
			bool existingIsOurs = flag && allPowerups.TryGetValue(value, out value2) && value2 == definition;
			int num = PowerupIds.Resolve(allPowerups.Keys, flag, value);
			switch (PowerupIds.Decide(flag, existingIsOurs, num, allPowerups.Count))
			{
			case RegistrationDecision.NameTaken:
				SquidPolypPlugin.Log.LogError((object)"The name Squid Polyp is already used by another powerup. This one will not be added to the drop pool.");
				SquidPolypPowerup.Id = -1;
				return;
			case RegistrationDecision.IdGap:
				SquidPolypPlugin.Log.LogError((object)$"Powerup ids are not contiguous (id {num}, count {allPowerups.Count}). Squid Polyp will not register.");
				SquidPolypPowerup.Id = -1;
				return;
			case RegistrationDecision.Add:
				allPowerups.Add(num, definition);
				stringToPowerupId.Add("Squid Polyp", num);
				definition.id = num;
				break;
			case RegistrationDecision.AlreadyOurs:
				definition.id = num;
				break;
			}
			manager.powerupsBlue = PowerupNames.With(manager.powerupsBlue, definition);
			SquidPolypPowerup.Id = num;
			SquidPolypPlugin.Log.LogInfo((object)$"Registered Squid Polyp as Blue powerup id {num} ({allPowerups.Count} powerups total, {manager.powerupsBlue.Length} in the Blue pool).");
			if (SquidPolypPlugin.Cfg.VerboseLogging.Value)
			{
				SquidPolypPlugin.Log.LogInfo((object)("Powerup ids: " + string.Join(", ", from kv in allPowerups
					orderby kv.Key
					select $"{kv.Key}={kv.Value.name}")));
			}
		}
	}
	internal sealed class SquidAgent
	{
		public TurretClock Clock;

		public int Stacks;

		public int Slot;

		public Vector3 Position;

		public TurretView View;
	}
	internal sealed class SquidPolypRunner : MonoBehaviour
	{
		private static SquidPolypRunner _instance;

		private readonly List<SquidAgent> _agents = new List<SquidAgent>();

		private readonly Retrigger _retrigger = new Retrigger();

		private int _errorsLogged;

		private bool _loggedGroundMask;

		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("MuckSquidPolyp")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<SquidPolypRunner>();
			}
		}

		public static void Notify(InteractKind kind, int instanceId, bool opening)
		{
			if (!((Object)(object)_instance == (Object)null))
			{
				_instance.Accept(kind, instanceId, opening);
			}
		}

		public static void DebugSpawn()
		{
			if (!((Object)(object)_instance == (Object)null))
			{
				int num = SquidPolypPowerup.LocalStacks();
				if (num <= 0)
				{
					SquidPolypPlugin.Log.LogWarning((object)"Spawn turret key ignored: Squid Polyp stacks are 0.");
				}
				else
				{
					_instance.Spawn(num, SquidPolypPlugin.Cfg.Rules());
				}
			}
		}

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

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

		private void OnSceneChanged(Scene previous, Scene next)
		{
			ClearAgents();
			_retrigger.Clear();
		}

		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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				KeyboardShortcut value = SquidPolypPlugin.Cfg.GiveSquidPolypKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					SquidPolypPowerup.GiveOneLocal();
				}
				value = SquidPolypPlugin.Cfg.SpawnTurretKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					DebugSpawn();
				}
				PolypRules polypRules = SquidPolypPlugin.Cfg.Rules();
				float deltaTime = Time.deltaTime;
				for (int num = _agents.Count - 1; num >= 0; num--)
				{
					SquidAgent squidAgent = _agents[num];
					switch (squidAgent.Clock.Advance(deltaTime, polypRules.IntervalFor(squidAgent.Stacks), polypRules.Duration))
					{
					case TurretPhase.Expired:
						DestroyAgent(num, fade: true);
						continue;
					case TurretPhase.Fire:
					{
						if (TurretCombat.TryPrepare(squidAgent.Position, out var target, out var damage, out var point))
						{
							if ((Object)(object)squidAgent.View != (Object)null)
							{
								squidAgent.View.Face(point);
							}
							Vector3 muzzle = (((Object)(object)squidAgent.View != (Object)null) ? squidAgent.View.Muzzle : (squidAgent.Position + Vector3.up));
							TurretCombat.Commit(target, damage, point, muzzle, squidAgent.Stacks);
							continue;
						}
						break;
					}
					}
					if ((Object)(object)squidAgent.View != (Object)null && TurretCombat.TryAimPoint(squidAgent.Position, out var point2))
					{
						squidAgent.View.Track(point2, deltaTime);
					}
				}
			}
			catch (Exception arg)
			{
				if (_errorsLogged < 5)
				{
					_errorsLogged++;
					SquidPolypPlugin.Log.LogError((object)$"Squid Polyp tick failed. {arg}");
				}
			}
		}

		private void Accept(InteractKind kind, int instanceId, bool opening)
		{
			PolypRules rules = SquidPolypPlugin.Cfg.Rules();
			if (Activation.Counts(kind, rules, opening))
			{
				int num = SquidPolypPowerup.LocalStacks();
				if (num > 0 && (!((Object)(object)PlayerStatus.Instance != (Object)null) || !PlayerStatus.Instance.IsPlayerDead()) && (kind != InteractKind.BattleShrine || _retrigger.ClaimOnce(instanceId)) && _retrigger.Allow(kind, instanceId, Time.time, rules.StationDebounce, rules.RetriggerSeconds))
				{
					Spawn(num, rules);
				}
			}
		}

		private void Spawn(int stacks, PolypRules rules)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			int num = Roster.EvictIndex(_agents.Count, rules.MaxTurrets);
			if (num >= 0)
			{
				DestroyAgent(num, fade: true);
			}
			if (TryPlace(out var position, out var rotation, out var slot))
			{
				SquidAgent item = new SquidAgent
				{
					Stacks = stacks,
					Position = position,
					Slot = slot,
					View = TurretView.Spawn(position, rotation)
				};
				_agents.Add(item);
				if (SquidPolypPlugin.Cfg.VerboseLogging.Value)
				{
					SquidPolypPlugin.Log.LogInfo((object)$"Squid Polyp spawned a turret. Stacks {stacks}. Alive {_agents.Count}.");
				}
			}
		}

		private bool TryPlace(out Vector3 position, out Quaternion rotation, out int slot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			rotation = Quaternion.identity;
			slot = 0;
			PlayerMovement instance = PlayerMovement.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			Vector3 forward = Vector3.forward;
			if ((Object)(object)instance.playerCam != (Object)null)
			{
				forward = instance.playerCam.forward;
			}
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = Vector3.forward;
			}
			((Vector3)(ref forward)).Normalize();
			PolypRules polypRules = SquidPolypPlugin.Cfg.Rules();
			int[] array = new int[_agents.Count];
			for (int i = 0; i < _agents.Count; i++)
			{
				array[i] = _agents[i].Slot;
			}
			slot = SpawnRing.NextSlot(array, polypRules.MaxTurrets, polypRules.SpawnSpreadDegrees);
			int num = GroundMask();
			bool flag = false;
			for (int j = 0; j < 5; j++)
			{
				SpawnRing.Candidate(forward.x, forward.z, slot, polypRules.SpawnDistance, polypRules.SpawnSpreadDegrees, j, out var x, out var z);
				if (TryGround(((Component)instance).transform.position + new Vector3(x, 0f, z), num, out var point) && !Blocked(point, num, ((Component)instance).transform))
				{
					position = point;
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				SpawnRing.Candidate(forward.x, forward.z, slot, polypRules.SpawnDistance, polypRules.SpawnSpreadDegrees, 1, out var x2, out var z2);
				position = ((Component)instance).transform.position + new Vector3(x2, 0f, z2);
			}
			rotation = Quaternion.LookRotation(forward, Vector3.up);
			return true;
		}

		private int GroundMask()
		{
			int mask = LayerMask.GetMask(new string[2] { "Ground", "Terrain" });
			if (mask == 0)
			{
				mask = LayerMask.GetMask(new string[1] { "Ground" });
			}
			if (mask == 0 && !_loggedGroundMask)
			{
				_loggedGroundMask = true;
				SquidPolypPlugin.Log.LogWarning((object)"Squid Polyp found no Ground layer. Turrets drop along the look direction.");
			}
			return mask;
		}

		private static bool TryGround(Vector3 flat, int mask, out Vector3 point)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			point = flat;
			if (mask == 0)
			{
				return false;
			}
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(flat + Vector3.up * 8f, Vector3.down, ref val, 40f, mask, (QueryTriggerInteraction)1))
			{
				return false;
			}
			point = ((RaycastHit)(ref val)).point;
			return true;
		}

		private static bool Blocked(Vector3 point, int groundMask, Transform player)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (groundMask == 0)
			{
				return false;
			}
			Collider[] array = Physics.OverlapSphere(point + Vector3.up * 0.55f, 0.5f, ~groundMask, (QueryTriggerInteraction)1);
			foreach (Collider val in array)
			{
				if (!((Object)(object)val == (Object)null) && (!((Object)(object)player != (Object)null) || (!((Object)(object)((Component)val).transform == (Object)(object)player) && !((Component)val).transform.IsChildOf(player))))
				{
					return true;
				}
			}
			return false;
		}

		private void DestroyAgent(int index, bool fade)
		{
			SquidAgent squidAgent = _agents[index];
			_agents.RemoveAt(index);
			if (!((Object)(object)squidAgent.View == (Object)null))
			{
				if (fade)
				{
					squidAgent.View.Expire();
				}
				else
				{
					Object.Destroy((Object)(object)((Component)squidAgent.View).gameObject);
				}
			}
		}

		private void ClearAgents()
		{
			for (int num = _agents.Count - 1; num >= 0; num--)
			{
				DestroyAgent(num, fade: false);
			}
		}
	}
	internal static class TurretCombat
	{
		private static readonly List<TurretCandidate> Snapshot = new List<TurretCandidate>();

		private static readonly List<HitableMob> Hitables = new List<HitableMob>();

		private static readonly List<Vector3> Points = new List<Vector3>();

		private static bool _loggedWeapon;

		private static bool _loggedStrength;

		private static int _errorsLogged;

		public static bool TryAimPoint(Vector3 origin, out Vector3 point)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			point = default(Vector3);
			PolypRules polypRules = SquidPolypPlugin.Cfg.Rules();
			if (!Collect(out var candidates))
			{
				return false;
			}
			int num = TargetPick.Pick(candidates, origin.x, origin.y, origin.z, polypRules.Range, polypRules.HitNeutral);
			if (num < 0 || num >= Points.Count)
			{
				return false;
			}
			point = Points[num];
			return true;
		}

		public static bool TryPrepare(Vector3 origin, out HitableMob target, out int damage, out Vector3 point)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			target = null;
			damage = 0;
			point = default(Vector3);
			PolypRules rules = SquidPolypPlugin.Cfg.Rules();
			if (!Collect(out var candidates))
			{
				return false;
			}
			if (!ShotOrder.TryFire(candidates, origin.x, origin.y, origin.z, rules.Range, rules.HitNeutral, delegate
			{
				ReadWeapon(out var heldAttack, out var heldIsBow, out var arrowAttack);
				return rules.ShotDamage(heldAttack, heldIsBow, arrowAttack, ReadStrength());
			}, out var targetIndex, out damage) || targetIndex < 0 || targetIndex >= Hitables.Count)
			{
				return false;
			}
			target = Hitables[targetIndex];
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			point = Points[targetIndex];
			return true;
		}

		public static void Commit(HitableMob target, int damage, Vector3 point, Vector3 muzzle, int stacks)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			try
			{
				((Hitable)target).Hit(damage, 0f, 0, point, -1);
			}
			catch (Exception arg)
			{
				if (_errorsLogged < 5)
				{
					_errorsLogged++;
					SquidPolypPlugin.Log.LogError((object)$"Squid Polyp shot failed. {arg}");
				}
			}
			TurretView.Bolt(muzzle, point);
			if (SquidPolypPlugin.Cfg.VerboseLogging.Value)
			{
				SquidPolypPlugin.Log.LogInfo((object)$"Squid Polyp shot: {stacks} stacks, {damage} damage.");
			}
		}

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

		private static bool Collect(out TurretCandidate[] candidates)
		{
			//IL_0086: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Invalid comparison between Unknown and I4
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			candidates = null;
			Snapshot.Clear();
			Hitables.Clear();
			Points.Clear();
			if ((Object)(object)MobManager.Instance == (Object)null || MobManager.Instance.mobs == null)
			{
				return false;
			}
			foreach (KeyValuePair<int, Mob> mob in MobManager.Instance.mobs)
			{
				Mob value = mob.Value;
				if (!((Object)(object)value == (Object)null))
				{
					HitableMob component = ((Component)value).GetComponent<HitableMob>();
					if (!((Object)(object)component == (Object)null))
					{
						Vector3 position = ((Component)component).transform.position;
						bool neutral = (Object)(object)value.mobType != (Object)null && (int)value.mobType.behaviour == 0;
						float defense = (((Object)(object)value.mobType != (Object)null) ? value.mobType.defense : 0f);
						Snapshot.Add(new TurretCandidate(mob.Key, position.x, position.y, position.z, ((Hitable)component).hp > 0, neutral, defense));
						Hitables.Add(component);
						Points.Add(position);
					}
				}
			}
			if (Snapshot.Count == 0)
			{
				return false;
			}
			candidates = Snapshot.ToArray();
			return true;
		}

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

		private static void WarnWeapon()
		{
			if (!_loggedWeapon)
			{
				_loggedWeapon = true;
				SquidPolypPlugin.Log.LogWarning((object)"Squid Polyp could not read the held weapon. Shots use MinimumWeaponDamage until it can.");
			}
		}

		private static void WarnStrength(Exception e)
		{
			if (!_loggedStrength)
			{
				_loggedStrength = true;
				SquidPolypPlugin.Log.LogWarning((object)((e == null) ? "Squid Polyp could not read strength. Shots use a strength of 1." : ("Squid Polyp could not read strength. Shots use a strength of 1. " + e.Message)));
			}
		}
	}
	internal sealed class TurretView : MonoBehaviour
	{
		private sealed class BoltFlight : MonoBehaviour
		{
			private float _thick;

			private float _length;

			private float _life;

			private float _age;

			private bool _beam;

			public void Beam(float thick, float length, float life)
			{
				_beam = true;
				_thick = thick;
				_length = length;
				_life = life;
			}

			public void Flash(float size, float life)
			{
				_thick = size;
				_life = life;
			}

			private void Update()
			{
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Unknown result type (might be due to invalid I