Decompiled source of InvisibilityPotion v0.4.1

plugins/InvisibilityPotion.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using InvisibilityPotion.Commands;
using InvisibilityPotion.Config;
using InvisibilityPotion.Effects;
using InvisibilityPotion.Goggles;
using InvisibilityPotion.Items;
using InvisibilityPotion.Net;
using InvisibilityPotion.Plants;
using InvisibilityPotion.Visuals;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Splatform;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("InvisibilityPotion")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.4.1.0")]
[assembly: AssemblyInformationalVersion("0.4.1+7581b4c4750816ffce77b850b96f8b62f61851e2")]
[assembly: AssemblyProduct("InvisibilityPotion")]
[assembly: AssemblyTitle("InvisibilityPotion")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.1.0")]
namespace InvisibilityPotion
{
	public static class PatchHealth
	{
		public static string TargetKey(string typeName, string methodName)
		{
			return typeName + "." + methodName;
		}

		public static IReadOnlyList<string> MissingTargets(IEnumerable<string> expected, IEnumerable<string> patched)
		{
			HashSet<string> patchedSet = new HashSet<string>(patched);
			return expected.Where((string t) => !patchedSet.Contains(t)).ToList();
		}

		public static IReadOnlyList<string> PatchedTargets(Harmony harmony)
		{
			return (from m in harmony.GetPatchedMethods().Where(delegate(MethodBase m)
				{
					Patches patchInfo = Harmony.GetPatchInfo(m);
					return patchInfo != null && patchInfo.Owners.Contains(harmony.Id);
				})
				select TargetKey(m.DeclaringType?.Name ?? "?", m.Name)).Distinct().ToList();
		}

		public static IReadOnlyList<string> Report(Harmony harmony, IEnumerable<string> expectedTargets)
		{
			IReadOnlyList<string> readOnlyList = PatchedTargets(harmony);
			IReadOnlyList<string> readOnlyList2 = MissingTargets(expectedTargets, readOnlyList);
			foreach (string item in readOnlyList2)
			{
				Plugin.Log.LogError((object)("Patch missing: " + item + ". The game may have changed; this feature is disabled."));
			}
			Plugin.Log.LogInfo((object)$"Patch health: {readOnlyList.Count} targets patched, {readOnlyList2.Count} missing");
			return readOnlyList2;
		}
	}
	[BepInPlugin("saikopsych.InvisibilityPotion", "InvisibilityPotion", "0.4.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "saikopsych.InvisibilityPotion";

		public const string PluginName = "InvisibilityPotion";

		public const string PluginVersion = "0.4.1";

		public static readonly List<string> ExpectedPatchTargets = new List<string>
		{
			PatchHealth.TargetKey("BaseAI", "CanHearTarget"),
			PatchHealth.TargetKey("BaseAI", "CanSeeTarget"),
			PatchHealth.TargetKey("BaseAI", "FindEnemy"),
			PatchHealth.TargetKey("MonsterAI", "UpdateTarget"),
			PatchHealth.TargetKey("MonsterAI", "UpdateSleep"),
			PatchHealth.TargetKey("Player", "UpdateStealth"),
			PatchHealth.TargetKey("Character", "Damage"),
			PatchHealth.TargetKey("Attack", "StartDraw"),
			PatchHealth.TargetKey("Humanoid", "StartAttack"),
			PatchHealth.TargetKey("Humanoid", "BlockAttack"),
			PatchHealth.TargetKey("VisEquipment", "UpdateLodgroup"),
			PatchHealth.TargetKey("VisEquipment", "SetChestEquipped"),
			PatchHealth.TargetKey("VisEquipment", "SetLegEquipped"),
			PatchHealth.TargetKey("Player", "CanConsumeItem"),
			PatchHealth.TargetKey("EnemyHud", "TestShow"),
			PatchHealth.TargetKey("ZNet", "UpdatePlayerList"),
			PatchHealth.TargetKey("ZDOMan", "SendZDOs"),
			PatchHealth.TargetKey("Player", "UpdatePlacementGhost"),
			PatchHealth.TargetKey("Player", "GetMaxCarryWeight"),
			PatchHealth.TargetKey("Player", "PlacePiece"),
			PatchHealth.TargetKey("Player", "CheckRun"),
			PatchHealth.TargetKey("Plant", "Grow"),
			PatchHealth.TargetKey("ZoneSystem", "CreateGhostZones")
		};

		public static ManualLogSource Log { get; private set; }

		public static Harmony HarmonyInstance { get; private set; }

		public static IReadOnlyList<string> MissingPatches { get; private set; } = new List<string>();

		public static void Verbose(string message)
		{
			if (PluginConfig.VerboseLogging)
			{
				Log.LogInfo((object)message);
			}
			else
			{
				Log.LogDebug((object)message);
			}
		}

		private void Awake()
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			try
			{
				PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Config binding failed: {arg}");
			}
			try
			{
				Localization.Register();
			}
			catch (Exception arg2)
			{
				Log.LogError((object)$"Localization registration failed: {arg2}");
			}
			try
			{
				StatusEffects.Register();
			}
			catch (Exception arg3)
			{
				Log.LogError((object)$"Status effect registration failed: {arg3}");
			}
			try
			{
				AssetBundles.Load();
			}
			catch (Exception arg4)
			{
				Log.LogError((object)$"Asset bundle load failed: {arg4}");
			}
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				PluginConfig.Refresh();
				Cultivation.ApplyGrowTime();
			};
			PrefabManager.OnVanillaPrefabsAvailable += RegisterItemsOnce;
			HarmonyInstance = new Harmony("saikopsych.InvisibilityPotion");
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					HarmonyInstance.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Failed to apply patches in " + type.FullName + ": " + ex.Message));
				}
			}
			MissingPatches = PatchHealth.Report(HarmonyInstance, ExpectedPatchTargets);
			try
			{
				((Component)this).gameObject.AddComponent<VeilController>();
			}
			catch (Exception arg5)
			{
				Log.LogError((object)$"Veil controller setup failed: {arg5}");
			}
			try
			{
				((Component)this).gameObject.AddComponent<VeilSightDriver>();
			}
			catch (Exception arg6)
			{
				Log.LogError((object)$"Veil sight driver setup failed: {arg6}");
			}
			try
			{
				AdminCommands.Register();
			}
			catch (Exception arg7)
			{
				Log.LogError((object)$"Admin command registration failed: {arg7}");
			}
			Log.LogInfo((object)"InvisibilityPotion 0.4.1 loaded");
		}

		private void RegisterItemsOnce()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterItemsOnce;
			try
			{
				PotionItems.Register();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Item registration failed: {arg}");
			}
			try
			{
				ModelItems.Register();
			}
			catch (Exception arg2)
			{
				Log.LogError((object)$"Model registration failed: {arg2}");
			}
		}

		private void OnDestroy()
		{
			Harmony harmonyInstance = HarmonyInstance;
			if (harmonyInstance != null)
			{
				harmonyInstance.UnpatchSelf();
			}
		}
	}
}
namespace InvisibilityPotion.Visuals
{
	public sealed class FogAnchor
	{
		public string Name = "";

		public bool Enabled = true;

		public float Radius = 0.15f;

		public float X;

		public float Y;

		public float Z;

		public FogAnchor Clone()
		{
			return (FogAnchor)MemberwiseClone();
		}

		public string Format()
		{
			return (Enabled ? "on" : "off") + "," + FloatList.Format(Radius) + "," + FloatList.Format(X) + "," + FloatList.Format(Y) + "," + FloatList.Format(Z);
		}

		public static bool TryParse(string name, string text, out FogAnchor anchor)
		{
			anchor = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string[] array = text.Split(new char[1] { ',' });
			if (array.Length != 5)
			{
				return false;
			}
			if (!TryParseSwitch(array[0], out var on))
			{
				return false;
			}
			float[] array2 = new float[4];
			for (int i = 0; i < 4; i++)
			{
				if (!FloatList.TryParseOne(array[i + 1], out array2[i]))
				{
					return false;
				}
			}
			if (array2[0] < 0f)
			{
				return false;
			}
			anchor = new FogAnchor
			{
				Name = name,
				Enabled = on,
				Radius = array2[0],
				X = array2[1],
				Y = array2[2],
				Z = array2[3]
			};
			return true;
		}

		public static bool TryParseSwitch(string text, out bool on)
		{
			switch ((text ?? "").Trim().ToLowerInvariant())
			{
			case "on":
			case "true":
			case "1":
				on = true;
				return true;
			case "off":
			case "false":
			case "0":
				on = false;
				return true;
			default:
				on = false;
				return false;
			}
		}
	}
	public enum FogAlphaMode
	{
		Both,
		Material,
		Vertex
	}
	public struct FogRgb
	{
		public float R;

		public float G;

		public float B;

		public FogRgb(float r, float g, float b)
		{
			R = r;
			G = g;
			B = b;
		}

		public static FogRgb Blend(FogRgb a, FogRgb b, float t)
		{
			t = ((t < 0f) ? 0f : ((t > 1f) ? 1f : t));
			return new FogRgb(a.R + (b.R - a.R) * t, a.G + (b.G - a.G) * t, a.B + (b.B - a.B) * t);
		}
	}
	public enum FogEmitterMode
	{
		Bones,
		Mesh
	}
	public enum FogTrailMode
	{
		Follow,
		Trail
	}
	public enum FogValueKind
	{
		Float,
		Bool,
		Text
	}
	public enum FogOuterShape
	{
		Volume,
		Ring
	}
	public enum FogLayerKind
	{
		Inner,
		Outer,
		Ground
	}
	public static class LookDefaults
	{
		public const int Revision = 14;

		public const int FullResetRevision = 14;

		public const int SoftFogRevision = 5;

		public const int OuterRingRevision = 6;

		public const string OuterKeyPrefix = "Outer";

		public const int Tier1BodyOffRevision = 3;

		public static readonly int[] ResetTiers = new int[2] { 1, 2 };

		public static readonly string[] BodyVeilModes = new string[4] { null, "Off", "Distortion", "Distortion" };

		public static bool ResetsOuterKey(int tier, string key, int fromRevision)
		{
			if (tier == 2 && fromRevision < 6 && key != null)
			{
				return key.StartsWith("Outer", StringComparison.Ordinal);
			}
			return false;
		}

		public static bool ResetsTiers(int fromRevision)
		{
			return fromRevision < 14;
		}

		public static string MigrateFogMaterial(string current, int fromRevision)
		{
			if (fromRevision >= 5 || !(FogSettings.NormalizeFogMaterial(current) == "swamp_mist"))
			{
				return current;
			}
			return "soft";
		}

		public static string MigrateBodyVeilMode(int tier, string current, int fromRevision)
		{
			if (tier != 1 || fromRevision >= 3 || !string.Equals((current ?? "").Trim(), "Distortion", StringComparison.Ordinal))
			{
				return current;
			}
			return "Off";
		}
	}
	public sealed class FogKey
	{
		public readonly string Name;

		public readonly FogValueKind Kind;

		public readonly string Description;

		public FogKey(string name, FogValueKind kind, string description)
		{
			Name = name;
			Kind = kind;
			Description = description;
		}
	}
	public sealed class FogSettings
	{
		public const int ParticleWarnThreshold = 200;

		public const int ParticleHardCap = 300;

		public const float OuterRingThickness = 0.25f;

		public const float OuterVolumeSizeMinFactor = 0.8f;

		public const float OuterVolumeSizeMaxFactor = 1.2f;

		public const float OuterVolumeSpeed = 0.05f;

		public const float OuterVolumeFadeIn = 0.15f;

		public const float OuterVolumeFadeOut = 0.35f;

		public const float OuterVolumeGrow = 1.35f;

		public const float OuterVolumeSpinDegPerSecond = 4f;

		public const float OuterVolumeLifetimeMeanAlpha = 0.75f;

		public const float OuterVolumeMeanGrow = 1.175f;

		public const float OuterMaxColumnOpacity = 0.5f;

		public const int InnerParticleCap = 24;

		public const int OuterVolumeParticleCap = 200;

		public const float OuterBurstRemainingMinFactor = 0.3f;

		public const float OuterBurstRemainingMaxFactor = 0.85f;

		public const float OuterFollowRadiusFactor = 0.7f;

		public const float OuterFollowBurstFraction = 0.6f;

		public const float OuterFollowSizeFactor = 0.85f;

		public const float OuterFollowTangentialSpeed = 0.15f;

		public const float OuterFollowOrbitRadiusFactor = 0.65f;

		public const float MaxOuterRotation = 180f;

		public static readonly string[] DefaultOuterAnchors = new string[3] { "Head", "Chest", "Hips" };

		public static readonly string[] AnchorNames = new string[13]
		{
			"Head", "Chest", "Hips", "LeftShoulder", "RightShoulder", "LeftHand", "RightHand", "LeftUpperLeg", "RightUpperLeg", "LeftLowerLeg",
			"RightLowerLeg", "LeftFoot", "RightFoot"
		};

