Decompiled source of TrailMarks v0.2.0

plugins/TrailMarks/TrailMarks.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TrailMarks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+3b197a541a29b5ef51c2838292d047ccd265ab1f")]
[assembly: AssemblyProduct("TrailMarks")]
[assembly: AssemblyTitle("TrailMarks")]
[assembly: AssemblyVersion("0.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TrailMarks
{
	internal struct SurfaceAnchor
	{
		public Vector3 Position;

		public Vector3 Normal;

		public BoneWeight Weight;

		public Color Color;
	}
	internal sealed class AttachmentSurface
	{
		private struct Triangle
		{
			public SurfaceAnchor A;

			public SurfaceAnchor B;

			public SurfaceAnchor C;

			public bool Seed;
		}

		public readonly List<SurfaceSite> Sites = new List<SurfaceSite>();

		public readonly List<SurfaceAnchor> Anchors = new List<SurfaceAnchor>();

		private static readonly Dictionary<string, AttachmentSurface> cache = new Dictionary<string, AttachmentSurface>();

		public static void Clear()
		{
			cache.Clear();
		}

		public static AttachmentSurface Build(SkinnedMeshRenderer main, IEnumerable<Renderer> parts)
		{
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0704: Unknown result type (might be due to invalid IL or missing references)
			//IL_0709: Unknown result type (might be due to invalid IL or missing references)
			//IL_070e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: 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)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0745: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0515: Unknown result type (might be due to invalid IL or missing references)
			//IL_0519: Unknown result type (might be due to invalid IL or missing references)
			//IL_051e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_052c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0535: Unknown result type (might be due to invalid IL or missing references)
			//IL_053a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0556: Unknown result type (might be due to invalid IL or missing references)
			//IL_054b: Unknown result type (might be due to invalid IL or missing references)
			//IL_077a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0781: Unknown result type (might be due to invalid IL or missing references)
			//IL_078b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0790: Unknown result type (might be due to invalid IL or missing references)
			//IL_0797: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0809: Unknown result type (might be due to invalid IL or missing references)
			//IL_080e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0838: Unknown result type (might be due to invalid IL or missing references)
			//IL_0844: Unknown result type (might be due to invalid IL or missing references)
			//IL_0850: Unknown result type (might be due to invalid IL or missing references)
			//IL_0861: Unknown result type (might be due to invalid IL or missing references)
			//IL_086d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0879: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_047e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0483: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_049e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04be: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] array = parts.ToArray();
			string key = ((Object)main).GetInstanceID() + "|" + string.Join(";", array.Select((Renderer r) => ((Object)r).GetInstanceID() + ":" + ((Object)TrailVisual.MeshOf(r)).GetInstanceID()));
			if (cache.TryGetValue(key, out var value))
			{
				return value;
			}
			AttachmentSurface attachmentSurface = new AttachmentSurface();
			List<Triangle> list = new List<Triangle>();
			Mesh obj = MeshCache.Get(main.sharedMesh);
			Transform[] mainBones = main.bones;
			Matrix4x4[] bindposes = obj.bindposes;
			string[] array2 = mainBones.Select((Transform b) => (!Object.op_Implicit((Object)(object)b)) ? "" : ((Object)b).name).ToArray();
			Renderer[] array3 = array;
			Vector3 val6;
			foreach (Renderer val in array3)
			{
				Mesh val2 = MeshCache.Get(TrailVisual.MeshOf(val));
				Vector3[] vertices = val2.vertices;
				Vector3[] normals = val2.normals;
				BoneWeight[] boneWeights = val2.boneWeights;
				Color[] colors = val2.colors;
				Matrix4x4[] bindposes2 = val2.bindposes;
				SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
				if (!Object.op_Implicit((Object)(object)val3))
				{
					Transform val4 = ((Component)val).transform;
					int num2 = -1;
					while (Object.op_Implicit((Object)(object)val4) && num2 < 0)
					{
						num2 = Array.IndexOf(mainBones, val4);
						if (num2 < 0)
						{
							val4 = val4.parent;
						}
					}
					if (num2 < 0)
					{
						continue;
					}
					Matrix4x4 val5 = ((Matrix4x4)(ref bindposes[num2])).inverse * mainBones[num2].worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix;
					SurfaceAnchor[] array4 = new SurfaceAnchor[vertices.Length];
					for (int num3 = 0; num3 < vertices.Length; num3++)
					{
						int num4 = num3;
						SurfaceAnchor surfaceAnchor = new SurfaceAnchor
						{
							Position = ((Matrix4x4)(ref val5)).MultiplyPoint3x4(vertices[num3])
						};
						val6 = ((Matrix4x4)(ref val5)).MultiplyVector(normals[num3]);
						surfaceAnchor.Normal = ((Vector3)(ref val6)).normalized;
						BoneWeight weight = default(BoneWeight);
						((BoneWeight)(ref weight)).boneIndex0 = num2;
						((BoneWeight)(ref weight)).weight0 = 1f;
						surfaceAnchor.Weight = weight;
						surfaceAnchor.Color = ((colors.Length == vertices.Length) ? colors[num3] : Color.white);
						array4[num4] = surfaceAnchor;
					}
					for (int num5 = 0; num5 < val2.subMeshCount; num5++)
					{
						int[] triangles = val2.GetTriangles(num5);
						for (int num6 = 0; num6 < triangles.Length; num6 += 3)
						{
							list.Add(new Triangle
							{
								A = array4[triangles[num6]],
								B = array4[triangles[num6 + 1]],
								C = array4[triangles[num6 + 2]],
								Seed = ((Object)val).name.ToLowerInvariant().Contains("head")
							});
						}
					}
				}
				else
				{
					if (boneWeights.Length != vertices.Length || normals.Length != vertices.Length)
					{
						continue;
					}
					int[] array5 = val3.bones.Select((Transform b) => Array.FindIndex(mainBones, (Transform m) => Object.op_Implicit((Object)(object)m) && Object.op_Implicit((Object)(object)b) && ((Object)m).name == ((Object)b).name)).ToArray();
					SurfaceAnchor[] array6 = new SurfaceAnchor[vertices.Length];
					bool[] array7 = new bool[vertices.Length];
					for (int num7 = 0; num7 < vertices.Length; num7++)
					{
						int[] array8 = new int[4]
						{
							((BoneWeight)(ref boneWeights[num7])).boneIndex0,
							((BoneWeight)(ref boneWeights[num7])).boneIndex1,
							((BoneWeight)(ref boneWeights[num7])).boneIndex2,
							((BoneWeight)(ref boneWeights[num7])).boneIndex3
						};
						float[] array9 = new float[4]
						{
							((BoneWeight)(ref boneWeights[num7])).weight0,
							((BoneWeight)(ref boneWeights[num7])).weight1,
							((BoneWeight)(ref boneWeights[num7])).weight2,
							((BoneWeight)(ref boneWeights[num7])).weight3
						};
						Dictionary<int, float> dictionary = new Dictionary<int, float>();
						Vector3 val7 = Vector3.zero;
						Vector3 val8 = Vector3.zero;
						float num8 = 0f;
						for (int num9 = 0; num9 < 4; num9++)
						{
							int num10 = array8[num9];
							float num11 = array9[num9];
							if (!(num11 <= 0f) && num10 < array5.Length && num10 < bindposes2.Length)
							{
								int num12 = array5[num10];
								if (num12 >= 0 && num12 < bindposes.Length)
								{
									Matrix4x4 val9 = ((Matrix4x4)(ref bindposes[num12])).inverse * bindposes2[num10];
									val7 += ((Matrix4x4)(ref val9)).MultiplyPoint3x4(vertices[num7]) * num11;
									val8 += ((Matrix4x4)(ref val9)).MultiplyVector(normals[num7]) * num11;
									dictionary[num12] = (dictionary.TryGetValue(num12, out var value2) ? (value2 + num11) : num11);
									num8 += num11;
								}
							}
						}
						if (!(num8 < 0.99f))
						{
							array6[num7] = new SurfaceAnchor
							{
								Position = val7 / num8,
								Normal = ((Vector3)(ref val8)).normalized,
								Weight = Weights(dictionary),
								Color = ((colors.Length == vertices.Length) ? colors[num7] : Color.white)
							};
							array7[num7] = true;
						}
					}
					bool seed = (Object)(object)val == (Object)(object)main || ((Object)val).name.ToLowerInvariant().Contains("head");
					for (int num13 = 0; num13 < val2.subMeshCount; num13++)
					{
						int[] triangles2 = val2.GetTriangles(num13);
						for (int num14 = 0; num14 < triangles2.Length; num14 += 3)
						{
							int num15 = triangles2[num14];
							int num16 = triangles2[num14 + 1];
							int num17 = triangles2[num14 + 2];
							if (array7[num15] && array7[num16] && array7[num17])
							{
								list.Add(new Triangle
								{
									A = array6[num15],
									B = array6[num16],
									C = array6[num17],
									Seed = seed
								});
							}
						}
					}
				}
			}
			foreach (Triangle item2 in list.Where((Triangle t) => t.Seed))
			{
				SurfaceAnchor surfaceAnchor2 = Blend(item2.A, item2.B, item2.C, 1f / 3f, 1f / 3f, 1f / 3f);
				val6 = Vector3.Cross(item2.B.Position - item2.A.Position, item2.C.Position - item2.A.Position);
				if (((Vector3)(ref val6)).sqrMagnitude < 1E-07f)
				{
					continue;
				}
				int num18 = Region(array2[((BoneWeight)(ref surfaceAnchor2.Weight)).boneIndex0]);
				if (num18 < 0 || (num18 == 0 && surfaceAnchor2.Normal.y < 0.15f))
				{
					continue;
				}
				float num19 = 0.002f;
				SurfaceAnchor item = surfaceAnchor2;
				foreach (Triangle item3 in list)
				{
					if (Hit(surfaceAnchor2.Position + surfaceAnchor2.Normal * 0.002f, surfaceAnchor2.Normal, item3, out var distance, out var u, out var v) && distance > num19 && distance < 0.65f)
					{
						num19 = distance;
						item = Blend(item3.A, item3.B, item3.C, 1f - u - v, u, v);
					}
				}
				item.Normal = surfaceAnchor2.Normal;
				int count = attachmentSurface.Anchors.Count;
				attachmentSurface.Anchors.Add(item);
				attachmentSurface.Sites.Add(new SurfaceSite(new Vector3(item.Position.x, item.Position.y, item.Position.z), new Vector3(item.Normal.x, item.Normal.y, item.Normal.z), num18, count));
			}
			if (cache.Count > 16)
			{
				cache.Clear();
			}
			cache[key] = attachmentSurface;
			return attachmentSurface;
		}

		internal static int Region(string name)
		{
			switch (name)
			{
			case "Head":
			case "Face":
				return 0;
			case "Arm_L":
			case "S_Shoulder_L":
			case "ShirtArm_L":
				return 1;
			case "Arm_R":
			case "S_Shoulder_R":
			case "ShirtArm_R":
				return 2;
			case "Elbow_L":
				return 3;
			case "Elbow_R":
				return 4;
			case "ChestF":
			case "Torso":
			case "Collar":
				return 5;
			case "ChestB":
			case "CollarB":
				return 6;
			case "WaistF1":
				return 7;
			case "WaistB1":
				return 8;
			case "Leg_L":
			case "PantWeight_L":
				return 9;
			case "Leg_R":
			case "PantWeight_R":
				return 10;
			case "Knee_L":
				return 11;
			case "Knee_R":
				return 12;
			case "WaistL1":
				return 13;
			case "WaistR1":
				return 14;
			default:
				return -1;
			}
		}

		private static bool Hit(Vector3 origin, Vector3 direction, Triangle t, out float distance, out float u, out float v)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			distance = (u = (v = 0f));
			Vector3 val = t.B.Position - t.A.Position;
			Vector3 val2 = t.C.Position - t.A.Position;
			Vector3 val3 = Vector3.Cross(direction, val2);
			float num = Vector3.Dot(val, val3);
			if (Mathf.Abs(num) < 1E-07f)
			{
				return false;
			}
			Vector3 val4 = origin - t.A.Position;
			u = Vector3.Dot(val4, val3) / num;
			if (u < 0f || u > 1f)
			{
				return false;
			}
			Vector3 val5 = Vector3.Cross(val4, val);
			v = Vector3.Dot(direction, val5) / num;
			if (v < 0f || u + v > 1f)
			{
				return false;
			}
			distance = Vector3.Dot(val2, val5) / num;
			return distance > 0f;
		}

		private static BoneWeight Weights(Dictionary<int, float> values)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			KeyValuePair<int, float>[] array = (from p in values
				orderby p.Value descending, p.Key
				select p).Take(4).ToArray();
			float num = array.Sum((KeyValuePair<int, float> p) => p.Value);
			BoneWeight result = default(BoneWeight);
			for (int num2 = 0; num2 < array.Length; num2++)
			{
				switch (num2)
				{
				case 0:
					((BoneWeight)(ref result)).boneIndex0 = array[num2].Key;
					((BoneWeight)(ref result)).weight0 = array[num2].Value / num;
					break;
				case 1:
					((BoneWeight)(ref result)).boneIndex1 = array[num2].Key;
					((BoneWeight)(ref result)).weight1 = array[num2].Value / num;
					break;
				case 2:
					((BoneWeight)(ref result)).boneIndex2 = array[num2].Key;
					((BoneWeight)(ref result)).weight2 = array[num2].Value / num;
					break;
				case 3:
					((BoneWeight)(ref result)).boneIndex3 = array[num2].Key;
					((BoneWeight)(ref result)).weight3 = array[num2].Value / num;
					break;
				}
			}
			return result;
		}

		private static SurfaceAnchor Blend(SurfaceAnchor a, SurfaceAnchor b, SurfaceAnchor c, float x, float y, float z)
		{
			//IL_000d: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<int, float> values = new Dictionary<int, float>();
			Add(a.Weight, x);
			Add(b.Weight, y);
			Add(c.Weight, z);
			SurfaceAnchor result = new SurfaceAnchor
			{
				Position = a.Position * x + b.Position * y + c.Position * z
			};
			Vector3 val = a.Normal * x + b.Normal * y + c.Normal * z;
			result.Normal = ((Vector3)(ref val)).normalized;
			result.Weight = Weights(values);
			result.Color = a.Color * x + b.Color * y + c.Color * z;
			return result;
			void Add(BoneWeight w, float amount)
			{
				int[] array = new int[4]
				{
					((BoneWeight)(ref w)).boneIndex0,
					((BoneWeight)(ref w)).boneIndex1,
					((BoneWeight)(ref w)).boneIndex2,
					((BoneWeight)(ref w)).boneIndex3
				};
				float[] array2 = new float[4]
				{
					((BoneWeight)(ref w)).weight0,
					((BoneWeight)(ref w)).weight1,
					((BoneWeight)(ref w)).weight2,
					((BoneWeight)(ref w)).weight3
				};
				for (int i = 0; i < 4; i++)
				{
					if (array2[i] > 0f)
					{
						values[array[i]] = (values.TryGetValue(array[i], out var value) ? (value + array2[i] * amount) : (array2[i] * amount));
					}
				}
			}
		}
	}
	public readonly struct SurfaceSite
	{
		public readonly Vector3 Position;

		public readonly Vector3 Normal;

		public readonly int Region;

		public readonly int Index;

		public SurfaceSite(Vector3 position, Vector3 normal, int region, int index)
		{
			Position = position;
			Normal = normal;
			Region = region;
			Index = index;
		}
	}
	public readonly struct GrowthSite
	{
		public readonly SurfaceSite Surface;

		public readonly float Scale;

		public readonly float Angle;

		public GrowthSite(SurfaceSite s, float scale, float angle)
		{
			Surface = s;
			Scale = scale;
			Angle = angle;
		}
	}
	public static class GrowthLayout
	{
		public static uint Hash(int seed, int value)
		{
			int num = seed ^ (value * 747796405);
			int num2 = (num ^ (num >>> 16)) * -2048144777;
			int num3 = (num2 ^ (num2 >>> 13)) * -1028477379;
			return (uint)(num3 ^ (num3 >>> 16));
		}

		public static List<GrowthSite> Build(IReadOnlyList<SurfaceSite> source, int seed)
		{
			List<GrowthSite> list = new List<GrowthSite>();
			int[] array = (from r in source.Select((SurfaceSite s) => s.Region).Distinct()
				orderby Hash(seed, r + 101)
				select r).ToArray();
			HashSet<int> hashSet = new HashSet<int>();
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			int step;
			for (step = 0; step < 48; step++)
			{
				bool flag = false;
				for (int num = 0; num < array.Length; num++)
				{
					if (flag)
					{
						break;
					}
					int region = array[(step + num) % array.Length];
					int num2 = ((region == 0) ? 3 : 4);
					if (dictionary.TryGetValue(region, out var value) && value >= num2)
					{
						continue;
					}
					foreach (SurfaceSite site in from s in source
						where s.Region == region
						orderby Hash(seed + step * 31, s.Index)
						select s)
					{
						if (!hashSet.Contains(site.Index))
						{
							float scale = 0.9f + (float)(Hash(seed, site.Index + 911) % 1000) / 1000f * 0.2f;
							if (!list.Any((GrowthSite other) => (double)Vector3.DistanceSquared(other.Surface.Position, site.Position) < Math.Pow(0.49f * Math.Max(scale, other.Scale), 2.0)))
							{
								list.Add(new GrowthSite(site, scale, (float)(Hash(seed, site.Index + 211) % 360) * (MathF.PI / 180f)));
								hashSet.Add(site.Index);
								dictionary[region] = value + 1;
								flag = true;
								break;
							}
						}
					}
				}
				if (!flag)
				{
					break;
				}
			}
			return list;
		}
	}
	public readonly struct History : IEquatable<History>
	{
		public readonly float Burn;

		public readonly float Spores;

		public readonly float Cold;

		public readonly float Poison;

		public readonly int Seed;

		public readonly string Order;

		public const int TotalLimit = 48;

		public const int KindLimit = 32;

		public bool IsEmpty => (Order ?? "").Length == 0;

		public string VisualKey => Seed + ":" + Order;

		public History(float burn, float spores, float cold, float poison, int seed = 1729, string order = null)
		{
			Burn = Clamp(burn);
			Spores = Clamp(spores);
			Cold = Clamp(cold);
			Poison = Clamp(poison);
			Seed = seed;
			Order = order ?? BuildOrder(Burn, Spores, Cold, Poison);
		}

		private static float Clamp(float v)
		{
			if (!float.IsNaN(v) && !float.IsInfinity(v))
			{
				return Math.Max(0f, Math.Min(300f, v));
			}
			return 0f;
		}

		public static float Threshold(int index)
		{
			return (float)(2.5 + 2.8 * (double)index + 0.137 * (double)index * (double)index);
		}

		public static int Count(float amount)
		{
			int i;
			for (i = 0; i < 32 && amount + 0.0001f >= Threshold(i); i++)
			{
			}
			return i;
		}

		public float Value(int channel)
		{
			return channel switch
			{
				2 => Cold, 
				1 => Spores, 
				0 => Burn, 
				_ => Poison, 
			};
		}

		private static string BuildOrder(params float[] values)
		{
			int[] array = new int[4];
			StringBuilder stringBuilder = new StringBuilder();
			while (stringBuilder.Length < 48)
			{
				int num = -1;
				float num2 = float.MaxValue;
				for (int i = 0; i < 4; i++)
				{
					if (array[i] < Count(values[i]))
					{
						float num3 = Threshold(array[i]) / values[i];
						if (num3 < num2)
						{
							num2 = num3;
							num = i;
						}
					}
				}
				if (num < 0)
				{
					break;
				}
				stringBuilder.Append((char)(48 + num));
				array[num]++;
			}
			return stringBuilder.ToString();
		}

		public History Add(int channel, float amount)
		{
			if (channel < 0 || channel > 3 || float.IsNaN(amount) || float.IsInfinity(amount) || amount <= 0f)
			{
				return this;
			}
			float num = Burn;
			float num2 = Spores;
			float num3 = Cold;
			float num4 = Poison;
			switch (channel)
			{
			case 0:
				num = Clamp(num + amount);
				break;
			case 1:
				num2 = Clamp(num2 + amount);
				break;
			case 2:
				num3 = Clamp(num3 + amount);
				break;
			case 3:
				num4 = Clamp(num4 + amount);
				break;
			}
			string text = Order ?? "";
			int val = Count(channel switch
			{
				2 => num3, 
				1 => num2, 
				0 => num, 
				_ => num4, 
			}) - Count(Value(channel));
			text += new string((char)(48 + channel), Math.Min(Math.Max(0, val), 48 - text.Length));
			return new History(num, num2, num3, num4, (Seed == 0) ? 1729 : Seed, text);
		}

		public History WithSeed(int seed)
		{
			return new History(Burn, Spores, Cold, Poison, seed, Order ?? "");
		}

		public bool Equals(History h)
		{
			if (Burn == h.Burn && Spores == h.Spores && Cold == h.Cold && Poison == h.Poison && Seed == h.Seed)
			{
				return (Order ?? "") == (h.Order ?? "");
			}
			return false;
		}

		public string Encode(string run)
		{
			return string.Join("|", "2", run, Burn.ToString("R", CultureInfo.InvariantCulture), Spores.ToString("R", CultureInfo.InvariantCulture), Cold.ToString("R", CultureInfo.InvariantCulture), Poison.ToString("R", CultureInfo.InvariantCulture), Seed.ToString(CultureInfo.InvariantCulture), Order ?? "");
		}

		public static bool TryDecode(string text, string run, out History history)
		{
			history = default(History);
			if (text == null || text.Length > 320)
			{
				return false;
			}
			string[] array = text.Split('|');
			if (array.Length != 8 || array[0] != "2" || array[1] != run)
			{
				return false;
			}
			float[] array2 = new float[4];
			for (int i = 0; i < 4; i++)
			{
				if (!float.TryParse(array[i + 2], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]) || float.IsNaN(array2[i]) || float.IsInfinity(array2[i]) || array2[i] < 0f || array2[i] > 300f)
				{
					return false;
				}
			}
			if (!int.TryParse(array[6], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || array[7].Length > 48)
			{
				return false;
			}
			int[] array3 = new int[4];
			string text2 = array[7];
			foreach (char c in text2)
			{
				if (c < '0' || c > '3')
				{
					return false;
				}
				if (++array3[c - 48] > Count(array2[c - 48]))
				{
					return false;
				}
			}
			int num = Math.Min(48, Count(array2[0]) + Count(array2[1]) + Count(array2[2]) + Count(array2[3]));
			if (array[7].Length != num)
			{
				return false;
			}
			history = new History(array2[0], array2[1], array2[2], array2[3], result, array[7]);
			return true;
		}

		public static int StableSeed(string text)
		{
			uint num = 2166136261u;
			string text2 = text ?? "";
			foreach (char c in text2)
			{
				num ^= c;
				num *= 16777619;
			}
			return (int)num;
		}

		public override string ToString()
		{
			return string.Format("Heat {0:0.#} | Spores {1:0.#} | Cold {2:0.#} | Poison {3:0.#} | Growth {4}/{5}", Burn, Spores, Cold, Poison, (Order ?? "").Length, 48);
		}
	}
	internal static class MeshCache
	{
		private static readonly Dictionary<Mesh, Mesh> cache = new Dictionary<Mesh, Mesh>();

		public static Mesh Get(Mesh mesh)
		{
			if (cache.TryGetValue(mesh, out var value))
			{
				return value;
			}
			Mesh[] array = cache.Keys.Where((Mesh k) => !Object.op_Implicit((Object)(object)k)).ToArray();
			foreach (Mesh key in array)
			{
				if (Object.op_Implicit((Object)(object)cache[key]))
				{
					Object.Destroy((Object)(object)cache[key]);
				}
				cache.Remove(key);
			}
			value = ReadableMesh.Copy(mesh);
			cache.Add(mesh, value);
			return value;
		}

		public static void Clear()
		{
			foreach (KeyValuePair<Mesh, Mesh> item in cache)
			{
				if (Object.op_Implicit((Object)(object)item.Value) && (Object)(object)item.Value != (Object)(object)item.Key)
				{
					Object.Destroy((Object)(object)item.Value);
				}
			}
			cache.Clear();
		}
	}
	internal sealed class ModelTemplates
	{
		public Vector3[] Vertices;

		public Vector3[] Normals;

		public int[] Triangles;

		public int[] Materials;

		public Color[] Palette;

		private static readonly Dictionary<string, ModelTemplates> loaded = new Dictionary<string, ModelTemplates>();

		public static ModelTemplates Get(string name)
		{
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			if (loaded.TryGetValue(name, out var value))
			{
				return value;
			}
			Assembly assembly = typeof(ModelTemplates).Assembly;
			string name2 = assembly.GetManifestResourceNames().Single((string n) => n.EndsWith("." + name + ".tmsh", StringComparison.Ordinal));
			using (Stream stream = assembly.GetManifestResourceStream(name2))
			{
				using BinaryReader binaryReader = new BinaryReader(stream);
				if (binaryReader.ReadInt32() != 860705876)
				{
					throw new InvalidDataException("Unsupported TrailMarks mesh");
				}
				int num = binaryReader.ReadInt32();
				int num2 = binaryReader.ReadInt32();
				if (num <= 0 || num > 20000 || num2 <= 0 || num2 > 120000 || num2 % 3 != 0)
				{
					throw new InvalidDataException("Invalid mesh size");
				}
				value = new ModelTemplates
				{
					Vertices = (Vector3[])(object)new Vector3[num],
					Normals = (Vector3[])(object)new Vector3[num],
					Triangles = new int[num2],
					Materials = new int[num2 / 3],
					Palette = (Color[])(object)new Color[3]
				};
				for (int num3 = 0; num3 < 3; num3++)
				{
					value.Palette[num3] = new Color(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), 1f);
				}
				for (int num4 = 0; num4 < num; num4++)
				{
					value.Vertices[num4] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
				}
				for (int num5 = 0; num5 < num; num5++)
				{
					value.Normals[num5] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
				}
				for (int num6 = 0; num6 < num2; num6++)
				{
					int num7 = binaryReader.ReadInt32();
					if (num7 < 0 || num7 >= num)
					{
						throw new InvalidDataException("Invalid mesh index");
					}
					value.Triangles[num6] = num7;
				}
				for (int num8 = 0; num8 < num2 / 3; num8++)
				{
					int num9 = binaryReader.ReadInt32();
					if (num9 < 0 || num9 > 2)
					{
						throw new InvalidDataException("Invalid palette slot");
					}
					value.Materials[num8] = num9;
				}
				if (stream.Position != stream.Length)
				{
					throw new InvalidDataException("Trailing mesh data");
				}
			}
			loaded.Add(name, value);
			return value;
		}
	}
	[BepInPlugin("trailmarks.core", "TrailMarks", "0.2.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class VisualEntry
		{
			public TrailVisual Visual;

			public History History;

			public Material Material;

			public Color Skin;
		}

		private const string Property = "TrailMarks.v2";

		private History preview;

		private string run = "";

		private string lastPublished = "";

		private float nextRefresh;

		private float nextPublish;

		private bool dirty;

		private ConfigEntry<float> growthCost;

		private Harmony harmony;

		private readonly Dictionary<int, VisualEntry> visuals = new Dictionary<int, VisualEntry>();

		private readonly Dictionary<int, float> retryAfter = new Dictionary<int, float>();

		public static Plugin Instance { get; private set; }

		public History LocalHistory { get; private set; }

		public bool PreviewEnabled { get; private set; }

		public bool CanEditHistory
		{
			get
			{
				if (run.Length > 0 && Object.op_Implicit((Object)(object)Character.localCharacter))
				{
					return !Character.localCharacter.inAirport;
				}
				return false;
			}
		}

		public string PreviewStatus { get; private set; } = "Waiting for character";

		public History PreviewHistory
		{
			get
			{
				if (!PreviewEnabled)
				{
					return new History(0f, 0f, 0f, 0f, (LocalHistory.Seed == 0) ? 1729 : LocalHistory.Seed);
				}
				return preview;
			}
		}

		private string SaveFile => Path.Combine(Paths.ConfigPath, "TrailMarks.v2.last-run.txt");

		public void SetPreviewHistory(History h)
		{
			PreviewEnabled = true;
			preview = h;
			nextRefresh = 0f;
		}

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			Instance = this;
			growthCost = ((BaseUnityPlugin)this).Config.Bind<float>("Growth", "Exposure cost multiplier", 1f, new ConfigDescription("Lower = faster growth. Only affects future accepted exposure, not saved history.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			harmony = new Harmony("trailmarks.core");
			harmony.PatchAll(typeof(Plugin).Assembly);
			SceneManager.activeSceneChanged += OnScene;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"TrailMarks 0.2.0 ready. Automatic history enabled.");
		}

		private void OnScene(Scene previous, Scene current)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_0097: Expected O, but got Unknown
			ClearVisuals();
			MeshCache.Clear();
			AttachmentSurface.Clear();
			retryAfter.Clear();
			if (((Scene)(ref current)).name == "Airport" || ((Scene)(ref current)).name == "Title")
			{
				Save();
				run = "";
				LocalHistory = default(History);
				PreviewEnabled = false;
				lastPublished = "";
				if (PhotonNetwork.LocalPlayer != null)
				{
					Player localPlayer = PhotonNetwork.LocalPlayer;
					Hashtable val = new Hashtable();
					((Dictionary<object, object>)val).Add((object)"TrailMarks.v2", (object)null);
					localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				}
			}
			nextRefresh = 0f;
		}

		private void RefreshRun()
		{
			if (!Object.op_Implicit((Object)(object)RunManager.Instance))
			{
				return;
			}
			Guid runId = RunManager.Instance.RunId;
			if (runId == Guid.Empty || runId.ToString("N") == run)
			{
				return;
			}
			bool num = run.Length == 0;
			run = runId.ToString("N");
			if (!num)
			{
				LocalHistory = default(History);
			}
			bool flag = false;
			try
			{
				if (File.Exists(SaveFile) && History.TryDecode(File.ReadAllText(SaveFile), run, out var history))
				{
					LocalHistory = history.Add(0, LocalHistory.Burn).Add(1, LocalHistory.Spores).Add(2, LocalHistory.Cold)
						.Add(3, LocalHistory.Poison);
					flag = true;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("History restore: " + ex.Message));
			}
			if (!flag)
			{
				History localHistory = LocalHistory;
				string text = run;
				Player localPlayer = PhotonNetwork.LocalPlayer;
				LocalHistory = localHistory.WithSeed(History.StableSeed(text + "|" + ((localPlayer != null) ? new int?(localPlayer.ActorNumber) : ((int?)null))));
			}
			dirty = true;
			lastPublished = "";
			ClearVisuals();
			retryAfter.Clear();
		}

		public void SetPreview(bool enabled, float burn, float spores, float cold, float poison)
		{
			PreviewEnabled = enabled;
			int num = ((LocalHistory.Seed == 0) ? 1729 : LocalHistory.Seed);
			if (preview.Burn != burn || preview.Spores != spores || preview.Cold != cold || preview.Poison != poison || preview.Seed != num)
			{
				preview = new History(burn, spores, cold, poison, num);
			}
			nextRefresh = 0f;
		}

		public void SetLocalHistory(float burn, float spores, float cold, float poison)
		{
			if (CanEditHistory)
			{
				LocalHistory = ((PreviewEnabled && preview.Burn == burn && preview.Spores == spores && preview.Cold == cold && preview.Poison == poison) ? preview.WithSeed(LocalHistory.Seed) : new History(burn, spores, cold, poison, LocalHistory.Seed));
				dirty = true;
				nextRefresh = 0f;
			}
		}

		internal void Record(CharacterAfflictions afflictions, STATUSTYPE type, float before)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)Character.localCharacter) || Character.localCharacter.inAirport || (Object)(object)Character.localCharacter.refs.afflictions != (Object)(object)afflictions)
			{
				return;
			}
			int num = (((int)type != 8) ? (((int)type == 10) ? 1 : (((int)type == 2) ? 2 : (((int)type == 3) ? 3 : (-1)))) : 0);
			if (num >= 0)
			{
				RefreshRun();
				History localHistory = LocalHistory.Add(num, (afflictions.GetCurrentStatus(type) - before) * 100f / growthCost.Value);
				if (!localHistory.Equals(LocalHistory))
				{
					LocalHistory = localHistory;
					dirty = true;
				}
			}
		}

		private void Update()
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00a6: Expected O, but got Unknown
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: 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_0291: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime < nextRefresh)
			{
				return;
			}
			nextRefresh = Time.unscaledTime + 0.5f;
			RefreshRun();
			if (Time.unscaledTime >= nextPublish)
			{
				nextPublish = Time.unscaledTime + 1f;
				if (run.Length > 0 && PhotonNetwork.InRoom)
				{
					string text = LocalHistory.Encode(run);
					if (text != lastPublished)
					{
						Player localPlayer = PhotonNetwork.LocalPlayer;
						Hashtable val = new Hashtable();
						((Dictionary<object, object>)val).Add((object)"TrailMarks.v2", (object)text);
						if (localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null))
						{
							lastPublished = text;
						}
					}
				}
				Save();
			}
			HashSet<int> live = new HashSet<int>();
			int num = 0;
			int num2 = 0;
			CharacterCustomization[] array = Object.FindObjectsByType<CharacterCustomization>((FindObjectsSortMode)0);
			foreach (CharacterCustomization val2 in array)
			{
				SkinnedMeshRenderer val3 = val2.refs?.mainRenderer;
				if (!Object.op_Implicit((Object)(object)val3) || !Object.op_Implicit((Object)(object)val3.sharedMesh))
				{
					continue;
				}
				Character component = ((Component)val2).GetComponent<Character>();
				Player val4 = val2.overridePhotonPlayer ?? (Object.op_Implicit((Object)(object)component) ? ((MonoBehaviourPun)component).photonView.Owner : null);
				bool num3 = val4 != null && val4 == PhotonNetwork.LocalPlayer;
				History history = default(History);
				if (num3)
				{
					history = (PreviewEnabled ? preview : LocalHistory);
				}
				else if (val4 != null && run.Length > 0 && ((Dictionary<object, object>)(object)val4.CustomProperties).ContainsKey((object)"TrailMarks.v2"))
				{
					History.TryDecode(val4.CustomProperties[(object)"TrailMarks.v2"] as string, run, out history);
				}
				int instanceID = ((Object)val3).GetInstanceID();
				if (history.IsEmpty)
				{
					continue;
				}
				live.Add(instanceID);
				if (retryAfter.TryGetValue(instanceID, out var value) && Time.unscaledTime < value)
				{
					continue;
				}
				Material val5 = ((Renderer)val3).sharedMaterials.FirstOrDefault();
				Color val6 = ((Object.op_Implicit((Object)(object)val5) && val5.HasProperty("_SkinColor")) ? val5.GetColor("_SkinColor") : Color.white);
				if (visuals.TryGetValue(instanceID, out var value2) && (Object)(object)value2.Visual.OriginalMesh == (Object)(object)val3.sharedMesh && value2.History.VisualKey == history.VisualKey && (Object)(object)value2.Material == (Object)(object)val5 && value2.Skin == val6 && value2.Visual.CostumeSignature == TrailVisual.Signature(val2))
				{
					num++;
					num2 += value2.Visual.GrowthCount;
					continue;
				}
				value2?.Visual.Dispose();
				visuals.Remove(instanceID);
				try
				{
					visuals[instanceID] = new VisualEntry
					{
						Visual = TrailVisual.Create(val2, history),
						History = history,
						Material = val5,
						Skin = val6
					};
					num++;
					num2 += visuals[instanceID].Visual.GrowthCount;
				}
				catch (Exception ex)
				{
					retryAfter[instanceID] = Time.unscaledTime + 30f;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Appearance skipped for " + ((Object)val3).name + ": " + ex.Message));
				}
			}
			int[] array2 = visuals.Keys.Where((int k) => !live.Contains(k) || !Object.op_Implicit((Object)(object)visuals[k].Visual.Source)).ToArray();
			foreach (int key in array2)
			{
				visuals[key].Visual.Dispose();
				visuals.Remove(key);
			}
			PreviewStatus = ((!Object.op_Implicit((Object)(object)Character.localCharacter)) ? "Waiting for local character | " : "") + (PreviewEnabled ? "Preview (local only)" : "Natural history") + " | renderers: " + num + " | drawn growth units: " + num2 + "\n" + (PreviewEnabled ? preview : LocalHistory);
		}

		private void Save()
		{
			if (!dirty || run.Length == 0)
			{
				return;
			}
			try
			{
				File.WriteAllText(SaveFile, LocalHistory.Encode(run));
				dirty = false;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("History save: " + ex.Message));
			}
		}

		private void ClearVisuals()
		{
			foreach (VisualEntry value in visuals.Values)
			{
				value.Visual.Dispose();
			}
			visuals.Clear();
		}

		private void OnDestroy()
		{
			Save();
			SceneManager.activeSceneChanged -= OnScene;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			ClearVisuals();
			MeshCache.Clear();
			AttachmentSurface.Clear();
			Instance = null;
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "AddStatus")]
	internal static class AcceptedStatusPatch
	{
		private static void Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, out float __state)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			__state = __instance.GetCurrentStatus(statusType);
		}

		private static void Postfix(CharacterAfflictions __instance, STATUSTYPE statusType, float __state)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Instance?.Record(__instance, statusType, __state);
		}
	}
	internal static class ReadableMesh
	{
		public static Mesh Copy(Mesh source)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			if (source.isReadable)
			{
				return source;
			}
			Mesh val = new Mesh
			{
				name = ((Object)source).name + "_TrailMarksReadable"
			};
			try
			{
				val.SetVertexBufferParams(source.vertexCount, source.GetVertexAttributes());
				for (int i = 0; i < source.vertexBufferCount; i++)
				{
					GraphicsBuffer vertexBuffer = source.GetVertexBuffer(i);
					try
					{
						if (vertexBuffer == null)
						{
							throw new InvalidOperationException("GPU vertex buffer unavailable");
						}
						byte[] array = new byte[vertexBuffer.count * vertexBuffer.stride];
						vertexBuffer.GetData((Array)array);
						val.SetVertexBufferData<byte>(array, 0, 0, array.Length, i, (MeshUpdateFlags)8);
					}
					finally
					{
						((IDisposable)vertexBuffer)?.Dispose();
					}
				}
				GraphicsBuffer indexBuffer = source.GetIndexBuffer();
				try
				{
					if (indexBuffer == null)
					{
						throw new InvalidOperationException("GPU index buffer unavailable");
					}
					int count = indexBuffer.count;
					val.SetIndexBufferParams(count, source.indexFormat);
					if ((int)source.indexFormat == 0)
					{
						ushort[] array2 = new ushort[count];
						indexBuffer.GetData((Array)array2);
						val.SetIndexBufferData<ushort>(array2, 0, 0, count, (MeshUpdateFlags)0);
					}
					else
					{
						uint[] array3 = new uint[count];
						indexBuffer.GetData((Array)array3);
						val.SetIndexBufferData<uint>(array3, 0, 0, count, (MeshUpdateFlags)0);
					}
				}
				finally
				{
					((IDisposable)indexBuffer)?.Dispose();
				}
				val.subMeshCount = source.subMeshCount;
				for (int j = 0; j < source.subMeshCount; j++)
				{
					val.SetSubMesh(j, source.GetSubMesh(j), (MeshUpdateFlags)8);
				}
				val.bindposes = source.bindposes;
				BoneWeight[] boneWeights = source.boneWeights;
				if (boneWeights.Length == source.vertexCount)
				{
					val.boneWeights = boneWeights;
				}
				val.bounds = source.bounds;
				if (val.vertices.Length != source.vertexCount)
				{
					throw new InvalidOperationException("GPU copy lacks complete vertex/skin data");
				}
				return val;
			}
			catch
			{
				Object.Destroy((Object)(object)val);
				throw;
			}
		}
	}
	internal sealed class TrailVisual : IDisposable
	{
		public SkinnedMeshRenderer Source;

		public Mesh OriginalMesh;

		public string CostumeSignature;

		public int GrowthCount;

		private GameObject root;

		private readonly List<Object> owned = new List<Object>();

		public static IEnumerable<Renderer> Parts(CharacterCustomization customization)
		{
			CustomizationRefs refs = customization.refs;
			return (from r in ((IEnumerable<Renderer>)(object)new Renderer[4]
				{
					(Renderer)refs.mainRenderer,
					refs.shorts,
					refs.skirt,
					refs.sashRenderer
				}).Concat(refs.PlayerRenderers ?? Array.Empty<Renderer>())
				where Object.op_Implicit((Object)(object)r) && ((Component)r).gameObject.activeInHierarchy && Object.op_Implicit((Object)(object)MeshOf(r)) && !((Object)r).name.ToLowerInvariant().Contains("shadow") && !((Object)r).name.ToLowerInvariant().Contains("eye") && !((Object)r).name.ToLowerInvariant().Contains("mouth")
				select r).Distinct();
		}

		public static Mesh MeshOf(Renderer r)
		{
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((r is SkinnedMeshRenderer) ? r : null);
			if (val == null)
			{
				MeshFilter component = ((Component)r).GetComponent<MeshFilter>();
				if (component == null)
				{
					return null;
				}
				return component.sharedMesh;
			}
			return val.sharedMesh;
		}

		public static string Signature(CharacterCustomization c)
		{
			return string.Join(";", from r in Parts(c)
				select ((Object)r).GetInstanceID() + ":" + ((Object)MeshOf(r)).GetInstanceID());
		}

		public static TrailVisual Create(CharacterCustomization customization, History history)
		{
			TrailVisual trailVisual = new TrailVisual
			{
				Source = customization.refs.mainRenderer
			};
			trailVisual.OriginalMesh = trailVisual.Source.sharedMesh;
			trailVisual.CostumeSignature = Signature(customization);
			try
			{
				trailVisual.Build(customization, history);
				return trailVisual;
			}
			catch
			{
				trailVisual.Dispose();
				throw;
			}
		}

		private void Build(CharacterCustomization customization, History history)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: 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_025e: Unknown result type (might be due to invalid IL or missing references)
			AttachmentSurface attachmentSurface = AttachmentSurface.Build(Source, Parts(customization));
			List<GrowthSite> list = GrowthLayout.Build(attachmentSurface.Sites, history.Seed);
			root = new GameObject("TrailMarks_Growth");
			root.transform.SetParent(((Component)Source).transform, false);
			GrowthMesh[] array = new GrowthMesh[4]
			{
				new GrowthMesh(),
				new GrowthMesh(),
				new GrowthMesh(),
				new GrowthMesh()
			};
			int[] array2 = new int[4];
			string text = history.Order ?? "";
			for (int i = 0; i < Math.Min(list.Count, text.Length); i++)
			{
				int num = text[i] - 48;
				int num2 = array2[num]++;
				GrowthSite growthSite = list[i];
				SurfaceAnchor anchor = attachmentSurface.Anchors[growthSite.Surface.Index];
				array[num].Append(ModelTemplates.Get(num switch
				{
					2 => "frost", 
					1 => (num2 < 5) ? "spore_small" : "spore_cluster", 
					0 => "lava", 
					_ => "toxin", 
				}), anchor, growthSite.Scale, growthSite.Angle);
				GrowthCount++;
			}
			TrailVisibility trailVisibility = root.AddComponent<TrailVisibility>();
			trailVisibility.Source = Source;
			List<SkinnedMeshRenderer> list2 = new List<SkinnedMeshRenderer>();
			for (int j = 0; j < 4; j++)
			{
				GrowthMesh growthMesh = array[j];
				if (growthMesh.Count != 0)
				{
					Mesh val = growthMesh.ToMesh(MeshCache.Get(OriginalMesh).bindposes);
					owned.Add((Object)(object)val);
					GameObject val2 = new GameObject("TrailMarks_" + (new string[4] { "Lava", "Spores", "Frost", "Toxin" })[j]);
					val2.transform.SetParent(root.transform, false);
					val2.layer = ((Component)Source).gameObject.layer;
					SkinnedMeshRenderer val3 = val2.AddComponent<SkinnedMeshRenderer>();
					val3.sharedMesh = val;
					val3.bones = Source.bones;
					val3.rootBone = Source.rootBone;
					((Renderer)val3).localBounds = val.bounds;
					val3.updateWhenOffscreen = Source.updateWhenOffscreen;
					((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0;
					((Renderer)val3).sharedMaterials = growthMesh.Palette.Select((Color color) => MakeMaterial(color)).ToArray();
					list2.Add(val3);
				}
			}
			trailVisibility.Layers = list2.ToArray();
			trailVisibility.Materials = owned.OfType<Material>().ToArray();
		}

		private Material MakeMaterial(Color color)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(((Renderer)Source).sharedMaterials[0]);
			owned.Add((Object)(object)val);
			((Object)val).name = "TrailMarks_SolidGeometry";
			if (val.HasProperty("_SkinColor"))
			{
				val.SetColor("_SkinColor", color);
			}
			if (val.HasProperty("_UseSkinTone"))
			{
				val.SetFloat("_UseSkinTone", 1f);
			}
			if (val.HasProperty("_MainTex"))
			{
				val.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
			}
			if (val.HasProperty("_UseRawVertexColor"))
			{
				val.SetFloat("_UseRawVertexColor", 0f);
			}
			return val;
		}

		public void Dispose()
		{
			if (Object.op_Implicit((Object)(object)root))
			{
				Object.Destroy((Object)(object)root);
			}
			foreach (Object item in owned)
			{
				if (Object.op_Implicit(item))
				{
					Object.Destroy(item);
				}
			}
			owned.Clear();
		}
	}
	public sealed class TrailVisibility : MonoBehaviour
	{
		public SkinnedMeshRenderer Source;

		public SkinnedMeshRenderer[] Layers = Array.Empty<SkinnedMeshRenderer>();

		public Material[] Materials = Array.Empty<Material>();

		private static readonly string[] VisibilityProperties = new string[3] { "_VertexGhost", "_Opacity", "_Stepalphabyvertexcolor" };

		private void LateUpdate()
		{
			SkinnedMeshRenderer[] layers = Layers;
			foreach (SkinnedMeshRenderer val in layers)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					((Renderer)val).enabled = Object.op_Implicit((Object)(object)Source) && ((Renderer)Source).enabled;
					if (Object.op_Implicit((Object)(object)Source))
					{
						((Renderer)val).forceRenderingOff = ((Renderer)Source).forceRenderingOff;
						((Component)val).gameObject.layer = ((Component)Source).gameObject.layer;
					}
				}
			}
			Material val2 = (Object.op_Implicit((Object)(object)Source) ? ((Renderer)Source).sharedMaterial : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return;
			}
			Material[] materials = Materials;
			foreach (Material val3 in materials)
			{
				string[] visibilityProperties = VisibilityProperties;
				foreach (string text in visibilityProperties)
				{
					if (val2.HasProperty(text) && val3.HasProperty(text))
					{
						val3.SetFloat(text, val2.GetFloat(text));
					}
				}
			}
		}
	}
	internal sealed class GrowthMesh
	{
		private readonly List<Vector3> points = new List<Vector3>();

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

		private readonly List<BoneWeight> weights = new List<BoneWeight>();

		private readonly List<Color> colors = new List<Color>();

		private readonly List<int>[] triangles = new List<int>[3]
		{
			new List<int>(),
			new List<int>(),
			new List<int>()
		};

		public Color[] Palette;

		public int Count => points.Count;

		public void Append(ModelTemplates model, SurfaceAnchor anchor, float scale, float angle)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0058: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0050: 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_00c5: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: 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_0196: 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_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			Palette = model.Palette;
			Vector3 normalized = ((Vector3)(ref anchor.Normal)).normalized;
			Vector3 val = Vector3.Cross(normalized, Vector3.up);
			Vector3 normalized2 = ((Vector3)(ref val)).normalized;
			if (((Vector3)(ref normalized2)).sqrMagnitude < 0.1f)
			{
				val = Vector3.Cross(normalized, Vector3.right);
				normalized2 = ((Vector3)(ref val)).normalized;
			}
			Vector3 val2 = Vector3.Cross(normalized, normalized2);
			Vector3 val3 = normalized2 * Mathf.Cos(angle) + val2 * Mathf.Sin(angle);
			Vector3 val4 = -normalized2 * Mathf.Sin(angle) + val2 * Mathf.Cos(angle);
			int count = points.Count;
			Vector3[] vertices = model.Vertices;
			foreach (Vector3 val5 in vertices)
			{
				points.Add(anchor.Position + normalized * 0.025f + (val3 * val5.x + val4 * val5.y + normalized * val5.z) * scale);
				weights.Add(anchor.Weight);
				colors.Add(anchor.Color);
			}
			vertices = model.Normals;
			foreach (Vector3 val6 in vertices)
			{
				List<Vector3> list = normals;
				val = val3 * val6.x + val4 * val6.y + normalized * val6.z;
				list.Add(((Vector3)(ref val)).normalized);
			}
			for (int j = 0; j < model.Triangles.Length; j++)
			{
				triangles[model.Materials[j / 3]].Add(count + model.Triangles[j]);
			}
		}

		public Mesh ToMesh(Matrix4x4[] bindposes)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			Mesh val = new Mesh
			{
				name = "TrailMarks_GrowthMesh",
				indexFormat = (IndexFormat)(points.Count > 65535)
			};
			val.SetVertices(points);
			val.subMeshCount = 3;
			for (int i = 0; i < 3; i++)
			{
				val.SetTriangles(triangles[i], i);
			}
			val.boneWeights = weights.ToArray();
			val.bindposes = bindposes;
			val.colors = colors.ToArray();
			val.uv = (Vector2[])(object)new Vector2[points.Count];
			val.normals = normals.ToArray();
			val.RecalculateBounds();
			return val;
		}
	}
}