Decompiled source of GlacialElite v0.3.0

plugins/MuckGlacial.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
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("MuckGlacial")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Some hostile Muck mobs spawn as Glacial elites with more health, more damage, a slowing hit, and an ice bomb on death.")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+13a7b0fccd916691a58920c58d61129c62648f76")]
[assembly: AssemblyProduct("Glacial Elite")]
[assembly: AssemblyTitle("MuckGlacial")]
[assembly: InternalsVisibleTo("MuckGlacial.Tests")]
[assembly: AssemblyVersion("0.3.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckGlacial
{
	internal struct AttackMark
	{
		public float X;

		public float Y;

		public float Z;

		public float Time;
	}
	internal sealed class AttackLedger
	{
		public const int Capacity = 32;

		public const float Tolerance = 0.05f;

		public const float Window = 0.5f;

		private readonly List<AttackMark> marks = new List<AttackMark>();

		public int Count => marks.Count;

		public void Record(float x, float y, float z, float now)
		{
			if (marks.Count >= 32)
			{
				marks.RemoveAt(0);
			}
			marks.Add(new AttackMark
			{
				X = x,
				Y = y,
				Z = z,
				Time = now
			});
		}

		public bool TryConsume(float x, float y, float z, float now, float tolerance, float window)
		{
			int num = 0;
			while (num < marks.Count)
			{
				if (now - marks[num].Time > window)
				{
					marks.RemoveAt(num);
				}
				else
				{
					num++;
				}
			}
			float num2 = tolerance * tolerance;
			for (int i = 0; i < marks.Count; i++)
			{
				AttackMark attackMark = marks[i];
				float num3 = x - attackMark.X;
				float num4 = y - attackMark.Y;
				float num5 = z - attackMark.Z;
				if (num3 * num3 + num4 * num4 + num5 * num5 <= num2)
				{
					marks.RemoveAt(i);
					return true;
				}
			}
			return false;
		}

		public void Clear()
		{
			marks.Clear();
		}
	}
	internal static class BombCues
	{
		public static readonly string[] Hiss = new string[4] { "Fireball_ambient", "DragonBreath", "GuardianAmbience", "WindFlyby" };

		public static readonly string[] Crackle = new string[4] { "Creaking", "SpikeCharge", "SwordCharge", "Bow_charge" };

		public static readonly string[] Sting = new string[3] { "WindHitFx", "ElectroHitFx", "Hit_crit" };

		public static readonly string[] End = new string[3] { "SpikesStart", "SwordHitGround", "RockImpactLoud" };

		public static int Pick(string[] prefer, string[] available)
		{
			if (prefer == null || available == null)
			{
				return -1;
			}
			for (int i = 0; i < prefer.Length; i++)
			{
				if (prefer[i] == null)
				{
					continue;
				}
				for (int j = 0; j < available.Length; j++)
				{
					if (string.Equals(prefer[i], available[j], StringComparison.OrdinalIgnoreCase))
					{
						return j;
					}
				}
			}
			return -1;
		}
	}
	internal static class BombHit
	{
		public static bool Resolve(GlacialStatus status, float now, float freezeSeconds, int damage, Action send, Action<Exception> sendFailed)
		{
			bool result = status.Freeze(now, freezeSeconds);
			if (damage > 0 && send != null)
			{
				try
				{
					send();
				}
				catch (Exception obj)
				{
					sendFailed?.Invoke(obj);
				}
			}
			return result;
		}
	}
	internal static class ConfigFingerprint
	{
		public static ulong Of(float chance, bool includeNeutral, float healthFactor, float damageFactor)
		{
			return Hash01.SplitMix64(Hash01.SplitMix64(Hash01.SplitMix64(Hash01.SplitMix64(Bits(chance)) ^ (ulong)(includeNeutral ? 1 : 0)) ^ Bits(healthFactor)) ^ Bits(damageFactor));
		}

		private static ulong Bits(float value)
		{
			return BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
		}
	}
	internal sealed class FreezeClink
	{
		public const float MaxWaitSeconds = 0.5f;

		private bool armed;

		private float armedAt;

		private int hpBefore;

		public bool Armed => armed;

		public void Arm(float now, int hpAndShield)
		{
			armed = true;
			armedAt = now;
			hpBefore = hpAndShield;
		}

		public bool Due(float now, int hpAndShield, bool dead)
		{
			if (!armed)
			{
				return false;
			}
			if (dead)
			{
				armed = false;
				return false;
			}
			if (hpAndShield >= hpBefore && now - armedAt < 0.5f)
			{
				return false;
			}
			armed = false;
			return true;
		}

		public void Clear()
		{
			armed = false;
		}
	}
	internal static class FrostOverlay
	{
		public const float SlowedAlpha = 0.3f;

		public const float FrozenAlpha = 0.6f;

		public const float FadeSeconds = 0.25f;

		public static float Alpha(float now, float slowUntil, float freezeUntil)
		{
			return Math.Max(Level(now, freezeUntil, 0.6f), Level(now, slowUntil, 0.3f));
		}

		public static float Shown(float alpha, bool menuOpen, bool dead)
		{
			if (!(menuOpen || dead))
			{
				return alpha;
			}
			return 0f;
		}

		public static float Edge(float u, float v)
		{
			float num = (u - 0.5f) * 2f;
			float num2 = (v - 0.5f) * 2f;
			float num3 = ((float)Math.Sqrt((num * num + num2 * num2) / 2f) - 0.35f) / 0.65f;
			if (num3 < 0f)
			{
				num3 = 0f;
			}
			if (num3 > 1f)
			{
				num3 = 1f;
			}
			return num3 * num3 * (3f - 2f * num3);
		}

		private static float Level(float now, float until, float full)
		{
			float num = until - now;
			if (!(num > 0f))
			{
				return 0f;
			}
			if (!(num >= 0.25f))
			{
				return full * num / 0.25f;
			}
			return full;
		}
	}
	internal sealed class FrostVignette : MonoBehaviour
	{
		private const int Size = 64;

		private static readonly Color Frost = new Color(0.65f, 0.88f, 1f, 1f);

		private static Texture2D texture;

		private static Texture2D iconThin;

		private static Texture2D iconThick;

		private static bool iconFailed;

		private void OnGUI()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: 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)
			float num = GlacialStatus.Local.OverlayAlpha(Time.time);
			if (!(num > 0f))
			{
				((Behaviour)this).enabled = false;
			}
			else
			{
				if ((int)Event.current.type != 7)
				{
					return;
				}
				num = FrostOverlay.Shown(num, VanillaMenuOpen(), GlacialRunner.LocalPlayerDead());
				if (num > 0f)
				{
					Texture2D val = Mask();
					Color color = GUI.color;
					if ((Object)(object)val != (Object)null)
					{
						GUI.color = new Color(Frost.r, Frost.g, Frost.b, num);
						GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)val, (ScaleMode)0, true);
					}
					bool flag = GlacialPlugin.Cfg == null || GlacialPlugin.Cfg.HighContrast.Value;
					Texture2D val2 = Icon(flag);
					if ((Object)(object)val2 != (Object)null)
					{
						float num2 = HazardMark.IconScale(frozen: GlacialStatus.Local.IsFrozen(Time.time), time: Time.time);
						float num3 = (flag ? 84f : 56f) * num2;
						GUI.color = Color.white;
						GUI.DrawTexture(new Rect(28f, (float)Screen.height - num3 - 28f, num3, num3), (Texture)(object)val2, (ScaleMode)2, true);
					}
					GUI.color = color;
				}
			}
		}

		private static bool VanillaMenuOpen()
		{
			OtherInput instance = OtherInput.Instance;
			if ((Object)(object)instance != (Object)null && (Shown(instance.pauseUi) || Shown(instance.settingsUi)))
			{
				return true;
			}
			if ((Object)(object)Map.Instance != (Object)null && Map.Instance.active)
			{
				return true;
			}
			if ((Object)(object)InventoryUI.Instance != (Object)null && ((Component)InventoryUI.Instance).gameObject.activeInHierarchy)
			{
				return true;
			}
			if ((Object)(object)RespawnTotemUI.Instance != (Object)null && Shown(RespawnTotemUI.Instance.root))
			{
				return true;
			}
			if ((Object)(object)TraderUI.Instance != (Object)null)
			{
				return Shown(TraderUI.Instance.root);
			}
			return false;
		}

		private static bool Shown(GameObject go)
		{
			if ((Object)(object)go != (Object)null)
			{
				return go.activeInHierarchy;
			}
			return false;
		}

		private static Texture2D Mask()
		{
			//IL_0019: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)texture != (Object)null)
			{
				return texture;
			}
			texture = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				name = "GlacialFrostVignette",
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)61
			};
			Color[] array = (Color[])(object)new Color[4096];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num = FrostOverlay.Edge(((float)j + 0.5f) / 64f, ((float)i + 0.5f) / 64f);
					array[i * 64 + j] = new Color(1f, 1f, 1f, num);
				}
			}
			texture.SetPixels(array);
			texture.Apply(false, true);
			return texture;
		}

		private static Texture2D Icon(bool highContrast)
		{
			if (highContrast)
			{
				if (!((Object)(object)iconThick != (Object)null) && !iconFailed)
				{
					return iconThick = LoadIcon("MuckGlacial.Art.slow_hc.png");
				}
				return iconThick;
			}
			if (!((Object)(object)iconThin != (Object)null) && !iconFailed)
			{
				return iconThin = LoadIcon("MuckGlacial.Art.slow.png");
			}
			return iconThin;
		}

		private static Texture2D LoadIcon(string resource)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_0022: 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_0036: Expected O, but got Unknown
			//IL_004a: Expected O, but got Unknown
			try
			{
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = "GlacialSlowIcon",
					wrapMode = (TextureWrapMode)1,
					filterMode = (FilterMode)1,
					hideFlags = (HideFlags)61
				};
				if (!ImageConversion.LoadImage(val, IceArt.ReadBytes(resource)))
				{
					throw new InvalidDataException(resource + " failed to decode");
				}
				return val;
			}
			catch (Exception ex)
			{
				iconFailed = true;
				if (GlacialPlugin.Log != null)
				{
					GlacialPlugin.Log.LogWarning((object)("Glacial slow icon failed to load: " + ex.Message));
				}
				return null;
			}
		}
	}
	internal sealed class GlacialConfig
	{
		public readonly ConfigEntry<float> Chance;

		public readonly ConfigEntry<bool> IncludeNeutral;

		public readonly ConfigEntry<float> HealthFactor;

		public readonly ConfigEntry<float> DamageFactor;

		public readonly ConfigEntry<bool> EnableSlow;

		public readonly ConfigEntry<float> SlowSpeedFactor;

		public readonly ConfigEntry<float> SlowSeconds;

		public readonly ConfigEntry<bool> EnableBomb;

		public readonly ConfigEntry<float> BombFuse;

		public readonly ConfigEntry<float> BombRadius;

		public readonly ConfigEntry<float> BombDamageFraction;

		public readonly ConfigEntry<int> BombBaseDamage;

		public readonly ConfigEntry<int> BombDamageCap;

		public readonly ConfigEntry<float> FreezeSeconds;

		public readonly ConfigEntry<float> FreezeSpeedFactor;

		public readonly ConfigEntry<bool> FreezeBlocksJump;

		public readonly ConfigEntry<int> MaxLiveBombs;

		public readonly ConfigEntry<float> TintStrength;

		public readonly ConfigEntry<Color> TintColor;

		public readonly ConfigEntry<bool> HighContrast;

		public readonly ConfigEntry<bool> VerboseLogging;

		public GlacialConfig(ConfigFile cfg)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Expected O, but got Unknown
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Expected O, but got Unknown
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Expected O, but got Unknown
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Expected O, but got Unknown
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Expected O, but got Unknown
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Expected O, but got Unknown
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Expected O, but got Unknown
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			Chance = cfg.Bind<float>("Spawn", "Chance", 0.1f, new ConfigDescription("Chance from 0 to 1 that an eligible mob spawns Glacial. Not from the wiki. RoR2 elites come from director credits, not a flat chance. Set this to 1 so every eligible mob is Glacial. Every peer should use the same Spawn values.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			IncludeNeutral = cfg.Bind<bool>("Spawn", "IncludeNeutral", false, "When true, neutral mobs can roll Glacial. Not from the wiki. Bosses and guardians never roll, whatever this is set to. Every peer should use the same Spawn values.");
			HealthFactor = cfg.Bind<float>("Stats", "HealthFactor", 4f, new ConfigDescription("Health multiplier for a Glacial mob. 4 is the RoR2 wiki tier 1 elite health. It multiplies the incoming bossMultiplier, so an Artifact of the Night Swarms night (0.5) gives a 2x Glacial. The health part of bossMultiplier is rounded to 1/32 and a 1/64 tag is added so a save can recognize the mob, which makes the default product 4.03125. Every peer should use the same Stats values.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 100f), Array.Empty<object>()));
			DamageFactor = cfg.Bind<float>("Stats", "DamageFactor", 2f, new ConfigDescription("Damage multiplier for a Glacial mob. 2 is the RoR2 wiki tier 1 elite damage. It is written to mob.multiplier, which SpawnAttackFx already applies to melee. HealthFactor divided by this, plus the 1/64 tag, is written to bossMultiplier. The ice bomb does not use this. Muck treats multiplier above 1 as a buff, so the default enlarges the mob and shows the buff bar. A value of 1 or less does not.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 100f), Array.Empty<object>()));
			EnableSlow = cfg.Bind<bool>("Slow", "EnableSlow", true, "When true, a Glacial mob's melee hit slows the local player. Not from the wiki. This is per player and is not synced.");
			SlowSpeedFactor = cfg.Bind<float>("Slow", "SlowSpeedFactor", 0.2f, new ConfigDescription("Speed multiplier while slowed. 0.2 is the RoR2 wiki, an 80% slow. 0 is the freeze default. On the ground this caps horizontal speed. In the air the player keeps momentum and can still steer. 1 leaves speed unchanged. Per player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			SlowSeconds = cfg.Bind<float>("Slow", "SlowSeconds", 1.5f, new ConfigDescription("Seconds a melee slow lasts. 1.5 is the RoR2 wiki duration. A later slow extends the end and does not shorten it. Per player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			EnableBomb = cfg.Bind<bool>("Bomb", "EnableBomb", true, "When true, a Glacial mob leaves an ice bomb when it dies. Not from the wiki. This is per player and is not synced.");
			BombFuse = cfg.Bind<float>("Bomb", "BombFuse", 2f, new ConfigDescription("Seconds before the ice bomb explodes. 2 is the RoR2 wiki fuse. The ring spends the first 0.5 seconds growing and cannot hurt until both that wind-up and this fuse have elapsed, so a fuse under 0.5 still waits out the wind-up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			BombRadius = cfg.Bind<float>("Bomb", "BombRadius", 4f, new ConfigDescription("Explosion radius in meters. Not from the wiki. The RoR2 wiki lists no radius. 4 is the ideas doc's number.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f), Array.Empty<object>()));
			BombDamageFraction = cfg.Bind<float>("Bomb", "BombDamageFraction", 1.5f, new ConfigDescription("Bomb damage as a fraction of BombBaseDamage only. 1.5 is the RoR2 wiki 150%. It is not multiplied by DamageFactor or by mob.multiplier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>()));
			BombBaseDamage = cfg.Bind<int>("Bomb", "BombBaseDamage", 15, new ConfigDescription("Flat damage the bomb fraction applies to. Not from the RoR2 wiki. 15 is ImpactDamage.baseDamage on GoblinAttack in a pre-Update-4 asset rip. The host then applies MobDamageMultiplier and armor.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			BombDamageCap = cfg.Bind<int>("Bomb", "BombDamageCap", 30, new ConfigDescription("Maximum bomb damage as this mod sends it, after the fraction. The host then multiplies it by the day's mob damage multiplier and applies armor, so this caps the value before that multiplier, not the damage taken. Not from the wiki. Default 30.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			FreezeSeconds = cfg.Bind<float>("Bomb", "FreezeSeconds", 1.5f, new ConfigDescription("Seconds the bomb freezes the local player. 1.5 is the RoR2 wiki freeze. A later freeze extends the end and does not shorten it. Per player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			FreezeSpeedFactor = cfg.Bind<float>("Bomb", "FreezeSpeedFactor", 0f, new ConfigDescription("Speed multiplier while frozen. 0 is the RoR2 freeze. On the ground a cap of 0 clamps horizontal speed, though a player who is already still can creep. In the air the clamp does not run, so momentum remains. Per player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			FreezeBlocksJump = cfg.Bind<bool>("Bomb", "FreezeBlocksJump", true, "When true, a frozen local player cannot jump. Not from the wiki. It comes from the ideas doc. Per player.");
			MaxLiveBombs = cfg.Bind<int>("Bomb", "MaxLiveBombs", 16, new ConfigDescription("Most ice bombs kept at once on this client. Further deaths spawn no bomb. Not from the wiki.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>()));
			TintStrength = cfg.Bind<float>("Visual", "TintStrength", 0.6f, new ConfigDescription("How far each mob material color moves toward TintColor. 0 leaves the mob unchanged. 1 replaces the color. Not from the wiki.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			TintColor = cfg.Bind<Color>("Visual", "TintColor", new Color(0.55f, 0.85f, 1f, 1f), "Color blended into the mob. Not from the wiki. Default is 0.55, 0.85, 1.");
			HighContrast = cfg.Bind<bool>("Visual", "HighContrast", true, "When true, the ice bomb's dark frost crust is wider, its crystals are 15% larger, and the slow icon has a thicker dark outline. The crust is dark against grass and snow either way, so the edge reads in grayscale. This makes it larger. Per player. Read when that bomb spawns. Not from the wiki.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "When true, log each Glacial mob id as the component is attached, and log when another mod already promoted that mob. Not from the wiki.");
		}
	}
	internal sealed class GlacialElite : MonoBehaviour
	{
		private bool bombClaimed;

		public bool TryClaimBomb()
		{
			if (bombClaimed)
			{
				return false;
			}
			bombClaimed = true;
			return true;
		}

		private void Start()
		{
			Tint();
		}

		private void Tint()
		{
			//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_0081: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			Color value = GlacialPlugin.Cfg.TintColor.Value;
			float value2 = GlacialPlugin.Cfg.TintStrength.Value;
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!OnceClaim.TryClaim(val))
				{
					continue;
				}
				Material[] materials = val.materials;
				List<Material> list = new List<Material>();
				foreach (Material val2 in materials)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						list.Add(val2);
						if (val2.HasProperty("_Color"))
						{
							val2.color = Color.Lerp(val2.color, value, value2);
						}
					}
				}
				if (list.Count != 0)
				{
					((Component)val).gameObject.AddComponent<TintCleanup>().Hold(list.ToArray());
				}
			}
		}
	}
	internal static class GlacialHooks
	{
		public static AttackLedger Attacks = new AttackLedger();

		public static bool InGlacialImpact;
	}
	internal static class GlacialLimits
	{
		public static float Positive(float value, float fallback)
		{
			if (!(value > 0f))
			{
				return fallback;
			}
			return value;
		}
	}
	internal enum GlacialDecision
	{
		Ignore,
		Promote,
		Adopt
	}
	internal enum FinishAction
	{
		None,
		WarnMissing,
		KeepExisting,
		ForeignClaim,
		Attach
	}
	internal sealed class GlacialIntent
	{
		public GlacialDecision Decision;

		public float Multiplier;

		public float BossMultiplier;

		public float IncomingMultiplier;

		public float IncomingBossMultiplier;
	}
	internal static class GlacialPlan
	{
		public static GlacialIntent Decide(bool seedOk, bool eligible, bool rolled, float multiplier, float bossMultiplier, float healthFactor, float damageFactor)
		{
			GlacialIntent glacialIntent = new GlacialIntent
			{
				IncomingMultiplier = multiplier,
				IncomingBossMultiplier = bossMultiplier,
				Multiplier = multiplier,
				BossMultiplier = bossMultiplier
			};
			if (!eligible)
			{
				glacialIntent.Decision = GlacialDecision.Ignore;
				return glacialIntent;
			}
			if (GlacialStats.IsTagged(multiplier, bossMultiplier, healthFactor, damageFactor))
			{
				glacialIntent.Decision = GlacialDecision.Adopt;
				return glacialIntent;
			}
			if (multiplier != 1f || !GlacialStats.IsPlain(bossMultiplier) || !seedOk || !rolled)
			{
				glacialIntent.Decision = GlacialDecision.Ignore;
				return glacialIntent;
			}
			ScaledMultipliers scaledMultipliers = GlacialStats.Tagged(bossMultiplier, healthFactor, damageFactor);
			glacialIntent.Decision = GlacialDecision.Promote;
			glacialIntent.Multiplier = scaledMultipliers.Multiplier;
			glacialIntent.BossMultiplier = scaledMultipliers.BossMultiplier;
			return glacialIntent;
		}

		public static FinishAction Finish(GlacialDecision decision, bool found, bool alreadyAttached, float multiplierNow, float bossMultiplierNow, float incomingMultiplier, float incomingBossMultiplier)
		{
			if (decision == GlacialDecision.Ignore)
			{
				return FinishAction.None;
			}
			if (!found)
			{
				return FinishAction.WarnMissing;
			}
			if (alreadyAttached)
			{
				return FinishAction.KeepExisting;
			}
			if (decision == GlacialDecision.Promote && (multiplierNow != incomingMultiplier || bossMultiplierNow != incomingBossMultiplier))
			{
				return FinishAction.ForeignClaim;
			}
			return FinishAction.Attach;
		}
	}
	[BepInPlugin("evan.muck.glacialelite", "Glacial Elite", "0.3.0")]
	public class GlacialPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.glacialelite";

		public const string Name = "Glacial Elite";

		public const string Version = "0.3.0";

		internal static ManualLogSource Log;

		internal static GlacialConfig Cfg;

		private void Awake()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new GlacialConfig(((BaseUnityPlugin)this).Config);
			GlacialSeed.Warn = delegate(string message)
			{
				Log.LogWarning((object)message);
			};
			Harmony val = new Harmony("evan.muck.glacialelite");
			if (!TryPatch(val, typeof(SpawnMobPatch)))
			{
				val.UnpatchSelf();
				Log.LogError((object)"Glacial spawn patch failed, mod disabled.");
				return;
			}
			bool flag = TryPatch(val, typeof(SpawnFxPatch)) & TryPatch(val, typeof(ImpactStartPatch)) & TryPatch(val, typeof(PlayerHitPatch));
			bool flag2 = TryPatch(val, typeof(SpeedPatch));
			bool flag3 = TryPatch(val, typeof(JumpPatch));
			bool flag4 = TryPatch(val, typeof(OnKillPatch));
			GlacialRunner.Ensure();
			Log.LogInfo((object)string.Format("{0} {1} loaded. slow={2} freezeJump={3} bomb={4}. All players should install this mod.", "Glacial Elite", "0.3.0", flag && flag2, flag3, flag4));
		}

		private static bool TryPatch(Harmony harmony, Type type)
		{
			try
			{
				harmony.CreateClassProcessor(type).Patch();
				return true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Glacial patch {type.Name} failed, feature off: {arg}");
				return false;
			}
		}
	}
	internal static class GlacialRoll
	{
		public const int Salt = 1196179779;

		public static int SaltedSeed(int seed)
		{
			return seed ^ 0x474C4143;
		}

		public static bool IsGlacial(int seed, int mobId, float chance)
		{
			if (!(chance > 0f))
			{
				return false;
			}
			return Hash01.Of(SaltedSeed(seed), mobId) < (double)chance;
		}
	}
	internal static class GlacialRules
	{
		public static bool Eligible(bool isBoss, bool isNeutral, int guardianType, bool includeNeutral)
		{
			if (isBoss)
			{
				return false;
			}
			if (guardianType != -1)
			{
				return false;
			}
			if (isNeutral && !includeNeutral)
			{
				return false;
			}
			return true;
		}
	}
	internal sealed class GlacialRunner : MonoBehaviour
	{
		private const float ClinkVolume = 0.55f;

		private const float ClinkPitch = 0.6f;

		private static readonly FreezeClink Clink = new FreezeClink();

		private static GlacialRunner instance;

		private FrostVignette vignette;

		public static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (!((Object)(object)instance != (Object)null))
			{
				GameObject val = new GameObject("MuckGlacial");
				Object.DontDestroyOnLoad((Object)(object)val);
				instance = val.AddComponent<GlacialRunner>();
				instance.vignette = val.AddComponent<FrostVignette>();
				((Behaviour)instance.vignette).enabled = false;
			}
		}

		public static void ShowFrost()
		{
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.vignette != (Object)null)
			{
				((Behaviour)instance.vignette).enabled = true;
			}
		}

		public static void Teardown()
		{
			if (!((Object)(object)instance == (Object)null))
			{
				GameObject gameObject = ((Component)instance).gameObject;
				instance = null;
				Object.Destroy((Object)(object)gameObject);
			}
		}

		public static void Detonate(Vector3 center)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)PlayerMovement.Instance == (Object)null)
			{
				return;
			}
			LocalClient local = LocalClient.instance;
			if ((Object)(object)local == (Object)null)
			{
				return;
			}
			Dictionary<int, PlayerManager> players = GameManager.players;
			if (players == null || !players.TryGetValue(local.myId, out var value) || (Object)(object)value == (Object)null || value.dead)
			{
				return;
			}
			GlacialConfig cfg = GlacialPlugin.Cfg;
			if (cfg == null)
			{
				return;
			}
			Vector3 val = ((Component)PlayerMovement.Instance).transform.position - center;
			float radius = GlacialLimits.Positive(cfg.BombRadius.Value, 4f);
			if (!IceBlast.InRange(val.x, val.y, val.z, radius))
			{
				return;
			}
			int damage = IceBlast.Damage(cfg.BombBaseDamage.Value, cfg.BombDamageFraction.Value, cfg.BombDamageCap.Value);
			PlayerStatus val2 = PlayerStatus.Instance;
			int hpAndShield = (((Object)(object)val2 != (Object)null) ? val2.HpAndShield() : 0);
			float time = Time.time;
			float value2 = cfg.FreezeSeconds.Value;
			if (BombHit.Resolve(GlacialStatus.Local, time, value2, damage, delegate
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				ClientSend.PlayerHit(damage, local.myId, 0f, 0, center);
			}, delegate(Exception e)
			{
				ManualLogSource log2 = GlacialPlugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)$"Glacial ice bomb hit threw on the way to the host; the freeze still applies. {e}");
				}
			}))
			{
				ManualLogSource log = GlacialPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Glacial freeze for {value2:0.##} s, from t={time:F2} to t={time + value2:F2}.");
				}
				if (damage > 0 && (Object)(object)val2 != (Object)null)
				{
					Clink.Arm(time, hpAndShield);
				}
				else
				{
					PlayClink();
				}
			}
			ShowFrost();
		}

		public static bool LocalPlayerDead()
		{
			LocalClient val = LocalClient.instance;
			Dictionary<int, PlayerManager> players = GameManager.players;
			if ((Object)(object)val == (Object)null || players == null || !players.TryGetValue(val.myId, out var value) || (Object)(object)value == (Object)null)
			{
				return false;
			}
			return value.dead;
		}

		private void Update()
		{
			if (Clink.Armed)
			{
				PlayerStatus val = PlayerStatus.Instance;
				if ((Object)(object)val == (Object)null)
				{
					Clink.Clear();
				}
				else if (Clink.Due(Time.time, val.HpAndShield(), LocalPlayerDead()))
				{
					PlayClink();
				}
			}
		}

		private static void PlayClink()
		{
			UiSfx val = UiSfx.Instance;
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val.audio == (Object)null) && !((Object)(object)val.armor == (Object)null))
			{
				val.audio.clip = val.armor;
				val.audio.volume = 0.55f;
				val.audio.pitch = 0.6f;
				val.audio.Play();
			}
		}

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

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

		private void OnDestroy()
		{
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
			ResetWorld();
		}

		private static void OnSceneChanged(Scene previous, Scene next)
		{
			ResetWorld();
			LogFingerprint(((Scene)(ref next)).name);
		}

		private static void ResetWorld()
		{
			IceBomb.DestroyAll();
			GlacialStatus.Local.Clear();
			Clink.Clear();
			GlacialHooks.Attacks.Clear();
			GlacialHooks.InGlacialImpact = false;
		}

		private static void LogFingerprint(string when)
		{
			GlacialConfig cfg = GlacialPlugin.Cfg;
			if (cfg != null && GlacialPlugin.Log != null)
			{
				ulong num = ConfigFingerprint.Of(cfg.Chance.Value, cfg.IncludeNeutral.Value, cfg.HealthFactor.Value, cfg.DamageFactor.Value);
				GlacialPlugin.Log.LogInfo((object)$"Glacial config {num:X16} at {when}. Chance={cfg.Chance.Value} IncludeNeutral={cfg.IncludeNeutral.Value} HealthFactor={cfg.HealthFactor.Value} DamageFactor={cfg.DamageFactor.Value}. Every peer should log the same hash.");
			}
		}
	}
	internal static class GlacialSeed
	{
		private static bool warned;

		internal static Func<int> ReadSeed;

		internal static Action<string> Warn;

		public static bool TryRead(out int seed)
		{
			try
			{
				Func<int> func = ReadSeed ?? new Func<int>(DefaultRead);
				seed = func();
				return true;
			}
			catch (Exception ex)
			{
				seed = 0;
				if (!warned)
				{
					warned = true;
					Warn?.Invoke("GameManager.GetSeed failed. No new mob will be Glacial. " + ex.Message);
				}
				return false;
			}
		}

		private static int DefaultRead()
		{
			return GameManager.GetSeed();
		}
	}
	internal static class GlacialStats
	{
		public const float Tag = 1f / 64f;

		public const float TagTolerance = 0.0001f;

		public static ScaledMultipliers Tagged(float bossMultiplier, float healthFactor, float damageFactor)
		{
			float num = Factor(damageFactor);
			float num2 = Factor(healthFactor);
			float num3 = (float)Math.Round(bossMultiplier * num2 / num * 32f, MidpointRounding.AwayFromZero) / 32f;
			return new ScaledMultipliers(num, num3 + 1f / 64f);
		}

		public static bool IsTagged(float multiplier, float bossMultiplier, float healthFactor, float damageFactor)
		{
			float num = Factor(damageFactor);
			if (Math.Abs(multiplier - num) > 0.0001f || !(bossMultiplier > 0f))
			{
				return false;
			}
			double num2 = (double)bossMultiplier * 64.0;
			double num3 = Math.Round(num2);
			if (Math.Abs(num2 - num3) <= 0.006399999838322401)
			{
				return Math.Abs(Math.IEEERemainder(num3, 2.0)) == 1.0;
			}
			return Math.Abs(bossMultiplier - (Factor(healthFactor) / num + 1f / 64f)) <= 0.0001f;
		}

		public static bool IsPlain(float bossMultiplier)
		{
			if (!(bossMultiplier > 0f) || float.IsInfinity(bossMultiplier))
			{
				return false;
			}
			double num = (double)bossMultiplier * 16.0;
			return Math.Abs(num - Math.Round(num)) <= 0.0015999999595806003;
		}

		private static float Factor(float value)
		{
			if (value > 0f)
			{
				return value;
			}
			return 1f;
		}
	}
	internal sealed class GlacialStatus
	{
		public static readonly GlacialStatus Local = new GlacialStatus();

		private float slowUntil = float.NegativeInfinity;

		private float freezeUntil = float.NegativeInfinity;

		public void Slow(float now, float seconds)
		{
			if (seconds > 0f)
			{
				slowUntil = Math.Max(slowUntil, now + seconds);
			}
		}

		public bool Freeze(float now, float seconds)
		{
			if (!(seconds > 0f))
			{
				return false;
			}
			bool num = IsFrozen(now);
			freezeUntil = Math.Max(freezeUntil, now + seconds);
			return !num;
		}

		public float OverlayAlpha(float now)
		{
			return FrostOverlay.Alpha(now, slowUntil, freezeUntil);
		}

		public bool IsSlowed(float now)
		{
			return now < slowUntil;
		}

		public bool IsFrozen(float now)
		{
			return now < freezeUntil;
		}

		public float SpeedFactor(float now, float slowFactor, float freezeFactor)
		{
			if (IsFrozen(now))
			{
				return Factor(freezeFactor);
			}
			if (IsSlowed(now))
			{
				return Factor(slowFactor);
			}
			return 1f;
		}

		public void Clear()
		{
			slowUntil = float.NegativeInfinity;
			freezeUntil = float.NegativeInfinity;
		}

		private static float Factor(float value)
		{
			if (float.IsNaN(value))
			{
				return 1f;
			}
			if (value < 0f)
			{
				return 0f;
			}
			if (value > 1f)
			{
				return 1f;
			}
			return value;
		}
	}
	internal static class Hash01
	{
		public static ulong SplitMix64(ulong x)
		{
			x += 11400714819323198485uL;
			x = (x ^ (x >> 30)) * 13787848793156543929uL;
			x = (x ^ (x >> 27)) * 10723151780598845931uL;
			return x ^ (x >> 31);
		}

		public static double Of(int a, int b)
		{
			return ToUnit(SplitMix64(((ulong)(uint)a << 32) | (uint)b));
		}

		private static double ToUnit(ulong h)
		{
			return (double)(h >> 11) * 1.1102230246251565E-16;
		}
	}
	internal static class HazardMark
	{
		public const float WindupSeconds = 0.5f;

		public const int CookieSize = 128;

		public const float TintR = 0.45f;

		public const float TintG = 0.75f;

		public const float TintB = 1f;

		public const float BeatsStart = 1.5f;

		public const float BeatsGain = 9f;

		public const float PeakGain = 1.4f;

		public const float FlashGain = 2f;

		public const float HissPeakVolume = 0.29f;

		public const float HissPeakPitch = 1.79f;

		public const float CrystalGlowShare = 0.85f;

		public const float CrystalWhiten = 0.35f;

		public const float CrystalEmission = 0.6f;

		public static float CrustInner(bool highContrast)
		{
			if (!highContrast)
			{
				return 0.8f;
			}
			return 0.7f;
		}

		public static float PieceScale(float radius, float designRadius, bool highContrast)
		{
			float num = ((radius > 0f && designRadius > 0f) ? (radius / designRadius) : 1f);
			if (num < 0.6f)
			{
				num = 0.6f;
			}
			if (num > 1.5f)
			{
				num = 1.5f;
			}
			if (!highContrast)
			{
				return num;
			}
			return num * 1.15f;
		}

		public static float HurtDelay(float fuse)
		{
			return Math.Max((fuse > 0f && !float.IsNaN(fuse)) ? fuse : 0.5f, 0.5f);
		}

		public static float Grow(float age)
		{
			if (float.IsNaN(age) || age <= 0f)
			{
				return 0f;
			}
			float num = age / 0.5f;
			if (num > 1f)
			{
				num = 1f;
			}
			return 1f - (1f - num) * (1f - num);
		}

		public static bool Armed(float age)
		{
			return age >= 0.5f;
		}

		public static float ShownRadius(float radius, float age)
		{
			return ((radius > 0f && !float.IsNaN(radius)) ? radius : 4f) * Grow(age);
		}

		public static bool CrossedIn(bool wasInside, bool nowInside)
		{
			if (nowInside)
			{
				return !wasInside;
			}
			return false;
		}

		public static float IconScale(float time, bool frozen)
		{
			if (frozen || float.IsNaN(time))
			{
				return 1f;
			}
			return 1f + 0.06f * (0.5f + 0.5f * (float)Math.Sin((double)time * 3.5));
		}

		public static float ProjectorAlpha(float dist, bool highContrast)
		{
			float num = CrustInner(highContrast);
			if (float.IsNaN(dist) || !(dist < num))
			{
				return 0f;
			}
			if (!(dist >= num - 0.14f))
			{
				return 0.6f;
			}
			return 0.85f;
		}

		public static float ProjectorAdds(float alpha)
		{
			return alpha * alpha;
		}

		public static float Luma(float r, float g, float b)
		{
			return 0.2126f * r + 0.7152f * g + 0.0722f * b;
		}

		public static float BuildProgress(float age, float hurtDelay)
		{
			if (float.IsNaN(age) || age < 0.5f)
			{
				return 0f;
			}
			float num = hurtDelay - 0.5f;
			if (!(num > 0.0001f))
			{
				return 1f;
			}
			return Clamp01((age - 0.5f) / num);
		}

		public static float Intensity(float progress)
		{
			float num = Clamp01(progress);
			return num * (0.4f + 0.6f * num);
		}

		public static float Beat(float progress)
		{
			float num = Clamp01(progress);
			double num2 = (double)(1.5f * num) + 4.5 * (double)num * (double)num;
			double num3 = (1.0 - Math.Cos(Math.PI * 2.0 * num2)) / 2.0;
			return (float)(num3 * num3);
		}

		public static float BuildGain(float progress)
		{
			float num = Intensity(progress);
			return 1f + 0.39999998f * num + (0.22f + 0.18f * num) * Beat(progress);
		}

		public static float BuildGlow(float progress)
		{
			float num = Intensity(progress);
			return Clamp01(0.6f * num + (0.25f + 0.25f * num) * Beat(progress));
		}

		public static float HissPitch(float progress)
		{
			return 1.25f + 0.5f * Intensity(progress) + 0.04f * Beat(progress);
		}

		public static float HissVolume(float progress)
		{
			return 0.09f + 0.17f * Intensity(progress) + 0.03f * Beat(progress);
		}

		public static float CrystalGlow(float rimGlow)
		{
			return 0.85f * Clamp01(rimGlow);
		}

		public static float Melted(float dist, float melt)
		{
			return dist + (1f - dist) * Clamp01(melt);
		}

		private static float Clamp01(float v)
		{
			if (!float.IsNaN(v) && !(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}
	}
	internal readonly struct HazardFrame
	{
		public readonly string Clip;

		public readonly float Seconds;

		public readonly float Shown;

		public readonly float Gain;

		public readonly float Glow;

		public readonly float Melt;

		public readonly bool Done;

		public HazardFrame(string clip, float seconds, float shown, float gain, float glow, float melt, bool done)
		{
			Clip = clip;
			Seconds = seconds;
			Shown = shown;
			Gain = gain;
			Glow = glow;
			Melt = melt;
			Done = done;
		}
	}
	internal sealed class HazardClock
	{
		private readonly float born;

		private readonly float hurtDelay;

		private readonly float buildClipSeconds;

		private readonly float pulseSeconds;

		private readonly float shatterSeconds;

		private float pulseAt = float.NaN;

		private float shatterAt = float.NaN;

		private float heldGain = 1f;

		private float heldGlow;

		public bool Shattering => !float.IsNaN(shatterAt);

		public HazardClock(float born, float hurtDelay, float buildClipSeconds, float pulseSeconds, float shatterSeconds)
		{
			this.born = born;
			this.hurtDelay = HazardMark.HurtDelay(hurtDelay);
			this.buildClipSeconds = Math.Max(buildClipSeconds, 0f);
			this.pulseSeconds = Math.Max(pulseSeconds, 0.001f);
			this.shatterSeconds = Math.Max(shatterSeconds, 0.001f);
		}

		public float Progress(float now)
		{
			return HazardMark.BuildProgress(now - born, hurtDelay);
		}

		public void Pulse(float now)
		{
			if (!Shattering)
			{
				Hold(now);
				pulseAt = now;
			}
		}

		public void Shatter(float now)
		{
			if (!Shattering)
			{
				bool flag = !float.IsNaN(pulseAt) && now < pulseAt + pulseSeconds;
				if (!flag)
				{
					Hold(now);
				}
				shatterAt = (flag ? (pulseAt + pulseSeconds) : now);
			}
		}

		private void Hold(float now)
		{
			bool flag = now - born >= 0.5f;
			heldGain = (flag ? HazardMark.BuildGain(Progress(now)) : 1f);
			heldGlow = (flag ? HazardMark.BuildGlow(Progress(now)) : 0f);
		}

		public HazardFrame At(float now)
		{
			float num = now - born;
			if (Shattering && now >= shatterAt)
			{
				float num2 = now - shatterAt;
				if (num2 >= shatterSeconds)
				{
					return new HazardFrame("shatter", shatterSeconds, 1f, 0f, 0f, 1f, done: true);
				}
				float num3 = 1f - num2 / shatterSeconds;
				return new HazardFrame("shatter", num2, 1f, heldGain * num3, heldGlow * num3, 1f - num3, done: false);
			}
			if (!float.IsNaN(pulseAt) && now >= pulseAt && now < pulseAt + pulseSeconds)
			{
				float num4 = now - pulseAt;
				float num5 = (float)Math.Sin(Math.PI * (double)num4 / (double)pulseSeconds);
				float gain = heldGain + (2f - heldGain) * num5;
				float glow = heldGlow + (1f - heldGlow) * num5;
				return new HazardFrame("pulse", num4, 1f, gain, glow, 0f, done: false);
			}
			if (num < 0.5f)
			{
				return new HazardFrame("emerge", Math.Max(num, 0f), HazardMark.Grow(num), 1f, 0f, 0f, done: false);
			}
			float num6 = Progress(now);
			return new HazardFrame("build", num6 * buildClipSeconds, 1f, HazardMark.BuildGain(num6), HazardMark.BuildGlow(num6), 0f, done: false);
		}
	}
	public sealed class HazardTelegraph : MonoBehaviour
	{
		private sealed class OwnIce
		{
			public Material Shared;

			public Material Copy;

			public Color Base;
		}

		private sealed class Piece
		{
			public RigPiece Data;

			public Transform Transform;

			public Renderer Renderer;

			public Vector3 Anchor;
		}

		private sealed class CrustShape
		{
			public Vector2[] Spots;

			public int[] Corner;

			public int[] Triangles;

			public Color[] Colors;

			public int[] SubMeshStart;

			public int[] SubMeshCount;

			public string[] SubMeshMaterial;
		}

		private static bool drawLogged;

		private static Texture2D discThin;

		private static Texture2D discThick;

		private static GameObject warningPrefab;

		private static bool warningSearched;

		private static readonly Dictionary<string, CrustShape> crustShapes = new Dictionary<string, CrustShape>();

		private IceRig rig;

		private HazardClock clock;

		private Projector projector;

		private Material projectorMaterial;

		private Transform crust;

		private Material crustRim;

		private float lastGlow = -1f;

		private float lastCrystalGlow = -1f;

		private readonly List<OwnIce> iceMaterials = new List<OwnIce>();

		private Mesh crustMesh;

		private CrustShape crustShape;

		private readonly List<Piece> pieces = new List<Piece>();

		private float full = 4f;

		private float builtShown = -1f;

		private float builtMelt = -1f;

		private float lastGain = -1f;

		private float pieceScale = 1f;

		private bool highContrast;

		private bool finished;

		private int groundMask = 512;

		private string drawPath = "unbuilt";

		private const string RimMaterial = "ice_pale";

		private const string GlowMaterial = "ice_white";

		public bool Finished => finished;

		public static HazardTelegraph Show(Vector3 center, float radius, bool highContrast, float hurtAfter)
		{
			//IL_0005: 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)
			HazardTelegraph hazardTelegraph = new GameObject("HazardRing").AddComponent<HazardTelegraph>();
			hazardTelegraph.Place(center, radius, highContrast, hurtAfter);
			return hazardTelegraph;
		}

		public static bool CanDraw()
		{
			return IceArt.CanDraw();
		}

		public void Place(Vector3 worldCenter, float radius, bool contrast, float hurtAfter)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			rig = IceArt.Rig();
			full = ((radius > 0f && !float.IsNaN(radius)) ? radius : 4f);
			highContrast = contrast;
			finished = false;
			builtShown = -1f;
			builtMelt = -1f;
			lastGain = -1f;
			lastGlow = -1f;
			lastCrystalGlow = -1f;
			groundMask = GroundMask();
			((Component)this).transform.localScale = Vector3.one;
			((Component)this).transform.rotation = Quaternion.identity;
			((Component)this).transform.position = SnapCenter(worldCenter);
			EnsureProjector();
			if (rig == null)
			{
				clock = new HazardClock(Time.time, hurtAfter, 0f, 0.2f, 0.5f);
				return;
			}
			clock = new HazardClock(Time.time, hurtAfter, rig.Seconds("build"), rig.Seconds("pulse"), rig.Seconds("shatter"));
			pieceScale = HazardMark.PieceScale(full, rig.Radius, highContrast);
			EnsureCrust();
			EnsurePieces();
			Apply(clock.At(Time.time));
		}

		public void Pulse()
		{
			if (clock != null)
			{
				clock.Pulse(Time.time);
			}
		}

		public void Shatter()
		{
			if (clock != null)
			{
				clock.Shatter(Time.time);
			}
		}

		public void Retire()
		{
			if ((Object)(object)this != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private void Update()
		{
			if (clock != null && !finished)
			{
				HazardFrame frame = clock.At(Time.time);
				Apply(frame);
				if (frame.Done)
				{
					finished = true;
				}
			}
		}

		private void Apply(HazardFrame frame)
		{
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: 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_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			float num = full * frame.Shown;
			if ((Object)(object)projector != (Object)null)
			{
				projector.orthographicSize = Math.Max(num, 0.05f);
				((Behaviour)projector).enabled = num > 0.02f && frame.Gain > 0.01f;
				SetGain(frame.Gain);
			}
			if (rig == null)
			{
				return;
			}
			if ((Object)(object)crust != (Object)null)
			{
				if (Math.Abs(num - builtShown) > 0.001f || Math.Abs(frame.Melt - builtMelt) > 0.001f)
				{
					Drape(num, frame.Melt);
				}
				((Component)crust).gameObject.SetActive(num > 0.02f && frame.Melt < 0.999f);
				SetGlow(frame.Glow);
			}
			SetCrystalGlow(HazardMark.CrystalGlow(frame.Glow));
			for (int i = 0; i < pieces.Count; i++)
			{
				Piece piece = pieces[i];
				RigPose rigPose = rig.Sample(frame.Clip, piece.Data.Name, frame.Seconds, loop: false);
				bool flag = !rigPose.Hidden && !frame.Done;
				if (piece.Renderer.enabled != flag)
				{
					piece.Renderer.enabled = flag;
				}
				if (flag)
				{
					V3 v = rigPose.Position - piece.Data.Pivot;
					piece.Transform.localPosition = piece.Anchor + new Vector3(v.X, v.Y, v.Z) * pieceScale;
					piece.Transform.localRotation = new Quaternion(rigPose.Qx, rigPose.Qy, rigPose.Qz, rigPose.Qw);
					piece.Transform.localScale = new Vector3(rigPose.Scale.X, rigPose.Scale.Y, rigPose.Scale.Z) * pieceScale;
				}
			}
		}

		private void SetGain(float gain)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)projectorMaterial == (Object)null) && !(Math.Abs(gain - lastGain) < 0.001f))
			{
				lastGain = gain;
				if (projectorMaterial.HasProperty("_Color"))
				{
					projectorMaterial.SetColor("_Color", new Color(0.45f * gain, 0.75f * gain, 1f * gain, 1f));
				}
			}
		}

		private void SetGlow(float glow)
		{
			//IL_0030: 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_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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)crustRim == (Object)null) && !(Math.Abs(glow - lastGlow) < 0.001f))
			{
				lastGlow = glow;
				Color color = Color.Lerp(RigColor("ice_pale"), RigColor("ice_white"), glow);
				if (crustRim.HasProperty("_EmissionColor"))
				{
					crustRim.SetColor("_EmissionColor", RigColor("ice_white") * (0.8f * glow));
					crustRim.color = color;
				}
				else
				{
					crustRim.color = Color.Lerp(Color.white, new Color(1.5f, 1.5f, 1.5f, 1f), glow);
				}
			}
		}

		private void SetCrystalGlow(float glow)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			if (iceMaterials.Count == 0 || Math.Abs(glow - lastCrystalGlow) < 0.001f)
			{
				return;
			}
			lastCrystalGlow = glow;
			Color val = RigColor("ice_white");
			foreach (OwnIce iceMaterial in iceMaterials)
			{
				if (iceMaterial.Copy.HasProperty("_EmissionColor"))
				{
					Color val2 = Color.Lerp(iceMaterial.Base, val, glow * 0.35f);
					iceMaterial.Copy.color = val2;
					iceMaterial.Copy.SetColor("_EmissionColor", val2 * (0.6f * glow));
				}
				else
				{
					iceMaterial.Copy.color = Color.Lerp(Color.white, new Color(1.6f, 1.6f, 1.6f, 1f), glow);
				}
			}
		}

		private Material OwnCopy(Material shared, string paletteName)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//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_0084: Unknown result type (might be due to invalid IL or missing references)
			foreach (OwnIce iceMaterial in iceMaterials)
			{
				if ((Object)(object)iceMaterial.Shared == (Object)(object)shared)
				{
					return iceMaterial.Copy;
				}
			}
			Material val = new Material(shared)
			{
				name = ((Object)shared).name + " (bomb)"
			};
			if (val.HasProperty("_EmissionColor"))
			{
				val.EnableKeyword("_EMISSION");
				val.SetColor("_EmissionColor", Color.black);
			}
			iceMaterials.Add(new OwnIce
			{
				Shared = shared,
				Copy = val,
				Base = RigColor(paletteName)
			});
			return val;
		}

		private Color RigColor(string name)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			V3 value;
			V3 v = ((rig != null && rig.Materials.TryGetValue(name, out value)) ? value : new V3(1f, 1f, 1f));
			return new Color(v.X, v.Y, v.Z, 1f);
		}

		private void OnDestroy()
		{
			if ((Object)(object)crustMesh != (Object)null)
			{
				Object.Destroy((Object)(object)crustMesh);
			}
			if ((Object)(object)crustRim != (Object)null)
			{
				Object.Destroy((Object)(object)crustRim);
			}
			foreach (OwnIce iceMaterial in iceMaterials)
			{
				if ((Object)(object)iceMaterial.Copy != (Object)null)
				{
					Object.Destroy((Object)(object)iceMaterial.Copy);
				}
			}
			if ((Object)(object)projectorMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)projectorMaterial);
			}
		}

		private void EnsurePieces()
		{
			//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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			if (pieces.Count == 0)
			{
				foreach (RigPiece piece in rig.Pieces)
				{
					if (!piece.IsCrust)
					{
						GameObject val = new GameObject("Ice " + piece.Name);
						val.transform.SetParent(((Component)this).transform, false);
						val.AddComponent<MeshFilter>().sharedMesh = IceArt.MeshFor(piece);
						MeshRenderer val2 = val.AddComponent<MeshRenderer>();
						Material[] array = IceArt.MaterialsFor(piece);
						for (int i = 0; i < array.Length; i++)
						{
							array[i] = OwnCopy(array[i], piece.SubMeshes[i].Material);
						}
						((Renderer)val2).sharedMaterials = array;
						((Renderer)val2).shadowCastingMode = (ShadowCastingMode)1;
						((Renderer)val2).receiveShadows = true;
						pieces.Add(new Piece
						{
							Data = piece,
							Transform = val.transform,
							Renderer = (Renderer)(object)val2
						});
					}
				}
			}
			float num = full / rig.Radius;
			foreach (Piece piece2 in pieces)
			{
				float num2 = piece2.Data.Pivot.X * num;
				float num3 = piece2.Data.Pivot.Z * num;
				piece2.Anchor = new Vector3(num2, GroundY(num2, num3) + piece2.Data.Pivot.Y * pieceScale, num3);
			}
		}

		private void EnsureCrust()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			RigPiece rigPiece = rig.Piece(highContrast ? "crust_wide" : "crust");
			if (rigPiece == null)
			{
				return;
			}
			crustShape = ShapeOf(rigPiece);
			if ((Object)(object)crust == (Object)null)
			{
				GameObject val = new GameObject("FrostCrust");
				val.transform.SetParent(((Component)this).transform, false);
				val.AddComponent<MeshFilter>();
				MeshRenderer obj = val.AddComponent<MeshRenderer>();
				((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)obj).receiveShadows = true;
				crustMesh = new Mesh
				{
					name = "GlacialFrostCrust"
				};
				val.GetComponent<MeshFilter>().sharedMesh = crustMesh;
				crust = val.transform;
			}
			Material[] array = IceArt.MaterialsFor(rigPiece);
			if ((Object)(object)crustRim != (Object)null)
			{
				Object.Destroy((Object)(object)crustRim);
			}
			crustRim = null;
			for (int i = 0; i < rigPiece.SubMeshes.Count; i++)
			{
				if (!(rigPiece.SubMeshes[i].Material != "ice_pale"))
				{
					crustRim = new Material(array[i])
					{
						name = "GlacialFrostCrustRim"
					};
					if (crustRim.HasProperty("_EmissionColor"))
					{
						crustRim.EnableKeyword("_EMISSION");
						crustRim.SetColor("_EmissionColor", Color.black);
					}
					array[i] = crustRim;
				}
			}
			((Renderer)((Component)crust).GetComponent<MeshRenderer>()).sharedMaterials = array;
		}

		private static CrustShape ShapeOf(RigPiece data)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (crustShapes.TryGetValue(data.Name, out var value))
			{
				return value;
			}
			List<Vector2> list = new List<Vector2>();
			Dictionary<long, int> dictionary = new Dictionary<long, int>();
			int[] array = new int[data.Positions.Length];
			for (int i = 0; i < array.Length; i++)
			{
				V3 v = data.Positions[i];
				long key = ((long)Mathf.RoundToInt(v.X * 1000f) << 32) ^ (uint)Mathf.RoundToInt(v.Z * 1000f);
				if (!dictionary.TryGetValue(key, out var value2))
				{
					value2 = list.Count;
					list.Add(new Vector2(v.X, v.Z));
					dictionary[key] = value2;
				}
				array[i] = value2;
			}
			List<int> list2 = new List<int>();
			int[] array2 = new int[data.SubMeshes.Count];
			int[] array3 = new int[data.SubMeshes.Count];
			string[] array4 = new string[data.SubMeshes.Count];
			for (int j = 0; j < data.SubMeshes.Count; j++)
			{
				int[] triangles = data.SubMeshes[j].Triangles;
				array2[j] = list2.Count;
				array3[j] = triangles.Length;
				array4[j] = data.SubMeshes[j].Material;
				for (int k = 0; k + 2 < triangles.Length; k += 3)
				{
					V3 v2 = data.Positions[triangles[k]];
					V3 v3 = data.Positions[triangles[k + 1]];
					V3 v4 = data.Positions[triangles[k + 2]];
					float num = (v3.Z - v2.Z) * (v4.X - v2.X) - (v3.X - v2.X) * (v4.Z - v2.Z);
					list2.Add(triangles[k]);
					if (num >= 0f)
					{
						list2.Add(triangles[k + 1]);
						list2.Add(triangles[k + 2]);
					}
					else
					{
						list2.Add(triangles[k + 2]);
						list2.Add(triangles[k + 1]);
					}
				}
			}
			Mesh val = IceArt.MeshFor(data);
			value = new CrustShape
			{
				Spots = list.ToArray(),
				Corner = array,
				Triangles = list2.ToArray(),
				Colors = val.colors,
				SubMeshStart = array2,
				SubMeshCount = array3,
				SubMeshMaterial = array4
			};
			crustShapes[data.Name] = value;
			return value;
		}

		private void Drape(float shown, float melt)
		{
			//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_009d: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: 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_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: 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_014a: Unknown result type (might be due to invalid IL or missing references)
			builtShown = shown;
			builtMelt = melt;
			if (!((Object)(object)crustMesh == (Object)null) && crustShape != null)
			{
				float num = shown / rig.Radius;
				Vector3[] array = (Vector3[])(object)new Vector3[crustShape.Spots.Length];
				Vector3[] array2 = (Vector3[])(object)new Vector3[array.Length];
				for (int i = 0; i < array.Length; i++)
				{
					Vector2 val = crustShape.Spots[i];
					float num2 = ((Vector2)(ref val)).magnitude / rig.Radius;
					float num3 = ((num2 > 0.0001f) ? (HazardMark.Melted(num2, melt) / num2) : 1f);
					float x = val.x * num * num3;
					float z = val.y * num * num3;
					array[i] = Ground(x, z, out array2[i]);
				}
				int num4 = crustShape.Corner.Length;
				Vector3[] array3 = (Vector3[])(object)new Vector3[num4];
				Vector3[] array4 = (Vector3[])(object)new Vector3[num4];
				for (int j = 0; j < num4; j++)
				{
					int num5 = crustShape.Corner[j];
					array3[j] = array[num5] + array2[num5] * 0.06f;
					array4[j] = array2[num5];
				}
				crustMesh.Clear();
				crustMesh.vertices = array3;
				crustMesh.normals = array4;
				crustMesh.colors = crustShape.Colors;
				crustMesh.subMeshCount = crustShape.SubMeshStart.Length;
				for (int k = 0; k < crustShape.SubMeshStart.Length; k++)
				{
					int[] array5 = new int[crustShape.SubMeshCount[k]];
					Array.Copy(crustShape.Triangles, crustShape.SubMeshStart[k], array5, 0, array5.Length);
					crustMesh.SetTriangles(array5, k);
				}
				crustMesh.RecalculateBounds();
			}
		}

		private Vector3 SnapCenter(Vector3 from)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(from + Vector3.up * 30f, Vector3.down, ref val, 80f, groundMask))
			{
				return ((RaycastHit)(ref val)).point;
			}
			return from;
		}

		private Vector3 Ground(float x, float z, out Vector3 normal)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)this).transform.position + new Vector3(x, 0f, z);
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(new Vector3(val.x, ((Component)this).transform.position.y + 40f, val.z), Vector3.down, ref val2, 90f, groundMask))
			{
				normal = ((RaycastHit)(ref val2)).normal;
				return ((Component)this).transform.InverseTransformPoint(((RaycastHit)(ref val2)).point);
			}
			normal = Vector3.up;
			return new Vector3(x, 0f, z);
		}

		private float GroundY(float x, float z)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Vector3 normal;
			return Ground(x, z, out normal).y;
		}

		private static int GroundMask()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameManager instance = GameManager.instance;
				if ((Object)(object)instance != (Object)null)
				{
					int num = LayerMask.op_Implicit(instance.whatIsGround);
					if (num != 0)
					{
						return num;
					}
				}
			}
			catch (Exception)
			{
			}
			return 512;
		}

		private void EnsureProjector()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)projector != (Object)null)
			{
				ApplyCookie();
				return;
			}
			Material val = CopyVanillaProjector();
			Shader val2 = (((Object)(object)val != (Object)null) ? val.shader : Shader.Find("Projector/AdditiveTint"));
			if ((Object)(object)val2 == (Object)null)
			{
				drawPath = "the frost crust alone";
				LogDraw();
				return;
			}
			GameObject val3 = new GameObject("GroundProjector");
			val3.transform.SetParent(((Component)this).transform, false);
			val3.transform.localPosition = new Vector3(0f, 12f, 0f);
			val3.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
			projector = val3.AddComponent<Projector>();
			projector.orthographic = true;
			projector.nearClipPlane = 0.05f;
			projector.farClipPlane = 40f;
			projector.aspectRatio = 1f;
			projectorMaterial = (Material)(((Object)(object)val != (Object)null) ? ((object)val) : ((object)new Material(val2)));
			((Object)projectorMaterial).name = "HazardProjector";
			projector.material = projectorMaterial;
			drawPath = (((Object)(object)val != (Object)null) ? "the vanilla AttackWarning projector" : "Projector/AdditiveTint");
			ApplyCookie();
			LogDraw();
		}

		private void ApplyCookie()
		{
			if (!((Object)(object)projectorMaterial == (Object)null))
			{
				if (projectorMaterial.HasProperty("_ShadowTex"))
				{
					projectorMaterial.SetTexture("_ShadowTex", (Texture)(object)Disc(highContrast));
				}
				if (projectorMaterial.HasProperty("_Attenuation"))
				{
					projectorMaterial.SetFloat("_Attenuation", 1f);
				}
				lastGain = -1f;
				SetGain(1f);
			}
		}

		private static Texture2D Disc(bool high)
		{
			if (high)
			{
				if (!((Object)(object)discThick != (Object)null))
				{
					return discThick = BakeDisc(high: true);
				}
				return discThick;
			}
			if (!((Object)(object)discThin != (Object)null))
			{
				return discThin = BakeDisc(high: false);
			}
			return discThin;
		}

		private static Texture2D BakeDisc(bool high)
		{
			//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_0024: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			int num = 128;
			Texture2D val = new Texture2D(num, num, (TextureFormat)4, false)
			{
				name = (high ? "GlacialIceWashWide" : "GlacialIceWash"),
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)61
			};
			Color[] array = (Color[])(object)new Color[num * num];
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < num; j++)
				{
					float num2 = (((float)j + 0.5f) / (float)num - 0.5f) * 2f;
					float num3 = (((float)i + 0.5f) / (float)num - 0.5f) * 2f;
					float num4 = HazardMark.ProjectorAlpha(Mathf.Sqrt(num2 * num2 + num3 * num3), high);
					array[i * num + j] = new Color(1f, 1f, 1f, num4);
				}
			}
			val.SetPixels(array);
			val.Apply(false, true);
			return val;
		}

		private Material CopyVanillaProjector()
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			GameObject val = WarningPrefab();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = null;
			try
			{
				val2 = Object.Instantiate<GameObject>(val);
				val2.SetActive(false);
				EnemyAttackIndicator component = val2.GetComponent<EnemyAttackIndicator>();
				if ((Object)(object)component != (Object)null)
				{
					((Behaviour)component).enabled = false;
				}
				Material material = val2.GetComponentInChildren<Projector>(true).material;
				if ((Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null)
				{
					return null;
				}
				return new Material(material);
			}
			catch (Exception ex)
			{
				if (GlacialPlugin.Log != null)
				{
					GlacialPlugin.Log.LogWarning((object)("Glacial hazard ring could not copy AttackWarning. " + ex.Message));
				}
				return null;
			}
			finally
			{
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)val2);
				}
			}
		}

		private static GameObject WarningPrefab()
		{
			if ((Object)(object)warningPrefab != (Object)null)
			{
				return warningPrefab;
			}
			if (warningSearched)
			{
				return null;
			}
			try
			{
				ItemManager instance = ItemManager.Instance;
				if (instance == null)
				{
					return null;
				}
				warningSearched = true;
				InventoryItem itemByName = instance.GetItemByName("Spike Attack");
				if ((Object)(object)itemByName == (Object)null || (Object)(object)itemByName.prefab == (Object)null)
				{
					return null;
				}
				GuardianSpikes component = itemByName.prefab.GetComponent<GuardianSpikes>();
				if ((Object)(object)component == (Object)null || (Object)(object)component.warningFx == (Object)null)
				{
					return null;
				}
				warningPrefab = component.warningFx;
			}
			catch (Exception ex)
			{
				if (GlacialPlugin.Log != null)
				{
					GlacialPlugin.Log.LogWarning((object)("Glacial hazard ring could not find AttackWarning. " + ex.Message));
				}
			}
			return warningPrefab;
		}

		private void LogDraw()
		{
			if (!drawLogged && GlacialPlugin.Log != null)
			{
				drawLogged = true;
				GlacialPlugin.Log.LogInfo((object)("Glacial hazard ring core uses " + drawPath + "."));
			}
		}
	}
	internal static class IceArt
	{
		public const string ObjResource = "MuckGlacial.Art.ice_bomb.obj";

		public const string RigResource = "MuckGlacial.Art.ice_bomb.rig";

		public const string IconResource = "MuckGlacial.Art.slow.png";

		public const string IconWideResource = "MuckGlacial.Art.slow_hc.png";

		private static IceRig rig;

		private static bool rigFailed;

		private static readonly Dictionary<string, Mesh> Meshes = new Dictionary<string, Mesh>();

		private static readonly Dictionary<string, Material> Materials = new Dictionary<string, Material>();

		private static Material fallback;

		private static Shader lit;

		private static bool shaderLogged;

		private static readonly V3 Light = Normalize(new V3(0.3f, 1f, 0.2f));

		public static IceRig LoadRig()
		{
			return IceRig.Parse(ReadText("MuckGlacial.Art.ice_bomb.obj"), ReadText("MuckGlacial.Art.ice_bomb.rig"));
		}

		public static IceRig Rig()
		{
			if (rig != null || rigFailed)
			{
				return rig;
			}
			try
			{
				rig = LoadRig();
			}
			catch (Exception ex)
			{
				rigFailed = true;
				if (GlacialPlugin.Log != null)
				{
					GlacialPlugin.Log.LogError((object)("Glacial ice bomb model failed to load: " + ex.Message));
				}
			}
			return rig;
		}

		public static byte[] ReadBytes(string resource)
		{
			using Stream stream = Open(resource);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static string ReadText(string resource)
		{
			using Stream stream = Open(resource);
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		private static Stream Open(string resource)
		{
			return typeof(IceArt).Assembly.GetManifestResourceStream(resource) ?? throw new FormatException("Embedded resource " + resource + " is missing.");
		}

		public static Mesh MeshFor(RigPiece piece)
		{
			//IL_001f: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: 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)
			if (Meshes.TryGetValue(piece.Name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = new Mesh
			{
				name = "GlacialIce " + piece.Name,
				hideFlags = (HideFlags)61
			};
			Vector3[] array = (Vector3[])(object)new Vector3[piece.Positions.Length];
			Vector3[] array2 = (Vector3[])(object)new Vector3[piece.Positions.Length];
			Color[] array3 = (Color[])(object)new Color[piece.Positions.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Vector3(piece.Positions[i].X, piece.Positions[i].Y, piece.Positions[i].Z);
				array2[i] = new Vector3(piece.Normals[i].X, piece.Normals[i].Y, piece.Normals[i].Z);
			}
			foreach (RigSubMesh subMesh in piece.SubMeshes)
			{
				V3 v = Rig().Materials[subMesh.Material];
				int[] triangles = subMesh.Triangles;
				foreach (int num in triangles)
				{
					float num2 = 0.6f + 0.4f * Math.Max(0f, Dot(piece.Normals[num], Light));
					array3[num] = new Color(v.X * num2, v.Y * num2, v.Z * num2, 1f);
				}
			}
			value.vertices = array;
			value.normals = array2;
			value.colors = array3;
			value.subMeshCount = piece.SubMeshes.Count;
			for (int k = 0; k < piece.SubMeshes.Count; k++)
			{
				value.SetTriangles(piece.SubMeshes[k].Triangles, k);
			}
			value.RecalculateBounds();
			Meshes[piece.Name] = value;
			return value;
		}

		public static Material[] MaterialsFor(RigPiece piece)
		{
			Material[] array = (Material[])(object)new Material[piece.SubMeshes.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = MaterialFor(piece.SubMeshes[i].Material);
			}
			return array;
		}

		public static bool CanDraw()
		{
			if (Rig() != null)
			{
				if (!((Object)(object)Lit() != (Object)null))
				{
					return (Object)(object)Fallback() != (Object)null;
				}
				return true;
			}
			return false;
		}

		private static Material MaterialFor(string name)
		{
			//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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (Materials.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Shader val = Lit();
			if ((Object)(object)val == (Object)null)
			{
				return Fallback();
			}
			V3 v = Rig().Materials[name];
			value = new Material(val)
			{
				name = "GlacialIce " + name,
				hideFlags = (HideFlags)61
			};
			value.color = new Color(v.X, v.Y, v.Z, 1f);
			if (value.HasProperty("_Glossiness"))
			{
				value.SetFloat("_Glossiness", 0.15f);
			}
			if (value.HasProperty("_Metallic"))
			{
				value.SetFloat("_Metallic", 0f);
			}
			Materials[name] = value;
			return value;
		}

		private static Shader Lit()
		{
			if ((Object)(object)lit != (Object)null)
			{
				return lit;
			}
			lit = Shader.Find("Standard");
			LogShader(((Object)(object)lit != (Object)null) ? "Standard" : null);
			return lit;
		}

		private static Material Fallback()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			if ((Object)(object)fallback != (Object)null)
			{
				return fallback;
			}
			string[] array = new string[3] { "Sprites/Default", "Hidden/Internal-Colored", "Unlit/Color" };
			foreach (string text in array)
			{
				Shader val = Shader.Find(text);
				if (!((Object)(object)val == (Object)null))
				{
					fallback = new Material(val)
					{
						name = "GlacialIce fallback",
						hideFlags = (HideFlags)61
					};
					LogShader(text);
					return fallback;
				}
			}
			return null;
		}

		private static void LogShader(string name)
		{
			if (!shaderLogged && name != null && GlacialPlugin.Log != null)
			{
				shaderLogged = true;
				GlacialPlugin.Log.LogInfo((object)("Glacial ice bomb model uses shader " + name + "."));
			}
		}

		private static float Dot(V3 a, V3 b)
		{
			return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}

		private static V3 Normalize(V3 v)
		{
			float num = (float)Math.Sqrt(Dot(v, v));
			return new V3(v.X / num, v.Y / num, v.Z / num);
		}
	}
	internal static class IceBlast
	{
		public static bool InRange(float dx, float dy, float dz, float radius)
		{
			if (!(radius > 0f))
			{
				return false;
			}
			return dx * dx + dy * dy + dz * dz <= radius * radius;
		}

		public static int Damage(int baseDamage, float fraction, int cap)
		{
			if (baseDamage <= 0 || !(fraction > 0f) || cap <= 0)
			{
				return 0;
			}
			return Math.Min(cap, Math.Max(1, (int)((float)baseDamage * fraction)));
		}

		public static int Landed(int sent, float mobDamageMultiplier, float armor)
		{
			int num = (int)((double)sent * (double)mobDamageMultiplier);
			float num2 = (100f - Math.Max(0f, Math.Min(100f, armor))) / 100f;
			if (num2 < 0.02f)
			{
				num2 = 0.02f;
			}
			return (int)((double)num * (double)num2);
		}

		public static float NormalMobDamageMultiplier(int day)
		{
			return 0.9f + (float)Math.Pow(0.26f * (float)day, 1.5499999523162842);
		}
	}
	internal sealed class IceBomb : MonoBehaviour
	{
		private const float RetireAfter = 2f;

		private static readonly Stack<IceBomb> pool = new Stack<IceBomb>();

		private static readonly List<IceBomb> live = new List<IceBomb>();

		private static AudioClip hissClip;

		private static AudioClip crackleClip;

		private static AudioClip stingClip;

		private static AudioClip endClip;

		private static bool cuesResolved;

		private static bool cuesLogged;

		private static bool missingLogged;

		private HazardTelegraph ring;

		private AudioSource hiss;

		private AudioSource sting;

		private float born;

		private float hurtAt;

		private float radius;

		private bool detonated;

		private bool wasInside;

		private float retireAt;

		public static bool TrySpawn(Vector3 pos, float now)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if (!HazardTelegraph.CanDraw())
			{
				LogMissing();
				return false;
			}
			GlacialConfig cfg = GlacialPlugin.Cfg;
			if (cfg == null)
			{
				return false;
			}
			if (live.Count >= cfg.MaxLiveBombs.Value)
			{
				return false;
			}
			IceBomb iceBomb = ((pool.Count > 0) ? pool.Pop() : Create());
			((Component)iceBomb).gameObject.SetActive(true);
			iceBomb.Begin(pos, now, GlacialLimits.Positive(cfg.BombFuse.Value, 2f), GlacialLimits.Positive(cfg.BombRadius.Value, 4f));
			live.Add(iceBomb);
			return true;
		}

		public static void DestroyAll()
		{
			for (int i = 0; i < live.Count; i++)
			{
				if ((Object)(object)live[i] != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)live[i]).gameObject);
				}
			}
			live.Clear();
			while (pool.Count > 0)
			{
				IceBomb iceBomb = pool.Pop();
				if ((Object)(object)iceBomb != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)iceBomb).gameObject);
				}
			}
		}

		private static IceBomb Create()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			GameObject val = new GameObject("IceBomb");
			IceBomb iceBomb = val.AddComponent<IceBomb>();
			iceBomb.hiss = val.AddComponent<AudioSource>();
			iceBomb.sting = val.AddComponent<AudioSource>();
			Prepare(iceBomb.hiss, loop: true);
			Prepare(iceBomb.sting, loop: false);
			return iceBomb;
		}

		private static void Prepare(AudioSource source, bool loop)
		{
			source.playOnAwake = false;
			source.loop = loop;
			source.spatialBlend = 1f;
			source.dopplerLevel = 0f;
			source.minDistance = 2f;
			source.maxDistance = 36f;
			source.rolloffMode = (AudioRolloffMode)1;
			source.volume = 1f;
		}

		private void Begin(Vector3 pos, float now, float fuseSeconds, float blastRadius)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			((Component)this).transform.position = pos;
			((Component)this).transform.localScale = Vector3.one;
			born = now;
			hurtAt = now + HazardMark.HurtDelay(fuseSeconds);
			radius = blastRadius;
			detonated = false;
			wasInside = false;
			retireAt = 0f;
			ResolveCues();
			bool flag = GlacialPlugin.Cfg?.HighContrast.Value ?? true;
			if ((Object)(object)ring == (Object)null)
			{
				ring = HazardTelegraph.Show(pos, radius, flag, hurtAt - now);
				((Component)ring).transform.SetParent(((Component)this).transform, true);
			}
			else
			{
				((Component)ring).gameObject.SetActive(true);
				ring.Place(pos, radius, flag, hurtAt - now);
			}
			if ((Object)(object)hiss != (Object)null && (Object)(object)hissClip != (Object)null)
			{
				hiss.clip = hissClip;
				hiss.volume = HazardMark.HissVolume(0f);
				hiss.pitch = HazardMark.HissPitch(0f);
				hiss.loop = true;
				hiss.Play();
			}
			if ((Object)(object)sting != (Object)null && (Object)(object)crackleClip != (Object)null)
			{
				sting.PlayOneShot(crackleClip, 0.45f);
			}
		}

		private void Update()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			float age = Time.time - born;
			if (!detonated && HazardMark.Armed(age))
			{
				bool nowInside = LocalInside(((Component)this).transform.position, radius);
				if (HazardMark.CrossedIn(wasInside, nowInside) && (Object)(object)sting != (Object)null && (Object)(object)stingClip != (Object)null)
				{
					sting.PlayOneShot(stingClip, 0.6f);
				}
				wasInside = nowInside;
			}
			if (!detonated && (Object)(object)hiss != (Object)null && hiss.isPlaying)
			{
				float progress = HazardMark.BuildProgress(age, hurtAt - born);
				hiss.pitch = HazardMark.HissPitch(progress);
				hiss.volume = HazardMark.HissVolume(progress);
			}
			if (!detonated && Time.time >= hurtAt)
			{
				detonated = true;
				if ((Object)(object)ring != (Object)null)
				{
					ring.Pulse();
					ring.Shatter();
				}
				if ((Object)(object)hiss != (Object)null)
				{
					hiss.Stop();
				}
				if ((Object)(object)sting != (Object)null && (Object)(object)endClip != (Object)null)
				{
					sting.PlayOneShot(endClip, 0.75f);
				}
				GlacialRunner.Detonate(((Component)this).transform.position);
				retireAt = Time.time + 2f;
			}
			if (detonated && (Time.time >= retireAt || (Object)(object)ring == (Object)null || ring.Finished))
			{
				Retire();
			}
		}

		private void OnDisable()
		{
			if ((Object)(object)hiss != (Object)null)
			{
				hiss.Stop();
			}
		}

		private static bool LocalInside(Vector3 center, float reach)
		{
			//IL_0019: 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_001f: 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_0025: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)PlayerMovement.Instance == (Object)null)
			{
				return false;
			}
			Vector3 val = ((Component)PlayerMovement.Instance).transform.position - center;
			return IceBlast.InRange(val.x, val.y, val.z, reach);
		}

		private void Retire()
		{
			for (int i = 0; i < live.Count; i++)
			{
				if (!((Object)(object)live[i] != (Object)(object)this))
				{
					int index = live.Count - 1;
					live[i] = live[index];
					live.RemoveAt(index);
					break;
				}
			}
			((Component)this).gameObject.SetActive(false);
			pool.Push(this);
		}

		private static void ResolveCues()
		{
			if (cuesResolved)
			{
				return;
			}
			cuesResolved = true;
			AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>();
			if (array == null || array.Length == 0)
			{
				LogCues("none loaded");
				return;
			}
			string[] array2 = new string[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = (((Object)(object)array[i] != (Object)null) ? ((Object)array[i]).name : null);
			}
			hissClip = Take(array, array2, BombCues.Hiss);
			crackleClip = Take(array, array2, BombCues.Crackle);
			stingClip = Take(array, array2, BombCues.Sting);
			endClip = Take(array, array2, BombCues.End);
			if ((Object)(object)hissClip == (Object)null && (Object)(object)crackleClip == (Object)null && (Object)(object)stingClip == (Object)null && (Object)(object)endClip == (Object)null)
			{
				cuesResolved = false;
			}
			if (!cuesLogged)
			{
				cuesLogged = true;
				LogCues("hiss=" + NameOf(hissClip) + " crackle=" + NameOf(crackleClip) + " sting=" + NameOf(stingClip) + " end=" + NameOf(endClip));
			}
		}

		private static AudioClip Take(AudioClip[] clips, string[] names, string[] prefer)
		{
			int num = BombCues.Pick(prefer, names);
			if (num < 0 || (Object)(object)clips[num] == (Object)null)
			{
				return null;
			}
			return clips[num];
		}

		private static string NameOf(AudioClip clip)
		{
			if (!((Object)(object)clip != (Object)null))
			{
				return "missing";
			}
			return ((Object)clip).name;
		}

		private static void LogCues(string message)
		{
			if (GlacialPlugin.Log != null)
			{
				GlacialPlugin.Log.LogInfo((object)("Glacial ice bomb sounds: " + message + "."));
			}
		}

		private static void LogMissing()
		{
			if (!missingLogged)
			{
				missingLogged = true;
				if (GlacialPlugin.Log != null)
				{
					GlacialPlugin.Log.LogWarning((object)"The ice bomb model or its shader is missing. Bombs are off.");
				}
			}
		}
	}
	internal readonly struct V3
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

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

		public static V3 operator -(V3 a, V3 b)
		{
			return new V3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}

		public static V3 Lerp(V3 a, V3 b, float t)
		{
			return new V3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t);
		}
	}
	internal readonly struct RigPose
	{
		public readonly V3 Position;

		public readonly float Qx;

		public readonly float Qy;

		public readonly float Qz;

		public readonly float Qw;

		public readonly V3 Scale;

		public bool Hidden
		{
			get
			{
				if (Scale.X < 0.001f && Scale.Y < 0.001f)
				{
					return Scale.Z < 0.001f;
				}
				return false;
			}
		}

		public float Largest => Math.Max(Scale.X, Math.Max(Scale.Y, Scale.Z));

		public RigPose(V3 position, float qx, float qy, float qz, float qw, V3 scale)
		{
			Position = position;
			Qx = qx;
			Qy = qy;
			Qz = qz;
			Qw = qw;
			Scale = scale;
		}

		public static RigPose Lerp(RigPose a, RigPose b, float t)
		{
			float num = ((a.Qx * b.Qx + a.Qy * b.Qy + a.Qz * b.Qz + a.Qw * b.Qw < 0f) ? (-1f) : 1f);
			float num2 = a.Qx + (b.Qx * num - a.Qx) * t;
			float num3 = a.Qy + (b.Qy * num - a.Qy) * t;
			float num4 = a.Qz + (b.Qz * num - a.Qz) * t;
			float num5 = a.Qw + (b.Qw * num - a.Qw) * t;
			float num6 = (float)Math.Sqrt(num2 * num2 + num3 * num3 + num4 * num4 + num5 * num5);
			if (num6 < 1E-06f)
			{
				num6 = 1f;
			}
			return new RigPose(V3.Lerp(a.Position, b.Position, t), num2 / num6, num3 / num6, num4 / num6, num5 / num6, V3.Lerp(a.Scale, b.Scale, t));
		}
	}
	internal sealed class RigSubMesh
	{
		public readonly string Material;

		public readonly int[] Triangles;

		public RigSubMesh(string material, int[] triangles)
		{
			Material = material;
			Triangles = triangles;
		}
	}
	internal sealed class RigPiece
	{
		public readonly string Name;

		public readonly string Role;

		public readonly V3 Pivot;

		public readonly V3[] Positions;

		public readonly V3[] Normals;

		public readonly IReadOnlyList<RigSubMesh> SubMeshes;

		public int TriangleCount => Positions.Length / 3;

		public bool IsCrust
		{
			get
			{
				if (!(Role == "crust"))
				{
					return Role == "crust_wide";
				}
				return true;
			}
		}

		public RigPiece(string name, string role, V3 pivot, V3[] positions, V3[] normals, IReadOnlyList<RigSubMesh> subMeshes)
		{
			Name = name;
			Role = role;
			Pivot = pivot;
			Positions = positions;
			Normals = normals;
			SubMeshes = subMeshes;
		}
	}
	internal sealed class RigClip
	{
		public readonly string Name;

		public readonly int Frames;

		public readonly Dictionary<string, RigPose[]> Tracks = new Dictionary<string, RigPose[]>();

		public RigClip(string name, int frames)
		{
			Name = name;
			Frames = frames;
		}
	}
	internal sealed class IceRig
	{
		private sealed class ObjObject
		{
			public readonly List<V3> Positions = new List<V3>();

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

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

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

		public const string Crust = "crust";

		public const string CrustWide = "crust_wide";

		public const string Emerge = "emerge";

		public const string Build = "build";

		public const string Pulse = "pulse";

		public const string Shatter = "shatter";

		public readonly float Radius;

		public readonly int Fps;

		public readonly Dictionary<string, V3> Materials;

		public readonly List<RigPiece> Pieces;

		public readonly Dictionary<string, RigClip> Clips;

		private IceRig(float radius, int fps, Dictionary<string, V3> materials, List<RigPiece> pieces, Dictionary<string, RigClip> clips)
		{
			Radius = radius;
			Fps = fps;
			Materials = materials;
			Pieces = pieces;
			Clips = clips;
		}

		public float Seconds(string clip)
		{
			if (!Clips.TryGetValue(clip, out var value))
			{
				return 0f;
			}
			return (float)(value.Frames - 1) / (float)Fps;
		}

		public RigPiece Piece(string name)
		{
			foreach (RigPiece piece in Pieces)
			{
				if (piece.Name == name)
				{
					return piece;
				}
			}
			return null;
		}

		public RigPose Sample(string clip, string piece, float seconds, bool loop)
		{
			if (!Clips.TryGetValue(clip, out var value) || !value.Tracks.TryGetValue(piece, out var value2) || value2.Length == 0)
			{
				return new RigPose(new V3(0f, 0f, 0f), 0f, 0f, 0f, 1f, new V3(0f, 0f, 0f));
			}
			if (float.IsNaN(seconds) || seconds < 0f)
			{
				seconds = 0f;
			}
			float num = seconds * (float)Fps;
			int num2 = value2.Length - 1;
			if (loop && num2 > 0)
			{
				num %= (float)num2;
			}
			else if (num >= (float)num2)
			{
				return value2[num2];
			}
			int num3 = (int)Math.Floor(num);
			if (num3 >= num2)
			{
				return value2[num2];
			}
			return RigPose.Lerp(value2[num3], value2[num3 + 1], num - (float)num3);
		}

		public static IceRig Parse(string objText, string rigText)
		{
			if (rigText == null)
			{
				throw new FormatException("ice_bomb.rig is missing.");
			}
			float num = 0f;
			int num2 = 0;
			Dictionary<string, V3> dictionary = new Dictionary<string, V3>();
			List<(string, string, V3, int)> list = new List<(string, string, V3, int)>();
			Dictionary<string, RigClip> dictionary2 = new Dictionary<string, RigClip>();
			RigClip clip = null;
			List<RigPose> track = null;
			string[] array = rigText.Split(new char[1] { '\n' });
			bool flag = false;
			string name = null;
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text[0] == '#')
				{
					continue;
				}
				string[] array2 = text.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
				switch (array2[0])
				{
				case "icerig":
					if (array2.Length < 2 || array2[1] != "1")
					{
						throw new FormatException("ice_bomb.rig: unknown version.");
					}
					flag = true;
					break;
				case "radius":
					num = Float(array2, 1, i);
					break;
				case "fps":
					num2 = (int)Float(array2, 1, i);
					break;
				case "material":
					Need(array2, 5, i);
					dictionary[array2[1]] = new V3(Float(array2, 2, i), Float(array2, 3, i), Float(array2, 4, i));
					break;
				case "object":
					Need(array2, 7, i);
					list.Add((array2[1], array2[2], new V3(Float(array2, 3, i), Float(array2, 4, i), Float(array2, 5, i)), (int)Float(array2, 6, i)));
					break;
				case "clip":
					Need(array2, 3, i);
					EndTrack(name);
					track = null;
					clip = new RigClip(array2[1], (int)Float(array2, 2, i));
					dictionary2[clip.Name] = clip;
					break;
				case "track":
					Need(array2, 2, i);
					if (clip == null)
					{
						throw new FormatException($"ice_bomb.rig line {i + 1}: a track outside a clip.");
					}
					EndTrack(name);
					name = array2[1];
					track = new List<RigPose>();
					break;
				default:
					if (track == null)
					{
						throw new FormatException($"ice_bomb.rig line {i + 1}: unexpected '{array2[0]}'.");
					}
					if (array2.Length != 10)
					{
						throw new FormatException($"ice_bomb.rig line {i + 1}: a frame needs 10 numbers.");
					}
					track.Add(new RigPose(new V3(Float(array2, 0, i), Float(array2, 1, i), Float(array2, 2, i)), Float(array2, 3, i), Float(array2, 4, i), Float(array2, 5, i), Float(array2, 6, i), new V3(Float(array2, 7, i), Float(array2, 8, i), Float(array2, 9, i))));
					break;
				}
			}
			EndTrack(name);
			if (!flag)
			{
				throw new FormatException("ice_bomb.rig: no icerig header.");
			}
			if (!(num > 0f) || num2 <= 0)
			{
				throw new FormatException("ice_bomb.rig: radius and fps must be positive.");
			}
			Dictionary<string, ObjObject> dictionary3 = ReadObj(objText);
			List<RigPiece> list2 = new List<RigPiece>();
			foreach (var item in list)
			{
				if (!dictionary3.TryGetValue(item.Item1, out var value))
				{
					throw new FormatException("ice_bomb: piece " + item.Item1 + " is missing from the OBJ.");
				}
				if (value.Positions.Count / 3 != item.Item4)
				{
					throw new FormatException($"ice_bomb: piece {item.Item1} has {value.Positions.Count / 3} triangles, the rig says {item.Item4}.");
				}
				V3[] array3 = new V3[value.Positions.Count];
				for (int j = 0; j < array3.Length; j++)
				{
					array3[j] = value.Positions[j] - item.Item3;
				}
				List<RigSubMesh> list3 = new List<RigSubMesh>();
				foreach (string item2 in value.MaterialOrder)
				{
					if (!dictionary.ContainsKey(item2))
					{
						throw new FormatException("ice_bomb: piece " + item.Item1 + " uses undefined material '" + item2 + "'.");
					}
					list3.Add(new RigSubMesh(item2, value.Triangles[item2].ToArray()));
				}
				list2.Add(new RigPiece(item.Item1, item.Item2, item.Item3, array3, value.Normals.ToArray(), list3));
			}
			if (dictionary3.Count != list.Count)
			{
				throw new FormatException($"ice_bomb: the OBJ has {dictionary3.Count} objects, the rig lists {list.Count}.");
			}
			foreach (RigPiece item3 in list2)
			{
				if (item3.IsCrust)
				{
					continue;
				}
				foreach (RigClip value2 in dictionary2.Values)
				{
					if (!value2.Tracks.ContainsKey(item3.Name))
					{
						throw new FormatException("ice_bomb.rig: " + item3.Name + " has no track in " + value2.Name + ".");
					}
				}
			}
			return new IceRig(num, num2, dictionary, list2, dictionary2);
			void EndTrack(string text2)
			{
				if (clip != null && track != null)
				{
					if (track.Count != clip.Frames)
					{
						throw new FormatException($"ice_bomb.rig: track {text2} in {clip.Name} has {track.Count} frames, the clip has {clip.Frames}.");
					}
					clip.Tracks[text2] = track.ToArray();
				}
			}
		}

		private static Dictionary<string, ObjObject> ReadObj(string text)
		{
			if (text == null)
			{
				throw new FormatException("ice_bomb.obj is missing.");
			}
			List<V3> list = new List<V3>();
			List<V3> list2 = new List<V3>();
			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":
					Need(array2, 4, i);
					list.Add(new V3(0f - Float(array2, 1, i), Float(array2, 2, i), Float(array2, 3, i)));
					break;
				case "vn":
					Need(array2, 4, i);
					list2.Add(new V3