Decompiled source of SandevistanFX v1.5.0

Mods/SandevistanFX.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.AI;
using Il2CppSLZ.Marrow.Interaction;
using Il2CppSLZ.VRMK;
using MelonLoader;
using MelonLoader.Preferences;
using SandevistanFX;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Main), "SandevistanFX", "1.5.0", "YourName", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib", "Quicksilver" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyFileVersion("1.5.0.0")]
[assembly: AssemblyInformationalVersion("1.5.0")]
[assembly: AssemblyTitle("SandevistanFX")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.0.0")]
[module: UnverifiableCode]
namespace SandevistanFX;

internal sealed class AfterimageSystem
{
	private sealed class PartMotion
	{
		private const float HeadTravel = 1f;

		private const float StillLimit = 0.1f;

		private Vector3 _refPart;

		private Vector3 _refHead;

		private bool _hasRef;

		private bool _reported;

		public bool Stationary;

		public bool Update(Vector3 part, Vector3 head, string name)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!_hasRef)
			{
				_refPart = part;
				_refHead = head;
				_hasRef = true;
				return Stationary;
			}
			if (FxMath.DistanceSq(head, _refHead) < 1f)
			{
				return Stationary;
			}
			Stationary = FxMath.DistanceSq(part, _refPart) < 0.010000001f;
			if (Stationary && !_reported)
			{
				_reported = true;
				Main.Log.Msg("Leaving '" + name + "' out of afterimages: it stays put while you move, so it isn't part of your body.");
			}
			_refPart = part;
			_refHead = head;
			return Stationary;
		}
	}

	private sealed class SkinnedProfile
	{
		public SkinnedMeshRenderer Source;

		public string Name;

		public int[] BoneMap;

		public int[] UniqueBones;

		public int RootIndex;

		public int SubMeshCount;

		public int BlendShapeCount;

		public IntPtr MeshKey;

		public IntPtr RootKey;

		public IntPtr[] BoneKeys;

		public bool ReportedFar;

		public readonly PartMotion Motion = new PartMotion();
	}

	private sealed class StaticProfile
	{
		public MeshRenderer Source;

		public MeshFilter Filter;

		public string Name;

		public bool IsHead;

		public int SubMeshCount;

		public IntPtr MeshKey;

		public bool ReportedFar;

		public readonly PartMotion Motion = new PartMotion();
	}

	private sealed class AvatarProfile
	{
		public int Id;

		public IntPtr AvatarKey;

		public Transform[] Bones;

		public int[] HeadBones;

		public float Reach;

		public int SkinnedComponentCount;

		public int StaticComponentCount;

		public readonly List<SkinnedProfile> Skinned = new List<SkinnedProfile>();

		public readonly List<StaticProfile> Statics = new List<StaticProfile>();
	}

	private sealed class AvatarGhost
	{
		public GameObject Root;

		public Transform[] Bones;

		public Vector3[] HeadScales;

		public SkinnedMeshRenderer[] Skinned;

		public bool[] SkinnedOn;

		public MeshRenderer[] Statics;

		public Transform[] StaticTransforms;

		public bool[] StaticOn;

		public Material Material;

		public int Generation;

		public int ProfileId;
	}

	private sealed class ItemPart
	{
		public GameObject Go;

		public Transform Transform;

		public MeshFilter Filter;

		public MeshRenderer Renderer;

		public int MaterialSlots = -1;

		public IntPtr MaterialKey = IntPtr.Zero;

		public Vector3 Position;

		public bool Shown;
	}

	private sealed class Snapshot
	{
		public float Born;

		public Color Color;

		public Vector3 Eye;

		public Vector3 LeftHand;

		public Vector3 RightHand;

		public bool HasLeftHand;

		public bool HasRightHand;

		public bool Revealed;

		public bool HeadHidden;

		public AvatarGhost Avatar;

		public readonly List<ItemPart> Items = new List<ItemPart>();

		public Material ItemMaterial;

		public int ItemMaterialGeneration;
	}

	private sealed class ItemSources
	{
		public float RefreshAt;

		public readonly List<MeshRenderer> Renderers = new List<MeshRenderer>();

		public readonly List<MeshFilter> Filters = new List<MeshFilter>();
	}

	private struct PlayerPose
	{
		public Vector3 Eye;

		public Vector3 Body;

		public Vector3 LeftHand;

		public Vector3 RightHand;

		public bool HasLeftHand;

		public bool HasRightHand;

		public static PlayerPose Read(Camera camera)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			PlayerPose result = new PlayerPose
			{
				Eye = ((Component)camera).transform.position
			};
			result.Body = BodyReference(result.Eye);
			Hand leftHand = Player.LeftHand;
			if (FxMath.Alive((Object)(object)leftHand))
			{
				result.LeftHand = ((Component)leftHand).transform.position;
				result.HasLeftHand = true;
			}
			Hand rightHand = Player.RightHand;
			if (FxMath.Alive((Object)(object)rightHand))
			{
				result.RightHand = ((Component)rightHand).transform.position;
				result.HasRightHand = true;
			}
			return result;
		}
	}

	private enum PoseResult
	{
		Ok,
		NothingToShow,
		Stale
	}

	private const int MaxItemPartsPerSnapshot = 48;

	private const float ItemEyeHideRadius = 0.3f;

	private const float AvatarSettleTime = 1f;

	private const float ValidateInterval = 0.5f;

	private const float HandRevealFactor = 1.75f;

	private GameObject _root;

	private AvatarProfile _profile;

	private int _nextProfileId;

	private IntPtr _failedAvatar = IntPtr.Zero;

	private float _profileRetryAt;

	private int _avatarGhostsCreated;

	private IntPtr _seenAvatar = IntPtr.Zero;

	private float _seenAvatarAt;

	private float _nextValidate;

	private bool _forceRebuild;

	private bool _validationOff;

	private readonly Queue<float> _rebuildTimes = new Queue<float>();

	private Vector3[] _bonePositions = (Vector3[])(object)new Vector3[0];

	private readonly Stack<AvatarGhost> _avatarPool = new Stack<AvatarGhost>();

	private readonly Stack<ItemPart> _itemPool = new Stack<ItemPart>();

	private readonly Stack<Snapshot> _snapshotPool = new Stack<Snapshot>();

	private readonly List<Snapshot> _live = new List<Snapshot>();

	private readonly Dictionary<IntPtr, ItemSources> _itemSources = new Dictionary<IntPtr, ItemSources>();

	private readonly List<Vector3> _points = new List<Vector3>();

	private readonly List<Vector3> _lastPoints = new List<Vector3>();

	private float _lastSpawn = -999f;

	private static bool _meshInfoBroken;

	private static bool _blendShapesBroken;

	private static BlendKind GhostBlend
	{
		get
		{
			if (!Settings.AfterimageAdditive.Value)
			{
				return BlendKind.Alpha;
			}
			return BlendKind.Additive;
		}
	}

	private static Vector3 BodyReference(Vector3 fallback)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		Transform head = Player.Head;
		if (!FxMath.Alive((Object)(object)head))
		{
			return fallback;
		}
		return head.position;
	}

	public void Tick(PlayerScanner scanner, Camera camera)
	{
		float now = FxClock.Now;
		if (!EnsureRoot())
		{
			return;
		}
		if (!FxMath.Alive((Object)(object)camera))
		{
			HideAll();
			return;
		}
		EnsureProfile(now, immediate: true);
		PlayerPose pose = PlayerPose.Read(camera);
		if (_lastPoints.Count == 0)
		{
			GatherTrackedPoints(pose);
			_lastPoints.AddRange(_points);
		}
		else if (now - _lastSpawn >= Math.Max(0.01f, Settings.AfterimageInterval.Value))
		{
			GatherTrackedPoints(pose);
			if (HasMoved())
			{
				Spawn(scanner, now, pose);
				_lastSpawn = now;
				_lastPoints.Clear();
				_lastPoints.AddRange(_points);
			}
		}
		UpdateLive(now, pose);
	}

	public void HideAll()
	{
		for (int i = 0; i < _live.Count; i++)
		{
			Recycle(_live[i]);
		}
		_live.Clear();
		_lastPoints.Clear();
		_lastSpawn = -999f;
	}

	public void Prewarm()
	{
		if (!Settings.AfterimagesEnabled.Value || !EnsureRoot())
		{
			return;
		}
		EnsureProfile(FxClock.Now, immediate: false);
		if (_profile == null)
		{
			return;
		}
		int num = MaxAlive();
		if (_avatarGhostsCreated < num)
		{
			AvatarGhost avatarGhost = CreateAvatarGhost();
			if (avatarGhost != null)
			{
				_avatarPool.Push(avatarGhost);
			}
		}
	}

	public void OnAvatarChanged()
	{
		HideAll();
		DropProfile();
		_seenAvatar = IntPtr.Zero;
	}

	public void OnMaterialSettingsChanged()
	{
		HideAll();
		DropProfile();
	}

	public void DestroyAll()
	{
		HideAll();
		DropProfile();
		while (_snapshotPool.Count > 0)
		{
			MaterialFactory.Destroy(_snapshotPool.Pop().ItemMaterial);
		}
		_itemPool.Clear();
		_itemSources.Clear();
		_seenAvatar = IntPtr.Zero;
		_failedAvatar = IntPtr.Zero;
		if (FxMath.Alive((Object)(object)_root))
		{
			Object.Destroy((Object)(object)_root);
		}
		_root = null;
	}

	private void Spawn(PlayerScanner scanner, float now, PlayerPose pose)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: 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_012e: Unknown result type (might be due to invalid IL or missing references)
		int num = MaxAlive();
		while (_live.Count >= num)
		{
			Recycle(_live[0]);
			_live.RemoveAt(0);
		}
		Snapshot snapshot = ((_snapshotPool.Count > 0) ? _snapshotPool.Pop() : new Snapshot());
		snapshot.Born = now;
		snapshot.Revealed = false;
		snapshot.HeadHidden = false;
		snapshot.Eye = pose.Eye;
		snapshot.LeftHand = pose.LeftHand;
		snapshot.RightHand = pose.RightHand;
		snapshot.HasLeftHand = pose.HasLeftHand;
		snapshot.HasRightHand = pose.HasRightHand;
		float h = now * Settings.AfterimageHueSpeed.Value;
		snapshot.Color = FxMath.HsvToRgb(h, Settings.AfterimageSaturation.Value, 1f);
		try
		{
			if (_profile != null)
			{
				AvatarGhost avatarGhost = RentAvatarGhost();
				if (avatarGhost != null)
				{
					PoseResult poseResult = PoseAvatarGhost(avatarGhost, pose.Body);
					if (poseResult == PoseResult.Ok)
					{
						snapshot.Avatar = avatarGhost;
					}
					else
					{
						ReturnAvatarGhost(avatarGhost);
						if (poseResult == PoseResult.Stale)
						{
							_forceRebuild = true;
						}
					}
				}
			}
			if (Settings.AfterimageIncludeItems.Value && scanner.ItemRoots.Count > 0)
			{
				SpawnItemParts(snapshot, scanner, now, pose.Body);
			}
		}
		catch
		{
			Recycle(snapshot);
			throw;
		}
		if (snapshot.Avatar == null && snapshot.Items.Count == 0)
		{
			_snapshotPool.Push(snapshot);
			return;
		}
		SetSnapshotAlpha(snapshot, 0f);
		_live.Add(snapshot);
	}

	private void UpdateLive(float now, PlayerPose pose)
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		float num = Math.Max(0.05f, Settings.AfterimageLifetime.Value);
		float num2 = Math.Min(Math.Max(0f, Settings.AfterimageRevealDelay.Value), num * 0.5f);
		float alpha = FxMath.Clamp01(Settings.AfterimageOpacity.Value);
		float num3 = Math.Max(0f, Settings.AfterimageHeadHideRadius.Value);
		float num4 = num3 * num3;
		float num5 = Math.Max(0f, Settings.AfterimageRevealDistance.Value);
		float num6 = num5 * num5;
		float num7 = num6 * 1.75f * 1.75f;
		float num8 = 0.09f;
		Vector3 eye = pose.Eye;
		for (int num9 = _live.Count - 1; num9 >= 0; num9--)
		{
			Snapshot snapshot = _live[num9];
			float num10 = now - snapshot.Born;
			if (num10 >= num)
			{
				Recycle(snapshot);
				_live.RemoveAt(num9);
				continue;
			}
			bool flag = FxMath.DistanceSq(eye, snapshot.Eye) < num4;
			if (flag != snapshot.HeadHidden)
			{
				SetHeadHidden(snapshot, flag);
			}
			if (!snapshot.Revealed)
			{
				if (num10 < num2 || (!(FxMath.DistanceSq(eye, snapshot.Eye) >= num6) && (!pose.HasLeftHand || !snapshot.HasLeftHand || !(FxMath.DistanceSq(pose.LeftHand, snapshot.LeftHand) >= num7)) && (!pose.HasRightHand || !snapshot.HasRightHand || !(FxMath.DistanceSq(pose.RightHand, snapshot.RightHand) >= num7))))
				{
					continue;
				}
				SetSnapshotAlpha(snapshot, alpha);
				if (snapshot.Avatar != null && FxMath.Alive((Object)(object)snapshot.Avatar.Root))
				{
					snapshot.Avatar.Root.SetActive(true);
				}
				snapshot.Revealed = true;
			}
			for (int i = 0; i < snapshot.Items.Count; i++)
			{
				ItemPart itemPart = snapshot.Items[i];
				bool flag2 = FxMath.DistanceSq(itemPart.Position, eye) >= num8;
				if (flag2 != itemPart.Shown && FxMath.Alive((Object)(object)itemPart.Go))
				{
					itemPart.Go.SetActive(flag2);
					itemPart.Shown = flag2;
				}
			}
		}
	}

	private static int MaxAlive()
	{
		int value = Settings.AfterimageMaxCount.Value;
		if (value < 1)
		{
			return 1;
		}
		if (value <= 100)
		{
			return value;
		}
		return 100;
	}

	private void GatherTrackedPoints(PlayerPose pose)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		_points.Clear();
		_points.Add(pose.Eye);
		if (pose.HasLeftHand)
		{
			_points.Add(pose.LeftHand);
		}
		if (pose.HasRightHand)
		{
			_points.Add(pose.RightHand);
		}
	}

	private bool HasMoved()
	{
		//IL_003c: 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)
		if (_lastPoints.Count != _points.Count)
		{
			return true;
		}
		float num = Math.Max(0f, Settings.AfterimageMinMove.Value);
		float num2 = num * num;
		for (int i = 0; i < _points.Count; i++)
		{
			if (FxMath.DistanceSq(_points[i], _lastPoints[i]) >= num2)
			{
				return true;
			}
		}
		return false;
	}

	private void SetSnapshotAlpha(Snapshot snapshot, float alpha)
	{
		//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_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		Color color = snapshot.Color;
		color.a = alpha;
		if (snapshot.Avatar != null)
		{
			MaterialFactory.SetColor(snapshot.Avatar.Material, color);
		}
		if (snapshot.Items.Count > 0)
		{
			MaterialFactory.SetColor(snapshot.ItemMaterial, color);
		}
	}

	private void SetVisible(Snapshot snapshot, bool visible)
	{
		if (snapshot.Avatar != null && FxMath.Alive((Object)(object)snapshot.Avatar.Root))
		{
			snapshot.Avatar.Root.SetActive(visible);
		}
		for (int i = 0; i < snapshot.Items.Count; i++)
		{
			ItemPart itemPart = snapshot.Items[i];
			if (FxMath.Alive((Object)(object)itemPart.Go))
			{
				itemPart.Go.SetActive(visible);
			}
			itemPart.Shown = visible;
		}
	}

	private void SetHeadHidden(Snapshot snapshot, bool hidden)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		snapshot.HeadHidden = hidden;
		AvatarGhost avatar = snapshot.Avatar;
		if (avatar == null || _profile == null)
		{
			return;
		}
		int[] headBones = _profile.HeadBones;
		for (int i = 0; i < headBones.Length; i++)
		{
			int num = headBones[i];
			Transform val = avatar.Bones[num];
			if (!((Object)(object)val == (Object)null))
			{
				val.localScale = (hidden ? Vector3.zero : avatar.HeadScales[i]);
			}
		}
		for (int j = 0; j < avatar.Statics.Length; j++)
		{
			if (_profile.Statics[j].IsHead)
			{
				MeshRenderer val2 = avatar.Statics[j];
				if ((Object)(object)val2 != (Object)null)
				{
					((Renderer)val2).enabled = avatar.StaticOn[j] && !hidden;
				}
			}
		}
	}

	private void Recycle(Snapshot snapshot)
	{
		SetVisible(snapshot, visible: false);
		if (snapshot.Avatar != null)
		{
			AvatarGhost avatar = snapshot.Avatar;
			snapshot.Avatar = null;
			ReturnAvatarGhost(avatar);
		}
		for (int i = 0; i < snapshot.Items.Count; i++)
		{
			ItemPart itemPart = snapshot.Items[i];
			if (FxMath.Alive((Object)(object)itemPart.Go))
			{
				_itemPool.Push(itemPart);
			}
		}
		snapshot.Items.Clear();
		snapshot.Revealed = false;
		snapshot.HeadHidden = false;
		_snapshotPool.Push(snapshot);
	}

	private bool Reusable(AvatarGhost ghost)
	{
		if (_profile != null && ghost.ProfileId == _profile.Id && FxMath.Alive((Object)(object)ghost.Root) && FxMath.Alive((Object)(object)ghost.Material))
		{
			return ghost.Generation == MaterialFactory.Generation;
		}
		return false;
	}

	private AvatarGhost RentAvatarGhost()
	{
		while (_avatarPool.Count > 0)
		{
			AvatarGhost avatarGhost = _avatarPool.Pop();
			if (Reusable(avatarGhost))
			{
				return avatarGhost;
			}
			DestroyAvatarGhost(avatarGhost);
		}
		return CreateAvatarGhost();
	}

	private void ReturnAvatarGhost(AvatarGhost ghost)
	{
		if (ghost != null)
		{
			if (FxMath.Alive((Object)(object)ghost.Root))
			{
				ghost.Root.SetActive(false);
			}
			if (Reusable(ghost))
			{
				_avatarPool.Push(ghost);
			}
			else
			{
				DestroyAvatarGhost(ghost);
			}
		}
	}

	private AvatarGhost CreateAvatarGhost()
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Expected O, but got Unknown
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Expected O, but got Unknown
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Expected O, but got Unknown
		//IL_0244: Unknown result type (might be due to invalid IL or missing references)
		//IL_0258: Unknown result type (might be due to invalid IL or missing references)
		//IL_025d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cd: Expected O, but got Unknown
		AvatarProfile profile = _profile;
		if (profile == null)
		{
			return null;
		}
		Material val = MaterialFactory.Create("Afterimage", GhostBlend, new Color(1f, 1f, 1f, 0f), 4550, cullOff: false, alwaysOnTop: false);
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		GameObject val2 = new GameObject("[SandevistanFX] Afterimage");
		val2.SetActive(false);
		FxMath.AttachAtOrigin(val2.transform, _root.transform);
		AvatarGhost avatarGhost = new AvatarGhost
		{
			Root = val2,
			Material = val,
			Generation = MaterialFactory.Generation,
			ProfileId = profile.Id,
			Bones = (Transform[])(object)new Transform[profile.Bones.Length],
			HeadScales = (Vector3[])(object)new Vector3[profile.HeadBones.Length],
			Skinned = (SkinnedMeshRenderer[])(object)new SkinnedMeshRenderer[profile.Skinned.Count],
			SkinnedOn = new bool[profile.Skinned.Count],
			Statics = (MeshRenderer[])(object)new MeshRenderer[profile.Statics.Count],
			StaticTransforms = (Transform[])(object)new Transform[profile.Statics.Count],
			StaticOn = new bool[profile.Statics.Count]
		};
		for (int i = 0; i < avatarGhost.Bones.Length; i++)
		{
			GameObject val3 = new GameObject();
			FxMath.AttachAtOrigin(val3.transform, val2.transform);
			avatarGhost.Bones[i] = val3.transform;
		}
		for (int j = 0; j < profile.Skinned.Count; j++)
		{
			SkinnedProfile skinnedProfile = profile.Skinned[j];
			GameObject val4 = new GameObject("Skin");
			val4.layer = ((Component)skinnedProfile.Source).gameObject.layer;
			FxMath.AttachAtOrigin(val4.transform, val2.transform);
			Transform[] array = (Transform[])(object)new Transform[skinnedProfile.BoneMap.Length];
			for (int k = 0; k < array.Length; k++)
			{
				array[k] = avatarGhost.Bones[skinnedProfile.BoneMap[k]];
			}
			SkinnedMeshRenderer val5 = val4.AddComponent<SkinnedMeshRenderer>();
			val5.sharedMesh = skinnedProfile.Source.sharedMesh;
			val5.bones = Il2CppReferenceArray<Transform>.op_Implicit(array);
			val5.rootBone = avatarGhost.Bones[skinnedProfile.RootIndex];
			((Renderer)val5).localBounds = new Bounds(new Vector3(0f, 0f, 0f), new Vector3(5f, 5f, 5f));
			RendererUtil.DisableShadows((Renderer)(object)val5);
			((Renderer)val5).sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(RendererUtil.Repeat(val, skinnedProfile.SubMeshCount));
			avatarGhost.Skinned[j] = val5;
		}
		for (int l = 0; l < profile.Statics.Count; l++)
		{
			StaticProfile staticProfile = profile.Statics[l];
			GameObject val6 = new GameObject("Static");
			val6.layer = ((Component)staticProfile.Source).gameObject.layer;
			FxMath.AttachAtOrigin(val6.transform, val2.transform);
			val6.AddComponent<MeshFilter>().sharedMesh = staticProfile.Filter.sharedMesh;
			MeshRenderer val7 = val6.AddComponent<MeshRenderer>();
			RendererUtil.DisableShadows((Renderer)(object)val7);
			((Renderer)val7).sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(RendererUtil.Repeat(val, staticProfile.SubMeshCount));
			avatarGhost.Statics[l] = val7;
			avatarGhost.StaticTransforms[l] = val6.transform;
		}
		_avatarGhostsCreated++;
		return avatarGhost;
	}

	private PoseResult PoseAvatarGhost(AvatarGhost ghost, Vector3 body)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: 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_0231: Unknown result type (might be due to invalid IL or missing references)
		//IL_0236: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_023a: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02be: Unknown result type (might be due to invalid IL or missing references)
		//IL_0269: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		AvatarProfile profile = _profile;
		Transform[] bones = profile.Bones;
		Transform[] bones2 = ghost.Bones;
		if (_bonePositions.Length < bones.Length)
		{
			_bonePositions = (Vector3[])(object)new Vector3[bones.Length];
		}
		for (int i = 0; i < bones.Length; i++)
		{
			Transform val = bones[i];
			if ((Object)(object)val == (Object)null)
			{
				return PoseResult.Stale;
			}
			Vector3 position = val.position;
			_bonePositions[i] = position;
			Transform obj = bones2[i];
			obj.SetPositionAndRotation(position, val.rotation);
			obj.localScale = val.lossyScale;
		}
		int[] headBones = profile.HeadBones;
		for (int j = 0; j < headBones.Length; j++)
		{
			ghost.HeadScales[j] = bones2[headBones[j]].localScale;
		}
		float num = profile.Reach * profile.Reach;
		bool flag = false;
		for (int k = 0; k < profile.Skinned.Count; k++)
		{
			SkinnedProfile skinnedProfile = profile.Skinned[k];
			SkinnedMeshRenderer val2 = ghost.Skinned[k];
			bool flag2 = FxMath.Alive((Object)(object)skinnedProfile.Source) && ((Renderer)skinnedProfile.Source).enabled && ((Component)skinnedProfile.Source).gameObject.activeInHierarchy;
			if (flag2)
			{
				Vector3 val3 = Center(skinnedProfile.UniqueBones, _bonePositions);
				float num2 = FxMath.DistanceSq(val3, body);
				if (num2 > num)
				{
					flag2 = false;
					ReportFar(ref skinnedProfile.ReportedFar, skinnedProfile.Name, num2);
				}
				else if (skinnedProfile.Motion.Update(val3, body, skinnedProfile.Name))
				{
					flag2 = false;
				}
			}
			ghost.SkinnedOn[k] = flag2;
			((Renderer)val2).enabled = flag2;
			if (flag2)
			{
				flag = true;
				if (skinnedProfile.BlendShapeCount > 0)
				{
					CopyBlendShapes(skinnedProfile.Source, val2, skinnedProfile.BlendShapeCount);
				}
			}
		}
		for (int l = 0; l < profile.Statics.Count; l++)
		{
			StaticProfile staticProfile = profile.Statics[l];
			bool flag3 = FxMath.Alive((Object)(object)staticProfile.Source) && ((Renderer)staticProfile.Source).enabled && ((Component)staticProfile.Source).gameObject.activeInHierarchy;
			Transform val4 = null;
			if (flag3)
			{
				val4 = ((Component)staticProfile.Source).transform;
				Vector3 position2 = val4.position;
				float num3 = FxMath.DistanceSq(position2, body);
				if (num3 > num)
				{
					flag3 = false;
					ReportFar(ref staticProfile.ReportedFar, staticProfile.Name, num3);
				}
				else if (staticProfile.Motion.Update(position2, body, staticProfile.Name))
				{
					flag3 = false;
				}
			}
			ghost.StaticOn[l] = flag3;
			((Renderer)ghost.Statics[l]).enabled = flag3;
			if (flag3)
			{
				flag = true;
				Transform obj2 = ghost.StaticTransforms[l];
				obj2.SetPositionAndRotation(val4.position, val4.rotation);
				obj2.localScale = val4.lossyScale;
			}
		}
		if (!flag)
		{
			return PoseResult.NothingToShow;
		}
		return PoseResult.Ok;
	}

	private static Vector3 Center(int[] indices, Vector3[] positions)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: 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_0068: Unknown result type (might be due to invalid IL or missing references)
		float num = 0f;
		float num2 = 0f;
		float num3 = 0f;
		for (int i = 0; i < indices.Length; i++)
		{
			Vector3 val = positions[indices[i]];
			num += val.x;
			num2 += val.y;
			num3 += val.z;
		}
		float num4 = 1f / (float)Math.Max(1, indices.Length);
		return new Vector3(num * num4, num2 * num4, num3 * num4);
	}

	private static void ReportFar(ref bool reported, string name, float distSq)
	{
		if (!reported)
		{
			reported = true;
			Main.Log.Msg("Leaving '" + name + "' out of afterimages: it's " + Math.Sqrt(distSq).ToString("0.0") + " m from you, so it isn't following your body.");
		}
	}

	private void DestroyAvatarGhost(AvatarGhost ghost)
	{
		if (ghost != null)
		{
			if (FxMath.Alive((Object)(object)ghost.Root))
			{
				Object.Destroy((Object)(object)ghost.Root);
			}
			MaterialFactory.Destroy(ghost.Material);
		}
	}

	private void SpawnItemParts(Snapshot snapshot, PlayerScanner scanner, float now, Vector3 body)
	{
		//IL_0044: 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_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		//IL_0210: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		if (!FxMath.Alive((Object)(object)snapshot.ItemMaterial) || snapshot.ItemMaterialGeneration != MaterialFactory.Generation)
		{
			MaterialFactory.Destroy(snapshot.ItemMaterial);
			snapshot.ItemMaterial = MaterialFactory.Create("Afterimage Item", GhostBlend, new Color(1f, 1f, 1f, 0f), 4550, cullOff: false, alwaysOnTop: false);
			snapshot.ItemMaterialGeneration = MaterialFactory.Generation;
		}
		if ((Object)(object)snapshot.ItemMaterial == (Object)null)
		{
			return;
		}
		int num = 48;
		IntPtr pointer = ((Il2CppObjectBase)snapshot.ItemMaterial).Pointer;
		float num2 = ((_profile != null) ? _profile.Reach : 2.5f) + 1.5f;
		float num3 = num2 * num2;
		for (int i = 0; i < scanner.ItemRoots.Count; i++)
		{
			if (num <= 0)
			{
				break;
			}
			GameObject val = scanner.ItemRoots[i];
			if (!FxMath.Alive((Object)(object)val))
			{
				continue;
			}
			ItemSources itemSources = GetItemSources(val, now);
			for (int j = 0; j < itemSources.Renderers.Count; j++)
			{
				if (num <= 0)
				{
					break;
				}
				MeshRenderer val2 = itemSources.Renderers[j];
				MeshFilter val3 = itemSources.Filters[j];
				if (!FxMath.Alive((Object)(object)val2) || !FxMath.Alive((Object)(object)val3) || !((Renderer)val2).enabled || !((Component)val2).gameObject.activeInHierarchy)
				{
					continue;
				}
				Mesh sharedMesh = val3.sharedMesh;
				if ((Object)(object)sharedMesh == (Object)null)
				{
					continue;
				}
				Transform transform = ((Component)val2).transform;
				Vector3 position = transform.position;
				if (!(FxMath.DistanceSq(position, body) > num3))
				{
					ItemPart itemPart = RentItemPart();
					itemPart.Go.layer = ((Component)val2).gameObject.layer;
					itemPart.Filter.sharedMesh = sharedMesh;
					int num4 = SubMeshCount(sharedMesh);
					if (itemPart.MaterialSlots != num4 || itemPart.MaterialKey != pointer)
					{
						((Renderer)itemPart.Renderer).sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(RendererUtil.Repeat(snapshot.ItemMaterial, num4));
						itemPart.MaterialSlots = num4;
						itemPart.MaterialKey = pointer;
					}
					itemPart.Transform.SetPositionAndRotation(position, transform.rotation);
					itemPart.Transform.localScale = transform.lossyScale;
					itemPart.Position = position;
					itemPart.Shown = false;
					snapshot.Items.Add(itemPart);
					num--;
				}
			}
		}
	}

	private ItemSources GetItemSources(GameObject root, float now)
	{
		IntPtr pointer = ((Il2CppObjectBase)root).Pointer;
		if (!_itemSources.TryGetValue(pointer, out var value))
		{
			if (_itemSources.Count > 128)
			{
				_itemSources.Clear();
			}
			value = new ItemSources();
			_itemSources[pointer] = value;
		}
		if (now >= value.RefreshAt)
		{
			value.RefreshAt = now + 1f;
			value.Renderers.Clear();
			value.Filters.Clear();
			foreach (MeshRenderer componentsInChild in root.GetComponentsInChildren<MeshRenderer>(true))
			{
				if (!((Object)(object)componentsInChild == (Object)null))
				{
					MeshFilter component = ((Component)componentsInChild).GetComponent<MeshFilter>();
					if (!((Object)(object)component == (Object)null))
					{
						value.Renderers.Add(componentsInChild);
						value.Filters.Add(component);
					}
				}
			}
		}
		return value;
	}

	private ItemPart RentItemPart()
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Expected O, but got Unknown
		while (_itemPool.Count > 0)
		{
			ItemPart itemPart = _itemPool.Pop();
			if (FxMath.Alive((Object)(object)itemPart.Go))
			{
				return itemPart;
			}
		}
		GameObject val = new GameObject("[SandevistanFX] Afterimage Item");
		val.SetActive(false);
		FxMath.AttachAtOrigin(val.transform, _root.transform);
		ItemPart obj = new ItemPart
		{
			Go = val,
			Transform = val.transform,
			Filter = val.AddComponent<MeshFilter>(),
			Renderer = val.AddComponent<MeshRenderer>()
		};
		RendererUtil.DisableShadows((Renderer)(object)obj.Renderer);
		return obj;
	}

	private bool EnsureRoot()
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Expected O, but got Unknown
		if (FxMath.Alive((Object)(object)_root))
		{
			return true;
		}
		_live.Clear();
		_avatarPool.Clear();
		_itemPool.Clear();
		_avatarGhostsCreated = 0;
		_root = new GameObject("[SandevistanFX] Afterimages");
		Object.DontDestroyOnLoad((Object)(object)_root);
		return true;
	}

	private void EnsureProfile(float now, bool immediate)
	{
		RigManager rigManager = Player.RigManager;
		if (!FxMath.Alive((Object)(object)rigManager))
		{
			return;
		}
		Avatar avatar = rigManager.avatar;
		if (!FxMath.Alive((Object)(object)avatar))
		{
			return;
		}
		IntPtr pointer = ((Il2CppObjectBase)avatar).Pointer;
		if (pointer != _seenAvatar)
		{
			_seenAvatar = pointer;
			_seenAvatarAt = now;
			_rebuildTimes.Clear();
			_validationOff = false;
		}
		if (_profile != null && _profile.AvatarKey == pointer)
		{
			if (_forceRebuild)
			{
				Rebuild(((Component)avatar).gameObject, pointer, now);
			}
			else if (now >= _nextValidate)
			{
				_nextValidate = now + 0.5f;
				if (!_validationOff && !ProfileMatches(((Component)avatar).gameObject))
				{
					Main.Log.Msg("Avatar meshes or bones changed; refreshing afterimages.");
					Rebuild(((Component)avatar).gameObject, pointer, now);
				}
				else
				{
					TrackPartMotion();
				}
			}
		}
		else if ((immediate || !(now - _seenAvatarAt < 1f)) && (!(pointer == _failedAvatar) || !(now < _profileRetryAt)))
		{
			Rebuild(((Component)avatar).gameObject, pointer, now);
		}
	}

	private void Rebuild(GameObject avatar, IntPtr key, float now)
	{
		HideAll();
		DropProfile();
		_rebuildTimes.Enqueue(now);
		while (_rebuildTimes.Count > 0 && now - _rebuildTimes.Peek() > 10f)
		{
			_rebuildTimes.Dequeue();
		}
		if (_rebuildTimes.Count > 4 && !_validationOff)
		{
			_validationOff = true;
			Main.Log.Warning("This avatar keeps changing its meshes; afterimages will stop re-checking it until you switch avatars.");
		}
		_profile = BuildProfile(avatar, key, ++_nextProfileId);
		_nextValidate = now + 0.5f;
		if (_profile == null)
		{
			_failedAvatar = key;
			_profileRetryAt = now + 2f;
		}
	}

	private void TrackPartMotion()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		Transform head = Player.Head;
		if (!FxMath.Alive((Object)(object)head))
		{
			return;
		}
		Vector3 position = head.position;
		AvatarProfile profile = _profile;
		if (_bonePositions.Length < profile.Bones.Length)
		{
			_bonePositions = (Vector3[])(object)new Vector3[profile.Bones.Length];
		}
		for (int i = 0; i < profile.Bones.Length; i++)
		{
			Transform val = profile.Bones[i];
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			_bonePositions[i] = val.position;
		}
		for (int j = 0; j < profile.Skinned.Count; j++)
		{
			SkinnedProfile skinnedProfile = profile.Skinned[j];
			skinnedProfile.Motion.Update(Center(skinnedProfile.UniqueBones, _bonePositions), position, skinnedProfile.Name);
		}
		for (int k = 0; k < profile.Statics.Count; k++)
		{
			StaticProfile staticProfile = profile.Statics[k];
			if (FxMath.Alive((Object)(object)staticProfile.Source))
			{
				staticProfile.Motion.Update(((Component)staticProfile.Source).transform.position, position, staticProfile.Name);
			}
		}
	}

	private bool ProfileMatches(GameObject avatar)
	{
		AvatarProfile profile = _profile;
		for (int i = 0; i < profile.Skinned.Count; i++)
		{
			SkinnedProfile skinnedProfile = profile.Skinned[i];
			if (!FxMath.Alive((Object)(object)skinnedProfile.Source))
			{
				return false;
			}
			if (PointerOf((Object)(object)skinnedProfile.Source.sharedMesh) != skinnedProfile.MeshKey)
			{
				return false;
			}
			if (PointerOf((Object)(object)skinnedProfile.Source.rootBone) != skinnedProfile.RootKey)
			{
				return false;
			}
			Il2CppReferenceArray<Transform> bones = skinnedProfile.Source.bones;
			if (((Il2CppArrayBase<Transform>)(object)bones).Length != skinnedProfile.BoneKeys.Length)
			{
				return false;
			}
			for (int j = 0; j < skinnedProfile.BoneKeys.Length; j++)
			{
				if (PointerOf((Object)(object)((Il2CppArrayBase<Transform>)(object)bones)[j]) != skinnedProfile.BoneKeys[j])
				{
					return false;
				}
			}
		}
		for (int k = 0; k < profile.Statics.Count; k++)
		{
			StaticProfile staticProfile = profile.Statics[k];
			if (!FxMath.Alive((Object)(object)staticProfile.Source) || !FxMath.Alive((Object)(object)staticProfile.Filter))
			{
				return false;
			}
			if (PointerOf((Object)(object)staticProfile.Filter.sharedMesh) != staticProfile.MeshKey)
			{
				return false;
			}
		}
		if (avatar.GetComponentsInChildren<SkinnedMeshRenderer>(true).Length != profile.SkinnedComponentCount)
		{
			return false;
		}
		if (avatar.GetComponentsInChildren<MeshRenderer>(true).Length != profile.StaticComponentCount)
		{
			return false;
		}
		return true;
	}

	private static IntPtr PointerOf(Object obj)
	{
		if (!(obj == (Object)null))
		{
			return ((Il2CppObjectBase)obj).Pointer;
		}
		return IntPtr.Zero;
	}

	private void DropProfile()
	{
		while (_avatarPool.Count > 0)
		{
			DestroyAvatarGhost(_avatarPool.Pop());
		}
		_avatarGhostsCreated = 0;
		_profile = null;
		_forceRebuild = false;
	}

	private static AvatarProfile BuildProfile(GameObject avatar, IntPtr key, int id)
	{
		Transform val = FindHead(avatar);
		AvatarProfile avatarProfile = new AvatarProfile
		{
			AvatarKey = key,
			Id = id
		};
		List<Transform> union = new List<Transform>();
		Dictionary<IntPtr, int> unionIndex = new Dictionary<IntPtr, int>();
		foreach (SkinnedMeshRenderer componentsInChild in avatar.GetComponentsInChildren<SkinnedMeshRenderer>(true))
		{
			avatarProfile.SkinnedComponentCount++;
			if ((Object)(object)componentsInChild == (Object)null)
			{
				continue;
			}
			Mesh sharedMesh = componentsInChild.sharedMesh;
			if ((Object)(object)sharedMesh == (Object)null)
			{
				continue;
			}
			Il2CppReferenceArray<Transform> bones = componentsInChild.bones;
			int length = ((Il2CppArrayBase<Transform>)(object)bones).Length;
			if (length == 0)
			{
				continue;
			}
			Transform rootBone = componentsInChild.rootBone;
			int num = IndexOf(((Object)(object)rootBone != (Object)null) ? rootBone : ((Component)componentsInChild).transform);
			int[] array = new int[length];
			IntPtr[] array2 = new IntPtr[length];
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < length; i++)
			{
				Transform val2 = ((Il2CppArrayBase<Transform>)(object)bones)[i];
				array2[i] = PointerOf((Object)(object)val2);
				int num2 = IndexOf(val2);
				array[i] = ((num2 < 0) ? num : num2);
				if (num2 >= 0)
				{
					hashSet.Add(num2);
				}
			}
			if (hashSet.Count == 0)
			{
				hashSet.Add(num);
			}
			int[] array3 = new int[hashSet.Count];
			hashSet.CopyTo(array3);
			avatarProfile.Skinned.Add(new SkinnedProfile
			{
				Source = componentsInChild,
				Name = ((Object)componentsInChild).name,
				BoneMap = array,
				UniqueBones = array3,
				RootIndex = num,
				SubMeshCount = SubMeshCount(sharedMesh),
				BlendShapeCount = BlendShapeCount(sharedMesh),
				MeshKey = ((Il2CppObjectBase)sharedMesh).Pointer,
				RootKey = PointerOf((Object)(object)rootBone),
				BoneKeys = array2
			});
		}
		foreach (MeshRenderer componentsInChild2 in avatar.GetComponentsInChildren<MeshRenderer>(true))
		{
			avatarProfile.StaticComponentCount++;
			if ((Object)(object)componentsInChild2 == (Object)null)
			{
				continue;
			}
			MeshFilter component = ((Component)componentsInChild2).GetComponent<MeshFilter>();
			if (!((Object)(object)component == (Object)null))
			{
				Mesh sharedMesh2 = component.sharedMesh;
				if (!((Object)(object)sharedMesh2 == (Object)null))
				{
					avatarProfile.Statics.Add(new StaticProfile
					{
						Source = componentsInChild2,
						Filter = component,
						Name = ((Object)componentsInChild2).name,
						IsHead = ((Object)(object)val != (Object)null && FxMath.IsDescendantOf(((Component)componentsInChild2).transform, val)),
						SubMeshCount = SubMeshCount(sharedMesh2),
						MeshKey = ((Il2CppObjectBase)sharedMesh2).Pointer
					});
				}
			}
		}
		if (avatarProfile.Skinned.Count == 0 && avatarProfile.Statics.Count == 0)
		{
			return null;
		}
		avatarProfile.Bones = union.ToArray();
		List<int> list = new List<int>();
		if ((Object)(object)val != (Object)null)
		{
			for (int j = 0; j < avatarProfile.Bones.Length; j++)
			{
				if (FxMath.IsDescendantOf(avatarProfile.Bones[j], val))
				{
					list.Add(j);
				}
			}
		}
		avatarProfile.HeadBones = list.ToArray();
		float num3 = SkeletonSize(avatarProfile.Bones);
		avatarProfile.Reach = Math.Max(2.5f, num3 * 1.25f);
		StringBuilder stringBuilder = new StringBuilder();
		for (int k = 0; k < avatarProfile.Skinned.Count; k++)
		{
			stringBuilder.Append((k == 0) ? "" : ", ").Append(avatarProfile.Skinned[k].Name);
		}
		Main.Log.Msg("Afterimage profile built: " + avatarProfile.Skinned.Count + " skinned mesh(es) [" + stringBuilder?.ToString() + "], " + avatarProfile.Statics.Count + " static mesh(es), " + avatarProfile.Bones.Length + " bones, size " + num3.ToString("0.0") + " m" + (((Object)(object)val != (Object)null) ? ", head found" : ", no head bone found"));
		return avatarProfile;
		int IndexOf(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return -1;
			}
			if (unionIndex.TryGetValue(((Il2CppObjectBase)t).Pointer, out var value))
			{
				return value;
			}
			int count = union.Count;
			union.Add(t);
			unionIndex[((Il2CppObjectBase)t).Pointer] = count;
			return count;
		}
	}

	private static float SkeletonSize(Transform[] bones)
	{
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: 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_0055: 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_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: 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_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		bool flag = true;
		Vector3 val = default(Vector3);
		((Vector3)(ref val))..ctor(0f, 0f, 0f);
		Vector3 val2 = default(Vector3);
		((Vector3)(ref val2))..ctor(0f, 0f, 0f);
		for (int i = 0; i < bones.Length; i++)
		{
			if ((Object)(object)bones[i] == (Object)null)
			{
				continue;
			}
			Vector3 position = bones[i].position;
			if (flag)
			{
				val = position;
				val2 = position;
				flag = false;
				continue;
			}
			if (position.x < val.x)
			{
				val.x = position.x;
			}
			if (position.y < val.y)
			{
				val.y = position.y;
			}
			if (position.z < val.z)
			{
				val.z = position.z;
			}
			if (position.x > val2.x)
			{
				val2.x = position.x;
			}
			if (position.y > val2.y)
			{
				val2.y = position.y;
			}
			if (position.z > val2.z)
			{
				val2.z = position.z;
			}
		}
		return (float)Math.Sqrt(FxMath.DistanceSq(val, val2));
	}

	private static Transform FindHead(GameObject avatar)
	{
		try
		{
			Transform val = FindHumanoidHead(avatar);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		catch (Exception ex)
		{
			Main.Log.Warning("Humanoid head lookup failed, searching by name instead: " + ex.Message);
		}
		foreach (Transform componentsInChild in avatar.GetComponentsInChildren<Transform>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null))
			{
				string name = ((Object)componentsInChild).name;
				if (name.IndexOf("head", StringComparison.OrdinalIgnoreCase) >= 0 && name.IndexOf("end", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("top", StringComparison.OrdinalIgnoreCase) < 0)
				{
					return componentsInChild;
				}
			}
		}
		return null;
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static Transform FindHumanoidHead(GameObject avatar)
	{
		Animator componentInChildren = avatar.GetComponentInChildren<Animator>(true);
		if ((Object)(object)componentInChildren == (Object)null)
		{
			return null;
		}
		return componentInChildren.GetBoneTransform((HumanBodyBones)10);
	}

	private static int SubMeshCount(Mesh mesh)
	{
		if (_meshInfoBroken || (Object)(object)mesh == (Object)null)
		{
			return 1;
		}
		try
		{
			return Math.Max(1, ReadSubMeshCount(mesh));
		}
		catch (Exception ex)
		{
			_meshInfoBroken = true;
			Main.Log.Warning("Couldn't read sub-mesh counts (afterimages use one material slot): " + ex.Message);
			return 1;
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static int ReadSubMeshCount(Mesh mesh)
	{
		return mesh.subMeshCount;
	}

	private static int BlendShapeCount(Mesh mesh)
	{
		if (_blendShapesBroken || (Object)(object)mesh == (Object)null)
		{
			return 0;
		}
		try
		{
			return ReadBlendShapeCount(mesh);
		}
		catch (Exception ex)
		{
			_blendShapesBroken = true;
			Main.Log.Warning("Couldn't read blend shapes (afterimages keep a neutral face): " + ex.Message);
			return 0;
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static int ReadBlendShapeCount(Mesh mesh)
	{
		return mesh.blendShapeCount;
	}

	private static void CopyBlendShapes(SkinnedMeshRenderer from, SkinnedMeshRenderer to, int count)
	{
		if (_blendShapesBroken)
		{
			return;
		}
		try
		{
			CopyBlendShapeWeights(from, to, count);
		}
		catch (Exception ex)
		{
			_blendShapesBroken = true;
			Main.Log.Warning("Couldn't copy blend shapes (afterimages keep a neutral face): " + ex.Message);
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static void CopyBlendShapeWeights(SkinnedMeshRenderer from, SkinnedMeshRenderer to, int count)
	{
		for (int i = 0; i < count; i++)
		{
			to.SetBlendShapeWeight(i, from.GetBlendShapeWeight(i));
		}
	}
}
internal static class CameraLocator
{
	private const float MaxDistanceFromHead = 1f;

	private static Camera _camera;

	private static float _nextLookup;

	public static void Reset()
	{
		_camera = null;
		_nextLookup = 0f;
	}

	public static Camera Get()
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		float now = FxClock.Now;
		if (now < _nextLookup)
		{
			if (!FxMath.Alive((Object)(object)_camera))
			{
				return null;
			}
			if (((Behaviour)_camera).isActiveAndEnabled)
			{
				return _camera;
			}
		}
		_nextLookup = now + 1f;
		Transform head = Player.Head;
		bool flag = FxMath.Alive((Object)(object)head);
		Vector3 b = (Vector3)(flag ? head.position : new Vector3(0f, 0f, 0f));
		Camera val = null;
		Camera main = Camera.main;
		if (Usable(main) && (!flag || FxMath.DistanceSq(((Component)main).transform.position, b) <= 1f))
		{
			val = main;
		}
		else
		{
			float num = float.MaxValue;
			RigManager rigManager = Player.RigManager;
			if (FxMath.Alive((Object)(object)rigManager))
			{
				foreach (Camera componentsInChild in ((Component)rigManager).GetComponentsInChildren<Camera>(false))
				{
					if (Usable(componentsInChild))
					{
						float num2 = (flag ? FxMath.DistanceSq(((Component)componentsInChild).transform.position, b) : 0f);
						if (num2 < num)
						{
							num = num2;
							val = componentsInChild;
						}
					}
				}
			}
			if ((Object)(object)val == (Object)null && Usable(main))
			{
				val = main;
			}
		}
		_camera = val;
		return val;
	}

	private static bool Usable(Camera cam)
	{
		if (FxMath.Alive((Object)(object)cam) && ((Behaviour)cam).isActiveAndEnabled)
		{
			return (Object)(object)cam.targetTexture == (Object)null;
		}
		return false;
	}
}
internal static class FxClock
{
	private static readonly Stopwatch Watch = Stopwatch.StartNew();

	public static float Now => (float)Watch.Elapsed.TotalSeconds;
}
internal static class FxLog
{
	private const int MaxPerSource = 3;

	private static readonly Dictionary<string, int> Counts = new Dictionary<string, int>();

	public static void Error(string where, Exception e)
	{
		Counts.TryGetValue(where, out var value);
		value++;
		Counts[where] = value;
		if (Main.Log != null)
		{
			if (value <= 3)
			{
				Main.Log.Error(where + " failed: " + e);
			}
			else if (value == 4)
			{
				Main.Log.Error(where + " keeps failing; further errors from it are hidden.");
			}
		}
	}
}
internal static class FxMath
{
	public static bool Alive(Object obj)
	{
		if (obj != (Object)null)
		{
			return !((Il2CppObjectBase)obj).WasCollected;
		}
		return false;
	}

	public static bool IsDescendantOf(Transform child, Transform ancestor)
	{
		if ((Object)(object)child == (Object)null || (Object)(object)ancestor == (Object)null)
		{
			return false;
		}
		IntPtr pointer = ((Il2CppObjectBase)ancestor).Pointer;
		Transform val = child;
		for (int i = 0; i < 256; i++)
		{
			if (!((Object)(object)val != (Object)null))
			{
				break;
			}
			if (((Il2CppObjectBase)val).Pointer == pointer)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	public static void AttachAtOrigin(Transform child, Transform parent)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		child.SetParent(parent);
		child.localPosition = new Vector3(0f, 0f, 0f);
		child.localRotation = Quaternion.identity;
		child.localScale = new Vector3(1f, 1f, 1f);
	}

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

	public static float MoveTowards(float current, float target, float maxDelta)
	{
		float value = target - current;
		if (Math.Abs(value) <= maxDelta)
		{
			return target;
		}
		return current + (float)Math.Sign(value) * maxDelta;
	}

	public static float SmoothStep01(float t)
	{
		t = Clamp01(t);
		return t * t * (3f - 2f * t);
	}

	public static float DistanceSq(Vector3 a, Vector3 b)
	{
		//IL_0000: 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_000d: 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_001b: 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)
		float num = a.x - b.x;
		float num2 = a.y - b.y;
		float num3 = a.z - b.z;
		return num * num + num2 * num2 + num3 * num3;
	}

	public static Color Lerp(Color a, Color b, float t)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: 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_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		t = Clamp01(t);
		return new Color(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t, a.a + (b.a - a.a) * t);
	}

	public static Color HsvToRgb(float h, float s, float v)
	{
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		h -= (float)Math.Floor(h);
		s = Clamp01(s);
		v = Clamp01(v);
		float num = h * 6f;
		int num2 = (int)Math.Floor(num) % 6;
		float num3 = num - (float)Math.Floor(num);
		float num4 = v * (1f - s);
		float num5 = v * (1f - num3 * s);
		float num6 = v * (1f - (1f - num3) * s);
		return (Color)(num2 switch
		{
			0 => new Color(v, num6, num4, 1f), 
			1 => new Color(num5, v, num4, 1f), 
			2 => new Color(num4, v, num6, 1f), 
			3 => new Color(num4, num5, v, 1f), 
			4 => new Color(num6, num4, v, 1f), 
			_ => new Color(v, num4, num5, 1f), 
		});
	}

	public static bool TryParseHexColor(string text, out Color color)
	{
		//IL_0015: 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_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		color = new Color(1f, 1f, 1f, 1f);
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		string text2 = text.Trim();
		if (text2.StartsWith("#", StringComparison.Ordinal))
		{
			text2 = text2.Substring(1);
		}
		if (text2.Length == 3)
		{
			text2 = new string(new char[6]
			{
				text2[0],
				text2[0],
				text2[1],
				text2[1],
				text2[2],
				text2[2]
			});
		}
		if (text2.Length != 6 && text2.Length != 8)
		{
			return false;
		}
		int[] array = new int[4] { 0, 0, 0, 255 };
		for (int i = 0; i < text2.Length / 2; i++)
		{
			int num = HexDigit(text2[i * 2]);
			int num2 = HexDigit(text2[i * 2 + 1]);
			if (num < 0 || num2 < 0)
			{
				return false;
			}
			array[i] = num * 16 + num2;
		}
		color = new Color((float)array[0] / 255f, (float)array[1] / 255f, (float)array[2] / 255f, (float)array[3] / 255f);
		return true;
	}

	private static int HexDigit(char c)
	{
		if (c >= '0' && c <= '9')
		{
			return c - 48;
		}
		if (c >= 'a' && c <= 'f')
		{
			return c - 97 + 10;
		}
		if (c >= 'A' && c <= 'F')
		{
			return c - 65 + 10;
		}
		return -1;
	}
}
public static class BuildInfo
{
	public const string Name = "SandevistanFX";

	public const string Version = "1.5.0";

	public const string Author = "YourName";
}
public sealed class Main : MelonMod
{
	private SandevistanController _controller;

	internal static Instance Log { get; private set; }

	public override void OnInitializeMelon()
	{
		Log = ((MelonBase)this).LoggerInstance;
		Settings.Init();
		_controller = new SandevistanController();
		Hooking.OnLevelUnloaded += delegate
		{
			_controller.OnLevelUnloaded();
		};
		Hooking.OnLevelLoaded += delegate
		{
			_controller.OnLevelLoaded();
		};
		Hooking.OnSwitchAvatarPostfix += delegate
		{
			_controller.OnAvatarChanged();
		};
		Hooking.OnGrabObject += delegate
		{
			_controller.RequestRescan();
		};
		Hooking.OnReleaseObject += delegate
		{
			_controller.RequestRescan();
		};
		Log.Msg("Loaded. Hit slow-mo to go Sandevistan.");
	}

	public override void OnLateInitializeMelon()
	{
		if (!IsAssemblyLoaded("Quicksilver"))
		{
			Log.Warning("Quicksilver isn't installed. SandevistanFX requires it so you can move at full speed during slow-mo: https://thunderstore.io/c/bonelab/p/Lakatrazz/Quicksilver/");
		}
		try
		{
			MenuSetup.Create(_controller);
		}
		catch (Exception ex)
		{
			Log.Warning("BoneMenu page could not be created (settings still work through MelonPreferences.cfg): " + ex.Message);
		}
	}

	private static bool IsAssemblyLoaded(string name)
	{
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		for (int i = 0; i < assemblies.Length; i++)
		{
			if (string.Equals(assemblies[i].GetName().Name, name, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	public override void OnUpdate()
	{
		if (_controller == null)
		{
			return;
		}
		try
		{
			_controller.Update();
		}
		catch (Exception e)
		{
			FxLog.Error("Update", e);
		}
	}

	public override void OnLateUpdate()
	{
		if (_controller == null)
		{
			return;
		}
		try
		{
			_controller.LateUpdate();
		}
		catch (Exception e)
		{
			FxLog.Error("LateUpdate", e);
		}
	}
}
internal enum BlendKind
{
	Alpha,
	Additive,
	Multiply
}
internal static class MaterialFactory
{
	private const string UrpUnlitName = "Universal Render Pipeline/Unlit";

	private static readonly string[] FallbackNames = new string[2] { "UI/Default", "Sprites/Default" };

	private const float BlendZero = 0f;

	private const float BlendOne = 1f;

	private const float BlendDstColor = 2f;

	private const float BlendSrcAlpha = 5f;

	private const float BlendOneMinusSrcAlpha = 10f;

	private const float CullOff = 0f;

	private const float CullBack = 2f;

	private const float ZTestLessEqual = 4f;

	private const float ZTestAlways = 8f;

	private static Shader _urpUnlit;

	private static Shader _fallback;

	private static float _nextSearch;

	private static bool _reported;

	private static bool _keywordsBroken;

	public static int Generation { get; private set; } = 1;

	public static bool UsesFreeBlend
	{
		get
		{
			bool result = FxMath.Alive((Object)(object)_urpUnlit);
			bool flag = FxMath.Alive((Object)(object)_fallback);
			if (Settings.UseFallbackShader.Value && flag)
			{
				return false;
			}
			return result;
		}
	}

	public static void BumpGeneration()
	{
		Generation++;
	}

	public static bool Ready()
	{
		if (FxMath.Alive((Object)(object)_urpUnlit) || FxMath.Alive((Object)(object)_fallback))
		{
			return true;
		}
		float now = FxClock.Now;
		if (now < _nextSearch)
		{
			return false;
		}
		_nextSearch = now + 3f;
		_urpUnlit = FindShader("Universal Render Pipeline/Unlit");
		_fallback = null;
		string[] fallbackNames = FallbackNames;
		for (int i = 0; i < fallbackNames.Length; i++)
		{
			_fallback = FindShader(fallbackNames[i]);
			if ((Object)(object)_fallback != (Object)null)
			{
				break;
			}
		}
		bool flag = (Object)(object)_urpUnlit != (Object)null || (Object)(object)_fallback != (Object)null;
		if (!_reported)
		{
			_reported = true;
			if (flag)
			{
				Main.Log.Msg("Using shader: " + (((Object)(object)_urpUnlit != (Object)null) ? "Universal Render Pipeline/Unlit" : ((Object)_fallback).name) + (((Object)(object)_fallback != (Object)null) ? (" (fallback available: " + ((Object)_fallback).name + ")") : ""));
			}
			else
			{
				Main.Log.Warning("No usable unlit shader found yet; will keep looking.");
			}
		}
		return flag;
	}

	public static Material Create(string name, BlendKind kind, Color color, int queue, bool cullOff, bool alwaysOnTop)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready())
		{
			return null;
		}
		bool usesFreeBlend = UsesFreeBlend;
		Shader val = (usesFreeBlend ? _urpUnlit : (FxMath.Alive((Object)(object)_fallback) ? _fallback : _urpUnlit));
		if (!FxMath.Alive((Object)(object)val))
		{
			return null;
		}
		Material val2 = new Material(val);
		((Object)val2).name = "[SandevistanFX] " + name;
		((Object)val2).hideFlags = (HideFlags)61;
		if (usesFreeBlend)
		{
			float value;
			float value2;
			switch (kind)
			{
			case BlendKind.Multiply:
				value = 2f;
				value2 = 0f;
				break;
			case BlendKind.Additive:
				value = 5f;
				value2 = 1f;
				break;
			default:
				value = 5f;
				value2 = 10f;
				break;
			}
			SetFloat(val2, "_Surface", 1f);
			SetFloat(val2, "_AlphaClip", 0f);
			SetFloat(val2, "_ZWrite", 0f);
			SetFloat(val2, "_Cull", cullOff ? 0f : 2f);
			SetFloat(val2, "_SrcBlend", value);
			SetFloat(val2, "_DstBlend", value2);
			SetFloat(val2, "_SrcBlendAlpha", 1f);
			SetFloat(val2, "_DstBlendAlpha", 10f);
			if (alwaysOnTop)
			{
				SetFloat(val2, "_ZTest", 8f);
			}
			ApplyTransparentKeywords(val2);
		}
		else
		{
			SetFloat(val2, "unity_GUIZTestMode", alwaysOnTop ? 8f : 4f);
		}
		val2.renderQueue = queue;
		SetColor(val2, color);
		return val2;
	}

	public static void SetColor(Material material, Color color)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)material == (Object)null))
		{
			material.SetColor("_BaseColor", color);
			material.SetColor("_Color", color);
		}
	}

	public static void Destroy(Material material)
	{
		if (FxMath.Alive((Object)(object)material))
		{
			Object.Destroy((Object)(object)material);
		}
	}

	private static void SetFloat(Material material, string property, float value)
	{
		material.SetFloat(Shader.PropertyToID(property), value);
	}

	private static void ApplyTransparentKeywords(Material material)
	{
		if (_keywordsBroken)
		{
			return;
		}
		try
		{
			SetKeywords(material);
		}
		catch (Exception ex)
		{
			_keywordsBroken = true;
			Main.Log.Warning("Couldn't set shader keywords (continuing without them): " + ex.Message);
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static void SetKeywords(Material material)
	{
		material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
		material.DisableKeyword("_ALPHATEST_ON");
		material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
		material.DisableKeyword("_ALPHAMODULATE_ON");
	}

	private static Shader FindShader(string name)
	{
		Shader val = Shader.Find(name);
		if (FxMath.Alive((Object)(object)val))
		{
			return val;
		}
		foreach (Shader item in Resources.FindObjectsOfTypeAll<Shader>())
		{
			if ((Object)(object)item != (Object)null && ((Object)item).name == name)
			{
				return item;
			}
		}
		return null;
	}
}
internal static class MenuSetup
{
	public static void Create(SandevistanController controller)
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		//IL_026a: Unknown result type (might be due to invalid IL or missing references)
		//IL_028c: Unknown result type (might be due to invalid IL or missing references)
		//IL_029a: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0315: Unknown result type (might be due to invalid IL or missing references)
		//IL_035a: Unknown result type (might be due to invalid IL or missing references)
		//IL_039f: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0429: Unknown result type (might be due to invalid IL or missing references)
		//IL_0463: Unknown result type (might be due to invalid IL or missing references)
		//IL_0498: Unknown result type (might be due to invalid IL or missing references)
		if (Page.Root != null)
		{
			Color val = default(Color);
			((Color)(ref val))..ctor(0.24f, 1f, 0.45f, 1f);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(1f, 1f, 1f, 1f);
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(1f, 0.92f, 0.2f, 1f);
			Page obj = Page.Root.CreatePage("Sandevistan FX", val, 0, true);
			obj.CreateBool("Enabled", val2, Settings.Enabled.Value, (Action<bool>)delegate(bool v)
			{
				Settings.Set(Settings.Enabled, v);
			});
			obj.CreateBool("Preview (force on)", val3, false, (Action<bool>)delegate(bool v)
			{
				controller.Preview = v;
			});
			Page obj2 = obj.CreatePage("Color Grade", val, 0, true);
			obj2.CreateFloat("Strength", val2, Settings.GradeStrength.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.GradeStrength, v);
			});
			obj2.CreateFloat("Glow", val2, Settings.GlowStrength.Value, 0.02f, 0f, 1f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.GlowStrength, v);
			});
			obj2.CreateFloat("Activation Flash", val2, Settings.ActivationFlash.Value, 0.1f, 0f, 1f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.ActivationFlash, v);
			});
			obj2.CreateFloat("Fade In (s)", val2, Settings.FadeInTime.Value, 0.05f, 0f, 2f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.FadeInTime, v);
			});
			obj2.CreateFloat("Fade Out (s)", val2, Settings.FadeOutTime.Value, 0.05f, 0f, 2f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.FadeOutTime, v);
			});
			obj2.CreateBool("Keep Player Colors", val2, Settings.KeepPlayerColors.Value, (Action<bool>)delegate(bool v)
			{
				Settings.Set(Settings.KeepPlayerColors, v);
			});
			obj2.CreateBool("Use Fallback Shader", val2, Settings.UseFallbackShader.Value, (Action<bool>)delegate(bool v)
			{
				Settings.Set(Settings.UseFallbackShader, v);
				controller.OnMaterialSettingsChanged();
			});
			Page obj3 = obj.CreatePage("Afterimages", val, 0, true);
			obj3.CreateBool("Enabled", val2, Settings.AfterimagesEnabled.Value, (Action<bool>)delegate(bool v)
			{
				Settings.Set(Settings.AfterimagesEnabled, v);
			});
			obj3.CreateFloat("Spawn Interval (s)", val2, Settings.AfterimageInterval.Value, 0.01f, 0.02f, 0.5f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.AfterimageInterval, v);
			});
			obj3.CreateFloat("Lifetime (s)", val2, Settings.AfterimageLifetime.Value, 0.25f, 0.25f, 10f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.AfterimageLifetime, v);
			});
			obj3.CreateFloat("Opacity", val2, Settings.AfterimageOpacity.Value, 0.05f, 0.05f, 1f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.AfterimageOpacity, v);
			});
			obj3.CreateFloat("Rainbow Speed", val2, Settings.AfterimageHueSpeed.Value, 0.05f, 0f, 2f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.AfterimageHueSpeed, v);
			});
			obj3.CreateFloat("Saturation", val2, Settings.AfterimageSaturation.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
			{
				Settings.Set(Settings.AfterimageSaturation, v);
			});
			obj3.CreateInt("Max Count", val2, Settings.AfterimageMaxCount.Value, 5, 5, 100, (Action<int>)delegate(int v)
			{
				Settings.Set(Settings.AfterimageMaxCount, v);
			});
			obj3.CreateBool("Include Items", val2, Settings.AfterimageIncludeItems.Value, (Action<bool>)delegate(bool v)
			{
				Settings.Set(Settings.AfterimageIncludeItems, v);
			});
			obj3.CreateBool("Glowing (Additive)", val2, Settings.AfterimageAdditive.Value, (Action<bool>)delegate(bool v)
			{
				Settings.Set(Settings.AfterimageAdditive, v);
				controller.OnMaterialSettingsChanged();
			});
		}
	}
}
internal sealed class PlayerColorShield
{
	private readonly Dictionary<IntPtr, Renderer> _protected = new Dictionary<IntPtr, Renderer>();