		public bool Enabled = true;

		public float Rate = 4f;

		public float Size = 1.5f;

		public float Lifetime = 5f;

		public float Speed = 0.05f;

		public float Alpha = 0.5f;

		public float R = 0.85f;

		public float G = 0.87f;

		public float B = 0.9f;

		public bool DynamicColor;

		public float Emission;

		public float SpreadX = 1f;

		public float SpreadY = 0.35f;

		public float SpreadZ = 1f;

		public float Drift;

		public bool Trail;

		public float MeshRate;

		public bool OuterEnabled;

		public float OuterRadius = 1.4f;

		public float OuterAlpha = 0.35f;

		public float OuterRate = 14f;

		public float OuterRateDistance;

		public float OuterSize = 1.8f;

		public float OuterLifetime = 3f;

		public float OuterSpreadY = 0.35f;

		public float OuterHeightSigma = 0.9f;

		public float OuterBurst;

		public float OuterFollowShare = 0.4f;

		public bool OuterTrail;

		public float OuterRotation;

		public float OuterOffsetY;

		public bool OuterHorizontal = true;

		public FogOuterShape OuterShape;

		public List<string> OuterAnchors = new List<string>(DefaultOuterAnchors);

		public bool GroundEnabled;

		public float GroundRate = 4f;

		public float GroundRateDistance = 2f;

		public float GroundSize = 0.8f;

		public float GroundGrow = 3f;

		public float GroundLifetime = 7f;

		public float GroundAlpha = 0.22f;

		public float GroundRadius = 0.6f;

		public float GroundHeight = 0.15f;

		public float GroundDrift = 0.05f;

		public float DistortionStrength = 0.1f;

		public float DR = 1f;

		public float DG = 1f;

		public float DB = 1f;

		public float DA = 0.08f;

		public float DistortionWave = -1f;

		public string FogMaterial = "soft";

		public FogEmitterMode EmitterMode;

		public float MeshOffset = 0.03f;

		public List<FogAnchor> Anchors = new List<FogAnchor>();

		public const string DefaultFogMaterial = "soft";

		public const string SoftFogBase = "swamp_mist";

		public static readonly string[] FogMaterialNames = new string[5] { "soft", "swamp_mist", "ghost_smoke", "wraith_smoke", "slowwispysmoke" };

		public static readonly string[] ObsoleteKeys = new string[5] { "OuterRadiusMultiplier", "OuterAlphaFactor", "OuterRateFactor", "OuterSizeFactor", "OuterLifetimeFactor" };

		public static readonly string[] GroundKeys = new string[10] { "GroundEnabled", "GroundRate", "GroundRateDistance", "GroundSize", "GroundGrow", "GroundLifetime", "GroundAlpha", "GroundRadius", "GroundHeight", "GroundDrift" };

		public const float GroundBudgetSpeed = 7f;

		public const float GroundSizeMinFactor = 0.7f;

		public const float GroundSizeMaxFactor = 1.3f;

		public const float GroundUpperOffset = 0.3f;

		public const float GroundUpperRateFactor = 0.5f;

		public const float GroundUpperAlphaFactor = 0.7f;

		public const int GroundParticleBudget = 60;

		public const string AnchorPrefix = "Anchor.";

		public const string Readme = "How the fog of a hidden player is built (each tier has its own [Fog.TierN] section):\nBody cloud (keys without a prefix: Rate, Size, Alpha, ...): fog sitting directly on the body. It moves with you and hides your outline.\nWide fog (Outer* keys): large, soft, light fog puffs spreading far around you (7 to 9 metres) like the game's own mist patches, thickest near the ground and close to you, slowly dissolving upwards and outwards. OuterBurst creates it the moment the effect starts; part of it moves with you (OuterFollowShare), the rest stays behind as a trail.\nGround fog (Ground* keys): flat fog patches at your feet. They stay where they appeared and spread, so your path fills with low fog.\nAlpha keys say how visible a layer is (0 = invisible, 1 = solid); Rate keys how many puffs appear; Lifetime keys how long a puff lingers.";

		public static readonly IReadOnlyList<FogKey> Keys = BuildKeys();

		public FogRgb Color => new FogRgb(R, G, B);

		public FogTrailMode InnerTrailMode
		{
			get
			{
				if (!Trail)
				{
					return FogTrailMode.Follow;
				}
				return FogTrailMode.Trail;
			}
		}

		public FogTrailMode OuterTrailMode
		{
			get
			{
				if (!OuterTrail)
				{
					return FogTrailMode.Follow;
				}
				return FogTrailMode.Trail;
			}
		}

		public float OuterOrbitalRadPerSecond => OuterRotation * (float)Math.PI / 180f;

