Decompiled source of BustlingFungus v0.1.3

plugins/MuckFungus.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("MuckFungus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds a White powerup that heals its holder and nearby allies after they stand still.")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+f99cc9bd08da73fc49418df2b7537409ebc643df")]
[assembly: AssemblyProduct("Bustling Fungus")]
[assembly: AssemblyTitle("MuckFungus")]
[assembly: InternalsVisibleTo("MuckFungus.Tests")]
[assembly: AssemblyVersion("0.1.3.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckFungus
{
	internal static class AllyGate
	{
		public static bool Open(bool alliesAvailable, bool healAllies)
		{
			return alliesAvailable && healAllies;
		}
	}
	internal static class AuraSum
	{
		public static float Total(FungusScaling scaling, int stillStacks, int wardStacks, int wardCount, float allyFraction)
		{
			float num = scaling.HealFractionFor(stillStacks);
			if (wardCount > 0)
			{
				num += (float)wardCount * scaling.HealFractionFor(wardStacks);
			}
			if (allyFraction > 0f)
			{
				num += allyFraction;
			}
			return num;
		}
	}
	internal sealed class BeaconClock
	{
		private readonly float interval;

		private float last;

		private bool sent;

		public BeaconClock(float interval)
		{
			this.interval = interval;
		}

		public bool Due(float now)
		{
			if (!sent || now - last >= interval || now < last)
			{
				last = now;
				sent = true;
				return true;
			}
			return false;
		}

		public void Reset()
		{
			sent = false;
		}
	}
	[HarmonyPatch(typeof(PowerupCalculations), "SpawnOnHitEffect", new Type[]
	{
		typeof(int),
		typeof(bool),
		typeof(Vector3),
		typeof(int)
	})]
	internal static class BeaconReceivePatch
	{
		private static bool logged;

		[HarmonyPriority(800)]
		private static bool Prefix(int __0, Vector3 __2)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (!FungusBeacon.TryDecode(__0, out var stacks, out var slot, out var playerId))
			{
				return true;
			}
			try
			{
				FungusLoop.OnBeacon(__2, stacks, slot, playerId);
			}
			catch (Exception ex)
			{
				if (!logged)
				{
					logged = true;
					FungusPlugin.Log.LogError((object)("Bustling Fungus dropped a beacon: " + ex));
				}
			}
			return false;
		}
	}
	internal sealed class FrameGate
	{
		private int last = int.MinValue;

		public bool TryEnter(int frame)
		{
			if (frame == last)
			{
				return false;
			}
			last = frame;
			return true;
		}

		public void Reset()
		{
			last = int.MinValue;
		}
	}
	internal static class FungusBeacon
	{
		public const int Base = 1179975680;

		public const int MaxStacks = 65535;

		public const int MaxSlot = 15;

		public const int MaxPlayer = 40;

		public static int Encode(int stacks, int slot, int playerId)
		{
			if (stacks < 1)
			{
				stacks = 1;
			}
			if (stacks > 65535)
			{
				stacks = 65535;
			}
			if (slot < 0)
			{
				slot = 0;
			}
			if (slot > 15)
			{
				slot = 15;
			}
			if (playerId < 0)
			{
				playerId = 0;
			}
			if (playerId > 40)
			{
				playerId = 40;
			}
			return 1179975680 + stacks + (slot << 16) + (playerId << 24);
		}

		public static bool TryDecode(int effectId, out int stacks, out int slot, out int playerId)
		{
			stacks = 0;
			slot = 0;
			playerId = 0;
			if (effectId <= 1179975680)
			{
				return false;
			}
			int num = 1852112895;
			if (effectId > num)
			{
				return false;
			}
			int num2 = effectId - 1179975680;
			stacks = num2 & 0xFFFF;
			slot = (num2 >> 16) & 0xFF;
			playerId = num2 >> 24;
			if (stacks >= 1 && slot <= 15)
			{
				return playerId <= 40;
			}
			return false;
		}
	}
	internal static class BeaconOwner
	{
		public static bool IsOwn(int beaconPlayer, int localPlayer)
		{
			if (localPlayer >= 0)
			{
				return beaconPlayer == localPlayer;
			}
			return false;
		}
	}
	internal sealed class FungusConfig
	{
		public const string DefaultZoneColor = "0.35, 1, 0.45, 0.25";

		public readonly ConfigEntry<float> HealPercent;

		public readonly ConfigEntry<float> HealPercentPerStack;

		public readonly ConfigEntry<float> BaseRadius;

		public readonly ConfigEntry<float> RadiusPerStack;

		public readonly ConfigEntry<float> StillDelay;

		public readonly ConfigEntry<float> StillSpeed;

		public readonly ConfigEntry<bool> HealAllies;

		public readonly ConfigEntry<bool> ShowZone;

		public readonly ConfigEntry<string> ZoneColor;

		public readonly ConfigEntry<KeyboardShortcut> GiveFungusKey;

		public readonly ConfigEntry<bool> VerboseLogging;

		internal static int CardRefreshCount;

		internal static string LastCardText;

		private Color tint;

		public FungusConfig(ConfigFile cfg)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			HealPercent = cfg.Bind<float>("Bustling Fungus", "HealPercent", 2f, new ConfigDescription("Percent of max HP healed per second with 1 stack, after standing still. 2 is this mod's tuning, not RoR2's 4.5. Not synced. Each machine uses its own value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			HealPercentPerStack = cfg.Bind<float>("Bustling Fungus", "HealPercentPerStack", 1f, new ConfigDescription("Extra percent of max HP per second for each stack after the first. 1 is this mod's tuning, not RoR2's 2.25. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			BaseRadius = cfg.Bind<float>("Bustling Fungus", "BaseRadius", 3.5f, new ConfigDescription("Heal radius in meters with 1 stack. 3.5 is the RoR2 code value. The RoR2 card says 3. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			RadiusPerStack = cfg.Bind<float>("Bustling Fungus", "RadiusPerStack", 1.5f, new ConfigDescription("Extra meters of radius for each stack after the first. 1.5 is the RoR2 value. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			StillDelay = cfg.Bind<float>("Bustling Fungus", "StillDelay", 1f, new ConfigDescription("Seconds of standing still before healing starts. 1 is the RoR2 value. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			StillSpeed = cfg.Bind<float>("Bustling Fungus", "StillSpeed", 0.5f, new ConfigDescription("Horizontal speed in meters per second that still counts as standing still. Vertical velocity does not count. Movement at or above this resets the timer. Not from RoR2. Muck's walk speed is 6.5. Not synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>()));
			HealAllies = cfg.Bind<bool>("Bustling Fungus", "HealAllies", true, "Send beacons and accept other players' beacons on this machine. When off, other players' zones are not recorded or drawn. Not synced.");
			ShowZone = cfg.Bind<bool>("Visuals", "ShowZone", true, "Draw the heal radius on this machine. Not synced.");
			ZoneColor = cfg.Bind<string>("Visuals", "ZoneColor", "0.35, 1, 0.45, 0.25", "Red, green, blue, and alpha, separated by commas. Not from RoR2. Not synced.");
			GiveFungusKey = cfg.Bind<KeyboardShortcut>("Debug", "GiveFungusKey", KeyboardShortcut.Empty, "Press in-game to give yourself one Bustling Fungus stack. Local test helper. Empty means disabled.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each received beacon.");
			tint = DefaultTint();
			Color color = tint;
			if (TryParseTint(ZoneColor.Value, ref color))
			{
				tint = color;
			}
			cfg.SettingChanged += OnSettingChanged;
		}

		public FungusScaling Scaling()
		{
			return new FungusScaling(HealPercent.Value, HealPercentPerStack.Value, BaseRadius.Value, RadiusPerStack.Value, StillDelay.Value);
		}

		public Color Tint()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return tint;
		}

		public string Summary()
		{
			//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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			Color val = Tint();
			return string.Format(CultureInfo.InvariantCulture, "HealPercent={0} HealPercentPerStack={1} BaseRadius={2} RadiusPerStack={3} StillDelay={4} StillSpeed={5} HealAllies={6} ShowZone={7} ZoneColor={8},{9},{10},{11} VerboseLogging={12}", Num(HealPercent.Value), Num(HealPercentPerStack.Value), Num(BaseRadius.Value), Num(RadiusPerStack.Value), Num(StillDelay.Value), Num(StillSpeed.Value), HealAllies.Value, ShowZone.Value, Num(val.r), Num(val.g), Num(val.b), Num(val.a), VerboseLogging.Value);
		}

		internal static bool UpdatesCard(string section, string key)
		{
			if (section == "Bustling Fungus")
			{
				switch (key)
				{
				default:
					return key == "StillDelay";
				case "HealPercent":
				case "HealPercentPerStack":
				case "BaseRadius":
				case "RadiusPerStack":
					return true;
				}
			}
			return false;
		}

		internal static Color DefaultTint()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			return new Color
			{
				r = 0.35f,
				g = 1f,
				b = 0.45f,
				a = 0.25f
			};
		}

		internal static bool TryParseTint(string text, ref Color color)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string[] array = text.Split(new char[1] { ',' });
			if (array.Length != 4)
			{
				return false;
			}
			if (!TryPart(array[0], out var value) || !TryPart(array[1], out var value2) || !TryPart(array[2], out var value3) || !TryPart(array[3], out var value4))
			{
				return false;
			}
			color.r = value;
			color.g = value2;
			color.b = value3;
			color.a = value4;
			return true;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			ConfigDefinition definition = args.ChangedSetting.Definition;
			if (definition.Section == "Visuals" && definition.Key == "ZoneColor")
			{
				Color color = tint;
				if (TryParseTint(ZoneColor.Value, ref color))
				{
					tint = color;
				}
			}
			if (UpdatesCard(definition.Section, definition.Key))
			{
				string text = (LastCardText = Scaling().Description());
				CardRefreshCount++;
				FungusPowerup.ApplyDescription(text);
			}
		}

		private static bool TryPart(string text, out float value)
		{
			if (!float.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value))
			{
				return false;
			}
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return false;
			}
			if (value < 0f)
			{
				value = 0f;
			}
			else if (value > 1f)
			{
				value = 1f;
			}
			return true;
		}

		private static string Num(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}
	}
	internal static class FungusLoop
	{
		internal static readonly StillnessTimer Stillness = new StillnessTimer(1f, 0.5f);

		internal static readonly HealAccumulator Heal = new HealAccumulator();

		internal static readonly BeaconClock Clock = new BeaconClock(0.25f);

		internal static readonly BeaconClock WardClock = new BeaconClock(0.25f);

		internal static readonly ZoneBook Zones = new ZoneBook();

		internal static readonly FrameGate Gate = new FrameGate();

		internal static bool AlliesAvailable;

		internal static bool Broken;

		public static void Tick(PlayerStatus status)
		{
			//IL_0036: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			if (Broken || (Object)(object)status == (Object)null || (Object)(object)status != (Object)(object)PlayerStatus.Instance || !Gate.TryEnter(Time.frameCount))
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			Vector3 val = default(Vector3);
			float num = 0f;
			float num2 = 0f;
			Vector3[] array = Array.Empty<Vector3>();
			FungusScaling fungusScaling = default(FungusScaling);
			try
			{
				PlayerMovement instance = PlayerMovement.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					ResetState();
					return;
				}
				int num3 = FungusPowerup.LocalStacks();
				bool flag3 = status.IsPlayerDead() || status.hp <= 0f;
				fungusScaling = FungusPlugin.Cfg.Scaling();
				val = ((Component)instance).transform.position;
				bool flag4 = AllyGate.Open(AlliesAvailable, FungusPlugin.Cfg.HealAllies.Value);
				Stillness.Delay = FungusPlugin.Cfg.StillDelay.Value;
				Stillness.SpeedThreshold = FungusPlugin.Cfg.StillSpeed.Value;
				Vector3 velocity = instance.GetVelocity();
				int num4 = (Stillness.Update(Time.deltaTime, HorizontalSpeed.Of(velocity.x, velocity.y, velocity.z), instance.grounded, flag3 || num3 <= 0) ? num3 : 0);
				int playerId = LocalPlayer.Id();
				if (num4 > 0 && flag4 && Clock.Due(Time.time))
				{
					ClientSend.SpawnEffect(FungusBeacon.Encode(num4, 0, playerId), val);
				}
				if (num4 == 0)
				{
					Clock.Reset();
				}
				FungusSources.Copy(out var ox, out var oy, out var oz, out var count);
				array = FungusSources.ToVectors();
				num2 = fungusScaling.RadiusFor(num3);
				int wardCount = WardCoverage.CountAt(val.x, val.y, val.z, num3, num2, ox, oy, oz, count);
				if (num3 <= 0 || FungusSources.Count == 0)
				{
					WardClock.Reset();
				}
				else if (flag4 && WardClock.Due(Time.time))
				{
					FungusSources.Send(num3, playerId, delegate(int id, float x, float y, float z)
					{
						//IL_0005: Unknown result type (might be due to invalid IL or missing references)
						ClientSend.SpawnEffect(id, new Vector3(x, y, z));
					});
				}
				if (!flag4)
				{
					Zones.Clear();
				}
				float allyFraction = Zones.FractionAt(val.x, val.y, val.z, Time.time, fungusScaling);
				float num5 = AuraSum.Total(fungusScaling, num4, num3, wardCount, allyFraction);
				bool healing = num5 > 0f && !flag3 && status.hp < (float)status.maxHp;
				int num6 = StillHeal.Apply(Heal, healing, Stillness.Interrupted, status.maxHp, num5, Time.deltaTime);
				if (num6 > 0)
				{
					status.Heal(num6);
				}
				flag2 = num4 > 0;
				num = fungusScaling.RadiusFor(num4);
				flag = true;
			}
			catch (Exception ex)
			{
				Broken = true;
				FungusPlugin.Log.LogError((object)("Bustling Fungus disabled for this session: " + ex));
				ZoneVisuals.HideAll();
				return;
			}
			if (flag)
			{
				ZoneVisuals.Draw(flag2, val, num, Zones, fungusScaling, array, num2);
			}
		}

		public static void OnBeacon(Vector3 p, int stacks, int slot, int playerId)
		{
			//IL_0036: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (!Broken && AllyGate.Open(AlliesAvailable, FungusPlugin.Cfg.HealAllies.Value) && !BeaconOwner.IsOwn(playerId, LocalPlayer.Id()))
			{
				Zones.Record(p.x, p.y, p.z, stacks, Time.time, playerId, slot);
				if (FungusPlugin.Cfg.VerboseLogging.Value)
				{
					FungusPlugin.Log.LogInfo((object)$"Bustling Fungus beacon at ({p.x}, {p.y}, {p.z}) stacks {stacks}.");
				}
			}
		}

		public static void ResetState()
		{
			Stillness.Reset();
			Heal.Reset();
			Clock.Reset();
			WardClock.Reset();
			Gate.Reset();
			Zones.Clear();
			FungusSources.Clear();
			ZoneVisuals.HideAll();
		}
	}
	[BepInPlugin("evan.muck.bustlingfungus", "Bustling Fungus", "0.1.3")]
	public class FungusPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.bustlingfungus";

		public const string Name = "Bustling Fungus";

		public const string Version = "0.1.3";

		internal static ManualLogSource Log;

		internal static FungusConfig Cfg;

		private void Awake()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new FungusConfig(((BaseUnityPlugin)this).Config);
			Harmony harmony = new Harmony("evan.muck.bustlingfungus");
			if (!TryPatch(harmony, typeof(PlayerStatusUpdatePatch)))
			{
				Log.LogError((object)"Bustling Fungus did not register. The heal hook failed, so the powerup will not drop.");
				return;
			}
			if (!TryPatch(harmony, typeof(UnnumberFungusPatch)) || !TryPatch(harmony, typeof(RegisterFungusPatch)))
			{
				Log.LogError((object)"Bustling Fungus did not register. Registration failed, so the powerup will not drop.");
				return;
			}
			FungusLoop.AlliesAvailable = TryPatch(harmony, typeof(BeaconReceivePatch));
			if (!FungusLoop.AlliesAvailable)
			{
				Log.LogWarning((object)"Bustling Fungus could not patch SpawnOnHitEffect. Ally healing is off; self-heal still works.");
			}
			FungusPowerup.Create();
			FungusRunner.Ensure();
			Log.LogInfo((object)("Bustling Fungus 0.1.3 loaded. Every player needs this mod and the same other powerup mods. 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}", "Bustling Fungus", patchClass.Name, arg));
				return false;
			}
		}
	}
	internal static class FungusPowerup
	{
		public const string PowerupName = "Bustling Fungus";

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

		public static Powerup Definition { get; private set; }

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

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

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

		public static int LocalStacks()
		{
			PowerupInventory instance = PowerupInventory.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0;
			}
			return StackCount.Read(Counts.Invoke(instance), Id);
		}

		public static void GiveOneLocal()
		{
			PowerupInventory instance = PowerupInventory.Instance;
			int[] array = (((Object)(object)instance != (Object)null) ? Counts.Invoke(instance) : null);
			if (array == null || Id < 0 || Id >= array.Length)
			{
				FungusPlugin.Log.LogWarning((object)"Can't give Bustling Fungus: 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)
			{
				FungusPlugin.Log.LogWarning((object)"Can't give Bustling Fungus: the powerup UI or player status is not ready.");
				return;
			}
			array[Id]++;
			PowerupUI.Instance.AddPowerup(Id);
			PlayerStatus.Instance.UpdateStats();
			FungusPlugin.Log.LogInfo((object)$"Debug: Bustling Fungus stacks now {array[Id]} (local only; the host's copy of your powerups is not updated).");
		}

		private static Sprite LoadIcon()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				using Stream stream = typeof(FungusPowerup).Assembly.GetManifestResourceStream("MuckFungus.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 = "FungusIcon";
				((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 = "FungusIcon";
				((Object)obj).hideFlags = (HideFlags)32;
				return obj;
			}
			catch (Exception arg)
			{
				FungusPlugin.Log.LogError((object)$"Failed to load Bustling Fungus icon: {arg}");
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class UnnumberFungusPatch
	{
		[HarmonyPriority(600)]
		private static void Prefix(ItemManager __instance)
		{
			Powerup definition = FungusPowerup.Definition;
			if (!((Object)(object)definition == (Object)null))
			{
				__instance.powerupsWhite = PowerupNames.Without(__instance.powerupsWhite, definition);
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class RegisterFungusPatch
	{
		[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"
		})]
		private static void Postfix(ItemManager __instance)
		{
			Powerup definition = FungusPowerup.Definition;
			if ((Object)(object)definition == (Object)null)
			{
				return;
			}
			Dictionary<int, Powerup> allPowerups = __instance.allPowerups;
			Dictionary<string, int> stringToPowerupId = __instance.stringToPowerupId;
			if (allPowerups == null || stringToPowerupId == null)
			{
				return;
			}
			int value;
			bool num = stringToPowerupId.TryGetValue("Bustling Fungus", out value);
			Powerup value2;
			bool sameObject = num && allPowerups.TryGetValue(value, out value2) && (Object)(object)value2 == (Object)(object)definition;
			NameClaim nameClaim = PowerupNames.Claim(num, sameObject);
			if (nameClaim == NameClaim.Taken)
			{
				FungusPlugin.Log.LogWarning((object)"The name Bustling Fungus is already used by another powerup. This one will not be added to the drop pool.");
				return;
			}
			PickupArt.Apply(definition, __instance.powerupsWhite);
			int num2;
			if (nameClaim == NameClaim.Fresh)
			{
				num2 = PowerupIds.Resolve(allPowerups.Keys, nameExists: false, 0);
				allPowerups.Add(num2, definition);
				stringToPowerupId.Add("Bustling Fungus", num2);
				definition.id = num2;
			}
			else
			{
				num2 = value;
			}
			if (__instance.powerupsWhite != null && !__instance.powerupsWhite.Contains(definition))
			{
				__instance.powerupsWhite = CollectionExtensions.AddToArray<Powerup>(__instance.powerupsWhite, definition);
			}
			FungusPowerup.Id = num2;
			int num3 = ((__instance.powerupsWhite != null) ? __instance.powerupsWhite.Length : 0);
			FungusPlugin.Log.LogInfo((object)$"Registered Bustling Fungus as White powerup id {num2} ({allPowerups.Count} powerups total, {num3} in the White pool).");
			if (FungusPlugin.Cfg.VerboseLogging.Value)
			{
				FungusPlugin.Log.LogInfo((object)("Powerup ids: " + string.Join(", ", from kv in allPowerups
					orderby kv.Key
					select $"{kv.Key}={kv.Value.name}")));
			}
		}
	}
	internal sealed class FungusRunner : MonoBehaviour
	{
		private static FungusRunner instance;

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

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

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

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

		private static void OnSceneChanged(Scene previous, Scene next)
		{
			FungusLoop.ResetState();
			ZoneVisuals.DestroyAll();
		}
	}
	internal readonly struct FungusScaling
	{
		private readonly float healPercent;

		private readonly float healPercentPerStack;

		private readonly float baseRadius;

		private readonly float radiusPerStack;

		private readonly float stillDelay;

		public FungusScaling(float healPercent, float healPercentPerStack, float baseRadius, float radiusPerStack, float stillDelay)
		{
			this.healPercent = healPercent;
			this.healPercentPerStack = healPercentPerStack;
			this.baseRadius = baseRadius;
			this.radiusPerStack = radiusPerStack;
			this.stillDelay = stillDelay;
		}

		public float HealFractionFor(int stacks)
		{
			if (stacks <= 0)
			{
				return 0f;
			}
			float num = healPercent + healPercentPerStack * (float)(stacks - 1);
			if (!(num > 0f))
			{
				return 0f;
			}
			return Math.Min(num / 100f, 1f);
		}

		public float RadiusFor(int stacks)
		{
			if (stacks <= 0)
			{
				return 0f;
			}
			float num = baseRadius + radiusPerStack * (float)(stacks - 1);
			if (!(num > 0f))
			{
				return 0f;
			}
			return num;
		}

		public string Description()
		{
			return string.Format(CultureInfo.InvariantCulture, "After standing still for {0}s, heal yourself and allies within {1}m (+{2}m per stack) for {3}% (+{4}% per stack) of their max health per second.", Num(stillDelay), Num(baseRadius), Num(radiusPerStack), Num(healPercent), Num(healPercentPerStack));
		}

		private static string Num(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}
	}
	public static class FungusSources
	{
		internal const float SlotMatch = 0.05f;

		private static float[] xs = Array.Empty<float>();

		private static float[] ys = Array.Empty<float>();

		private static float[] zs = Array.Empty<float>();

		private static int[] slots = Array.Empty<int>();

		internal static int Count { get; private set; }

		public static void SetLocalWards(Vector3[] positions)
		{
			if (!Holds(positions))
			{
				int num = ((positions != null) ? positions.Length : 0);
				float[] array = new float[num];
				float[] array2 = new float[num];
				float[] array3 = new float[num];
				for (int i = 0; i < num; i++)
				{
					array[i] = positions[i].x;
					array2[i] = positions[i].y;
					array3[i] = positions[i].z;
				}
				Set(array, array2, array3, num);
			}
		}

		internal static int Slot(int index)
		{
			if (index < 0 || index >= Count)
			{
				return 0;
			}
			return slots[index];
		}

		internal static void Set(float[] ax, float[] ay, float[] az, int count)
		{
			int num = ((ax != null && ay != null && az != null) ? Math.Min(count, Math.Min(ax.Length, Math.Min(ay.Length, az.Length))) : 0);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				if (Finite(ax[i]) && Finite(ay[i]) && Finite(az[i]))
				{
					num2++;
				}
			}
			float[] array = new float[num2];
			float[] array2 = new float[num2];
			float[] array3 = new float[num2];
			num2 = 0;
			for (int j = 0; j < num; j++)
			{
				if (Finite(ax[j]) && Finite(ay[j]) && Finite(az[j]))
				{
					array[num2] = ax[j];
					array2[num2] = ay[j];
					array3[num2] = az[j];
					num2++;
				}
			}
			int[] array4 = MatchSlots(array, array2, array3);
			xs = array;
			ys = array2;
			zs = array3;
			slots = array4;
			Count = num2;
		}

		internal static void Copy(out float[] ox, out float[] oy, out float[] oz, out int count)
		{
			ox = xs;
			oy = ys;
			oz = zs;
			count = Count;
		}

		internal static Vector3[] ToVectors()
		{
			//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)
			Vector3[] array = (Vector3[])(object)new Vector3[Count];
			for (int i = 0; i < Count; i++)
			{
				array[i] = new Vector3(xs[i], ys[i], zs[i]);
			}
			return array;
		}

		internal static void Clear()
		{
			Set(null, null, null, 0);
		}

		internal static void Send(int stacks, int playerId, Action<int, float, float, float> spawn)
		{
			if (stacks <= 0 || spawn == null || Count == 0)
			{
				return;
			}
			for (int i = 0; i < Count; i++)
			{
				if (slots[i] > 0)
				{
					spawn(FungusBeacon.Encode(stacks, slots[i], playerId), xs[i], ys[i], zs[i]);
				}
			}
		}

		private static bool Holds(Vector3[] positions)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			while (positions != null && num2 < positions.Length)
			{
				Vector3 val = positions[num2];
				if (Finite(val.x) && Finite(val.y) && Finite(val.z))
				{
					if (num >= Count || xs[num] != val.x || ys[num] != val.y || zs[num] != val.z)
					{
						return false;
					}
					num++;
				}
				num2++;
			}
			return num == Count;
		}

		private static int[] MatchSlots(float[] cx, float[] cy, float[] cz)
		{
			int num = cx.Length;
			int[] array = new int[num];
			bool[] array2 = new bool[Count];
			float num2 = 0.0025000002f;
			for (int i = 0; i < num; i++)
			{
				int num3 = -1;
				for (int j = 0; j < Count; j++)
				{
					if (!array2[j])
					{
						float num4 = cx[i] - xs[j];
						float num5 = cy[i] - ys[j];
						float num6 = cz[i] - zs[j];
						if (!(num4 * num4 + num5 * num5 + num6 * num6 > num2))
						{
							num3 = j;
							break;
						}
					}
				}
				if (num3 >= 0)
				{
					array2[num3] = true;
					array[i] = slots[num3];
				}
			}
			bool[] array3 = new bool[16];
			for (int k = 0; k < num; k++)
			{
				if (array[k] > 0 && array[k] <= 15)
				{
					array3[array[k]] = true;
				}
			}
			for (int l = 0; l < num; l++)
			{
				if (array[l] != 0)
				{
					continue;
				}
				int num7 = 0;
				for (int m = 1; m <= 15; m++)
				{
					if (!array3[m])
					{
						num7 = m;
						array3[m] = true;
						break;
					}
				}
				array[l] = num7;
			}
			return array;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal sealed class HealAccumulator
	{
		public const float MaxStep = 0.5f;

		public double Pending { get; private set; }

		public int Add(float maxHp, float fractionPerSecond, float dt)
		{
			if (!(maxHp > 0f) || !(fractionPerSecond > 0f) || !(dt > 0f))
			{
				return 0;
			}
			dt = Math.Min(dt, 0.5f);
			Pending += (double)maxHp * (double)fractionPerSecond * (double)dt;
			int num = (int)Math.Floor(Pending + 1E-06);
			if (num <= 0)
			{
				return 0;
			}
			Pending = Math.Max(0.0, Pending - (double)num);
			return num;
		}

		public void Reset()
		{
			Pending = 0.0;
		}
	}
	internal static class HorizontalSpeed
	{
		public static float Of(float x, float y, float z)
		{
			if (float.IsNaN(x) || float.IsInfinity(x) || float.IsNaN(z) || float.IsInfinity(z))
			{
				return float.NaN;
			}
			return (float)Math.Sqrt(x * x + z * z);
		}
	}
	internal static class LocalPlayer
	{
		public static int Id()
		{
			LocalClient instance = LocalClient.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return instance.myId;
			}
			return -1;
		}
	}
	internal static class PickupArt
	{
		public const string ObjResource = "MuckFungus.pickup.obj";

		public const string JsonResource = "MuckFungus.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("MuckFungus.pickup.obj"), Read("MuckFungus.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)
			{
				FungusPlugin.Log.LogWarning((object)("Bustling Fungus 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
				{
					FungusPlugin.Log.LogWarning((object)"No White powerup to borrow a pickup model from; dropped fungi will be invisible.");
				}
			}
		}

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

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

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

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

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

		public readonly float Y;

		public readonly float Z;

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

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

		public readonly float G;

		public readonly float B;

		public readonly float Emission;

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

		public readonly int[] Triangles;

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

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

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

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

		public readonly string Name;

		public readonly Vec3[] Positions;

		public readonly Vec3[] Normals;

		public readonly IReadOnlyList<SubMesh> SubMeshes;

		public readonly IReadOnlyDictionary<string, MaterialSpec> Materials;

		public int TriangleCount => Positions.Length / 3;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		public readonly Vec3[] Normals;

		public readonly float[] U;

		public readonly float[] R;

		public readonly float[] G;

		public readonly float[] B;

		public readonly int[] Triangles;

		public readonly IReadOnlyList<MaterialSpec> Palette;

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

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

		private int pos;

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

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

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

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

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

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

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

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

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

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

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

		public static T[] Without<T>(T[] items, T remove) where T : class
		{
			if (items == null || remove == null)
			{
				return items;
			}
			int num = 0;
			for (int i = 0; i < items.Length; i++)
			{
				if (items[i] != remove)
				{
					num++;
				}
			}
			if (num == items.Length)
			{
				return items;
			}
			T[] array = new T[num];
			int num2 = 0;
			for (int j = 0; j < items.Length; j++)
			{
				if (items[j] != remove)
				{
					array[num2++] = items[j];
				}
			}
			return array;
		}
	}
	internal static class StackChoice
	{
		public static int Effective(int own, int ally)
		{
			return Math.Max(Math.Max(own, 0), Math.Max(ally, 0));
		}
	}
	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 static class StillHeal
	{
		public static int Apply(HealAccumulator heal, bool healing, bool interrupted, float maxHp, float fraction, float dt)
		{
			if (healing)
			{
				return heal.Add(maxHp, fraction, dt);
			}
			if (interrupted)
			{
				heal.Reset();
			}
			return 0;
		}
	}
	internal sealed class StillnessTimer
	{
		internal const float UngroundedGrace = 0.15f;

		private float airSeconds;

		public float Delay { get; set; }

		public float SpeedThreshold { get; set; }

		public float StillSeconds { get; private set; }

		public bool Interrupted { get; private set; }

		public StillnessTimer(float delay, float speedThreshold)
		{
			Delay = delay;
			SpeedThreshold = speedThreshold;
		}

		public bool Update(float dt, float speed, bool grounded, bool blocked)
		{
			if (blocked || IsRealMovement(speed))
			{
				Reset();
				Interrupted = true;
				return false;
			}
			if (!grounded)
			{
				if (dt > 0f)
				{
					airSeconds += dt;
				}
				if (!(airSeconds < 0.15f))
				{
					StillSeconds = 0f;
					Interrupted = true;
					return false;
				}
			}
			else
			{
				airSeconds = 0f;
			}
			Interrupted = false;
			if (dt > 0f)
			{
				StillSeconds = Math.Min(StillSeconds + dt, Math.Max(Delay, 0f));
			}
			return StillSeconds >= Delay;
		}

		public void Reset()
		{
			StillSeconds = 0f;
			airSeconds = 0f;
		}

		private bool IsRealMovement(float speed)
		{
			return !(speed < SpeedThreshold);
		}
	}
	internal static class WardCoverage
	{
		public static int CountAt(float x, float y, float z, int ownerStacks, float radius, float[] xs, float[] ys, float[] zs, int count)
		{
			if (ownerStacks <= 0 || !(radius > 0f) || xs == null || count <= 0)
			{
				return 0;
			}
			float num = radius * radius;
			int num2 = ((count < xs.Length) ? count : xs.Length);
			int num3 = 0;
			for (int i = 0; i < num2; i++)
			{
				float num4 = xs[i] - x;
				float num5 = ys[i] - y;
				float num6 = zs[i] - z;
				if (num4 * num4 + num5 * num5 + num6 * num6 <= num)
				{
					num3++;
				}
			}
			return num3;
		}
	}
	internal readonly struct Zone
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public int Stacks { get; }

		public int Player { get; }

		public int Slot { get; }

		public float Time { get; }

		public Zone(float x, float y, float z, int stacks, int player, int slot, float time)
		{
			X = x;
			Y = y;
			Z = z;
			Stacks = stacks;
			Player = player;
			Slot = slot;
			Time = time;
		}
	}
	internal sealed class ZoneBook
	{
		private readonly float ttl;

		private readonly Zone[] zones;

		private int count;

		public int Count => count;

		public Zone this[int i] => zones[i];

		public ZoneBook(float ttl = 1f, int maxZones = 32)
		{
			this.ttl = ttl;
			zones = new Zone[(maxZones > 0) ? maxZones : 0];
		}

		public void Record(float x, float y, float z, int stacks, float now, int player = 0, int slot = 0)
		{
			if (stacks <= 0 || !Finite(x) || !Finite(y) || !Finite(z))
			{
				return;
			}
			Zone zone = new Zone(x, y, z, stacks, player, slot, now);
			for (int i = 0; i < count; i++)
			{
				if (zones[i].Player == player && zones[i].Slot == slot)
				{
					zones[i] = zone;
					return;
				}
			}
			if (count == zones.Length)
			{
				if (count == 0)
				{
					return;
				}
				int num = 0;
				for (int j = 1; j < count; j++)
				{
					if (zones[j].Time < zones[num].Time)
					{
						num = j;
					}
				}
				zones[num] = zone;
			}
			else
			{
				zones[count++] = zone;
			}
		}

		public void Prune(float now)
		{
			int num = 0;
			while (num < count)
			{
				if (now - zones[num].Time >= ttl)
				{
					count--;
					zones[num] = zones[count];
					zones[count] = default(Zone);
				}
				else
				{
					num++;
				}
			}
		}

		public float FractionAt(float x, float y, float z, float now, FungusScaling scaling)
		{
			Prune(now);
			float num = 0f;
			for (int i = 0; i < count; i++)
			{
				float num2 = scaling.RadiusFor(zones[i].Stacks);
				float num3 = zones[i].X - x;
				float num4 = zones[i].Y - y;
				float num5 = zones[i].Z - z;
				if (num3 * num3 + num4 * num4 + num5 * num5 <= num2 * num2)
				{
					num += scaling.HealFractionFor(zones[i].Stacks);
				}
			}
			return num;
		}

		public void Clear()
		{
			for (int i = 0; i < count; i++)
			{
				zones[i] = default(Zone);
			}
			count = 0;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class ZoneVisuals
	{
		private const int AllySlots = 32;

		private const string PremultiplyShader = "Legacy Shaders/Particles/Alpha Blended Premultiply";

		private static readonly string[] ShaderCandidates = new string[4] { "Legacy Shaders/Particles/Alpha Blended", "Particles/Alpha Blended", "Sprites/Default", "Legacy Shaders/Particles/Alpha Blended Premultiply" };

		private static readonly GameObject[] Cylinders = (GameObject[])(object)new GameObject[33];

		private static Material material;

		private static int tintColorId;

		private static bool hasTintColor;

		private static bool premultiply;

		private static bool colorApplied;

		private static Color applied;

		private static bool anyVisible;

		private static bool failed;

		private static bool shaderMissingLogged;

		public static void Draw(bool ownActive, Vector3 ownPos, float ownRadius, ZoneBook zones, FungusScaling scaling, Vector3[] wards, float wardRadius)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			if (failed)
			{
				return;
			}
			try
			{
				bool flag = ownActive && ownRadius > 0f;
				bool flag2 = wards != null && wards.Length != 0 && wardRadius > 0f;
				if (!FungusPlugin.Cfg.ShowZone.Value || (!flag && !flag2 && zones.Count == 0))
				{
					if (anyVisible)
					{
						HideAll();
					}
					return;
				}
				Material val = SharedMaterial();
				if ((Object)(object)val == (Object)null)
				{
					if (anyVisible)
					{
						HideAll();
					}
					return;
				}
				ApplyTint(val);
				bool flag3 = false;
				if (flag)
				{
					Place(0, ownPos, ownRadius, val);
					flag3 = true;
				}
				else
				{
					Hide(0);
				}
				int num = 0;
				if (flag2)
				{
					for (int i = 0; i < wards.Length; i++)
					{
						if (num >= 32)
						{
							break;
						}
						Place(1 + num, wards[i], wardRadius, val);
						flag3 = true;
						num++;
					}
				}
				for (int j = 0; j < zones.Count; j++)
				{
					if (num >= 32)
					{
						break;
					}
					Zone zone = zones[j];
					float num2 = scaling.RadiusFor(zone.Stacks);
					if (num2 > 0f)
					{
						Place(1 + num, new Vector3(zone.X, zone.Y, zone.Z), num2, val);
						flag3 = true;
						num++;
					}
				}
				for (int k = num; k < 32; k++)
				{
					Hide(1 + k);
				}
				anyVisible = flag3;
			}
			catch (Exception ex)
			{
				failed = true;
				FungusPlugin.Log.LogError((object)("Bustling Fungus zone hidden for this scene: " + ex));
				HideAll();
			}
		}

		public static void HideAll()
		{
			for (int i = 0; i < Cylinders.Length; i++)
			{
				Hide(i);
			}
			anyVisible = false;
		}

		public static void DestroyAll()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < Cylinders.Length; i++)
			{
				if ((Object)(object)Cylinders[i] != (Object)null)
				{
					Object.Destroy((Object)(object)Cylinders[i]);
				}
				Cylinders[i] = null;
			}
			if ((Object)(object)material != (Object)null)
			{
				Object.Destroy((Object)(object)material);
				material = null;
			}
			hasTintColor = false;
			premultiply = false;
			colorApplied = false;
			applied = default(Color);
			anyVisible = false;
			failed = false;
			shaderMissingLogged = false;
		}

		private static void ApplyTint(Material shared)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			Color val = FungusPlugin.Cfg.Tint();
			if (premultiply)
			{
				val.r *= val.a;
				val.g *= val.a;
				val.b *= val.a;
			}
			if (!colorApplied || applied.r != val.r || applied.g != val.g || applied.b != val.b || applied.a != val.a)
			{
				if (hasTintColor)
				{
					shared.SetColor(tintColorId, val);
				}
				else
				{
					shared.color = val;
				}
				applied = val;
				colorApplied = true;
			}
		}

		private static void Place(int index, Vector3 pos, float radius, Material shared)
		{
			//IL_002f: 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)
			GameObject val = Cylinders[index];
			if ((Object)(object)val == (Object)null)
			{
				val = (Cylinders[index] = CreateCylinder(shared));
			}
			val.SetActive(true);
			val.transform.position = pos;
			val.transform.localScale = new Vector3(radius * 2f, 1f, radius * 2f);
		}

		private static void Hide(int index)
		{
			GameObject val = Cylinders[index];
			if ((Object)(object)val != (Object)null)
			{
				val.SetActive(false);
			}
		}

		private static GameObject CreateCylinder(Material shared)
		{
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)2);
			((Object)obj).name = "BustlingFungusZone";
			((Object)obj).hideFlags = (HideFlags)61;
			Object.DestroyImmediate((Object)(object)obj.GetComponent<Collider>());
			MeshRenderer component = obj.GetComponent<MeshRenderer>();
			((Renderer)component).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)component).receiveShadows = false;
			((Renderer)component).sharedMaterial = shared;
			return obj;
		}

		private static Material SharedMaterial()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			if ((Object)(object)material != (Object)null)
			{
				return material;
			}
			if (shaderMissingLogged)
			{
				return null;
			}
			string[] shaderCandidates = ShaderCandidates;
			foreach (string text in shaderCandidates)
			{
				Shader val = Shader.Find(text);
				if (!((Object)(object)val == (Object)null))
				{
					material = new Material(val)
					{
						name = "BustlingFungusZone",
						hideFlags = (HideFlags)61
					};
					if (material.HasProperty("_MainTex"))
					{
						material.mainTexture = (Texture)(object)Texture2D.whiteTexture;
					}
					tintColorId = Shader.PropertyToID("_TintColor");
					hasTintColor = material.HasProperty(tintColorId);
					premultiply = text == "Legacy Shaders/Particles/Alpha Blended Premultiply";
					colorApplied = false;
					FungusPlugin.Log.LogInfo((object)("Bustling Fungus zone uses shader '" + text + "'."));
					return material;
				}
			}
			shaderMissingLogged = true;
			FungusPlugin.Log.LogWarning((object)"No usable built-in shader found for the Bustling Fungus zone. The zone is hidden and healing still works.");
			return null;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckFungus";

		public const string PLUGIN_NAME = "Bustling Fungus";

		public const string PLUGIN_VERSION = "0.1.3";
	}
}