	private readonly Dictionary<IntPtr, Material> _cloneByOriginal = new Dictionary<IntPtr, Material>();

	private readonly Dictionary<IntPtr, Material> _originalByClone = new Dictionary<IntPtr, Material>();

	private readonly HashSet<IntPtr> _seen = new HashSet<IntPtr>();

	private readonly List<IntPtr> _stale = new List<IntPtr>();

	public bool IsActive
	{
		get
		{
			if (_protected.Count <= 0)
			{
				return _cloneByOriginal.Count > 0;
			}
			return true;
		}
	}

	public void Apply(List<Renderer> renderers)
	{
		_seen.Clear();
		for (int i = 0; i < renderers.Count; i++)
		{
			Renderer val = renderers[i];
			if (FxMath.Alive((Object)(object)val))
			{
				IntPtr pointer = ((Il2CppObjectBase)val).Pointer;
				if (_seen.Add(pointer))
				{
					EnsureProtected(val);
					_protected[pointer] = val;
				}
			}
		}
		_stale.Clear();
		foreach (KeyValuePair<IntPtr, Renderer> item in _protected)
		{
			if (!_seen.Contains(item.Key))
			{
				_stale.Add(item.Key);
			}
		}
		for (int j = 0; j < _stale.Count; j++)
		{
			Restore(_protected[_stale[j]]);
			_protected.Remove(_stale[j]);
		}
	}