		public bool InnerActive
		{
			get
			{
				if (Alpha > 0f)
				{
					if (!(Rate > 0f))
					{
						if (EmitterMode == FogEmitterMode.Mesh)
						{
							return MeshRate > 0f;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		public bool OuterActive
		{
			get
			{
				if (OuterEnabled && OuterRate > 0f && OuterAlpha > 0f)
				{
					return OuterAnchors.Count > 0;
				}
				return false;
			}
		}

		public bool GroundActive
		{
			get
			{
				if (GroundEnabled && GroundAlpha > 0f)
				{
					if (!(GroundRate > 0f))
					{
						return GroundRateDistance > 0f;
					}
					return true;
				}
				return false;
			}
		}

		public float GroundBudgetRate => Math.Max(0f, GroundRate) + Math.Max(0f, GroundRateDistance) * 7f;

		public float LiveParticlesGround
		{
			get
			{
				if (!GroundEnabled)
				{
					return 0f;
				}
				return GroundBudgetRate * Math.Max(0f, GroundLifetime);
			}
		}

		public int GroundMaxParticles => MaxParticles(GroundBudgetRate, GroundLifetime);

		public float GroundUpperHeight => GroundHeight + 0.3f;

		public float GroundUpperAlpha => GroundAlpha * 0.7f;

		public float GroundUpperRate => Math.Max(0f, GroundRate) * 0.5f;

		public float GroundUpperRateDistance => Math.Max(0f, GroundRateDistance) * 0.5f;

		private int GroundTotalMaxParticles
		{
			get
			{
				float num = GroundBudgetRate * 1.5f * Math.Max(0f, GroundLifetime) * 1.3f + 12f;
				return (int)Math.Min(60.0, Math.Max(12.0, Math.Ceiling(num)));
			}
		}

		public int GroundLowerMaxParticles
		{
			get
			{
				int groundTotalMaxParticles = GroundTotalMaxParticles;
				int val = (int)Math.Round((float)groundTotalMaxParticles / 1.5f);
				return Math.Min(groundTotalMaxParticles - 4, Math.Max(8, val));
			}
		}

		public int GroundUpperMaxParticles => GroundTotalMaxParticles - GroundLowerMaxParticles;

		public float LiveParticlesInner => Math.Max(0f, Rate) * Math.Max(0f, Lifetime);

		public float LiveParticlesOuter
		{
			get
			{
				if (!OuterEnabled)
				{
					return 0f;
				}
				return Math.Max(0f, OuterRate) * Math.Max(0f, OuterLifetime);
			}
		}

		public float LiveParticlesMesh
		{
			get
			{
				int num = 0;
				foreach (FogAnchor anchor in Anchors)
				{
					if (anchor.Enabled)
					{
						num++;
					}
				}
				return MeshEmitterRate(num) * Math.Max(0f, Lifetime);
			}
		}

		public bool ExceedsParticleBudget
		{
			get
			{
				if (!(((EmitterMode == FogEmitterMode.Mesh) ? LiveParticlesMesh : LiveParticlesInner) > 200f) && !(LiveParticlesOuter > 200f))
				{
					return LiveParticlesGround > 200f;
				}
				return true;
			}
		}

		public int InnerMaxParticles => Math.Min(24, MaxParticles(Rate, Lifetime));

		public float OuterEffectiveRateDistance
		{
			get
			{
				if (!OuterTrail)
				{
					return 0f;
				}
				return Math.Max(0f, OuterRateDistance);
			}
		}

		public int OuterMaxParticles => OuterFollowMaxParticles + OuterTrailMaxParticles;

		public float OuterFollowShareEffective
		{
			get
			{
				if (OuterShape != FogOuterShape.Volume || !OuterTrail)
				{
					return 0f;
				}
				return Clamp01(OuterFollowShare);
			}
		}

		public bool OuterHasFollow
		{
			get
			{
				if (OuterActive)
				{
					return OuterFollowShareEffective > 0f;
				}
				return false;
			}
		}

		public bool OuterHasTrail
		{
			get
			{
				if (OuterActive)
				{
					return OuterFollowShareEffective < 1f;
				}
				return false;
			}
		}

		public float OuterFollowRate => Math.Max(0f, OuterRate) * OuterFollowShareEffective;

		public float OuterTrailRate => Math.Max(0f, OuterRate) * (1f - OuterFollowShareEffective);

		public float OuterFollowRadius => OuterRadius * 0.7f;

		public float OuterFollowSize => OuterSize * 0.85f;

		public float OuterFollowOrbitalRadPerSecond
		{
			get
			{
				float num = OuterFollowRadius * 0.65f;
				return OuterOrbitalRadPerSecond + ((num > 0.0001f) ? (0.15f / num) : 0f);
			}
		}

		private int OuterFollowCap
		{
			get
			{
				float outerFollowShareEffective = OuterFollowShareEffective;
				if (!(outerFollowShareEffective > 0f))
				{
					return 0;
				}
				return Math.Max(1, (int)Math.Round(200f * outerFollowShareEffective, MidpointRounding.AwayFromZero));
			}
		}

		public int OuterFollowMaxParticles
		{
			get
			{
				if (!(OuterFollowShareEffective > 0f))
				{
					return 0;
				}
				return Math.Min(OuterFollowCap, MaxParticles(OuterFollowRate, OuterLifetime) + OuterFollowBurstShare);
			}
		}

		public int OuterTrailMaxParticles
		{
			get
			{
				if (OuterShape != FogOuterShape.Volume)
				{
					return MaxParticles(OuterRate, OuterLifetime);
				}
				if (OuterFollowShareEffective >= 1f)
				{
					return 0;
				}
				return Math.Min(200 - OuterFollowCap, MaxParticles(OuterTrailRate + OuterEffectiveRateDistance * 7f, OuterLifetime));
			}
		}

		private int OuterBurstTotal
		{
			get
			{
				if (!OuterActive || OuterShape != FogOuterShape.Volume)
				{
					return 0;
				}
				return (int)Math.Round(Math.Max(0f, OuterBurst), MidpointRounding.AwayFromZero);
			}
		}

		public float OuterFollowBurstFractionEffective
		{
			get
			{
				float outerFollowShareEffective = OuterFollowShareEffective;
				if (!(outerFollowShareEffective <= 0f))
				{
					if (!(outerFollowShareEffective >= 1f))
					{
						return 0.6f;
					}
					return 1f;
				}
				return 0f;
			}
		}

		private int OuterFollowBurstShare => (int)Math.Round((float)OuterBurstTotal * OuterFollowBurstFractionEffective, MidpointRounding.AwayFromZero);

		public int OuterFollowBurstCount => Math.Min(OuterFollowMaxParticles, OuterFollowBurstShare);

		public int OuterTrailBurstCount => Math.Min(OuterTrailMaxParticles, OuterBurstTotal - OuterFollowBurstShare);

		public int OuterBurstCount => OuterFollowBurstCount + OuterTrailBurstCount;

		public float OuterCentreOverlap
		{
			get
			{
				if (!OuterActive || OuterShape != FogOuterShape.Volume)
				{
					return 0f;
				}
				float num = Math.Max(0f, OuterLifetime);
				float num2 = 0f;
				if (OuterHasFollow)
				{
					num2 += VolumeCentreOverlap(Math.Min(OuterFollowMaxParticles, OuterFollowRate * num + (float)OuterFollowBurstCount), OuterFollowRadius, OuterFollowSize);
				}
				if (OuterHasTrail)
				{
					num2 += VolumeCentreOverlap(Math.Min(OuterTrailMaxParticles, OuterTrailRate * num + (float)OuterTrailBurstCount), OuterRadius, OuterSize);
				}
				return num2;
			}
		}

		public FogOverlapCap OuterOverlapCap
		{
			get
			{
				float outerCentreOverlap = OuterCentreOverlap;
				if (!(outerCentreOverlap > 0f))
				{
					return default(FogOverlapCap);
				}
				return new FogOverlapCap(outerCentreOverlap, OuterAlpha * 0.75f * FogSprite.MeanDiscAlpha, 0.5f);
			}
		}

		public static void OuterVolumeDrift(float uAngle, out float x, out float z)
		{
			OuterVolumeDrift(uAngle, 0.05f, out x, out z);
		}

		public static void OuterVolumeDrift(float uAngle, float speed, out float x, out float z)
		{
			double num = (double)uAngle * 2.0 * Math.PI;
			x = (float)(Math.Cos(num) * (double)speed);
			z = (float)(Math.Sin(num) * (double)speed);
		}

		public static string NormalizeFogMaterial(string text)
		{
			string b = (text ?? "").Trim();
			string[] fogMaterialNames = FogMaterialNames;
			foreach (string text2 in fogMaterialNames)
			{
				if (string.Equals(text2, b, StringComparison.OrdinalIgnoreCase))
				{
					return text2;
				}
			}
			return null;
		}

		public static bool TryParseEmitterMode(string text, out FogEmitterMode mode)
		{
			if (Enum.TryParse<FogEmitterMode>((text ?? "").Trim(), ignoreCase: true, out mode))
			{
				return Enum.IsDefined(typeof(FogEmitterMode), mode);
			}
			return false;
		}

		public static bool TryParseOuterShape(string text, out FogOuterShape shape)
		{
			shape = FogOuterShape.Volume;
			string b = (text ?? "").Trim();
			foreach (FogOuterShape value in Enum.GetValues(typeof(FogOuterShape)))
			{
				if (string.Equals(value.ToString(), b, StringComparison.OrdinalIgnoreCase))
				{
					shape = value;
					return true;
				}
			}
			return false;
		}

		private static void ApplyFogVolume(FogSettings s, float alpha, float radius, float heightSigma, float size, float lifetime, float rate, float rateDistance, float burst)
		{
			s.OuterEnabled = true;
			s.OuterShape = FogOuterShape.Volume;
			s.OuterAnchors = new List<string> { "Hips" };
			s.OuterHorizontal = false;
			s.OuterSize = size;
			s.OuterAlpha = alpha;
			s.OuterRadius = radius;
			s.OuterSpreadY = 0.35f;
			s.OuterHeightSigma = heightSigma;
			s.OuterRate = rate;
			s.OuterRateDistance = rateDistance;
			s.OuterLifetime = lifetime;
			s.OuterRotation = 0f;
			s.OuterOffsetY = 0f;
			s.OuterTrail = true;
			s.OuterBurst = burst;
			s.OuterFollowShare = 0.4f;
		}

		private static void ApplyHazeColor(FogSettings s)
		{
			s.R = 0.55f;
			s.G = 0.57f;
			s.B = 0.6f;
		}

		public static FogSettings Defaults(int tier)
		{
			FogSettings fogSettings = new FogSettings();
			string[] anchorNames = AnchorNames;
			foreach (string name in anchorNames)
			{
				fogSettings.Anchors.Add(DefaultAnchor(name));
			}
			switch (tier)
			{
			case 1:
				fogSettings.Rate = 7f;
				fogSettings.Size = 1.1f;
				fogSettings.Lifetime = 2.4f;
				fogSettings.Speed = 0.03f;
				fogSettings.Alpha = 0.12f;
				ApplyHazeColor(fogSettings);
				fogSettings.Emission = 0f;
				fogSettings.SpreadX = 1f;
				fogSettings.SpreadY = 0.6f;
				fogSettings.SpreadZ = 1f;
				fogSettings.Drift = 0.03f;
				fogSettings.Trail = false;
				ApplyFogVolume(fogSettings, 0.3f, 7f, 0.8f, 4.5f, 12f, 8f, 3f, 60f);
				fogSettings.GroundEnabled = true;
				fogSettings.GroundAlpha = 0.14f;
				fogSettings.DistortionStrength = 0.04f;
				fogSettings.DA = 0.5f;
				break;
			case 2:
				fogSettings.Rate = 9f;
				fogSettings.Size = 1.2f;
				fogSettings.Lifetime = 2.6f;
				fogSettings.Speed = 0.05f;
				fogSettings.Alpha = 0.14f;
				ApplyHazeColor(fogSettings);
				fogSettings.Emission = 0f;
				fogSettings.SpreadX = 1.125f;
				fogSettings.SpreadY = 0.6f;
				fogSettings.SpreadZ = 1.025f;
				fogSettings.Drift = -0.066f;
				ApplyFogVolume(fogSettings, 0.4f, 9f, 1f, 5.5f, 14f, 10f, 4f, 80f);
				fogSettings.GroundEnabled = true;
				fogSettings.GroundRate = 6f;
				fogSettings.GroundRateDistance = 3f;
				fogSettings.GroundSize = 1.2f;
				fogSettings.GroundGrow = 2.5f;
				fogSettings.GroundAlpha = 0.16f;
				fogSettings.GroundRadius = 0.9f;
				fogSettings.DistortionStrength = 0.1f;
				fogSettings.DA = 0.08f;
				break;
			case 3:
				fogSettings.Enabled = false;
				fogSettings.DistortionStrength = 0.03f;
				fogSettings.DA = 0.02f;
				break;
			}
			return fogSettings;
		}

		public static FogAnchor DefaultAnchor(string name)
		{
			switch (name)
			{
			case "Head":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.15f,
					Y = 0.05f
				};
			case "Chest":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.25f
				};
			case "Hips":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.22f
				};
			case "LeftShoulder":
			case "RightShoulder":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.12f
				};
			case "LeftHand":
			case "RightHand":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.12f
				};
			case "LeftUpperLeg":
			case "RightUpperLeg":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.12f,
					Y = -0.22f
				};
			case "LeftLowerLeg":
			case "RightLowerLeg":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.12f,
					Y = -0.2f
				};
			case "LeftFoot":
			case "RightFoot":
				return new FogAnchor
				{
					Name = name,
					Radius = 0.12f,
					Y = 0.05f
				};
			default:
				return new FogAnchor
				{
					Name = name
				};
			}
		}

		public FogSettings Clone()
		{
			FogSettings fogSettings = (FogSettings)MemberwiseClone();
			fogSettings.Anchors = new List<FogAnchor>(Anchors.Count);
			foreach (FogAnchor anchor in Anchors)
			{
				fogSettings.Anchors.Add(anchor.Clone());
			}
			fogSettings.OuterAnchors = new List<string>(OuterAnchors);
			return fogSettings;
		}

		public FogAnchor Anchor(string name)
		{
			foreach (FogAnchor anchor in Anchors)
			{
				if (string.Equals(anchor.Name, name, StringComparison.OrdinalIgnoreCase))
				{
					return anchor;
				}
			}
			return null;
		}

		public float MeshEmitterRate(int enabledAnchors)
		{
			if (!(MeshRate > 0f))
			{
				return Math.Max(0f, Rate) * (float)Math.Max(0, enabledAnchors);
			}
			return MeshRate;
		}

		public int MeshMaxParticles(int enabledAnchors)
		{
			return Math.Min(Math.Max(8, 24 * Math.Max(0, enabledAnchors)), MaxParticles(MeshEmitterRate(enabledAnchors), Lifetime));
		}

		public static float VolumeCentreOverlap(float live, float radius, float size)
		{
			if (!(radius > 0f) || !(live > 0f) || !(size > 0f))
			{
				return 0f;
			}
			float num = 0.5f * size * 0.8f * 1.175f;
			return live * (float)FogVolumeShape.RadiusShare(num / radius);
		}

		public static float OuterBurstRemainingLifetime(float startLifetime, float random01)
		{
			float num = ((random01 < 0f) ? 0f : ((random01 > 1f) ? 1f : random01));
			return startLifetime * (0.3f + 0.55f * num);
		}

		public int MaxParticles(float rate)
		{
			return MaxParticles(rate, Lifetime);
		}

		public static int MaxParticles(float rate, float lifetime)
		{
			float num = Math.Max(0f, rate) * Math.Max(0f, lifetime) * 1.3f + 4f;
			return (int)Math.Min(300.0, Math.Max(8.0, Math.Ceiling(num)));
		}

		public static bool TryParseAnchorList(string text, out List<string> names)
		{
			names = new List<string>();
			if (string.IsNullOrWhiteSpace(text))
			{
				return true;
			}
			string[] array = text.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (text3.Length == 0)
				{
					continue;
				}
				string text4 = null;
				string[] anchorNames = AnchorNames;
				foreach (string text5 in anchorNames)
				{
					if (string.Equals(text5, text3, StringComparison.OrdinalIgnoreCase))
					{
						text4 = text5;
						break;
					}
				}
				if (text4 == null)
				{
					names = null;
					return false;
				}
				if (!names.Contains(text4))
				{
					names.Add(text4);
				}
			}
			return true;
		}

		public static bool TryParseAlphaMode(string text, out FogAlphaMode mode)
		{
			if (Enum.TryParse<FogAlphaMode>((text ?? "").Trim(), ignoreCase: true, out mode))
			{
				return Enum.IsDefined(typeof(FogAlphaMode), mode);
			}
			return false;
		}

		private static List<FogKey> BuildKeys()
		{
			List<FogKey> list = new List<FogKey>
			{
				new FogKey("Enabled", FogValueKind.Bool, "Body cloud on or off: the fog that sits on the hidden player's body"),
				new FogKey("Rate", FogValueKind.Float, "Body cloud: new puffs per second on each body part; higher = a thicker cloud"),
				new FogKey("Size", FogValueKind.Float, "Body cloud: size of one puff in metres (each puff is 0.6x to 1.25x of this)"),
				new FogKey("Lifetime", FogValueKind.Float, "Body cloud: seconds a puff lingers before it fades; higher = a thicker, slower cloud"),
				new FogKey("Speed", FogValueKind.Float, "Body cloud: how fast a new puff drifts away from the body, in metres per second"),
				new FogKey("Alpha", FogValueKind.Float, "How visible the body cloud is (0 = invisible, 1 = solid)"),
				new FogKey("Color", FogValueKind.Text, "Colour of all fog of this tier as r,g,b (each 0..1; 0.55,0.57,0.6 = mid grey)"),
				new FogKey("DynamicColor", FogValueKind.Bool, "Mix the fog colour half and half with the weather's fog colour; the fog then turns dark in rain and at night"),
				new FogKey("Emission", FogValueKind.Float, "How much the fog glows by itself (0 = matte, lit by the scene only; 1 = glows in its own colour, stays bright at night)"),
				new FogKey("SpreadX", FogValueKind.Float, "Body cloud: how far it spreads sideways (multiplies each body part's radius)"),
				new FogKey("SpreadY", FogValueKind.Float, "Body cloud: how far it spreads up and down; below 1 = flatter"),
				new FogKey("SpreadZ", FogValueKind.Float, "Body cloud: how far it spreads to the front and back"),
				new FogKey("Drift", FogValueKind.Float, "Body cloud: how fast puffs rise (positive) or sink (negative), in metres per second; 0 = they stay at the body"),
				new FogKey("Trail", FogValueKind.Bool, "Body cloud: true = puffs stay where they appeared, so walking leaves a trail; false = they move with the body"),
				new FogKey("MeshRate", FogValueKind.Float, "Body cloud with FogEmitterMode Mesh: puffs per second on the whole body surface; 0 = Rate x active body parts"),
				new FogKey("OuterEnabled", FogValueKind.Bool, "Wide fog on or off: the large, light fog around the player"),
				new FogKey("OuterAnchors", FogValueKind.Text, "Wide fog: body parts it is centred on, as a comma list (Head, Chest, Hips, LeftShoulder, RightShoulder, LeftHand, RightHand, LeftUpperLeg, RightUpperLeg, LeftLowerLeg, RightLowerLeg, LeftFoot, RightFoot); one fog source each"),
				new FogKey("OuterRadius", FogValueKind.Float, "How far the wide fog reaches around you along the ground, in metres; most puffs sit close to you and the fog gets thinner and fainter towards this edge, where it fades out softly instead of ending in a line. Tier I default 7, tier II 9"),
				new FogKey("OuterAlpha", FogValueKind.Float, "How visible the wide fog around you is (0 = invisible, 1 = solid); puffs far from you are fainter (15 % at the edge). Close to you many puffs overlap, so there each puff is made fainter on its own; the fog right around you never gets thicker than about half see-through. Tier I default 0.3, tier II 0.4"),
				new FogKey("OuterRate", FogValueKind.Float, "Wide fog: new fog puffs per second while standing; higher = denser"),
				new FogKey("OuterRateDistance", FogValueKind.Float, "Wide fog: extra puffs per metre walked, so the fog keeps up with a moving player (only with OuterTrail true)"),
				new FogKey("OuterBurst", FogValueKind.Float, "Wide fog: puffs created at once when the effect starts, so the fog is there immediately instead of building up (Volume shape only; 0 = builds up over seconds). Tier I default 60, tier II 80"),
				new FogKey("OuterFollowShare", FogValueKind.Float, "Share of the wide fog that moves with you; the rest stays behind as a trail (0..1; 0.4 = 40 % of the puffs per second moves with you, within 0.7 x OuterRadius, with slightly smaller puffs that drift slowly around you; 60 % of OuterBurst starts there). Only with OuterTrail true and the Volume shape"),
				new FogKey("OuterSize", FogValueKind.Float, "Wide fog: size of one puff in metres. Puffs close to you are a bit smaller, puffs far out are larger and softer, and every puff grows slowly while it lives. Tier I default 4.5, tier II 5.5"),
				new FogKey("OuterSpreadY", FogValueKind.Float, "Wide fog with the Ring shape: band height as a fraction of OuterRadius (the Volume shape uses OuterHeightSigma instead)"),
				new FogKey("OuterHeightSigma", FogValueKind.Float, "Wide fog (Volume shape): how high it rises above the ground, in metres. Most puffs stay below this height, only a few rise to twice of it; puffs near the ground are smaller. Small = a low ground fog, large = a tall cloud (0.1..5). Tier I default 0.8, tier II 1"),
				new FogKey("OuterLifetime", FogValueKind.Float, "Wide fog: seconds a puff lingers before it fades; higher = the fog stays longer where you were. Each puff fades in slowly, drifts and grows a little, then fades out slowly. Tier I default 12, tier II 14"),
				new FogKey("OuterTrail", FogValueKind.Bool, "Wide fog: true = puffs stay where they appeared (you leave fog behind); false = the fog moves with you"),
				new FogKey("OuterRotation", FogValueKind.Float, "Wide fog: how fast it turns around you in degrees per second (negative = the other way, 0 = still)"),
				new FogKey("OuterOffsetY", FogValueKind.Float, "Wide fog: moves its centre up (positive) or down (negative), in metres"),
				new FogKey("OuterShape", FogValueKind.Text, "Wide fog shape: Volume = soft fog filling the space around you; Ring = a band of fog near the edge of OuterRadius"),
				new FogKey("OuterHorizontal", FogValueKind.Bool, "Wide fog: true = puffs lie flat like a carpet (seen from above); false = they face the camera like normal fog"),
				new FogKey("GroundEnabled", FogValueKind.Bool, "Ground fog on or off: flat fog patches at your feet that stay behind and spread, so walking leaves a field of fog"),
				new FogKey("GroundRate", FogValueKind.Float, "Ground fog: new patches per second, also while standing still"),
				new FogKey("GroundRateDistance", FogValueKind.Float, "Ground fog: extra patches per metre walked"),
				new FogKey("GroundSize", FogValueKind.Float, "Ground fog: size of a new patch in metres"),
				new FogKey("GroundGrow", FogValueKind.Float, "Ground fog: how much a patch grows before it fades (2 = to double size, 1 = no growth)"),
				new FogKey("GroundLifetime", FogValueKind.Float, "Ground fog: seconds a patch lingers"),
				new FogKey("GroundAlpha", FogValueKind.Float, "How visible the ground fog is (0 = invisible, 1 = solid)"),
				new FogKey("GroundRadius", FogValueKind.Float, "Ground fog: how far around your feet new patches appear, in metres"),
				new FogKey("GroundHeight", FogValueKind.Float, "Ground fog: height above your feet in metres (-0.5..2)"),
				new FogKey("GroundDrift", FogValueKind.Float, "Ground fog: how fast patches wander sideways, in metres per second"),
				new FogKey("DistortionStrength", FogValueKind.Float, "Distortion body look (BodyVeilMode Distortion): how strongly the body bends the view behind it"),
				new FogKey("DistortionColor", FogValueKind.Text, "Distortion body look: tint as r,g,b,a (a = how visible the tint is)"),
				new FogKey("DistortionWave", FogValueKind.Float, "Distortion body look: how fast the ripples move; negative = the game's own speed"),
				new FogKey("FogMaterial", FogValueKind.Text, "Texture of all fog puffs: soft (neutral soft puff, default), swamp_mist, ghost_smoke, wraith_smoke or slowwispysmoke (borrowed from the game)"),
				new FogKey("FogEmitterMode", FogValueKind.Text, "Where the body cloud comes from: Bones = one source per body part below; Mesh = the whole body surface (falls back to Bones if that fails)"),
				new FogKey("MeshOffset", FogValueKind.Float, "Body cloud with FogEmitterMode Mesh: how far off the body surface the puffs appear, in metres")
			};
			string[] anchorNames = AnchorNames;
			foreach (string text in anchorNames)
			{
				list.Add(new FogKey("Anchor." + text, FogValueKind.Text, "Body cloud source on the " + text + ": on|off,radius,x,y,z (radius and offset in metres; x right, y up, z forward)"));
			}
			return list;
		}

		public string Get(string key)
		{
			if (key.StartsWith("Anchor.", StringComparison.Ordinal))
			{
				return Anchor(key.Substring("Anchor.".Length))?.Format();
			}
			return key switch
			{
				"Enabled" => Bool(Enabled), 
				"Rate" => FloatList.Format(Rate), 
				"Size" => FloatList.Format(Size), 
				"Lifetime" => FloatList.Format(Lifetime), 
				"Speed" => FloatList.Format(Speed), 
				"Alpha" => FloatList.Format(Alpha), 
				"Color" => FloatList.Format(R, G, B), 
				"DynamicColor" => Bool(DynamicColor), 
				"SpreadX" => FloatList.Format(SpreadX), 
				"SpreadY" => FloatList.Format(SpreadY), 
				"SpreadZ" => FloatList.Format(SpreadZ), 
				"Drift" => FloatList.Format(Drift), 
				"Trail" => Bool(Trail), 
				"MeshRate" => FloatList.Format(MeshRate), 
				"OuterEnabled" => Bool(OuterEnabled), 
				"OuterAnchors" => string.Join(",", OuterAnchors), 
				"OuterSpreadY" => FloatList.Format(OuterSpreadY), 
				"OuterHeightSigma" => FloatList.Format(OuterHeightSigma), 
				"OuterLifetime" => FloatList.Format(OuterLifetime), 
				"OuterTrail" => Bool(OuterTrail), 
				"OuterHorizontal" => Bool(OuterHorizontal), 
				"OuterShape" => OuterShape.ToString(), 
				"OuterRadius" => FloatList.Format(OuterRadius), 
				"OuterAlpha" => FloatList.Format(OuterAlpha), 
				"OuterRate" => FloatList.Format(OuterRate), 
				"OuterRateDistance" => FloatList.Format(OuterRateDistance), 
				"OuterSize" => FloatList.Format(OuterSize), 
				"OuterBurst" => FloatList.Format(OuterBurst), 
				"OuterFollowShare" => FloatList.Format(OuterFollowShare), 
				"OuterRotation" => FloatList.Format(OuterRotation), 
				"OuterOffsetY" => FloatList.Format(OuterOffsetY), 
				"GroundEnabled" => Bool(GroundEnabled), 
				"GroundRate" => FloatList.Format(GroundRate), 
				"GroundRateDistance" => FloatList.Format(GroundRateDistance), 
				"GroundSize" => FloatList.Format(GroundSize), 
				"GroundGrow" => FloatList.Format(GroundGrow), 
				"GroundLifetime" => FloatList.Format(GroundLifetime), 
				"GroundAlpha" => FloatList.Format(GroundAlpha), 
				"GroundRadius" => FloatList.Format(GroundRadius), 
				"GroundHeight" => FloatList.Format(GroundHeight), 
				"GroundDrift" => FloatList.Format(GroundDrift), 
				"Emission" => FloatList.Format(Emission), 
				"DistortionStrength" => FloatList.Format(DistortionStrength), 
				"DistortionColor" => FloatList.Format(DR, DG, DB, DA), 
				"DistortionWave" => FloatList.Format(DistortionWave), 
				"FogMaterial" => FogMaterial, 
				"FogEmitterMode" => EmitterMode.ToString(), 
				"MeshOffset" => FloatList.Format(MeshOffset), 
				_ => null, 
			};
		}

		private static string Bool(bool b)
		{
			if (!b)
			{
				return "false";
			}
			return "true";
		}

		public bool TrySet(string key, string text)
		{
			if (key.StartsWith("Anchor.", StringComparison.Ordinal))
			{
				string name = key.Substring("Anchor.".Length);
				int num = Anchors.FindIndex((FogAnchor a) => string.Equals(a.Name, name, StringComparison.OrdinalIgnoreCase));
				if (num < 0 || !FogAnchor.TryParse(Anchors[num].Name, text, out var anchor))
				{
					return false;
				}
				Anchors[num] = anchor;
				return true;
			}
			switch (key)
			{
			case "Enabled":
				return SetBool(text, ref Enabled);
			case "DynamicColor":
				return SetBool(text, ref DynamicColor);
			case "OuterEnabled":
				return SetBool(text, ref OuterEnabled);
			case "Trail":
				return SetBool(text, ref Trail);
			case "OuterTrail":
				return SetBool(text, ref OuterTrail);
			case "OuterHorizontal":
				return SetBool(text, ref OuterHorizontal);
			case "GroundEnabled":
				return SetBool(text, ref GroundEnabled);
			case "OuterAnchors":
			{
				if (!TryParseAnchorList(text, out var names))
				{
					return false;
				}
				OuterAnchors = names;
				return true;
			}
			case "Color":
			{
				if (!FloatList.TryParse(text, 3, out var values))
				{
					return false;
				}
				R = Clamp01(values[0]);
				G = Clamp01(values[1]);
				B = Clamp01(values[2]);
				return true;
			}
			case "DistortionColor":
			{
				if (!FloatList.TryParse(text, 4, out var values2))
				{
					return false;
				}
				DR = Clamp01(values2[0]);
				DG = Clamp01(values2[1]);
				DB = Clamp01(values2[2]);
				DA = Clamp01(values2[3]);
				return true;
			}
			case "FogMaterial":
			{
				string text2 = NormalizeFogMaterial(text);
				if (text2 == null)
				{
					return false;
				}
				FogMaterial = text2;
				return true;
			}
			case "OuterShape":
			{
				if (!TryParseOuterShape(text, out var shape))
				{
					return false;
				}
				OuterShape = shape;
				return true;
			}
			case "FogEmitterMode":
			{
				if (!TryParseEmitterMode(text, out var mode))
				{
					return false;
				}
				EmitterMode = mode;
				return true;
			}
			default:
			{
				if (!FloatList.TryParseOne(text, out var value))
				{
					return false;
				}
				switch (key)
				{
				case "Rate":
					Rate = Math.Max(0f, value);
					return true;
				case "Size":
					Size = Math.Max(0.01f, value);
					return true;
				case "Lifetime":
					Lifetime = Math.Max(0.05f, value);
					return true;
				case "Speed":
					Speed = value;
					return true;
				case "Alpha":
					Alpha = Clamp01(value);
					return true;
				case "SpreadX":
					SpreadX = Math.Max(0.01f, value);
					return true;
				case "SpreadY":
					SpreadY = Math.Max(0.01f, value);
					return true;
				case "SpreadZ":
					SpreadZ = Math.Max(0.01f, value);
					return true;
				case "Drift":
					Drift = value;
					return true;
				case "OuterRadius":
					OuterRadius = Math.Max(0f, value);
					return true;
				case "OuterAlpha":
					OuterAlpha = Clamp01(value);
					return true;
				case "OuterRate":
					OuterRate = Math.Max(0f, value);
					return true;
				case "OuterRateDistance":
					OuterRateDistance = Math.Max(0f, value);
					return true;
				case "OuterSize":
					OuterSize = Math.Max(0.01f, value);
					return true;
				case "OuterBurst":
					OuterBurst = Math.Max(0f, value);
					return true;
				case "OuterFollowShare":
					OuterFollowShare = Clamp01(value);
					return true;
				case "Emission":
					Emission = Clamp01(value);
					return true;
				case "DistortionStrength":
					DistortionStrength = Math.Max(0f, value);
					return true;
				case "DistortionWave":
					DistortionWave = ((value < 0f) ? (-1f) : value);
					return true;
				case "MeshOffset":
					MeshOffset = Math.Max(0f, value);
					return true;
				case "MeshRate":
					MeshRate = Math.Max(0f, value);
					return true;
				case "OuterSpreadY":
					OuterSpreadY = Math.Max(0.01f, value);
					return true;
				case "OuterHeightSigma":
					OuterHeightSigma = ((value < 0.1f) ? 0.1f : ((value > 5f) ? 5f : value));
					return true;
				case "OuterLifetime":
					OuterLifetime = Math.Max(0.05f, value);
					return true;
				case "OuterRotation":
					OuterRotation = ((value < -180f) ? (-180f) : ((value > 180f) ? 180f : value));
					return true;
				case "OuterOffsetY":
					OuterOffsetY = ((value < -2f) ? (-2f) : ((value > 2f) ? 2f : value));
					return true;
				case "GroundRate":
					GroundRate = Math.Max(0f, value);
					return true;
				case "GroundRateDistance":
					GroundRateDistance = Math.Max(0f, value);
					return true;
				case "GroundSize":
					GroundSize = Math.Max(0.01f, value);
					return true;
				case "GroundGrow":
					GroundGrow = Math.Max(0.1f, value);
					return true;
				case "GroundLifetime":
					GroundLifetime = Math.Max(0.05f, value);
					return true;
				case "GroundAlpha":
					GroundAlpha = Clamp01(value);
					return true;
				case "GroundRadius":
					GroundRadius = Math.Max(0f, value);
					return true;
				case "GroundHeight":
					GroundHeight = ((value < -0.5f) ? (-0.5f) : ((value > 2f) ? 2f : value));
					return true;
				case "GroundDrift":
					GroundDrift = Math.Max(0f, value);
					return true;
				default:
					return false;
				}
			}
			}
		}

		private static bool SetBool(string text, ref bool field)
		{
			if (!FogAnchor.TryParseSwitch(text, out var on))
			{
				return false;
			}
			field = on;
			return true;
		}

		private static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}

		public static FogSettings Parse(int tier, Func<string, string> read, List<string> warnings)
		{
			FogSettings fogSettings = Defaults(tier);
			foreach (FogKey key in Keys)
			{
				string text = read(key.Name);
				if (text != null && !fogSettings.TrySet(key.Name, text))
				{
					warnings?.Add(key.Name + " '" + text + "' is malformed; using default " + fogSettings.Get(key.Name));
				}
			}
			return fogSettings;
		}
	}
	public struct FogVolumePoint
	{
		public float X;

		public float Height;

		public float Z;

		public float SizeFactor;

		public float AlphaFactor;

		public FogVolumePoint(float x, float height, float z, float sizeFactor, float alphaFactor)
		{
			X = x;
			Height = height;
			Z = z;
			SizeFactor = sizeFactor;
			AlphaFactor = alphaFactor;
		}
	}
	public static class FogVolumeShape
	{
		public const float DefaultHeightSigma = 0.9f;

		public const float HeightFloor = 0.1f;

		public const float MaxHeightSigma = 5f;

		public const float MaxHeightSigmas = 3f;

		public const float SizeAtGround = 0.7f;

		public const float FullSizeSigmas = 2f;

		public const float RadialSigmaFactor = 0.45f;

		public const float AlphaAtRim = 0.15f;

		public const float RadialSizeAtCentre = 0.8f;

		public const float RadialSizeAtRim = 1.2f;

		private static readonly double TruncatedShare = 1.0 - Gauss(1.0);

		private static double Gauss(double x)
		{
			return Math.Exp((0.0 - x) * x / 0.40499997854232817);
		}

		public static float AreaDensity(float r, float radius)
		{
			if (!(radius > 0f))
			{
				return 0f;
			}
			float num = Math.Abs(r) / radius;
			if (!(num > 1f))
			{
				return (float)Gauss(num);
			}
			return 0f;
		}

		public static double RadiusShare(double x)
		{
			if (!(x <= 0.0))
			{
				if (!(x >= 1.0))
				{
					return (1.0 - Gauss(x)) / TruncatedShare;
				}
				return 1.0;
			}
			return 0.0;
		}

		public static float RadiusAt(float u, float radius)
		{
			if (!(radius > 0f))
			{
				return 0f;
			}
			double num = Clamp01(u);
			double num2 = 1.0 - num * TruncatedShare;
			double val = ((num2 <= 0.0) ? 1.0 : (0.44999998807907104 * Math.Sqrt(Math.Max(0.0, -2.0 * Math.Log(num2)))));
			return (float)Math.Min(1.0, val) * radius;
		}

		public static float HeightDensity(float h, float sigma)
		{
			float num = Math.Max(0.1f, sigma);
			if (!(h < 0f))
			{
				return (float)Math.Exp((0.0 - (double)h) * (double)h / (2.0 * (double)num * (double)num));
			}
			return 0f;
		}

		public static float HeightAt(float u, float sigma)
		{
			float num = Math.Max(0.1f, sigma);
			float num2 = Clamp01(u);
			double num3 = 0.0;
			double num4 = 3.0;
			if (HalfNormalShare(num4) <= (double)num2)
			{
				return Math.Max(0.1f, 3f * num);
			}
			for (int i = 0; i < 40; i++)
			{
				double num5 = 0.5 * (num3 + num4);
				if (HalfNormalShare(num5) < (double)num2)
				{
					num3 = num5;
				}
				else
				{
					num4 = num5;
				}
			}
			return Math.Max(0.1f, (float)(0.5 * (num3 + num4)) * num);
		}

		public static float SizeFactor(float h, float sigma)
		{
			float num = 2f * Math.Max(0.1f, sigma);
			return 0.7f + 0.3f * Clamp01(h / num);
		}

		public static float RadialSizeFactor(float r, float radius)
		{
			if (!(radius > 0f))
			{
				return 1f;
			}
			return 0.8f + 0.40000004f * Clamp01(r / radius);
		}

		public static float AlphaFactor(float r, float radius)
		{
			if (!(radius > 0f))
			{
				return 1f;
			}
			float num = Clamp01(Math.Abs(r) / radius);
			return 0.15f + 0.85f * (float)((Gauss(num) - Gauss(1.0)) / TruncatedShare);
		}

		public static float CappedAlphaFactor(float r, float radius, FogOverlapCap cap)
		{
			return Math.Min(AlphaFactor(r, radius), cap.FactorLimit(r, radius));
		}

		public static FogVolumePoint Sample(float uRadius, float uAngle, float uHeight, float radius, float sigma, FogOverlapCap cap = default(FogOverlapCap))
		{
			return At(RadiusAt(uRadius, radius), (double)uAngle * 2.0 * Math.PI, HeightAt(uHeight, sigma), radius, sigma, cap);
		}

		internal static FogVolumePoint At(float r, double angle, float h, float radius, float sigma, FogOverlapCap cap)
		{
			return new FogVolumePoint((float)((double)r * Math.Cos(angle)), h, (float)((double)r * Math.Sin(angle)), SizeFactor(h, sigma) * RadialSizeFactor(r, radius), CappedAlphaFactor(r, radius, cap));
		}

		private static double HalfNormalShare(double z)
		{
			return Erf(z / Math.Sqrt(2.0));
		}

		private static double Erf(double x)
		{
			if (x <= 0.0)
			{
				return 0.0;
			}
			double num = 1.0 / (1.0 + 0.3275911 * x);
			double num2 = num * (0.254829592 + num * (-0.284496736 + num * (1.421413741 + num * (-1.453152027 + num * 1.061405429))));
			return 1.0 - num2 * Math.Exp((0.0 - x) * x);
		}

		private static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}
	}
	public readonly struct FogOverlapCap
	{
		public readonly float CentreOverlap;

		public readonly float PuffOpacity;

		public readonly float MaxOpacity;

		public bool Active
		{
			get
			{
				if (CentreOverlap > 0f && PuffOpacity > 0f && MaxOpacity > 0f)
				{
					return MaxOpacity < 1f;
				}
				return false;
			}
		}

		public FogOverlapCap(float centreOverlap, float puffOpacity, float maxOpacity)
		{
			CentreOverlap = centreOverlap;
			PuffOpacity = puffOpacity;
			MaxOpacity = maxOpacity;
		}

		public float Overlap(float r, float radius)
		{
			if (!Active)
			{
				return 0f;
			}
			return CentreOverlap * FogVolumeShape.AreaDensity(r, radius);
		}

		public float FactorLimit(float r, float radius)
		{
			if (!Active)
			{
				return 1f;
			}
			float num = Math.Max(1f, Overlap(r, radius));
			double num2 = 1.0 - Math.Pow(1.0 - (double)MaxOpacity, 1.0 / (double)num);
			return (float)Math.Min(1.0, num2 / (double)PuffOpacity);
		}

		public float ColumnOpacity(float r, float radius, float factor)
		{
			float num = Math.Min(1f, Math.Max(0f, PuffOpacity * factor));
			return (float)(1.0 - Math.Pow(1.0 - (double)num, Overlap(r, radius)));
		}
	}
	public static class FogBurstPattern
	{
		public const double GoldenAngle = 2.3999632297286535;

		public static FogVolumePoint Point(int index, int count, float radius, float sigma, FogOverlapCap cap = default(FogOverlapCap))
		{
			if (count <= 0 || index < 0)
			{
				return default(FogVolumePoint);
			}
			float u = (float)(((double)index + 0.5) / (double)count);
			return FogVolumeShape.At(FogVolumeShape.RadiusAt(u, radius), (double)index * 2.3999632297286535, FogVolumeShape.HeightAt((float)RadicalInverse2(index + 1), sigma), radius, sigma, cap);
		}

		private static double RadicalInverse2(int n)
		{
			double num = 0.0;
			double num2 = 0.5;
			while (n > 0)
			{
				if ((n & 1) != 0)
				{
					num += num2;
				}
				n >>= 1;
				num2 *= 0.5;
			}
			return num;
		}
	}
	public static class FogMaterialCheck
	{
		public static readonly string[] BlendProperties = new string[6] { "_BlendOp", "_SrcBlend", "_DstBlend", "_AlphaChannel", "_ZWrite", "_Mode" };

		private const float Epsilon = 0.002f;

		public static List<string> Problems(FogRgb configured, FogRgb color, FogRgb emission, float emissionSetting)
		{
			List<string> list = new List<string>();
			if (color.R > configured.R + 0.002f || color.G > configured.G + 0.002f || color.B > configured.B + 0.002f)
			{
				list.Add("_Color rgb " + FloatList.Format(color.R, color.G, color.B) + " is above the configured " + FloatList.Format(configured.R, configured.G, configured.B));
			}
			if (emissionSetting <= 0f && (emission.R > 0.002f || emission.G > 0.002f || emission.B > 0.002f))
			{
				list.Add("_EmissionColor rgb " + FloatList.Format(emission.R, emission.G, emission.B) + " is not black at Emission 0");
			}
			return list;
		}
	}
	public static class FogSprite
	{
		public const int Size = 128;

		public const float CentreAlpha = 0.8f;

		public const float Sharpness = 4f;

		public const float NoiseAmount = 0.1f;

		public const int NoiseCells = 6;

		private const int Seed = 7919;

		public static readonly float MeanDiscAlpha = (float)(0.800000011920929 * ((1.0 - Math.Exp(-4.0)) / 4.0 - Math.Exp(-4.0)) / (1.0 - Math.Exp(-4.0)));

		public static float Falloff(float r)
		{
			if (r >= 1f)
			{
				return 0f;
			}
			if (r < 0f)
			{
				r = 0f;
			}
			double num = Math.Exp(-4.0);
			return 0.8f * (float)((Math.Exp(-4f * r * r) - num) / (1.0 - num));
		}

		public static float Radius(int x, int y, int size)
		{
			float num = (float)(size - 1) * 0.5f;
			float num2 = ((float)x - num) / num;
			float num3 = ((float)y - num) / num;
			return (float)Math.Sqrt(num2 * num2 + num3 * num3);
		}

		public static float Noise(int x, int y, int size)
		{
			float num = (float)x / (float)Math.Max(1, size - 1) * 6f;
			float num2 = (float)y / (float)Math.Max(1, size - 1) * 6f;
			int num3 = (int)Math.Floor(num);
			int num4 = (int)Math.Floor(num2);
			float num5 = Smooth(num - (float)num3);
			float num6 = Smooth(num2 - (float)num4);
			float num7 = Lattice(num3, num4) + (Lattice(num3 + 1, num4) - Lattice(num3, num4)) * num5;
			float num8 = Lattice(num3, num4 + 1) + (Lattice(num3 + 1, num4 + 1) - Lattice(num3, num4 + 1)) * num5;
			return num7 + (num8 - num7) * num6;
		}

		public static float Alpha(int x, int y, int size)
		{
			float num = Falloff(Radius(x, y, size));
			if (num <= 0f)
			{
				return 0f;
			}
			float num2 = num * (1f + 0.1f * Noise(x, y, size));
			if (!(num2 < 0f))
			{
				if (!(num2 > 0.8f))
				{
					return num2;
				}
				return 0.8f;
			}
			return 0f;
		}

		private static float Smooth(float t)
		{
			return t * t * (3f - 2f * t);
		}

		private static float Lattice(int x, int y)
		{
			uint num = (uint)((x * 73856093) ^ (y * 19349663) ^ 0x1EEF);
			num ^= num >> 13;
			num *= 1540483477;
			num ^= num >> 15;
			return (float)(num & 0xFFFF) / 32767.5f - 1f;
		}
	}
	public static class FloatList
	{
		public static bool TryParseOne(string text, out float value)
		{
			if (!float.TryParse((text ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value))
			{
				return false;
			}
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				value = 0f;
				return false;
			}
			return true;
		}

		public static bool TryParse(string text, int count, out float[] values)
		{
			values = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string[] array = text.Split(new char[1] { ',' });
			if (array.Length != count)
			{
				return false;
			}
			float[] array2 = new float[count];
			for (int i = 0; i < count; i++)
			{
				if (!TryParseOne(array[i], out array2[i]))
				{
					return false;
				}
			}
			values = array2;
			return true;
		}

		public static string Format(float value)
		{
			return value.ToString("0.####", CultureInfo.InvariantCulture);
		}

		public static string Format(params float[] values)
		{
			string[] array = new string[values.Length];
			for (int i = 0; i < values.Length; i++)
			{
				array[i] = Format(values[i]);
			}
			return string.Join(",", array);
		}
	}
	public enum BodyVeilMode
	{
		Off,
		Cutoff,
		Hide,
		Tint,
		Ghost,
		Distortion,
		Shadow,
		Spirit
	}
	public sealed class FogVeil : IVeil
	{
		private sealed class Snapshot
		{
			public int Tier;

			public bool IsLocal;

			public bool ForceHide;

			public int Version;

			public BodyVeilMode Requested;

			public BodyVeilMode Effective;

			public bool FogWanted;

			public float BuiltAt;

			public readonly List<FogEmitter> Fog = new List<FogEmitter>();

			public readonly List<Material> FogMaterials = new List<Material>();

			public bool MaterialHasColor;

			public Color AppliedColor;

			public readonly Dictionary<Material, float> Cutoffs = new Dictionary<Material, float>();

			public readonly Dictionary<Material, Color> Colors = new Dictionary<Material, Color>();

			public readonly Dictionary<Renderer, Material[]> SharedMaterials = new Dictionary<Renderer, Material[]>();

			public readonly HashSet<Renderer> Hidden = new HashSet<Renderer>();

			public readonly Dictionary<Material, Material> SpiritByOriginal = new Dictionary<Material, Material>();

			public readonly List<Material> SpiritOwned = new List<Material>();

			public bool FogIntact()
			{
				if (!FogWanted)
				{
					return true;
				}
				if (Fog.Count == 0)
				{
					return false;
				}
				foreach (FogEmitter item in Fog)
				{
					if ((Object)(object)item.Go == (Object)null)
					{
						return false;
					}
				}
				return true;
			}
		}

		private sealed class FogEmitter
		{
			public GameObject Go;

			public ParticleSystem Ps;

			public string Anchor;

			public FogLayerKind Layer;

			public float VertexAlpha;

			public int Burst;
		}

		private sealed class FogLayer
		{
			public float Rate;

			public float Size;

			public float Lifetime;

			public float SpreadX;

			public float SpreadY;

			public float SpreadZ;

			public float VertexAlpha;

			public FogTrailMode Trail;

			public bool Horizontal;

			public bool Ring;

			public bool Volume;

			public float SizeMinFactor = 0.6f;

			public float SizeMaxFactor = 1.25f;

			public float Speed = float.NaN;

			public float Orbital;

			public float RateDistance;

			public float Grow = 1f;

			public int MaxParticles;

			public int Burst;

			public bool PositionOnly;

			public bool EvenBurst;

			public float HeightSigma = 0.9f;

			public float GroundOffsetY;

			public FogOverlapCap Cap;

			public Material Material;
		}

		private static readonly int CutoffId = Shader.PropertyToID("_Cutoff");

		private static readonly int ColorId = Shader.PropertyToID("_Color");

		private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor");

		private static readonly int RefractionId = Shader.PropertyToID("_RefractionIntensity");

		private static readonly int GlossinessId = Shader.PropertyToID("_Glossiness");

		private static readonly int MetallicId = Shader.PropertyToID("_Metallic");

		private static readonly int NormalTexId = Shader.PropertyToID("_NormalTex");

		private static readonly int MainTexId = Shader.PropertyToID("_MainTex");

		private static readonly int NormalScaleId = Shader.PropertyToID("_NormalScale");

		private static readonly int WaveVelId = Shader.PropertyToID("_WaveVel");

		private static readonly int TintColorId = Shader.PropertyToID("_TintColor");

		private static readonly int ZFadeDistanceId = Shader.PropertyToID("_ZFadeDistance");

		private static readonly int CameraFadeMinId = Shader.PropertyToID("_CameraFadeDistanceMin");

		private static readonly int CameraFadeMaxId = Shader.PropertyToID("_CameraFadeDistanceMax");

		private static readonly int CameraYFadeId = Shader.PropertyToID("_CameraYFadeDistance");

		private static readonly int BillboardId = Shader.PropertyToID("_Billboard");

		private static readonly int LightNormalFactorId = Shader.PropertyToID("_LightNormalFactor");

		private static readonly int SkyMaskId = Shader.PropertyToID("_SkyMask");

		private const float FogZFadeDistance = 0.25f;

		private const float FogCameraFadeMin = 0.3f;

		private const float FogCameraFadeMax = 1f;

		private static readonly Color TintTarget = new Color(0.3f, 0.35f, 0.4f);

		private const float TintAmount = 0.7f;

		private const string FogMaterialSource = "vfx_swamp_mist";

		private const string GhostPrefabName = "Ghost";

		private const string DistortionShaderName = "Custom/Distortion";

		private const string PreferredDistortionMaterial = "Aspect_mat";

		public const string FogObjectName = "ip_fog";

		private const string OuterObjectName = "ip_fog_outer";

		private const string OuterFollowObjectName = "ip_fog_outer_follow";

		private const string GroundObjectName = "ip_fog_ground";

		private const string GroundAnchorName = "Ground";

		private const string GroundUpperAnchorName = "GroundUpper";

		private const float DynamicColorBlend = 0.5f;

		public static readonly FogSettings[] Fog = new FogSettings[4]
		{
			null,
			FogSettings.Defaults(1),
			FogSettings.Defaults(2),
			FogSettings.Defaults(3)
		};

		public static FogAlphaMode AlphaMode = FogAlphaMode.Both;

		public static Color GhostColor = new Color(0.75f, 0.8f, 0.9f, 1f);

		public static float GhostEmission = 0.25f;

		public static Color ShadowColor = DefaultShadowColor;

		public static Color SpiritColor = DefaultSpiritColor;

		public static float SpiritStrength = 0.25f;

		public static readonly Color DefaultShadowColor = new Color(0f, 0f, 0f, 0.12f);

		public static readonly Color DefaultSpiritColor = new Color(0.6f, 0.7f, 0.8f, 1f);

		public const float DefaultSpiritStrength = 0.25f;

		public static readonly BodyVeilMode?[] ModeOverride = new BodyVeilMode?[4];

		private static readonly BodyVeilMode[] _configuredModes = new BodyVeilMode[4]
		{
			BodyVeilMode.Off,
			BodyVeilMode.Off,
			BodyVeilMode.Distortion,
			BodyVeilMode.Distortion
		};

		private readonly Dictionary<Player, Snapshot> _snapshots = new Dictionary<Player, Snapshot>();

		private static readonly List<Material> MatBuf = new List<Material>();

		private static readonly List<Renderer> RendererBuf = new List<Renderer>();

		private static bool _fogWarned;

		private static bool _noAnimatorWarned;

		private static readonly HashSet<string> _missingBoneWarned = new HashSet<string>();

		private static readonly Dictionary<string, string[]> BoneFallbacks = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
		{
			["Chest"] = new string[4] { "Spine2", "Spine1", "Spine", "UpperChest" },
			["Hips"] = new string[1] { "Pelvis" },
			["LeftShoulder"] = new string[2] { "LeftArm", "LeftShoulder" },
			["RightShoulder"] = new string[2] { "RightArm", "RightShoulder" },
			["LeftUpperLeg"] = new string[1] { "LeftUpLeg" },
			["RightUpperLeg"] = new string[1] { "RightUpLeg" },
			["LeftLowerLeg"] = new string[1] { "LeftLeg" },
			["RightLowerLeg"] = new string[1] { "RightLeg" }
		};

		private static readonly Dictionary<string, HumanBodyBones> AnchorBones = new Dictionary<string, HumanBodyBones>(StringComparer.OrdinalIgnoreCase)
		{
			["LeftShoulder"] = (HumanBodyBones)13,
			["RightShoulder"] = (HumanBodyBones)14
		};

		private static readonly Particle[] BurstBuf = (Particle[])(object)new Particle[300];

		private const float GroundStartSpeed = 0.01f;

		private const float GroundShapeFlatten = 0.05f;

		private static readonly HashSet<string> _meshWarned = new HashSet<string>();

		private const string MeshAnchorName = "Mesh";

		private static readonly Dictionary<string, KeyValuePair<string, string[]>> FogMaterialSources = new Dictionary<string, KeyValuePair<string, string[]>>
		{
			["ghost_smoke"] = new KeyValuePair<string, string[]>("ghost_smoke", new string[1] { "Ghost_Void" }),
			["wraith_smoke"] = new KeyValuePair<string, string[]>("wraith_smoke", new string[2] { "Wraith", "vfx_ghost_spawn" }),
			["slowwispysmoke"] = new KeyValuePair<string, string[]>("slowwispysmoke_gradient_alphablend", new string[1] { "Ghost" })
		};

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

		private static readonly HashSet<string> _fogMaterialMissing = new HashSet<string>();

		private static Material _fogMaterial;

		private static Material _softFogMaterial;

		private static Material _ghostSource;

		private static Material _ghostInstance;

		private static Color _ghostSourceEmission = Color.black;

		private const string ShadowPrefabName = "ShadowPerson";

		private const string ShadowMaterialName = "ShadowPerson 1";

		private static Material _shadowSource;

		private static Material _shadowInstance;

		private static bool _shadowSearched;

		private const string SpiritShaderName = "Custom/Fallen Warrior";

		private static readonly string[] SpiritPrefabNames = new string[5] { "FallenWarrior", "Wolf_spiritcaller", "Boar_spiritcaller", "Moose_spiritcaller", "Bjorn_spiritcaller" };

		private static readonly string[] SpiritTextureSlots = new string[6] { "_MainTex", "_SkinBumpMap", "_ChestTex", "_ChestBumpMap", "_LegsTex", "_LegsBumpMap" };

		private static readonly int SkinColorId = Shader.PropertyToID("_SkinColor");

		private static readonly int BumpScaleId = Shader.PropertyToID("_BumpScale");

		private static Shader _spiritShader;

		private static Material _spiritSource;

		private static bool _spiritSearched;

		private const string ShieldPrefabName = "vfx_StaffShield";

		private const string ShieldMaterialName = "staff_shield_shard";

		private static bool _shieldSearched;

		private static Texture _shieldNormal;

		private static float _shieldNormalScale = float.NaN;

		private static readonly Material[] _distortionInstances = (Material[])(object)new Material[4];

		private static Material _distortionSource;

		private static bool _distortionSearched;

		private static bool _distortionLogged;

		public static int Version { get; private set; }

		public static float BorrowedDistortionWave { get; private set; } = float.NaN;

		public static void Bump()
		{
			Version++;
			if ((Object)(object)_ghostInstance != (Object)null)
			{
				ApplyGhostLook(_ghostInstance);
			}
			if ((Object)(object)_shadowInstance != (Object)null)
			{
				ApplyShadowLook(_shadowInstance);
			}
			for (int i = 1; i <= 3; i++)
			{
				if ((Object)(object)_distortionInstances[i] != (Object)null)
				{
					ApplyDistortionLook(_distortionInstances[i], i);
				}
			}
		}

		public static void LoadFromConfig()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0083: 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)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			GlobalConfig global = PluginConfig.Global;
			for (int i = 1; i <= 3; i++)
			{
				Fog[i] = PluginConfig.Fog(i).Clone();
			}
			AlphaMode = PluginConfig.FogAlphaMode;
			GhostColor = ParseColor(global.GhostColor, 4, new Color(0.75f, 0.8f, 0.9f, 1f), "GhostColor");
			GhostEmission = Mathf.Max(0f, global.GhostEmission);
			ShadowColor = ParseColor(global.ShadowColor, 4, DefaultShadowColor, "ShadowColor");
			SpiritColor = ParseColor(global.SpiritColor, 3, DefaultSpiritColor, "SpiritColor");
			SpiritStrength = Mathf.Max(0f, global.SpiritStrength);
			ParseConfiguredModes();
			Bump();
		}

		private static Color ParseColor(string text, int count, Color fallback, string key)
		{
			//IL_0078: 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)
			if (FloatList.TryParse(text, count, out var values))
			{
				return new Color(values[0], values[1], values[2], (count > 3) ? values[3] : 1f);
			}
			Plugin.Log.LogWarning((object)("[Veil] " + key + " '" + text + "' is not " + ((count > 3) ? "r,g,b,a" : "r,g,b") + "; using default"));
			return fallback;
		}

		public static string FormatColor(Color c)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			return FloatList.Format(c.r, c.g, c.b, c.a);
		}

		public static string FormatRgb(Color c)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			return FloatList.Format(c.r, c.g, c.b);
		}

		public static void SaveGlobalLook()
		{
			//IL_0000: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			PluginConfig.SaveVeilLook(FormatColor(GhostColor), GhostEmission, FormatColor(ShadowColor), FormatRgb(SpiritColor), SpiritStrength);
		}

		public static bool TryParseMode(string text, out BodyVeilMode mode)
		{
			if (!string.IsNullOrEmpty(text) && Enum.TryParse<BodyVeilMode>(text.Trim(), ignoreCase: true, out mode) && Enum.IsDefined(typeof(BodyVeilMode), mode))
			{
				return true;
			}
			mode = BodyVeilMode.Off;
			return false;
		}

		public static BodyVeilMode ConfiguredMode(int tier)
		{
			if (tier < 1 || tier > 3)
			{
				return BodyVeilMode.Off;
			}
			return _configuredModes[tier];
		}

		private static void ParseConfiguredModes()
		{
			for (int i = 1; i <= 3; i++)
			{
				string bodyVeilMode = PluginConfig.Tier(i).BodyVeilMode;
				if (TryParseMode(bodyVeilMode, out var mode))
				{
					_configuredModes[i] = mode;
					continue;
				}
				string text = PluginConfig.DefaultBodyVeilMode(i);
				TryParseMode(text, out mode);
				Plugin.Log.LogWarning((object)string.Format("[Tier{0}] BodyVeilMode '{1}' is not one of {2}; using the default {3}", i, bodyVeilMode, string.Join(", ", Enum.GetNames(typeof(BodyVeilMode))), mode));
				_configuredModes[i] = mode;
			}
		}

		public static BodyVeilMode ModeFor(int tier)
		{
			if (tier < 1 || tier > 3)
			{
				return BodyVeilMode.Off;
			}
			return ModeOverride[tier] ?? ConfiguredMode(tier);
		}

		public void Apply(Player p, int tier, bool isLocal, bool forceHide)
		{
			if ((Object)(object)p == (Object)null)
			{
				return;
			}
			BodyVeilMode bodyVeilMode = (forceHide ? BodyVeilMode.Hide : ModeFor(tier));
			if (_snapshots.TryGetValue(p, out var value) && value.Tier == tier && value.IsLocal == isLocal && value.ForceHide == forceHide && value.Version == Version && value.Requested == bodyVeilMode && value.FogIntact())
			{
				ApplyBody(p, value);
				UpdateFogColor(value);
				return;
			}
			Remove(p);
			FogSettings fogSettings = Fog[tier];
			Snapshot snapshot = new Snapshot
			{
				Tier = tier,
				IsLocal = isLocal,
				ForceHide = forceHide,
				Version = Version,
				Requested = bodyVeilMode,
				Effective = Resolve(bodyVeilMode, isLocal, tier),
				FogWanted = (!forceHide && fogSettings.Enabled && (fogSettings.InnerActive || fogSettings.OuterActive || fogSettings.GroundActive)),
				BuiltAt = Time.time
			};
			_snapshots[p] = snapshot;
			if (snapshot.FogWanted)
			{
				SpawnFog(p, snapshot);
			}
			if (snapshot.Fog.Count == 0)
			{
				snapshot.FogWanted = false;
			}
			ApplyBody(p, snapshot);
			Plugin.Log.LogDebug((object)(string.Format("veil T{0} on {1}: mode {2} (effective {3}){4}, ", tier, p.GetPlayerName(), snapshot.Requested, snapshot.Effective, forceHide ? " [hidden from players]" : "") + $"fog {snapshot.Fog.Count} emitters, {snapshot.Hidden.Count} hidden, {snapshot.SharedMaterials.Count} swapped, {snapshot.Colors.Count} tinted, {snapshot.Cutoffs.Count} cut"));
		}

		private static BodyVeilMode Resolve(BodyVeilMode mode, bool isLocal, int tier)
		{
			switch (mode)
			{
			case BodyVeilMode.Hide:
				if (!isLocal || !PluginConfig.Global.ShowSelfFaintly)
				{
					return BodyVeilMode.Hide;
				}
				return BodyVeilMode.Tint;
			case BodyVeilMode.Ghost:
				if ((Object)(object)GhostInstance() != (Object)null)
				{
					return BodyVeilMode.Ghost;
				}
				Plugin.Log.LogWarning((object)"veil mode Ghost: no material found on prefab 'Ghost'; using Tint");
				return BodyVeilMode.Tint;
			case BodyVeilMode.Distortion:
				if ((Object)(object)DistortionInstance(tier) != (Object)null)
				{
					return BodyVeilMode.Distortion;
				}
				Plugin.Log.LogWarning((object)"veil mode Distortion: shader 'Custom/Distortion' not found; using Tint");
				return BodyVeilMode.Tint;
			case BodyVeilMode.Shadow:
				if ((Object)(object)ShadowInstance() != (Object)null)
				{
					return BodyVeilMode.Shadow;
				}
				Plugin.Log.LogWarning((object)"veil mode Shadow: material 'ShadowPerson 1' not found (prefab ShadowPerson, SP_* items, loaded materials); using Tint");
				return BodyVeilMode.Tint;
			case BodyVeilMode.Spirit:
				if ((Object)(object)SpiritShader() != (Object)null)
				{
					return BodyVeilMode.Spirit;
				}
				Plugin.Log.LogWarning((object)"veil mode Spirit: shader 'Custom/Fallen Warrior' not found; using Tint");
				return BodyVeilMode.Tint;
			default:
				return mode;
			}
		}

		private static void ApplyBody(Player p, Snapshot snap)
		{
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			if (snap.Effective == BodyVeilMode.Off)
			{
				return;
			}
			GlobalConfig global = PluginConfig.Global;
			float num = ((snap.IsLocal && global.ShowSelfFaintly) ? global.FogCutoffSelf : ((snap.Tier == 1) ? global.FogCutoffLight : global.FogCutoffDense));
			Material val = ((snap.Effective == BodyVeilMode.Ghost) ? GhostInstance() : ((snap.Effective == BodyVeilMode.Distortion) ? DistortionInstance(snap.Tier) : ((snap.Effective == BodyVeilMode.Shadow) ? ShadowInstance() : null)));
			bool flag = IsSwapMode(snap.Effective);
			((Component)p).GetComponentsInChildren<Renderer>(true, RendererBuf);
			foreach (Renderer item in RendererBuf)
			{
				if ((Object)(object)item == (Object)null || IsOurs(((Component)item).gameObject))
				{
					continue;
				}
				if (flag && item is ParticleSystemRenderer)
				{
					if (item.enabled)
					{
						snap.Hidden.Add(item);
						item.enabled = false;
					}
					continue;
				}
				switch (snap.Effective)
				{
				case BodyVeilMode.Cutoff:
					item.GetMaterials(MatBuf);
					foreach (Material item2 in MatBuf)
					{
						if (!((Object)(object)item2 == (Object)null) && item2.HasProperty(CutoffId))
						{
							if (!snap.Cutoffs.ContainsKey(item2))
							{
								snap.Cutoffs[item2] = item2.GetFloat(CutoffId);
							}
							item2.SetFloat(CutoffId, num);
						}
					}
					break;
				case BodyVeilMode.Hide:
					if (item.enabled)
					{
						snap.Hidden.Add(item);
						item.enabled = false;
					}
					break;
				case BodyVeilMode.Tint:
					if (!IsBodyRenderer(item))
					{
						break;
					}
					item.GetMaterials(MatBuf);
					foreach (Material item3 in MatBuf)
					{
						if (!((Object)(object)item3 == (Object)null) && item3.HasProperty(ColorId))
						{
							if (!snap.Colors.TryGetValue(item3, out var value))
							{
								value = (snap.Colors[item3] = item3.GetColor(ColorId));
							}
							Color val2 = Color.Lerp(value, TintTarget, 0.7f);
							val2.a = value.a;
							item3.SetColor(ColorId, val2);
						}
					}
					break;
				case BodyVeilMode.Ghost:
				case BodyVeilMode.Distortion:
				case BodyVeilMode.Shadow:
					if (!((Object)(object)val == (Object)null) && IsBodyRenderer(item) && !snap.SharedMaterials.ContainsKey(item))
					{
						Material[] sharedMaterials2 = item.sharedMaterials;
						snap.SharedMaterials[item] = sharedMaterials2;
						Material[] array2 = (Material[])(object)new Material[sharedMaterials2.Length];
						for (int j = 0; j < array2.Length; j++)
						{
							array2[j] = val;
						}
						item.sharedMaterials = array2;
					}
					break;
				case BodyVeilMode.Spirit:
					if (IsBodyRenderer(item) && !snap.SharedMaterials.ContainsKey(item))
					{
						Material[] sharedMaterials = item.sharedMaterials;
						Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
						for (int i = 0; i < array.Length; i++)
						{
							array[i] = SpiritFor(snap, sharedMaterials[i]);
						}
						snap.SharedMaterials[item] = sharedMaterials;
						item.sharedMaterials = array;
					}
					break;
				}
			}
			MatBuf.Clear();
		}

		private static bool IsSwapMode(BodyVeilMode m)
		{
			if (m != BodyVeilMode.Ghost && m != BodyVeilMode.Distortion && m != BodyVeilMode.Shadow)
			{
				return m == BodyVeilMode.Spirit;
			}
			return true;
		}

		private static bool IsBodyRenderer(Renderer r)
		{
			if (!(r is SkinnedMeshRenderer))
			{
				return r is MeshRenderer;
			}
			return true;
		}

		private static bool IsOurs(GameObject go)
		{
			return ((Object)go).name.StartsWith("ip_fog", StringComparison.Ordinal);
		}

		private static Transform FindBone(Animator animator, string anchorName)
		{
			//IL_0010: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			HumanBodyBones result;
			if (AnchorBones.TryGetValue(anchorName, out var value))
			{
				Transform boneTransform = Utils.GetBoneTransform(animator, value);
				if ((Object)(object)boneTransform != (Object)null)
				{
					return boneTransform;
				}
			}
			else if (Enum.TryParse<HumanBodyBones>(anchorName, ignoreCase: true, out result) && Enum.IsDefined(typeof(HumanBodyBones), result))
			{
				Transform boneTransform2 = Utils.GetBoneTransform(animator, result);
				if ((Object)(object)boneTransform2 != (Object)null)
				{
					return boneTransform2;
				}
			}
			if (BoneFallbacks.TryGetValue(anchorName, out var value2))
			{
				string[] array = value2;
				foreach (string text in array)
				{
					Transform val = Utils.FindChild(((Component)animator).transform, text, (IterativeSearchType)0);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			return null;
		}

		private static void SpawnFog(Player p, Snapshot snap)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0522: Unknown result type (might be due to invalid IL or missing references)
			//IL_0524: Unknown result type (might be due to invalid IL or missing references)
			//IL_057e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0580: Unknown result type (might be due to invalid IL or missing references)
			FogSettings s = Fog[snap.Tier];
			Material val = FogSourceMaterial(s.FogMaterial);
			if ((Object)(object)val == (Object)null)
			{
				if (!_fogWarned)
				{
					_fogWarned = true;
					Plugin.Log.LogWarning((object)"veil fog: no particle material available; fog skipped");
				}
				return;
			}
			Color color = (snap.AppliedColor = FogColor(s));
			bool innerActive = s.InnerActive;
			bool groundActive = s.GroundActive;
			float vertexAlpha = 0f;
			Material val2 = (innerActive ? MakeFogMaterial(val, snap, color, s.Alpha, s.Emission, out vertexAlpha) : null);
			Material outer = null;
			float outerVertexAlpha = 0f;
			if (s.OuterActive)
			{
				outer = MakeFogMaterial(val, snap, color, s.OuterAlpha, s.Emission, out outerVertexAlpha);
			}
			float vertexAlpha2 = 0f;
			Material val3 = (groundActive ? MakeFogMaterial(val, snap, color, s.GroundAlpha, s.Emission, out vertexAlpha2) : null);
			float vertexAlpha3 = 0f;
			Material val4 = (groundActive ? MakeFogMaterial(val, snap, color, s.GroundUpperAlpha, s.Emission, out vertexAlpha3) : null);
			Animator animator = ((Character)p).m_animator;
			if ((Object)(object)animator == (Object)null && !_noAnimatorWarned)
			{
				_noAnimatorWarned = true;
				Plugin.Log.LogWarning((object)"veil fog: player has no animator; using one emitter at the player root");
			}
			FogLayer fogLayer = new FogLayer
			{
				Rate = s.Rate,
				Size = s.Size,
				Lifetime = s.Lifetime,
				SpreadX = s.SpreadX,
				SpreadY = s.SpreadY,
				SpreadZ = s.SpreadZ,
				Trail = s.InnerTrailMode,
				VertexAlpha = vertexAlpha,
				Material = val2,
				MaxParticles = s.InnerMaxParticles
			};
			if ((Object)(object)val2 != (Object)null && (s.EmitterMode != FogEmitterMode.Mesh || !SpawnMeshFog(p, snap, s, color, fogLayer)))
			{
				bool flag = false;
				foreach (FogAnchor anchor in s.Anchors)
				{
					if (anchor.Enabled && !(s.Rate <= 0f))
					{
						flag = true;
						if ((Object)(object)animator == (Object)null)
						{
							break;
						}
						Transform val5 = BoneFor(animator, anchor.Name);
						if (!((Object)(object)val5 == (Object)null))
						{
							snap.Fog.Add(SpawnEmitter(((Component)p).transform, val5, anchor.Name, FogLayerKind.Inner, anchor.Radius, new Vector3(anchor.X, anchor.Y, anchor.Z), color, fogLayer, s, snap.MaterialHasColor));
						}
					}
				}
				if ((Object)(object)animator == (Object)null && flag)
				{
					snap.Fog.Add(SpawnEmitter(((Component)p).transform, ((Component)p).transform, "Root", FogLayerKind.Inner, 0.4f, new Vector3(0f, 1f, 0f), color, fogLayer, s, snap.MaterialHasColor));
				}
			}
			if ((Object)(object)val3 != (Object)null)
			{
				Transform hips = (((Object)(object)animator != (Object)null) ? FindBone(animator, "Hips") : null);
				snap.Fog.Add(SpawnGroundEmitter(((Component)p).transform, hips, color, s, upper: false, val3, vertexAlpha2, snap.MaterialHasColor));
				if ((Object)(object)val4 != (Object)null)
				{
					snap.Fog.Add(SpawnGroundEmitter(((Component)p).transform, hips, color, s, upper: true, val4, vertexAlpha3, snap.MaterialHasColor));
				}
			}
			if ((Object)(object)outer == (Object)null || (Object)(object)animator == (Object)null)
			{
				return;
			}
			FogOverlapCap overlapCap = s.OuterOverlapCap;
			bool volume = s.OuterShape == FogOuterShape.Volume;
			FogLayer fogLayer2 = (s.OuterHasTrail ? OuterLayer(follow: false) : null);
			FogLayer fogLayer3 = (s.OuterHasFollow ? OuterLayer(follow: true) : null);
			Vector3 offset = default(Vector3);
			foreach (string outerAnchor in s.OuterAnchors)
			{
				FogAnchor fogAnchor = s.Anchor(outerAnchor);
				if (fogAnchor == null)
				{
					continue;
				}
				Transform val6 = BoneFor(animator, fogAnchor.Name);
				if (!((Object)(object)val6 == (Object)null))
				{
					((Vector3)(ref offset))..ctor(fogAnchor.X, fogAnchor.Y + s.OuterOffsetY, fogAnchor.Z);
					if (fogLayer3 != null)
					{
						snap.Fog.Add(SpawnEmitter(((Component)p).transform, val6, fogAnchor.Name + " follow", FogLayerKind.Outer, s.OuterFollowRadius, offset, color, fogLayer3, s, snap.MaterialHasColor, "ip_fog_outer_follow"));
					}
					if (fogLayer2 != null)
					{
						snap.Fog.Add(SpawnEmitter(((Component)p).transform, val6, (fogLayer3 != null) ? (fogAnchor.Name + " trail") : fogAnchor.Name, FogLayerKind.Outer, s.OuterRadius, offset, color, fogLayer2, s, snap.MaterialHasColor));
					}
				}
			}
			FogLayer OuterLayer(bool follow)
			{
				FogLayer fogLayer4 = new FogLayer
				{
					Rate = (follow ? s.OuterFollowRate : s.OuterTrailRate),
					Size = (follow ? s.OuterFollowSize : s.OuterSize),
					Lifetime = s.OuterLifetime,
					SpreadX = 1f,
					SpreadY = s.OuterSpreadY,
					SpreadZ = 1f,
					Trail = ((!follow) ? s.OuterTrailMode : FogTrailMode.Follow),
					VertexAlpha = outerVertexAlpha,
					Material = outer,
					Horizontal = s.OuterHorizontal,
					Ring = !volume,
					Volume = volume,
					Orbital = (follow ? s.OuterFollowOrbitalRadPerSecond : s.OuterOrbitalRadPerSecond),
					PositionOnly = volume,
					EvenBurst = follow,
					RateDistance = (follow ? 0f : s.OuterEffectiveRateDistance),
					MaxParticles = (follow ? s.OuterFollowMaxParticles : s.OuterTrailMaxParticles)
				};
				if (volume)
				{
					fogLayer4.SizeMinFactor = 0.8f;
					fogLayer4.SizeMaxFactor = 1.2f;
					fogLayer4.Speed = 0.05f;
					fogLayer4.Grow = 1.35f;
					fogLayer4.Burst = (follow ? s.OuterFollowBurstCount : s.OuterTrailBurstCount);
					fogLayer4.HeightSigma = s.OuterHeightSigma;
					fogLayer4.GroundOffsetY = s.OuterOffsetY;
					fogLayer4.Cap = overlapCap;
				}
				return fogLayer4;
			}
		}

		private static Transform BoneFor(Animator animator, string anchor)
		{
			Transform val = FindBone(animator, anchor);
			if ((Object)(object)val == (Object)null && _missingBoneWarned.Add(anchor))
			{
				Plugin.Log.LogWarning((object)("veil fog: bone for anchor '" + anchor + "' not found; anchor skipped"));
			}
			return val;
		}

		private static Material MakeFogMaterial(Material source, Snapshot snap, Color color, float alpha, float emission, out float vertexAlpha)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(source)
			{
				name = "ip_fog_mat"
			};
			color = ClampRgb(color);
			bool flag = val.HasProperty(ColorId);
			float num;
			if (!flag || AlphaMode == FogAlphaMode.Vertex)
			{
				num = 1f;
				vertexAlpha = alpha;
			}
			else if (AlphaMode == FogAlphaMode.Material)
			{
				num = alpha;
				vertexAlpha = 1f;
			}
			else
			{
				num = (vertexAlpha = Mathf.Sqrt(alpha));
			}
			if (flag)
			{
				val.SetColor(ColorId, new Color(color.r, color.g, color.b, num));
			}
			if (val.HasProperty(EmissionColorId))
			{
				val.SetColor(EmissionColorId, Emission(color, emission));
			}
			if (val.HasProperty(ZFadeDistanceId))
			{
				val.SetFloat(ZFadeDistanceId, 0.25f);
			}
			if (val.HasProperty(CameraFadeMinId) && val.HasProperty(CameraFadeMaxId))
			{
				val.SetFloat(CameraFadeMinId, 0.3f);
				val.SetFloat(CameraFadeMaxId, 1f);
			}
			snap.MaterialHasColor = flag;
			snap.FogMaterials.Add(val);
			return val;
		}

		private static Color Emission(Color color, float emission)
		{
			//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_000d: 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_0017: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp01(emission);
			color = ClampRgb(color);
			return new Color(color.r * num, color.g * num, color.b * num, 1f);
		}

		private static Color ClampRgb(Color c)
		{
			//IL_0000: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			return new Color(Mathf.Clamp01(c.r), Mathf.Clamp01(c.g), Mathf.Clamp01(c.b), c.a);
		}

		private static Color FogColor(FogSettings s)
		{
			//IL_0025: 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_0031: 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_003d: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			if (!s.DynamicColor)
			{
				return ClampRgb(new Color(s.R, s.G, s.B));
			}
			Color fogColor = RenderSettings.fogColor;
			FogRgb fogRgb = FogRgb.Blend(s.Color, new FogRgb(fogColor.r, fogColor.g, fogColor.b), 0.5f);
			return ClampRgb(new Color(fogRgb.R, fogRgb.G, fogRgb.B));
		}

		private static void UpdateFogColor(Snapshot snap)
		{
			//IL_0025: 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_002c: 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)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_005d: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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)
			if (snap.Fog.Count == 0)
			{
				return;
			}
			FogSettings fogSettings = Fog[snap.Tier];
			if (!fogSettings.DynamicColor)
			{
				return;
			}
			Color val = FogColor(fogSettings);
			Color appliedColor = snap.AppliedColor;
			if (Mathf.Abs(val.r - appliedColor.r) + Mathf.Abs(val.g - appliedColor.g) + Mathf.Abs(val.b - appliedColor.b) < 0.01f)
			{
				return;
			}
			snap.AppliedColor = val;
			if (snap.MaterialHasColor)
			{
				foreach (Material fogMaterial in snap.FogMaterials)
				{
					if (!((Object)(object)fogMaterial == (Object)null))
					{
						float a = fogMaterial.GetColor(ColorId).a;
						fogMaterial.SetColor(ColorId, new Color(val.r, val.g, val.b, a));
						if (fogMaterial.HasProperty(EmissionColorId))
						{
							fogMaterial.SetColor(EmissionColorId, Emission(val, fogSettings.Emission));
						}
					}
				}
				return;
			}
			foreach (FogEmitter item in snap.Fog)
			{
				if (!((Object)(object)item.Ps == (Object)null))
				{
					MainModule main = item.Ps.main;
					((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(val.r, val.g, val.b, item.VertexAlpha));
				}
			}
		}

		private static FogEmitter SpawnEmitter(Transform root, Transform bone, string anchor, FogLayerKind kind, float radius, Vector3 offset, Color color, FogLayer layer, FogSettings s, bool materialHasColor, string objectName = null)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(objectName ?? ((kind == FogLayerKind.Outer) ? "ip_fog_outer" : "ip_fog"));
			val.SetActive(false);
			val.transform.SetParent(root, false);
			FogAnchorFollower fogAnchorFollower = val.AddComponent<FogAnchorFollower>();
			fogAnchorFollower.Bone = bone;
			fogAnchorFollower.Root = root;
			fogAnchorFollower.Offset = offset;
			fogAnchorFollower.PositionOnly = layer.PositionOnly;
			fogAnchorFollower.SpawnedAt = Time.time;
			fogAnchorFollower.Snap();
			ParticleSystem val2 = ConfigureSystem(val, color, layer, s, materialHasColor);
			ShapeModule shape = val2.shape;
			((ShapeModule)(ref shape)).enabled = true;
			if (layer.Ring)
			{
				((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)10;
				((ShapeModule)(ref shape)).radius = Mathf.Max(0.001f, radius);
				((ShapeModule)(ref shape)).radiusThickness = 0.25f;
				((ShapeModule)(ref shape)).arc = 360f;
				((ShapeModule)(ref shape)).rotation = new Vector3(90f, 0f, 0f);
				((ShapeModule)(ref shape)).scale = Vector3.one;
				((ShapeModule)(ref shape)).randomPositionAmount = Mathf.Max(0f, layer.SpreadY * radius);
			}
			else if (layer.Volume)
			{
				((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0;
				((ShapeModule)(ref shape)).radius = Mathf.Max(0.001f, radius);
				((ShapeModule)(ref shape)).radiusThickness = 1f;
				((ShapeModule)(ref shape)).scale = Vector3.one;
				((ShapeModule)(ref shape)).enabled = false;
				fogAnchorFollower.Volume = new FogVolumeEmission
				{
					Rate = Mathf.Max(0f, layer.Rate),
					RateDistance = ((layer.Trail == FogTrailMode.Trail) ? Mathf.Max(0f, layer.RateDistance) : 0f),
					Radius = Mathf.Max(0f, radius),
					Sigma = layer.HeightSigma,
					OffsetY = layer.GroundOffsetY,
					Size = layer.Size,
					SizeMinFactor = layer.SizeMinFactor,
					SizeMaxFactor = layer.SizeMaxFactor,
					Speed = (float.IsNaN(layer.Speed) ? s.Speed : layer.Speed),
					World = (layer.Trail == FogTrailMode.Trail),
					Cap = layer.Cap
				};
			}
			else
			{
				((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0;
				((ShapeModule)(ref shape)).radius = Mathf.Max(0.001f, radius);
				((ShapeModule)(ref shape)).scale = new Vector3(layer.SpreadX, layer.SpreadY, layer.SpreadZ);
			}
			val.SetActive(true);
			val2.Play();
			fogAnchorFollower.Ps = val2;
			int burst = ((layer.Volume && layer.Burst > 0 && fogAnchorFollower.Volume != null) ? EmitBurst(val2, layer.Burst, layer.EvenBurst && layer.Trail == FogTrailMode.Follow, fogAnchorFollower.Volume, val.transform, root) : 0);
			return new FogEmitter
			{
				Go = val,
				Ps = val2,
				Anchor = anchor,
				Layer = kind,
				VertexAlpha = layer.VertexAlpha,
				Burst = burst
			};
		}

		private static int EmitBurst(ParticleSystem ps, int count, bool even, FogVolumeEmissi