	public void RestoreAll(bool destroyClones)
	{
		foreach (KeyValuePair<IntPtr, Renderer> item in _protected)
		{
			Restore(item.Value);
		}
		_protected.Clear();
		if (!destroyClones)
		{
			return;
		}
		foreach (KeyValuePair<IntPtr, Material> item2 in _cloneByOriginal)
		{
			if (FxMath.Alive((Object)(object)item2.Value))
			{
				Object.Destroy((Object)(object)item2.Value);
			}
		}
		_cloneByOriginal.Clear();
		_originalByClone.Clear();
	}

	private void EnsureProtected(Renderer renderer)
	{
		Il2CppReferenceArray<Material> sharedMaterials = renderer.sharedMaterials;
		int length = ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length;
		if (length == 0)
		{
			return;
		}
		Material[] array = null;
		for (int i = 0; i < length; i++)
		{
			Material val = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[i];
			if ((Object)(object)val == (Object)null || _originalByClone.ContainsKey(((Il2CppObjectBase)val).Pointer))
			{
				continue;
			}
			if (array == null)
			{
				array = (Material[])(object)new Material[length];
				for (int j = 0; j < length; j++)
				{
					array[j] = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[j];
				}
			}
			array[i] = CloneFor(val);
		}
		if (array != null)
		{
			renderer.sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(array);
		}
	}

	private void Restore(Renderer renderer)
	{
		if (!FxMath.Alive((Object)(object)renderer))
		{
			return;
		}
		Il2CppReferenceArray<Material> sharedMaterials = renderer.sharedMaterials;
		int length = ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length;
		Material[] array = null;
		for (int i = 0; i < length; i++)
		{
			Material val = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[i];
			if ((Object)(object)val == (Object)null || !_originalByClone.TryGetValue(((Il2CppObjectBase)val).Pointer, out var value))
			{
				continue;
			}
			if (array == null)
			{
				array = (Material[])(object)new Material[length];
				for (int j = 0; j < length; j++)
				{
					array[j] = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[j];
				}
			}
			array[i] = value;
		}
		if (array != null)
		{
			renderer.sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(array);
		}
	}

	private Material CloneFor(Material original)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		IntPtr pointer = ((Il2CppObjectBase)original).Pointer;
		if (_cloneByOriginal.TryGetValue(pointer, out var value) && FxMath.Alive((Object)(object)value))
		{
			return value;
		}
		Material val = new Material(original);
		((Object)val).name = ((Object)original).name + " [SandevistanFX]";
		((Object)val).hideFlags = (HideFlags)61;
		val.renderQueue = RenderOrder.PlayerQueue(original.renderQueue);
		_cloneByOriginal[pointer] = val;
		_originalByClone[((Il2CppObjectBase)val).Pointer] = original;
		return val;
	}
}
internal sealed class PlayerScanner
{
	public readonly List<Renderer> Renderers = new List<Renderer>();

	public readonly List<GameObject> ItemRoots = new List<GameObject>();

	private readonly HashSet<IntPtr> _itemKeys = new HashSet<IntPtr>();

	private readonly Dictionary<IntPtr, bool> _npcCache = new Dictionary<IntPtr, bool>();

	private readonly List<InventorySlotReceiver> _slots = new List<InventorySlotReceiver>();

	private IntPtr _slotsRig = IntPtr.Zero;

	private float _slotsRefreshAt;

	private Vector3 _eye;

	private bool _hasEye;

	private const float MaxItemDistance = 4f;

	public void Reset()
	{
		Renderers.Clear();
		ItemRoots.Clear();
		_itemKeys.Clear();
		_npcCache.Clear();
		_slots.Clear();
		_slotsRig = IntPtr.Zero;
		_slotsRefreshAt = 0f;
	}

	public void Scan(Func<Renderer, bool> exclude, Camera camera)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		Renderers.Clear();
		ItemRoots.Clear();
		_itemKeys.Clear();
		Transform head = Player.Head;
		_hasEye = FxMath.Alive((Object)(object)head) || FxMath.Alive((Object)(object)camera);
		if (FxMath.Alive((Object)(object)head))
		{
			_eye = head.position;
		}
		else if (_hasEye)
		{
			_eye = ((Component)camera).transform.position;
		}
		RigManager rigManager = Player.RigManager;
		if (FxMath.Alive((Object)(object)rigManager))
		{
			CollectHeld(Player.LeftHand, rigManager);
			CollectHeld(Player.RightHand, rigManager);
			CollectHolstered(rigManager);
			AddRenderers(((Component)rigManager).gameObject, exclude);
			Avatar avatar = rigManager.avatar;
			if (FxMath.Alive((Object)(object)avatar))
			{
				AddRenderers(((Component)avatar).gameObject, exclude);
			}
			for (int i = 0; i < ItemRoots.Count; i++)
			{
				AddRenderers(ItemRoots[i], exclude);
			}
		}
	}

	private void CollectHeld(Hand hand, RigManager rig)
	{
		if (FxMath.Alive((Object)(object)hand))
		{
			GameObject objectInHand = Player.GetObjectInHand(hand);
			if (FxMath.Alive((Object)(object)objectInHand))
			{
				AddEntityOf(objectInHand.transform, rig);
			}
		}
	}

	private void CollectHolstered(RigManager rig)
	{
		float now = FxClock.Now;
		if (_slotsRig != ((Il2CppObjectBase)rig).Pointer || now >= _slotsRefreshAt)
		{
			_slots.Clear();
			foreach (InventorySlotReceiver componentsInChild in ((Component)rig).GetComponentsInChildren<InventorySlotReceiver>(true))
			{
				if ((Object)(object)componentsInChild != (Object)null)
				{
					_slots.Add(componentsInChild);
				}
			}
			_slotsRig = ((Il2CppObjectBase)rig).Pointer;
			_slotsRefreshAt = now + 5f;
		}
		for (int i = 0; i < _slots.Count; i++)
		{
			InventorySlotReceiver val = _slots[i];
			if (FxMath.Alive((Object)(object)val))
			{
				WeaponSlot slottedWeapon = val._slottedWeapon;
				if (FxMath.Alive((Object)(object)slottedWeapon))
				{
					AddEntityOf(((Component)slottedWeapon).transform, rig);
				}
			}
		}
	}

	private void AddEntityOf(Transform part, RigManager rig)
	{
		//IL_0009: 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)
		if (_hasEye && FxMath.DistanceSq(part.position, _eye) > 16f)
		{
			return;
		}
		MarrowEntity componentInParent = ((Component)part).GetComponentInParent<MarrowEntity>();
		if (!FxMath.Alive((Object)(object)componentInParent))
		{
			return;
		}
		Hand val = Player.LeftHand;
		if (!FxMath.Alive((Object)(object)val))
		{
			val = Player.RightHand;
		}
		if ((!FxMath.Alive((Object)(object)val) || !FxMath.IsDescendantOf(((Component)val).transform, ((Component)componentInParent).transform)) && !IsNpc(componentInParent))
		{
			GameObject gameObject = ((Component)componentInParent).gameObject;
			if (_itemKeys.Add(((Il2CppObjectBase)gameObject).Pointer))
			{
				ItemRoots.Add(gameObject);
			}
		}
	}

	private bool IsNpc(MarrowEntity entity)
	{
		IntPtr pointer = ((Il2CppObjectBase)entity).Pointer;
		if (_npcCache.TryGetValue(pointer, out var value))
		{
			return value;
		}
		bool flag = (Object)(object)((Component)entity).GetComponentInChildren<AIBrain>(true) != (Object)null;
		if (_npcCache.Count > 512)
		{
			_npcCache.Clear();
		}
		_npcCache[pointer] = flag;
		return flag;
	}

	private void AddRenderers(GameObject root, Func<Renderer, bool> exclude)
	{
		if (!FxMath.Alive((Object)(object)root))
		{
			return;
		}
		foreach (Renderer componentsInChild in root.GetComponentsInChildren<Renderer>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null) && !exclude(componentsInChild))
			{
				Renderers.Add(componentsInChild);
			}
		}
	}
}
internal static class RenderOrder
{
	public const int GradeTint = 3990;

	public const int GradeGlow = 3991;

	public const int Afterimage = 4550;

	public static int PlayerQueue(int originalQueue)
	{
		if (originalQueue < 0)
		{
			originalQueue = 2000;
		}
		if (originalQueue > 5000)
		{
			originalQueue = 5000;
		}
		return 4000 + originalQueue / 5;
	}
}
internal static class RendererUtil
{
	private static bool _shadowsBroken;

	public static void DisableShadows(Renderer renderer)
	{
		if (_shadowsBroken || (Object)(object)renderer == (Object)null)
		{
			return;
		}
		try
		{
			SetShadowsOff(renderer);
		}
		catch (Exception ex)
		{
			_shadowsBroken = true;
			Main.Log.Warning("Couldn't turn off effect shadows (harmless): " + ex.Message);
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static void SetShadowsOff(Renderer renderer)
	{
		renderer.shadowCastingMode = (ShadowCastingMode)0;
	}

	public static Material[] Repeat(Material material, int count)
	{
		Material[] array = (Material[])(object)new Material[Math.Max(1, count)];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = material;
		}
		return array;
	}
}
internal sealed class SandevistanController
{
	private const float ScanInterval = 0.2f;

	private const float FlashDuration = 0.3f;

	private readonly PlayerScanner _scanner = new PlayerScanner();

	private readonly PlayerColorShield _shield = new PlayerColorShield();

	private readonly ScreenGrade _grade = new ScreenGrade();

	private readonly AfterimageSystem _afterimages = new AfterimageSystem();

	private readonly Func<Renderer, bool> _isOwnRenderer;

	private bool _active;

	private float _weight;

	private float _flash;

	private float _nextScan;

	private bool _rescanRequested;

	private int _frame;

	private float _lastUpdate = -1f;

	public bool Preview { get; set; }

	public SandevistanController()
	{
		_isOwnRenderer = _grade.Owns;
	}

	public void Update()
	{
		float now = FxClock.Now;
		float num = ((_lastUpdate < 0f) ? 0f : (now - _lastUpdate));
		_lastUpdate = now;
		if (num > 0.1f)
		{
			num = 0.1f;
		}
		bool value = Settings.Enabled.Value;
		bool flag = FxMath.Alive((Object)(object)Player.RigManager);
		bool flag2 = value && flag && (Preview || SlowMoDetector.IsSlowMoActive());
		if (flag2 && !_active)
		{
			Activate();
		}
		else if (!flag2 && _active)
		{
			Deactivate();
		}
		float num2 = (_active ? 1f : 0f);
		float num3 = (_active ? Settings.FadeInTime.Value : Settings.FadeOutTime.Value);
		_weight = ((num3 <= 0.0001f) ? num2 : FxMath.MoveTowards(_weight, num2, num / num3));
		_flash = FxMath.MoveTowards(_flash, 0f, num / 0.3f);
		if (!value || !flag)
		{
			_weight = 0f;
			_flash = 0f;
		}
		bool flag3 = _weight > 0.0001f || _flash > 0.0001f;
		try
		{
			if (flag3)
			{
				if (_rescanRequested || now >= _nextScan)
				{
					_rescanRequested = false;
					_nextScan = now + 0.2f;
					_scanner.Scan(_isOwnRenderer, CameraLocator.Get());
					if (Settings.KeepPlayerColors.Value)
					{
						_shield.Apply(_scanner.Renderers);
					}
					else if (_shield.IsActive)
					{
						_shield.RestoreAll(destroyClones: true);
					}
				}
			}
			else if (_shield.IsActive)
			{
				_shield.RestoreAll(destroyClones: true);
			}
		}
		catch (Exception e)
		{
			FxLog.Error("Keeping player colors", e);
		}
		try
		{
			_grade.Update(CameraLocator.Get(), _weight, _flash);
		}
		catch (Exception e2)
		{
			FxLog.Error("Color grade", e2);
		}
		_frame++;
		if (!_active && value && flag && _frame % 8 == 0)
		{
			try
			{
				_afterimages.Prewarm();
			}
			catch (Exception e3)
			{
				FxLog.Error("Preparing afterimages", e3);
			}
		}
	}

	public void LateUpdate()
	{
		if (!_active)
		{
			return;
		}
		try
		{
			if (!Settings.AfterimagesEnabled.Value)
			{
				_afterimages.HideAll();
			}
			else
			{
				_afterimages.Tick(_scanner, CameraLocator.Get());
			}
		}
		catch (Exception e)
		{
			FxLog.Error("Afterimages", e);
			SafeHideAfterimages();
		}
	}

	private void SafeHideAfterimages()
	{
		try
		{
			_afterimages.HideAll();
		}
		catch (Exception e)
		{
			FxLog.Error("Hiding afterimages", e);
		}
	}

	public void RequestRescan()
	{
		_rescanRequested = true;
	}

	public void OnAvatarChanged()
	{
		try
		{
			_afterimages.OnAvatarChanged();
		}
		catch (Exception e)
		{
			FxLog.Error("Avatar change", e);
		}
		_rescanRequested = true;
	}

	public void OnMaterialSettingsChanged()
	{
		MaterialFactory.BumpGeneration();
		try
		{
			_afterimages.OnMaterialSettingsChanged();
		}
		catch (Exception e)
		{
			FxLog.Error("Applying settings", e);
		}
	}

	public void OnLevelUnloaded()
	{
		_active = false;
		_weight = 0f;
		_flash = 0f;
		try
		{
			_shield.RestoreAll(destroyClones: true);
		}
		catch (Exception e)
		{
			FxLog.Error("Level unload (colors)", e);
		}
		try
		{
			_afterimages.DestroyAll();
		}
		catch (Exception e2)
		{
			FxLog.Error("Level unload (afterimages)", e2);
		}
		try
		{
			_grade.Destroy();
		}
		catch (Exception e3)
		{
			FxLog.Error("Level unload (grade)", e3);
		}
		_scanner.Reset();
		CameraLocator.Reset();
	}

	public void OnLevelLoaded()
	{
		_scanner.Reset();
		CameraLocator.Reset();
		_rescanRequested = true;
	}

	private void Activate()
	{
		_active = true;
		_rescanRequested = true;
		_flash = 1f;
		SafeHideAfterimages();
	}

	private void Deactivate()
	{
		_active = false;
		_flash = 0f;
		SafeHideAfterimages();
	}
}
internal sealed class ScreenGrade
{
	private GameObject _root;

	private Camera _camera;

	private MeshRenderer _tint;

	private MeshRenderer _glow;

	private Material _tintMaterial;

	private Material _glowMaterial;

	private bool _freeBlend;

	private int _generation;

	private readonly HashSet<IntPtr> _ownRenderers = new HashSet<IntPtr>();

	public bool Owns(Renderer renderer)
	{
		if ((Object)(object)renderer != (Object)null)
		{
			return _ownRenderers.Contains(((Il2CppObjectBase)renderer).Pointer);
		}
		return false;
	}

	public void Update(Camera camera, float weight, float flash)
	{
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: 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_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		if (!(weight > 0.0001f) && !(flash > 0.0001f))
		{
			if (FxMath.Alive((Object)(object)_root) && _root.activeSelf)
			{
				_root.SetActive(false);
			}
			return;
		}
		if (!FxMath.Alive((Object)(object)camera) || !EnsureBuilt(camera))
		{
			if (FxMath.Alive((Object)(object)_root) && _root.activeSelf)
			{
				_root.SetActive(false);
			}
			return;
		}
		if (!_root.activeSelf)
		{
			_root.SetActive(true);
		}
		float num = Math.Max(_camera.nearClipPlane * 2f, 0.025f);
		_root.transform.localPosition = new Vector3(0f, 0f, num);
		_root.transform.localRotation = Quaternion.identity;
		Color gradeColorValue = Settings.GradeColorValue;
		float num2 = FxMath.Clamp01(Settings.GradeStrength.Value) * weight;
		float num3 = flash * FxMath.Clamp01(Settings.ActivationFlash.Value);
		if (_freeBlend)
		{
			Color color = FxMath.Lerp(new Color(1f, 1f, 1f, 1f), gradeColorValue, num2);
			color.a = 1f;
			MaterialFactory.SetColor(_tintMaterial, color);
			float num4 = FxMath.Clamp01(Settings.GlowStrength.Value) * weight + num3 * 0.6f;
			bool flag = num4 > 0.001f;
			((Renderer)_glow).enabled = flag;
			if (flag)
			{
				MaterialFactory.SetColor(_glowMaterial, new Color(gradeColorValue.r, gradeColorValue.g, gradeColorValue.b, FxMath.Clamp01(num4)));
			}
		}
		else
		{
			float num5 = FxMath.Clamp01(num2 * 0.45f + num3 * 0.35f);
			MaterialFactory.SetColor(_tintMaterial, new Color(gradeColorValue.r, gradeColorValue.g, gradeColorValue.b, num5));
			((Renderer)_glow).enabled = false;
		}
	}

	public void Destroy()
	{
		if (FxMath.Alive((Object)(object)_root))
		{
			Object.Destroy((Object)(object)_root);
		}
		MaterialFactory.Destroy(_tintMaterial);
		MaterialFactory.Destroy(_glowMaterial);
		_root = null;
		_camera = null;
		_tint = null;
		_glow = null;
		_tintMaterial = null;
		_glowMaterial = null;
		_ownRenderers.Clear();
	}

	private bool EnsureBuilt(Camera camera)
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Expected O, but got Unknown
		if (FxMath.Alive((Object)(object)_root) && FxMath.Alive((Object)(object)_camera) && ((Il2CppObjectBase)_camera).Pointer == ((Il2CppObjectBase)camera).Pointer && FxMath.Alive((Object)(object)_tintMaterial) && FxMath.Alive((Object)(object)_glowMaterial) && _generation == MaterialFactory.Generation)
		{
			return true;
		}
		Destroy();
		if (!MaterialFactory.Ready())
		{
			return false;
		}
		_freeBlend = MaterialFactory.UsesFreeBlend;
		_tintMaterial = MaterialFactory.Create("Grade", _freeBlend ? BlendKind.Multiply : BlendKind.Alpha, new Color(1f, 1f, 1f, 0f), 3990, cullOff: true, alwaysOnTop: true);
		_glowMaterial = MaterialFactory.Create("Glow", BlendKind.Additive, new Color(0f, 0f, 0f, 0f), 3991, cullOff: true, alwaysOnTop: true);
		if ((Object)(object)_tintMaterial == (Object)null || (Object)(object)_glowMaterial == (Object)null)
		{
			Destroy();
			return false;
		}
		_camera = camera;
		_generation = MaterialFactory.Generation;
		_root = new GameObject("[SandevistanFX] Screen Grade");
		_root.layer = PickLayer(camera);
		FxMath.AttachAtOrigin(_root.transform, ((Component)camera).transform);
		_tint = CreateQuad("Tint", _tintMaterial);
		_glow = CreateQuad("Glow", _glowMaterial);
		return true;
	}

	private MeshRenderer CreateQuad(string name, Material material)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5);
		Collider component = obj.GetComponent<Collider>();
		if ((Object)(object)component != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component);
		}
		((Object)obj).name = "[SandevistanFX] " + name;
		obj.layer = _root.layer;
		FxMath.AttachAtOrigin(obj.transform, _root.transform);
		obj.transform.localScale = new Vector3(6f, 6f, 1f);
		MeshRenderer component2 = obj.GetComponent<MeshRenderer>();
		((Renderer)component2).sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(RendererUtil.Repeat(material, 1));
		RendererUtil.DisableShadows((Renderer)(object)component2);
		_ownRenderers.Add(((Il2CppObjectBase)component2).Pointer);
		return component2;
	}

	private static int PickLayer(Camera camera)
	{
		int cullingMask = camera.cullingMask;
		if ((cullingMask & 1) != 0)
		{
			return 0;
		}
		for (int i = 1; i < 32; i++)
		{
			if ((cullingMask & (1 << i)) != 0)
			{
				return i;
			}
		}
		return 0;
	}
}
internal static class Settings
{
	private static MelonPreferences_Category _category;

	private const int CurrentConfigVersion = 3;

	public const float DefaultHueSpeed = 0.2f;

	public const int DefaultMaxCount = 50;

	public const float DefaultLifetime = 3f;

	public const int MaxCountLimit = 100;

	public static MelonPreferences_Entry<int> ConfigVersion;

	public static MelonPreferences_Entry<bool> Enabled;

	public static MelonPreferences_Entry<bool> TriggerOnAnyTimeSlowdown;

	public static MelonPreferences_Entry<bool> UseFallbackShader;

	public static MelonPreferences_Entry<string> GradeColor;

	public static MelonPreferences_Entry<float> GradeStrength;

	public static MelonPreferences_Entry<float> GlowStrength;

	public static MelonPreferences_Entry<float> ActivationFlash;

	public static MelonPreferences_Entry<float> FadeInTime;

	public static MelonPreferences_Entry<float> FadeOutTime;

	public static MelonPreferences_Entry<bool> KeepPlayerColors;

	public static MelonPreferences_Entry<bool> AfterimagesEnabled;

	public static MelonPreferences_Entry<float> AfterimageInterval;

	public static MelonPreferences_Entry<float> AfterimageLifetime;

	public static MelonPreferences_Entry<float> AfterimageOpacity;

	public static MelonPreferences_Entry<float> AfterimageHueSpeed;

	public static MelonPreferences_Entry<float> AfterimageSaturation;

	public static MelonPreferences_Entry<float> AfterimageMinMove;

	public static MelonPreferences_Entry<float> AfterimageRevealDelay;

	public static MelonPreferences_Entry<float> AfterimageHeadHideRadius;

	public static MelonPreferences_Entry<float> AfterimageRevealDistance;

	public static MelonPreferences_Entry<int> AfterimageMaxCount;

	public static MelonPreferences_Entry<bool> AfterimageIncludeItems;

	public static MelonPreferences_Entry<bool> AfterimageAdditive;

	private static string _parsedColorSource;

	private static Color _parsedColor = new Color(0.24f, 1f, 0.45f, 1f);

	public static Color GradeColorValue
	{
		get
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			string value = GradeColor.Value;
			if ((object)value != _parsedColorSource)
			{
				_parsedColorSource = value;
				if (FxMath.TryParseHexColor(value, out var color))
				{
					_parsedColor = color;
				}
				else
				{
					Main.Log.Warning("GradeColor '" + value + "' isn't a valid hex color, using the default neon green.");
					_parsedColor = new Color(0.24f, 1f, 0.45f, 1f);
				}
			}
			return _parsedColor;
		}
	}

	public static void Init()
	{
		_category = MelonPreferences.CreateCategory("SandevistanFX", "Sandevistan FX");
		Enabled = _category.CreateEntry<bool>("Enabled", true, (string)null, "Master switch for the whole effect.", false, false, (ValueValidator)null, (string)null);
		TriggerOnAnyTimeSlowdown = _category.CreateEntry<bool>("TriggerOnAnyTimeSlowdown", false, (string)null, "Off: only BONELAB's slow-mo ability triggers the effect. On: any slowed time scale does (other slow-mo mods, Fusion, etc.).", false, false, (ValueValidator)null, (string)null);
		UseFallbackShader = _category.CreateEntry<bool>("UseFallbackShader", false, (string)null, "Troubleshooting: turn on if the green grade or afterimages never show up. Uses a simpler built-in shader (grade becomes a green wash).", false, false, (ValueValidator)null, (string)null);
		GradeColor = _category.CreateEntry<string>("GradeColor", "#3DFF73", (string)null, "Hex color of the world grade.", false, false, (ValueValidator)null, (string)null);
		GradeStrength = _category.CreateEntry<float>("GradeStrength", 0.7f, (string)null, "0-1. How hard the world is pushed toward the grade color.", false, false, (ValueValidator)null, (string)null);
		GlowStrength = _category.CreateEntry<float>("GlowStrength", 0.1f, (string)null, "0-1. Extra neon green light added on top of the grade.", false, false, (ValueValidator)null, (string)null);
		ActivationFlash = _category.CreateEntry<float>("ActivationFlash", 0.5f, (string)null, "0-1. Brightness of the green flash when slow-mo kicks in.", false, false, (ValueValidator)null, (string)null);
		FadeInTime = _category.CreateEntry<float>("FadeInTime", 0.12f, (string)null, "Seconds (real time) for the grade to fade in.", false, false, (ValueValidator)null, (string)null);
		FadeOutTime = _category.CreateEntry<float>("FadeOutTime", 0.15f, (string)null, "Seconds (real time) for the grade to fade out. 0 = instant.", false, false, (ValueValidator)null, (string)null);
		KeepPlayerColors = _category.CreateEntry<bool>("KeepPlayerColors", true, (string)null, "Keep your body, held weapons/items and holstered gear in their normal colors while the grade is up.", false, false, (ValueValidator)null, (string)null);
		AfterimagesEnabled = _category.CreateEntry<bool>("AfterimagesEnabled", true, (string)null, "Rainbow afterimages while slow-mo is on.", false, false, (ValueValidator)null, (string)null);
		AfterimageInterval = _category.CreateEntry<float>("AfterimageInterval", 0.06f, (string)null, "Seconds (real time) between afterimages.", false, false, (ValueValidator)null, (string)null);
		AfterimageLifetime = _category.CreateEntry<float>("AfterimageLifetime", 3f, (string)null, "Seconds (real time) each afterimage stays before disappearing instantly (no fade).", false, false, (ValueValidator)null, (string)null);
		AfterimageOpacity = _category.CreateEntry<float>("AfterimageOpacity", 0.5f, (string)null, "0-1. Opacity of each afterimage (constant until it disappears).", false, false, (ValueValidator)null, (string)null);
		AfterimageHueSpeed = _category.CreateEntry<float>("AfterimageHueSpeed", 0.2f, (string)null, "How fast the afterimage color moves through the rainbow (cycles per second). Lower = slower color change.", false, false, (ValueValidator)null, (string)null);
		AfterimageSaturation = _category.CreateEntry<float>("AfterimageSaturation", 0.85f, (string)null, "0-1. Color saturation of the afterimages.", false, false, (ValueValidator)null, (string)null);
		AfterimageMinMove = _category.CreateEntry<float>("AfterimageMinMove", 0.02f, (string)null, "Meters your head, hands or held items must move before a new afterimage spawns.", false, false, (ValueValidator)null, (string)null);
		AfterimageRevealDelay = _category.CreateEntry<float>("AfterimageRevealDelay", 0f, (string)null, "Seconds an afterimage stays hidden after spawning. 0 = appears instantly.", false, false, (ValueValidator)null, (string)null);
		AfterimageHeadHideRadius = _category.CreateEntry<float>("AfterimageHeadHideRadius", 0.45f, (string)null, "Afterimage heads are hidden while your eyes are within this many meters of them, so they never block your view.", false,