Decompiled source of WristRewind v1.0.0

Mods/WristRewind.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
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.Data;
using Il2CppSLZ.Marrow.Input;
using Il2CppSLZ.Marrow.Interaction;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.PuppetMasta;
using Il2CppSLZ.Marrow.Utilities;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.XR;
using WristRewind;
using WristRewind.Core;
using WristRewind.Tracks;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(RewindMod), "WristRewind", "1.0.0", "MarrowByte", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MarrowByte")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Wrist controlled world rewind for BONELAB.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WristRewind")]
[assembly: AssemblyTitle("WristRewind")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 WristRewind
{
	internal static class ControllerInput
	{
		public static ControlInput Read(bool right, bool held)
		{
			//IL_003d: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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)
			XRApi xr = MarrowGame.xr;
			XRController val = ((xr == null) ? null : (right ? xr.RightController : xr.LeftController));
			if (val != null && ((XRDevice)val).IsConnected)
			{
				return ControlInput.Create(InputSource.Marrow, val.Grip, val.GripButton, ((XRDevice)val).IsTracking, Convert(((XRDevice)val).Rotation), held);
			}
			InputDevice deviceAtXRNode = InputDevices.GetDeviceAtXRNode((XRNode)(right ? 5 : 4));
			BaseController val2 = (right ? Player.RightController : Player.LeftController);
			float num = 0f;
			bool flag = false;
			float num2 = ((((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.grip, ref num)) ? num : (((Object)(object)val2 != (Object)null) ? val2.GetSecondaryInteractionButtonAxis() : 0f));
			bool flag2 = default(bool);
			bool button = ((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.gripButton, ref flag2) && flag2;
			bool flag3 = ((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.isTracked, ref flag);
			Quaternion value = default(Quaternion);
			if (((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.deviceRotation, ref value))
			{
				return ControlInput.Create(InputSource.UnityXR, num2, button, !flag3 || flag, Convert(value), held);
			}
			if (val == null && (Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy)
			{
				return ControlInput.Create(InputSource.Controller, MathF.Max(num2, val2.GetGripForce()), val2.GetSecondaryInteractionButton(), tracked: true, Convert(((Component)val2).transform.rotation), held);
			}
			return default(ControlInput);
		}

		private static Quaternion Convert(Quaternion value)
		{
			//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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Quaternion(value.x, value.y, value.z, value.w);
		}
	}
	internal readonly record struct BulletPose(Vector3 Position, Quaternion Rotation, Vector3 Direction, float Speed);
	internal sealed class Projectiles : IDisposable
	{
		private sealed class Shot
		{
			public readonly History<BulletPose> History = new History<BulletPose>(256);

			public Projectile? Projectile;

			public int Key;

			public double Born;

			public double End = double.PositiveInfinity;

			public LineRenderer? Line;

			public Material? Material;

			public BulletPose Live;

			public bool TrailEnabled;
		}

		private sealed class Observed
		{
			public Projectile Projectile;

			public readonly ProjectileLifetime Lifetime = new ProjectileLifetime();

			public Vector3 Position;

			public Vector3 TrailTip;

			public int TrailCount;

			public double Seen;
		}

		private sealed record FrozenProjectile(Projectile Projectile, bool Enabled);

		private readonly List<Shot> shots = new List<Shot>(512);

		private readonly Dictionary<int, Shot> active = new Dictionary<int, Shot>();

		private readonly List<Shot> retired = new List<Shot>();

		private readonly Dictionary<int, Observed> observed = new Dictionary<int, Observed>();

		private readonly List<int> missing = new List<int>();

		private readonly List<FrozenProjectile> frozenProjectiles = new List<FrozenProjectile>();

		private readonly Func<double> clock;

		private readonly Func<bool> scrubbing;

		private Material? fallback;

		private bool frozen;

		private double nextDiscovery;

		public int Count => shots.Count;

		public int Captured { get; private set; }

		public Projectiles(Func<double> clock, Func<bool> scrubbing)
		{
			this.clock = clock;
			this.scrubbing = scrubbing;
		}

		private static BulletPose Read(Projectile p)
		{
			//IL_0006: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			return new BulletPose(((Component)p).transform.position, ((Component)p).transform.rotation, p._direction, p.currentSpeed);
		}

		public void Poll()
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: 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_013f: 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)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: 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_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			if (frozen || scrubbing())
			{
				return;
			}
			double num = clock();
			if (num >= nextDiscovery)
			{
				nextDiscovery = num + 0.25;
				Discover();
			}
			missing.Clear();
			foreach (KeyValuePair<int, Observed> item in observed)
			{
				Observed value = item.Value;
				Projectile projectile = value.Projectile;
				if ((Object)(object)projectile == (Object)null)
				{
					if (active.Remove(item.Key, out Shot value2))
					{
						value2.End = num;
					}
					missing.Add(item.Key);
					continue;
				}
				bool flag = active.ContainsKey(item.Key);
				bool activeInHierarchy = ((Component)projectile).gameObject.activeInHierarchy;
				BulletPose bulletPose = Read(projectile);
				double num2 = Math.Clamp(num - value.Seen, 0.001, 0.1);
				float num3 = MathF.Max(1f, MathF.Abs(bulletPose.Speed)) * (float)num2 * 2f + 1f;
				Vector3 val;
				int num4;
				if (flag)
				{
					val = bulletPose.Position - value.Position;
					num4 = ((((Vector3)(ref val)).sqrMagnitude > num3 * num3) ? 1 : 0);
				}
				else
				{
					num4 = 0;
				}
				bool movedBackToEmitter = (byte)num4 != 0;
				bool flag2 = value.Lifetime.Observe(activeInHierarchy, projectile.isFinished, movedBackToEmitter, projectile._lastFrameUdate, num);
				if (flag2)
				{
					Spawn(projectile);
				}
				if (activeInHierarchy && !projectile.isFinished)
				{
					Moved(projectile);
				}
				else if (flag)
				{
					End(projectile);
				}
				TrailRenderer trail = projectile.trail;
				int num5 = (((Object)(object)trail != (Object)null) ? trail.positionCount : 0);
				if (num5 > 1)
				{
					Vector3 position = trail.GetPosition(0);
					Vector3 position2 = trail.GetPosition(num5 - 1);
					val = position - bulletPose.Position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					val = position2 - bulletPose.Position;
					Vector3 val2 = ((sqrMagnitude < ((Vector3)(ref val)).sqrMagnitude) ? position : position2);
					bool flag3 = num5 > value.TrailCount || val2 != value.TrailTip;
					if (!flag && !flag2 && projectile.isFinished && flag3)
					{
						ArchiveTrail(projectile, trail, num5);
					}
					value.TrailTip = val2;
				}
				value.TrailCount = num5;
				value.Position = bulletPose.Position;
				value.Seen = num;
			}
			foreach (int item2 in missing)
			{
				observed.Remove(item2);
			}
		}

		private void Discover()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			foreach (Projectile item in Object.FindObjectsOfType<Projectile>(true))
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				Scene scene = ((Component)item).gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					int instanceID = ((Object)item).GetInstanceID();
					if (!observed.ContainsKey(instanceID) && observed.Count < 1024)
					{
						observed.Add(instanceID, new Observed
						{
							Projectile = item,
							Position = ((Component)item).transform.position,
							Seen = clock()
						});
					}
				}
			}
		}

		private void ArchiveTrail(Projectile p, TrailRenderer trail, int length)
		{
			//IL_009c: 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_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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: 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_009a: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			if (shots.Count >= 512)
			{
				Remove(shots[0]);
			}
			int num = Math.Min(length, 128);
			double num2 = clock();
			double num3 = Math.Min(num2, Math.Clamp(trail.time, 0.001f, 0.12f));
			Shot shot = new Shot
			{
				Key = ((Object)p).GetInstanceID(),
				Born = num2 - num3,
				End = num2
			};
			if ((Object)(object)((Renderer)trail).sharedMaterial != (Object)null)
			{
				shot.Material = new Material(((Renderer)trail).sharedMaterial);
			}
			Vector3 val = trail.GetPosition(0) - ((Component)p).transform.position;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			val = trail.GetPosition(length - 1) - ((Component)p).transform.position;
			bool flag = sqrMagnitude < ((Vector3)(ref val)).sqrMagnitude;
			BulletPose bulletPose = Read(p);
			for (int i = 0; i < num; i++)
			{
				int num4 = (int)((long)i * (long)(length - 1) / (num - 1));
				if (flag)
				{
					num4 = length - 1 - num4;
				}
				shot.History.Add(shot.Born + num3 * (double)i / (double)(num - 1), bulletPose with
				{
					Position = trail.GetPosition(num4)
				});
			}
			shots.Add(shot);
			Captured++;
		}

		public void Spawn(Projectile projectile)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			if (!((Object)(object)projectile == (Object)null) && !scrubbing())
			{
				int instanceID = ((Object)projectile).GetInstanceID();
				if (active.Remove(instanceID, out Shot value))
				{
					value.End = clock();
				}
				if (shots.Count >= 512)
				{
					Remove(shots[0]);
				}
				Shot shot = new Shot
				{
					Projectile = projectile,
					Key = instanceID,
					Born = clock()
				};
				shot.History.Add(shot.Born, Read(projectile));
				if ((Object)(object)projectile.trail != (Object)null && (Object)(object)((Renderer)projectile.trail).sharedMaterial != (Object)null)
				{
					shot.Material = new Material(((Renderer)projectile.trail).sharedMaterial);
				}
				shots.Add(shot);
				active[instanceID] = shot;
				Captured++;
			}
		}

		public void Moved(Projectile projectile)
		{
			if (!((Object)(object)projectile == (Object)null) && !scrubbing() && active.TryGetValue(((Object)projectile).GetInstanceID(), out Shot value))
			{
				value.History.Add(Math.Max(clock(), value.Born + 1E-06), Read(projectile));
				if (projectile.isFinished || !((Component)projectile).gameObject.activeInHierarchy)
				{
					End(projectile);
				}
			}
		}

		public void End(Projectile projectile)
		{
			if (!((Object)(object)projectile == (Object)null) && !scrubbing() && active.Remove(((Object)projectile).GetInstanceID(), out Shot value))
			{
				double num = Math.Max(clock(), value.Born + 1E-06);
				value.History.Add(num, Read(projectile));
				value.End = num;
			}
		}

		public void Trim(double oldest)
		{
			retired.Clear();
			foreach (Shot shot in shots)
			{
				Shot value;
				if (shot.End < oldest)
				{
					retired.Add(shot);
				}
				else if (active.TryGetValue(shot.Key, out value) && value == shot && ((Object)(object)shot.Projectile == (Object)null || !((Component)shot.Projectile).gameObject.activeInHierarchy || shot.Projectile.isFinished))
				{
					shot.End = clock();
					active.Remove(shot.Key);
				}
			}
			foreach (Shot item in retired)
			{
				Remove(item);
			}
		}

		public void Freeze()
		{
			frozen = true;
			Discover();
			foreach (Observed value in observed.Values)
			{
				Projectile projectile = value.Projectile;
				if (!((Object)(object)projectile == (Object)null) && ((Component)projectile).gameObject.activeInHierarchy)
				{
					frozenProjectiles.Add(new FrozenProjectile(projectile, ((Behaviour)projectile).enabled));
					((Behaviour)projectile).enabled = false;
				}
			}
			foreach (Shot value2 in active.Values)
			{
				Projectile projectile2 = value2.Projectile;
				if (!((Object)(object)projectile2 == (Object)null))
				{
					value2.Live = Read(projectile2);
					value2.TrailEnabled = (Object)(object)projectile2.trail != (Object)null && ((Renderer)projectile2.trail).enabled;
					if ((Object)(object)projectile2.trail != (Object)null)
					{
						((Renderer)projectile2.trail).enabled = false;
					}
				}
			}
		}

		private LineRenderer View(Shot shot)
		{
			//IL_001a: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shot.Line != (Object)null)
			{
				return shot.Line;
			}
			LineRenderer val = (shot.Line = new GameObject("WristRewind bullet").AddComponent<LineRenderer>());
			val.useWorldSpace = true;
			((Renderer)val).sharedMaterial = shot.Material ?? Fallback();
			val.startWidth = 0.008f;
			val.endWidth = 0.002f;
			val.startColor = new Color(1f, 0.83f, 0.28f, 1f);
			val.endColor = new Color(1f, 0.42f, 0.1f, 0.1f);
			val.numCapVertices = 2;
			((Renderer)val).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val).receiveShadows = false;
			return val;
		}

		private Material Fallback()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_004b: Expected O, but got Unknown
			if ((Object)(object)fallback != (Object)null)
			{
				return fallback;
			}
			Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? throw new InvalidOperationException("No bullet trail shader was found.");
			Material val2 = new Material(val);
			Material result = val2;
			fallback = val2;
			return result;
		}

		public void Apply(double time)
		{
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			if (!frozen)
			{
				return;
			}
			foreach (FrozenProjectile frozenProjectile in frozenProjectiles)
			{
				if ((Object)(object)frozenProjectile.Projectile != (Object)null)
				{
					((Behaviour)frozenProjectile.Projectile).enabled = false;
				}
			}
			foreach (Shot shot in shots)
			{
				if (!(time >= shot.Born) || !(time <= shot.End) || !shot.History.Sample(time, out var _, out var _, out var _))
				{
					if ((Object)(object)shot.Line != (Object)null)
					{
						((Renderer)shot.Line).enabled = false;
					}
					continue;
				}
				LineRenderer val = View(shot);
				((Renderer)val).enabled = true;
				val.positionCount = 9;
				for (int i = 0; i < 9; i++)
				{
					double time2 = Math.Max(shot.Born, time - 0.035 + (double)i * 0.035 / 8.0);
					shot.History.Sample(time2, out var a2, out var b2, out var blend2);
					val.SetPosition(i, Vector3.LerpUnclamped(a2.Position, b2.Position, blend2));
				}
			}
		}

		public void Release(bool commit, double time)
		{
			//IL_012a: 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)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if (!frozen)
			{
				return;
			}
			foreach (Shot value in active.Values)
			{
				Projectile projectile = value.Projectile;
				if ((Object)(object)projectile == (Object)null)
				{
					continue;
				}
				BulletPose bulletPose = value.Live;
				if (commit)
				{
					if (time < value.Born || time > value.End || !value.History.Sample(time, out var a, out var b, out var blend))
					{
						projectile.isFinished = true;
						if ((Object)(object)projectile.trail != (Object)null)
						{
							projectile.trail.Clear();
							((Renderer)projectile.trail).enabled = value.TrailEnabled;
						}
						continue;
					}
					bulletPose = a with
					{
						Position = Vector3.LerpUnclamped(a.Position, b.Position, blend),
						Rotation = Quaternion.SlerpUnclamped(a.Rotation, b.Rotation, blend),
						Direction = Vector3.LerpUnclamped(a.Direction, b.Direction, blend),
						Speed = a.Speed + (b.Speed - a.Speed) * blend
					};
				}
				((Component)projectile).transform.SetPositionAndRotation(bulletPose.Position, bulletPose.Rotation);
				projectile._direction = bulletPose.Direction;
				projectile.currentSpeed = bulletPose.Speed;
				if ((Object)(object)projectile.trail != (Object)null)
				{
					projectile.trail.Clear();
					((Renderer)projectile.trail).enabled = value.TrailEnabled;
				}
			}
			retired.Clear();
			foreach (Shot shot in shots)
			{
				if ((Object)(object)shot.Line != (Object)null)
				{
					((Renderer)shot.Line).enabled = false;
				}
				if (!commit)
				{
					continue;
				}
				if (shot.Born > time)
				{
					retired.Add(shot);
					continue;
				}
				shot.History.CutAfter(time);
				if (shot.End > time && !active.ContainsKey(shot.Key))
				{
					shot.End = time;
				}
			}
			foreach (Shot item in retired)
			{
				Remove(item);
			}
			foreach (FrozenProjectile frozenProjectile in frozenProjectiles)
			{
				if ((Object)(object)frozenProjectile.Projectile != (Object)null)
				{
					((Behaviour)frozenProjectile.Projectile).enabled = frozenProjectile.Enabled;
				}
			}
			frozenProjectiles.Clear();
			foreach (Observed value2 in observed.Values)
			{
				value2.Seen = time;
				value2.Lifetime.Rebase(time);
			}
			nextDiscovery = 0.0;
			frozen = false;
		}

		private void Remove(Shot shot)
		{
			if (active.TryGetValue(shot.Key, out Shot value) && value == shot)
			{
				active.Remove(shot.Key);
			}
			shots.Remove(shot);
			shot.History.Clear();
			if ((Object)(object)shot.Line != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)shot.Line).gameObject);
			}
			if ((Object)(object)shot.Material != (Object)null)
			{
				Object.Destroy((Object)(object)shot.Material);
			}
		}

		public void Dispose()
		{
			Release(commit: false, 0.0);
			while (shots.Count > 0)
			{
				List<Shot> list = shots;
				Remove(list[list.Count - 1]);
			}
			observed.Clear();
			if ((Object)(object)fallback != (Object)null)
			{
				Object.Destroy((Object)(object)fallback);
			}
			fallback = null;
		}
	}
	public sealed class RewindMod : MelonMod
	{
		private Settings settings;

		private RewindWorld? world;

		private readonly LevelGate level = new LevelGate();

		private readonly WristDial dial = new WristDial();

		private readonly WristDisplay display = new WristDisplay();

		private readonly TimeEffects effects = new TimeEffects();

		private readonly TimeAudio audio = new TimeAudio();

		private bool held;

		private bool waitForRelease;

		private bool failed;

		private bool displayFailed;

		private float rate;

		private double menuAttempt;

		private Page? menuParent;

		private static double nextWarning;

		private ControlInput input;

		private InputSource activeInput;

		private bool keyboardHeld;

		private bool effectsFailed;

		private bool audioFailed;

		private float forwardTimer;

		private int forwardSamples;

		public override void OnInitializeMelon()
		{
			settings = new Settings();
			Hooking.OnLevelLoading += Loading;
			Hooking.OnLevelLoaded += Loaded;
			Hooking.OnLevelUnloaded += Unloaded;
			MelonLogger.Msg("[WristRewind] 1.0.0. Time sounds and wrist rings. Waiting for level loaded.");
		}

		public override void OnUpdate()
		{
			if (failed)
			{
				return;
			}
			try
			{
				try
				{
					Menu();
				}
				catch (Exception ex)
				{
					Warn("BoneMenu", ex);
				}
				if (!level.IsLoaded)
				{
					return;
				}
				bool flag = HelperMethods.IsLoading();
				RigManager rigManager = Player.RigManager;
				bool flag2 = !flag && (Object)(object)rigManager != (Object)null && (Object)(object)rigManager.physicsRig != (Object)null && (Object)(object)Player.Head != (Object)null && ((Component)rigManager).gameObject.activeInHierarchy && ((Component)Player.Head).gameObject.activeInHierarchy;
				if (!level.Ready(flag, flag2 ? ((Object)rigManager).GetInstanceID() : 0, Time.realtimeSinceStartupAsDouble))
				{
					if (world != null)
					{
						ResetWorld();
					}
					return;
				}
				if (world == null)
				{
					world = new RewindWorld(settings);
					MelonLogger.Msg("[WristRewind] Player rig stable. Starting scene discovery.");
				}
				float unscaledDeltaTime = Time.unscaledDeltaTime;
				world.Tick(unscaledDeltaTime);
				Keyboard current = Keyboard.current;
				if (current != null && ((ButtonControl)current.f9Key).wasPressedThisFrame)
				{
					world.End(commit: false);
					dial.Reset();
					waitForRelease = true;
				}
				input = ControllerInput.Read(settings.RightHand, held);
				bool flag3 = current != null && ((ButtonControl)current.f6Key).isPressed;
				if (flag3 && !keyboardHeld)
				{
					world.End(commit: false);
					held = (waitForRelease = false);
					dial.Reset();
				}
				else if (!flag3 && keyboardHeld)
				{
					world.End(commit: true);
					held = false;
					waitForRelease = input.Grip;
					dial.Reset();
					activeInput = InputSource.None;
					rate = 0f;
				}
				keyboardHeld = flag3;
				if (flag3)
				{
					input = ControlInput.Create(InputSource.Keyboard, 1f, button: true, tracked: true, Quaternion.Identity, held);
				}
				Quaternion rotation = input.Rotation;
				if (!input.Tracked)
				{
					if (world.Scrubbing)
					{
						world.End(commit: false);
					}
					if (held)
					{
						waitForRelease = true;
					}
					held |= input.Grip;
					dial.Reset();
					rate = 0f;
				}
				else if (!input.Grip)
				{
					if (held && world.Scrubbing)
					{
						MelonLogger.Msg($"[WristRewind] Time control released: -{world.Behind:0.00}s.");
						world.End(commit: true);
					}
					held = (waitForRelease = false);
					dial.Reset();
					rate = 0f;
				}
				else if (!settings.Enabled || waitForRelease)
				{
					if (world.Scrubbing)
					{
						world.End(commit: false);
					}
					held = true;
					rate = 0f;
				}
				else
				{
					if (!held)
					{
						held = true;
						activeInput = input.Source;
						if (!dial.Calibrate(rotation) || !world.Begin())
						{
							waitForRelease = true;
						}
						else
						{
							MelonLogger.Msg($"[WristRewind] Time control active: {input.Source}.");
						}
					}
					if (world.Scrubbing)
					{
						if (activeInput != input.Source)
						{
							dial.Calibrate(rotation);
							activeInput = input.Source;
						}
						if (input.Source == InputSource.Keyboard)
						{
							rate = -1f;
							world.Seek(rate, unscaledDeltaTime);
						}
						else if (!dial.Read(rotation, unscaledDeltaTime, settings.Deadzone, 70f, settings.Speed, settings.Invert, out rate))
						{
							world.End(commit: false);
							waitForRelease = true;
						}
						else
						{
							world.Seek(rate, unscaledDeltaTime);
						}
					}
				}
				if (current != null && ((ButtonControl)current.f8Key).wasPressedThisFrame)
				{
					PrintStatus();
				}
			}
			catch (Exception ex2)
			{
				Warn("Time control stopped", ex2);
				ResetWorld();
				failed = true;
			}
		}

		public override void OnLateUpdate()
		{
			if (world == null || failed)
			{
				return;
			}
			try
			{
				if (world.Scrubbing)
				{
					world.Apply();
				}
				else
				{
					world.Capture();
				}
				TraceForward();
				if (!audioFailed)
				{
					try
					{
						audio.Update(world.Scrubbing, world.PlaybackRate, settings.Sound, settings.SoundVolume, Time.unscaledDeltaTime);
					}
					catch (Exception ex)
					{
						Warn("Time audio", ex);
						audio.Dispose();
						audioFailed = true;
					}
				}
				if (!effectsFailed)
				{
					try
					{
						effects.Update(world.Scrubbing && settings.Magic, settings.RightHand, world.Behind, rate);
					}
					catch (Exception ex2)
					{
						Warn("Time effects", ex2);
						effects.Dispose();
						effectsFailed = true;
					}
				}
				if (!displayFailed)
				{
					try
					{
						display.Update(settings.Hud && world.Scrubbing, settings.RightHand, world.Behind, rate, world.AtPresent, world.AtOldest);
						return;
					}
					catch (Exception ex3)
					{
						Warn("Wrist display", ex3);
						display.Dispose();
						displayFailed = true;
						return;
					}
				}
			}
			catch (Exception ex4)
			{
				Warn("World playback stopped", ex4);
				ResetWorld();
				failed = true;
			}
		}

		private void PrintStatus()
		{
			world?.PrintStatus($"input={input.Source} grip={input.Grip} axis={input.Axis:0.00} tracked={input.Tracked} roll={dial.Roll:0.0} rate={rate:0.00} waitingForRelease={waitForRelease} enabled={settings.Enabled}");
		}

		private void TraceForward()
		{
			if (world == null || !world.Scrubbing || rate <= 0.02f)
			{
				forwardTimer = 0f;
				forwardSamples = 0;
				return;
			}
			forwardTimer += Math.Clamp(Time.unscaledDeltaTime, 0f, 0.1f);
			if (forwardSamples < 2 && !(forwardTimer < 0.2f + (float)forwardSamples))
			{
				forwardSamples++;
				MelonLogger.Msg($"[WristRewind] Forward playback sample {forwardSamples}.");
				PrintStatus();
			}
		}

		private void Menu()
		{
			//IL_003a: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: 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_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: 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_01e3: Unknown result type (might be due to invalid IL or missing references)
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			if (realtimeSinceStartupAsDouble < menuAttempt)
			{
				return;
			}
			menuAttempt = realtimeSinceStartupAsDouble + 3.0;
			Page root = Page.Root;
			if (root != null && root != menuParent)
			{
				Page obj = root.CreatePage("WristRewind", Color.cyan, 0, true);
				obj.CreateBool("Enabled", Color.green, settings.Enabled, (Action<bool>)delegate(bool value)
				{
					world?.End(commit: false);
					settings.Enabled = value;
					settings.Save();
					waitForRelease = true;
				});
				obj.CreateBool("Use right hand", Color.white, settings.RightHand, (Action<bool>)delegate(bool value)
				{
					world?.End(commit: false);
					settings.RightHand = value;
					settings.Save();
					waitForRelease = true;
					held = false;
					dial.Reset();
				});
				obj.CreateBool("Invert wrist twist", Color.white, settings.Invert, (Action<bool>)delegate(bool value)
				{
					settings.Invert = value;
					settings.Save();
				});
				obj.CreateBool("Wrist display", Color.white, settings.Hud, (Action<bool>)delegate(bool value)
				{
					settings.Hud = value;
					settings.Save();
				});
				obj.CreateBool("Wrist magic", Color.green, settings.Magic, (Action<bool>)delegate(bool value)
				{
					settings.Magic = value;
					settings.Save();
				});
				obj.CreateBool("Time sounds", Color.white, settings.Sound, (Action<bool>)delegate(bool value)
				{
					settings.Sound = value;
					settings.Save();
				});
				obj.CreateFloat("Time sound volume", Color.white, settings.SoundVolume, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
				{
					settings.SoundVolume = value;
					settings.Save();
				});
				obj.CreateFloat("Maximum speed", Color.white, settings.Speed, 0.25f, 0.25f, 12f, (Action<float>)delegate(float value)
				{
					settings.Speed = value;
					settings.Save();
				});
				obj.CreateFunction("Cancel rewind", Color.yellow, (Action)delegate
				{
					world?.End(commit: false);
					waitForRelease = true;
				});
				obj.CreateFunction("Clear history", Color.yellow, (Action)ResetWorld);
				obj.CreateFunction("Print status", Color.white, (Action)PrintStatus);
				menuParent = root;
			}
		}

		private void Loading(LevelInfo info)
		{
			level.Reset();
			ResetWorld();
			failed = false;
		}

		private void Loaded(LevelInfo info)
		{
			ResetWorld();
			failed = false;
			level.Loaded();
			MelonLogger.Msg("[WristRewind] Level loaded. Waiting for a stable player rig.");
		}

		private void Unloaded()
		{
			level.Reset();
			ResetWorld();
		}

		private void ResetWorld()
		{
			try
			{
				world?.Dispose();
			}
			catch (Exception ex)
			{
				Warn("World cleanup", ex);
			}
			world = null;
			display.Dispose();
			effects.Dispose();
			audio.Dispose();
			dial.Reset();
			held = false;
			waitForRelease = true;
			rate = 0f;
			displayFailed = false;
			input = default(ControlInput);
			activeInput = InputSource.None;
			keyboardHeld = false;
			effectsFailed = false;
			audioFailed = false;
			forwardTimer = 0f;
			forwardSamples = 0;
		}

		internal static void Warn(string context, Exception ex)
		{
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			if (!(realtimeSinceStartupAsDouble < nextWarning))
			{
				nextWarning = realtimeSinceStartupAsDouble + 5.0;
				MelonLogger.Warning($"[WristRewind] {context}: {ex.GetType().Name}: {ex.Message}");
			}
		}

		public override void OnDeinitializeMelon()
		{
			Hooking.OnLevelLoading -= Loading;
			Hooking.OnLevelLoaded -= Loaded;
			Hooking.OnLevelUnloaded -= Unloaded;
			ResetWorld();
		}
	}
	internal sealed class RewindWorld : IDisposable
	{
		private readonly Settings settings;

		private readonly TimeWindow window;

		private readonly Dictionary<(int Kind, int Id), ITrack> tracks = new Dictionary<(int, int), ITrack>();

		private readonly int[] counts = new int[9];

		private readonly HashSet<ITrack> skipped = new HashSet<ITrack>();

		private readonly HashSet<int> heldBodies = new HashSet<int>();

		private readonly List<Transform> heldRoots = new List<Transform>();

		private readonly HashSet<int> marrowBodies = new HashSet<int>();

		private readonly List<(int Kind, int Id)> stale = new List<(int, int)>();

		private readonly List<ParticleSystem> pausedParticles = new List<ParticleSystem>();

		private readonly List<ITrack> releaseOrder = new List<ITrack>();

		private double nextScan;

		private double nextSample;

		private readonly HashSet<int> warned = new HashSet<int>();

		private static readonly string[] categories = new string[9] { "physics bodies", "animation nodes", "NPCs", "animators", "guns", "particle effects", "trails", "lines", "audio sources" };

		private bool discovered;

		private readonly int capacity;

		public bool Scrubbing => window.Scrubbing;

		public double Now => window.Now;

		public double Cursor => window.Cursor;

		public double Oldest => window.Oldest;

		public double Behind => Math.Max(0.0, Now - Cursor);

		public float PlaybackRate => window.PlaybackRate;

		public bool AtPresent => Behind < 0.001;

		public bool AtOldest => Cursor - Oldest < 0.001;

		public int Count => tracks.Count;

		public Projectiles Bullets { get; }

		public RewindWorld(Settings settings)
		{
			this.settings = settings;
			window = new TimeWindow(settings.Seconds);
			capacity = (int)Math.Ceiling(settings.Seconds * 30f) + 4;
			Bullets = new Projectiles(() => Now, () => Scrubbing);
		}

		public void Tick(float dt)
		{
			if (!Scrubbing)
			{
				window.Advance(dt);
				if (Now >= nextScan)
				{
					nextScan = Now + 0.5;
					Discover();
				}
			}
		}

		public void Capture()
		{
			if (Scrubbing)
			{
				return;
			}
			Bullets.Poll();
			if (Now < nextSample)
			{
				return;
			}
			nextSample = Now + 1.0 / 30.0;
			foreach (ITrack value in tracks.Values)
			{
				try
				{
					value.Capture(Now, Oldest);
				}
				catch (Exception ex)
				{
					RewindMod.Warn(value.GetType().Name, ex);
				}
			}
			Bullets.Trim(Oldest);
		}

		private bool Eligible(Component component)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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)
			if ((Object)(object)component == (Object)null || !component.gameObject.activeInHierarchy || (Object)(object)Player.Head == (Object)null)
			{
				return false;
			}
			if ((Object)(object)component.GetComponentInParent<RigManager>() != (Object)null)
			{
				return false;
			}
			if (((Object)component.gameObject).name.StartsWith("WristRewind", StringComparison.Ordinal))
			{
				return false;
			}
			Vector3 val = component.transform.position - Player.Head.position;
			return ((Vector3)(ref val)).sqrMagnitude <= settings.Radius * settings.Radius;
		}

		private bool Add(int kind, Component component, int limit, Func<ITrack> create)
		{
			(int, int) key = (kind, ((Object)component).GetInstanceID());
			if (tracks.ContainsKey(key))
			{
				return false;
			}
			if (counts[kind] >= limit)
			{
				if (warned.Add(kind))
				{
					MelonLogger.Warning($"[WristRewind] Recording limit: {categories[kind]} {counts[kind]}/{limit}. Other recording remains active.");
				}
				return false;
			}
			ITrack track = create();
			tracks.Add(key, track);
			counts[kind]++;
			Safe(track, delegate
			{
				track.Capture(Now, Oldest);
			});
			return true;
		}

		private void Node(Transform node)
		{
			if (!((Object)(object)node == (Object)null) && !((Object)(object)((Component)node).GetComponent<Rigidbody>() != (Object)null) && Eligible((Component)(object)node))
			{
				Add(1, (Component)(object)node, 1536, () => new NodeTrack(node, capacity));
			}
		}

		public void Discover()
		{
			Phase("cleanup");
			stale.Clear();
			foreach (KeyValuePair<(int, int), ITrack> track in tracks)
			{
				if (!track.Value.Alive || !Eligible(track.Value.Source))
				{
					stale.Add(track.Key);
				}
			}
			foreach (var item in stale)
			{
				Safe(tracks[item], tracks[item].Dispose);
				tracks.Remove(item);
				counts[item.Kind]--;
			}
			Phase("physics");
			marrowBodies.Clear();
			foreach (MarrowBody body in Object.FindObjectsOfType<MarrowBody>())
			{
				if (Eligible((Component)(object)body))
				{
					if ((Object)(object)body._rigidbody != (Object)null)
					{
						marrowBodies.Add(((Object)body._rigidbody).GetInstanceID());
					}
					Add(0, (Component)(object)body, 768, () => new BodyTrack(body, capacity));
				}
			}
			foreach (Rigidbody body2 in Object.FindObjectsOfType<Rigidbody>())
			{
				if (Eligible((Component)(object)body2) && !marrowBodies.Contains(((Object)body2).GetInstanceID()))
				{
					Add(0, (Component)(object)body2, 768, () => new BodyTrack(body2, capacity));
				}
			}
			Phase("NPCs");
			foreach (AIBrain npc in Object.FindObjectsOfType<AIBrain>())
			{
				if (Eligible((Component)(object)npc))
				{
					Add(2, (Component)(object)npc, 48, () => new NpcTrack(npc, capacity));
					Node(((Component)npc).transform);
					if ((Object)(object)npc.puppetMaster != (Object)null && (Object)(object)npc.puppetMaster.targetRoot != (Object)null)
					{
						Node(npc.puppetMaster.targetRoot);
					}
				}
			}
			Phase("animation");
			foreach (Animator animator in Object.FindObjectsOfType<Animator>())
			{
				if (!Eligible((Component)(object)animator))
				{
					continue;
				}
				Add(3, (Component)(object)animator, 64, () => new AnimatorTrack(animator, capacity));
				foreach (Transform componentsInChild in ((Component)animator).GetComponentsInChildren<Transform>(true))
				{
					Node(componentsInChild);
				}
			}
			Phase("guns");
			foreach (Gun gun in Object.FindObjectsOfType<Gun>())
			{
				if (Eligible((Component)(object)gun))
				{
					Add(4, (Component)(object)gun, 64, () => new GunTrack(gun, capacity));
				}
			}
			Phase("particles");
			foreach (ParticleSystem system in Object.FindObjectsOfType<ParticleSystem>())
			{
				bool flag = !Eligible((Component)(object)system);
				if (!flag)
				{
					int particleCount = system.particleCount;
					bool flag2 = ((particleCount <= 0 || particleCount > 128) ? true : false);
					flag = flag2;
				}
				if (!flag)
				{
					Add(5, (Component)(object)system, 16, () => new ParticleTrack(system, capacity));
					Node(((Component)system).transform);
				}
			}
			Phase("trails");
			foreach (TrailRenderer trail in Object.FindObjectsOfType<TrailRenderer>())
			{
				if (Eligible((Component)(object)trail) && !((Object)(object)((Component)trail).GetComponentInParent<Projectile>() != (Object)null))
				{
					Add(6, (Component)(object)trail, 48, () => new TrailTrack(trail, capacity));
				}
			}
			foreach (LineRenderer line in Object.FindObjectsOfType<LineRenderer>())
			{
				if (Eligible((Component)(object)line))
				{
					Add(7, (Component)(object)line, 32, () => new TrailTrack(line, capacity));
				}
			}
			Phase("audio");
			foreach (AudioSource source in Object.FindObjectsOfType<AudioSource>())
			{
				if (Eligible((Component)(object)source))
				{
					Add(8, (Component)(object)source, 96, () => new AudioTrack(source, capacity));
				}
			}
			if (!discovered)
			{
				MelonLogger.Msg("[WristRewind] Scene discovery complete. Recording active.");
				discovered = true;
			}
		}

		private void Phase(string name)
		{
			if (!discovered)
			{
				MelonLogger.Msg("[WristRewind] Discovery: " + name + ".");
			}
		}

		public bool Begin()
		{
			if (Scrubbing || Now < 0.15)
			{
				return false;
			}
			nextSample = 0.0;
			Capture();
			if (!window.Begin())
			{
				return false;
			}
			heldBodies.Clear();
			heldRoots.Clear();
			RememberHeld(Player.LeftHand);
			RememberHeld(Player.RightHand);
			skipped.Clear();
			foreach (ITrack value in tracks.Values)
			{
				BodyTrack body = value as BodyTrack;
				if (body != null && (((Object)(object)body.Body != (Object)null && heldBodies.Contains(((Object)body.Body).GetInstanceID())) || heldRoots.Any((Transform root) => (Object)(object)body.Root == (Object)(object)root || body.Root.IsChildOf(root))))
				{
					skipped.Add(value);
				}
				else
				{
					Safe(value, value.Freeze);
				}
			}
			foreach (ParticleSystem item in Object.FindObjectsOfType<ParticleSystem>())
			{
				if (Eligible((Component)(object)item) && !tracks.ContainsKey((5, ((Object)item).GetInstanceID())) && item.isPlaying)
				{
					pausedParticles.Add(item);
					item.Pause(false);
				}
			}
			Bullets.Freeze();
			return true;
		}

		private void RememberHeld(Hand hand)
		{
			if ((Object)(object)hand == (Object)null)
			{
				return;
			}
			GameObject objectInHand = Player.GetObjectInHand(hand);
			if ((Object)(object)objectInHand == (Object)null)
			{
				return;
			}
			InteractableHost componentInParent = objectInHand.GetComponentInParent<InteractableHost>();
			GameObject val = (((Object)(object)componentInParent != (Object)null) ? componentInParent.GetHostGameObject() : objectInHand);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			heldRoots.Add(val.transform);
			foreach (Rigidbody componentsInChild in val.GetComponentsInChildren<Rigidbody>(true))
			{
				heldBodies.Add(((Object)componentsInChild).GetInstanceID());
			}
			Rigidbody componentInParent2 = objectInHand.GetComponentInParent<Rigidbody>();
			if ((Object)(object)componentInParent2 != (Object)null)
			{
				heldBodies.Add(((Object)componentInParent2).GetInstanceID());
			}
		}

		public void Seek(float speed, float dt)
		{
			if (Scrubbing)
			{
				window.Seek(speed, dt);
			}
		}

		public void Apply()
		{
			if (!Scrubbing)
			{
				return;
			}
			foreach (ITrack value in tracks.Values)
			{
				if (!(value is BodyTrack) && !skipped.Contains(value))
				{
					try
					{
						value.Apply(Cursor);
					}
					catch (Exception ex)
					{
						RewindMod.Warn(value.GetType().Name, ex);
					}
				}
			}
			foreach (ITrack value2 in tracks.Values)
			{
				if (value2 is BodyTrack && !skipped.Contains(value2))
				{
					try
					{
						value2.Apply(Cursor);
					}
					catch (Exception ex2)
					{
						RewindMod.Warn(value2.GetType().Name, ex2);
					}
				}
			}
			Physics.SyncTransforms();
			Bullets.Apply(Cursor);
		}

		public void End(bool commit)
		{
			if (!Scrubbing)
			{
				return;
			}
			releaseOrder.Clear();
			releaseOrder.AddRange(tracks.Values);
			releaseOrder.Sort((ITrack a, ITrack b) => Rank(a).CompareTo(Rank(b)));
			foreach (ITrack track in releaseOrder)
			{
				if (!skipped.Contains(track) && track is BodyTrack && commit)
				{
					Safe(track, delegate
					{
						track.Apply(Cursor);
					});
				}
				Safe(track, delegate
				{
					track.Release(commit, Cursor);
				});
			}
			foreach (ParticleSystem pausedParticle in pausedParticles)
			{
				if ((Object)(object)pausedParticle != (Object)null)
				{
					pausedParticle.Play(false);
				}
			}
			pausedParticles.Clear();
			Bullets.Release(commit, Cursor);
			window.End(commit);
			if (commit)
			{
				nextSample = (nextScan = Now);
			}
			skipped.Clear();
			Physics.SyncTransforms();
		}

		private static int Rank(ITrack track)
		{
			if (!(track is NpcTrack))
			{
				if (!(track is AnimatorTrack))
				{
					if (!(track is BodyTrack))
					{
						return 2;
					}
					return 3;
				}
				return 1;
			}
			return 0;
		}

		private static void Safe(ITrack track, Action action)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				RewindMod.Warn(track.GetType().Name, ex);
			}
		}

		public void PrintStatus(string input = "")
		{
			BodyTrack[] source = tracks.Values.OfType<BodyTrack>().ToArray();
			MelonLogger.Msg($"[WristRewind] history={settings.Seconds:0}s objects={Count} bodies={counts[0]} recordedBodies={source.Count((BodyTrack t) => t.SampleCount > 1)} movingBodies={source.Count((BodyTrack t) => t.DistanceRecorded > 0.05f)} appliedBodies={source.Count((BodyTrack t) => t.Applied)} nodes={counts[1]} particles={counts[5]} trails={counts[6] + counts[7]} bulletHistory={Bullets.Count} capturedBullets={Bullets.Captured} scrubbing={Scrubbing} behind={Behind:0.00}s cursor={Cursor:0.00} now={Now:0.00} playbackRate={PlaybackRate:0.00} atPresent={AtPresent} {input}");
			foreach (BodyTrack item in source.Where((BodyTrack t) => ((Object)((Component)t.Root).gameObject).name.Contains("cone", StringComparison.OrdinalIgnoreCase)).Take(4))
			{
				MelonLogger.Msg("[WristRewind] " + item.Describe(skipped.Contains(item)));
			}
			foreach (BodyTrack item2 in (from t in source
				where !((Object)((Component)t.Root).gameObject).name.Contains("cone", StringComparison.OrdinalIgnoreCase)
				orderby t.DistanceRecorded descending
				select t).Take(2))
			{
				MelonLogger.Msg("[WristRewind] " + item2.Describe(skipped.Contains(item2)));
			}
		}

		public void Dispose()
		{
			End(commit: false);
			foreach (ITrack value in tracks.Values)
			{
				Safe(value, value.Dispose);
			}
			tracks.Clear();
			Bullets.Dispose();
		}
	}
	internal sealed class Settings
	{
		private readonly MelonPreferences_Category category;

		private readonly MelonPreferences_Entry<bool> enabled;

		private readonly MelonPreferences_Entry<bool> rightHand;

		private readonly MelonPreferences_Entry<bool> invert;

		private readonly MelonPreferences_Entry<bool> hud;

		private readonly MelonPreferences_Entry<float> seconds;

		private readonly MelonPreferences_Entry<float> radius;

		private readonly MelonPreferences_Entry<float> speed;

		private readonly MelonPreferences_Entry<float> deadzone;

		private readonly MelonPreferences_Entry<bool> magic;

		private readonly MelonPreferences_Entry<bool> sound;

		private readonly MelonPreferences_Entry<float> soundVolume;

		public bool Enabled
		{
			get
			{
				return enabled.Value;
			}
			set
			{
				enabled.Value = value;
			}
		}

		public bool RightHand
		{
			get
			{
				return rightHand.Value;
			}
			set
			{
				rightHand.Value = value;
			}
		}

		public bool Invert
		{
			get
			{
				return invert.Value;
			}
			set
			{
				invert.Value = value;
			}
		}

		public bool Hud
		{
			get
			{
				return hud.Value;
			}
			set
			{
				hud.Value = value;
			}
		}

		public float Seconds => Clamp(seconds.Value, 2f, 20f, 10f);

		public float Radius => Clamp(radius.Value, 10f, 250f, 100f);

		public float Speed
		{
			get
			{
				return Clamp(speed.Value, 0.25f, 12f, 4f);
			}
			set
			{
				speed.Value = value;
			}
		}

		public float Deadzone => Clamp(deadzone.Value, 2f, 25f, 8f);

		public bool Magic
		{
			get
			{
				return magic.Value;
			}
			set
			{
				magic.Value = value;
			}
		}

		public bool Sound
		{
			get
			{
				return sound.Value;
			}
			set
			{
				sound.Value = value;
			}
		}

		public float SoundVolume
		{
			get
			{
				return Clamp(soundVolume.Value, 0f, 1f, 0.35f);
			}
			set
			{
				soundVolume.Value = value;
			}
		}

		public Settings()
		{
			category = MelonPreferences.CreateCategory("WristRewind");
			enabled = category.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			rightHand = category.CreateEntry<bool>("RightHand", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			invert = category.CreateEntry<bool>("InvertTwist", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			hud = category.CreateEntry<bool>("ShowWristDisplay", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			seconds = category.CreateEntry<float>("HistorySeconds", 10f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			radius = category.CreateEntry<float>("RecordRadius", 100f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			speed = category.CreateEntry<float>("MaximumSpeed", 4f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			deadzone = category.CreateEntry<float>("DeadzoneDegrees", 8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			magic = category.CreateEntry<bool>("WristMagic", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			sound = category.CreateEntry<bool>("TimeSounds", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			soundVolume = category.CreateEntry<float>("TimeSoundVolume", 0.35f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		}

		public void Save()
		{
			category.SaveToFile(false);
		}

		private static float Clamp(float value, float min, float max, float fallback)
		{
			if (!float.IsFinite(value))
			{
				return fallback;
			}
			return Math.Clamp(value, min, max);
		}
	}
	internal sealed class TimeAudio : IDisposable
	{
		private readonly List<AudioClip> clips = new List<AudioClip>();

		private GameObject? root;

		private AudioSource? rewind;

		private AudioSource? forward;

		private AudioSource? cues;

		private AudioClip? start;

		private AudioClip? stop;

		private bool wasActive;

		private float rewindGain;

		private float forwardGain;

		public void Update(bool active, float rate, bool enabled, float volume, float dt)
		{
			volume = (float.IsFinite(volume) ? Math.Clamp(volume, 0f, 1f) : 0f);
			if (!enabled || volume == 0f)
			{
				Silence();
				return;
			}
			if ((Object)(object)root == (Object)null)
			{
				if (!active)
				{
					return;
				}
				Create();
			}
			dt = (float.IsFinite(dt) ? Math.Clamp(dt, 0f, 0.1f) : 0f);
			rate = (float.IsFinite(rate) ? rate : 0f);
			cues.volume = volume;
			if (active && !wasActive)
			{
				cues.PlayOneShot(start, 0.8f);
			}
			else if (!active && wasActive)
			{
				cues.PlayOneShot(stop, 0.8f);
			}
			wasActive = active;
			float num = 1f - MathF.Exp((0f - dt) / 0.09f);
			rewindGain += ((float)((active && rate < -0.02f) ? 1 : 0) - rewindGain) * num;
			forwardGain += ((float)((active && rate > 0.02f) ? 1 : 0) - forwardGain) * num;
			float num2 = Math.Clamp(MathF.Abs(rate), 0f, 4f);
			Loop(rewind, rewindGain * volume, 0.8f + num2 * 0.08f);
			Loop(forward, forwardGain * volume, 0.95f + num2 * 0.08f);
		}

		private static void Loop(AudioSource source, float volume, float pitch)
		{
			source.pitch = pitch;
			source.volume = ((volume < 0.0005f) ? 0f : volume);
			if (source.volume == 0f)
			{
				if (source.isPlaying)
				{
					source.Stop();
				}
			}
			else if (!source.isPlaying)
			{
				source.Play();
			}
		}

		private AudioClip Load(string name)
		{
			using Stream stream = typeof(TimeAudio).Assembly.GetManifestResourceStream("WristRewind.Audio." + name + ".wav") ?? throw new FileNotFoundException("Missing time audio: " + name + ".");
			WaveData waveData = WaveData.Read(stream);
			AudioClip val = AudioClip.Create("WristRewind " + name, waveData.Frames, waveData.Channels, waveData.Rate, false);
			clips.Add(val);
			if (!val.SetData(Il2CppStructArray<float>.op_Implicit(waveData.Samples), 0))
			{
				throw new InvalidOperationException("Time audio could not be loaded.");
			}
			return val;
		}

		private AudioSource Source(AudioClip? clip, bool loop)
		{
			AudioSource obj = root.AddComponent<AudioSource>();
			obj.playOnAwake = false;
			obj.loop = loop;
			obj.clip = clip;
			obj.volume = 0f;
			obj.spatialBlend = 0f;
			obj.ignoreListenerPause = true;
			obj.bypassListenerEffects = true;
			obj.bypassReverbZones = true;
			obj.priority = 64;
			return obj;
		}

		private void Create()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			root = new GameObject("WristRewind time audio");
			rewind = Source(Load("rewind"), loop: true);
			forward = Source(Load("forward"), loop: true);
			start = Load("start");
			stop = Load("stop");
			cues = Source(null, loop: false);
		}

		private void Silence()
		{
			if ((Object)(object)rewind != (Object)null)
			{
				rewind.Stop();
				rewind.volume = 0f;
			}
			if ((Object)(object)forward != (Object)null)
			{
				forward.Stop();
				forward.volume = 0f;
			}
			if ((Object)(object)cues != (Object)null)
			{
				cues.Stop();
				cues.volume = 0f;
			}
			wasActive = false;
			rewindGain = (forwardGain = 0f);
		}

		public void Dispose()
		{
			Silence();
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
			foreach (AudioClip clip in clips)
			{
				Object.Destroy((Object)(object)clip);
			}
			clips.Clear();
			root = null;
			rewind = (forward = (cues = null));
			start = (stop = null);
		}
	}
	internal sealed class TimeEffects : IDisposable
	{
		private GameObject? wrist;

		private Transform? outer;

		private Transform? glyphs;

		private LineRenderer? hand;

		private LineRenderer? sweep;

		private Material? ink;

		private float phase;

		private float fade;

		public void Update(bool active, bool right, double behind, float speed)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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_0150: 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_01bf: 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_021f: 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_0276: Unknown result type (might be due to invalid IL or missing references)
			float num = Math.Clamp(Time.unscaledDeltaTime, 0f, 0.1f);
			fade = Mathf.MoveTowards(fade, (float)(active ? 1 : 0), num * 6f);
			if (fade <= 0f)
			{
				if ((Object)(object)wrist != (Object)null)
				{
					wrist.SetActive(false);
				}
				return;
			}
			Hand val = (right ? Player.RightHand : Player.LeftHand);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)Player.Head == (Object)null))
			{
				if ((Object)(object)wrist == (Object)null)
				{
					CreateWrist();
				}
				wrist.SetActive(true);
				wrist.transform.SetPositionAndRotation(((Component)val).transform.position + ((Component)val).transform.forward * 0.13f, ((Component)val).transform.rotation);
				wrist.transform.localScale = Vector3.one * (0.8f + fade * 0.2f);
				phase += num * ((MathF.Abs(speed) < 0.02f) ? 12f : ((0f - speed) * 55f));
				outer.localRotation = Quaternion.Euler(0f, 0f, phase);
				glyphs.localRotation = Quaternion.Euler(0f, 0f, (0f - phase) * 0.55f);
				float num2 = 0.88f + 0.12f * MathF.Sin(Time.unscaledTime * 3.5f);
				ink.color = new Color(0.22f * num2, 1.5f * num2, 0.48f * num2, fade);
				float x = (float)(behind * Math.PI * 2.0 / 10.0);
				hand.SetPosition(0, Vector3.zero);
				hand.SetPosition(1, new Vector3(MathF.Sin(x) * 0.055f, MathF.Cos(x) * 0.055f, 0f));
				sweep.SetPosition(0, Vector3.zero);
				sweep.SetPosition(1, new Vector3(MathF.Sin((0f - phase) * ((float)Math.PI / 180f)) * 0.036f, MathF.Cos((0f - phase) * ((float)Math.PI / 180f)) * 0.036f, 0f));
			}
		}

		private Material Material()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			Material val = new Material(Shader.Find("Sprites/Default") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? throw new InvalidOperationException("No time effect shader was found."))
			{
				renderQueue = 3100
			};
			if (val.HasProperty("_ZWrite"))
			{
				val.SetInt("_ZWrite", 0);
			}
			if (val.HasProperty("_Cull"))
			{
				val.SetInt("_Cull", 0);
			}
			if (val.HasProperty("_Surface"))
			{
				val.SetFloat("_Surface", 1f);
			}
			if (val.HasProperty("_SrcBlend"))
			{
				val.SetInt("_SrcBlend", 5);
			}
			if (val.HasProperty("_DstBlend"))
			{
				val.SetInt("_DstBlend", 10);
			}
			val.SetOverrideTag("RenderType", "Transparent");
			val.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
			return val;
		}

		private static GameObject Child(Transform parent, string name)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			GameObject val = new GameObject("WristRewind " + name);
			val.transform.SetParent(parent, false);
			return val;
		}

		private LineRenderer Line(Transform parent, float width, Color color, params Vector3[] points)
		{
			//IL_002d: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			LineRenderer val = Child(parent, "sigil").AddComponent<LineRenderer>();
			val.useWorldSpace = false;
			((Renderer)val).sharedMaterial = ink;
			val.widthMultiplier = width;
			Color startColor = (val.endColor = color);
			val.startColor = startColor;
			val.numCapVertices = 2;
			val.numCornerVertices = 2;
			((Renderer)val).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val).receiveShadows = false;
			val.positionCount = points.Length;
			for (int i = 0; i < points.Length; i++)
			{
				val.SetPosition(i, points[i]);
			}
			return val;
		}

		private void Arc(Transform parent, float radius, float from, float to, float width, Color color, float z = 0f)
		{
			//IL_004e: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Max(3, (int)(MathF.Abs(to - from) / 4f) + 1);
			Vector3[] array = (Vector3[])(object)new Vector3[num];
			for (int i = 0; i < num; i++)
			{
				float x = Mathf.Lerp(from, to, (float)i / (float)(num - 1)) * ((float)Math.PI / 180f);
				array[i] = new Vector3(MathF.Sin(x) * radius, MathF.Cos(x) * radius, z);
			}
			Line(parent, width, color, array);
		}

		private void CreateWrist()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: 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_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: 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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: 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_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_0444: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0452: Unknown result type (might be due to invalid IL or missing references)
			//IL_0459: 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_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_0489: 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_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ad: Unknown result type (might be due to invalid IL or missing references)
			ink = Material();
			wrist = new GameObject("WristRewind time sigil");
			outer = Child(wrist.transform, "outer ring").transform;
			glyphs = Child(wrist.transform, "runes").transform;
			Color color = default(Color);
			((Color)(ref color))..ctor(0.55f, 1f, 0.65f, 0.95f);
			Color color2 = default(Color);
			((Color)(ref color2))..ctor(0.2f, 1f, 0.5f, 0.45f);
			Arc(wrist.transform, 0.061f, 0f, 360f, 0.0018f, color);
			Arc(wrist.transform, 0.067f, 0f, 360f, 0.0006f, color2);
			Arc(outer, 0.099f, 0f, 360f, 0.0018f, color);
			Arc(outer, 0.106f, 0f, 360f, 0.0007f, color2);
			Arc(outer, 0.117f, 0f, 360f, 0.0012f, color);
			for (int i = 0; i < 48; i++)
			{
				float x = (float)i * 7.5f * ((float)Math.PI / 180f);
				float num = ((i % 4 == 0) ? 0.107f : 0.112f);
				Line(outer, 0.0009f, color, new Vector3(MathF.Sin(x) * num, MathF.Cos(x) * num, 0f), new Vector3(MathF.Sin(x) * 0.116f, MathF.Cos(x) * 0.116f, 0f));
			}
			for (int j = 0; j < 12; j++)
			{
				Transform transform = Child(glyphs, "rune").transform;
				transform.localRotation = Quaternion.Euler(0f, 0f, (float)(j * 30));
				float num2 = ((j % 2 == 0) ? 1 : (-1));
				Line(transform, 0.0011f, color, new Vector3(-0.005f, 0.075f, 0f), new Vector3(0.004f, 0.084f, 0f), new Vector3(-0.003f, 0.092f, 0f), new Vector3(0.005f, 0.089f, 0f));
				Line(transform, 0.0007f, color, new Vector3(-0.006f * num2, 0.087f, 0f), new Vector3(0.006f * num2, 0.08f, 0f));
			}
			for (int k = 0; k < 2; k++)
			{
				Transform transform2 = Child(wrist.transform, "geometry").transform;
				transform2.localRotation = Quaternion.Euler(0f, 0f, (float)(k * 45));
				Line(transform2, 0.0009f, color2, new Vector3(-0.043f, -0.043f, 0f), new Vector3(0.043f, -0.043f, 0f), new Vector3(0.043f, 0.043f, 0f), new Vector3(-0.043f, 0.043f, 0f), new Vector3(-0.043f, -0.043f, 0f));
			}
			Arc(wrist.transform, 0.009f, 0f, 360f, 0.0012f, color);
			hand = Line(wrist.transform, 0.0014f, color, Vector3.zero, Vector3.up * 0.055f);
			sweep = Line(wrist.transform, 0.0008f, color2, Vector3.zero, Vector3.up * 0.036f);
		}

		public void Dispose()
		{
			if ((Object)(object)wrist != (Object)null)
			{
				Object.Destroy((Object)(object)wrist);
			}
			if ((Object)(object)ink != (Object)null)
			{
				Object.Destroy((Object)(object)ink);
			}
			wrist = null;
			ink = null;
			hand = (sweep = null);
			outer = (glyphs = null);
			fade = (phase = 0f);
		}
	}
	internal sealed class WristDisplay : IDisposable
	{
		private TextMesh? label;

		public void Update(bool visible, bool right, double behind, float speed, bool atPresent, bool atOldest)
		{
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: 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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: 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_014d: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			if (!visible)
			{
				if ((Object)(object)label != (Object)null)
				{
					((Component)label).gameObject.SetActive(false);
				}
				return;
			}
			Hand val = (right ? Player.RightHand : Player.LeftHand);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)Player.Head == (Object)null))
			{
				if ((Object)(object)label == (Object)null)
				{
					GameObject val2 = new GameObject("WristRewind display");
					label = val2.AddComponent<TextMesh>();
					label.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
					((Renderer)((Component)label).GetComponent<MeshRenderer>()).sharedMaterial = label.font.material;
					label.fontSize = 44;
					label.characterSize = 0.002f;
					label.anchor = (TextAnchor)4;
					label.alignment = (TextAlignment)1;
					label.richText = false;
				}
				((Component)label).gameObject.SetActive(true);
				((Component)label).transform.position = ((Component)val).transform.position + Vector3.up * 0.12f;
				((Component)label).transform.rotation = Quaternion.LookRotation(((Component)label).transform.position - Player.Head.position, Player.Head.up);
				string value = ((speed > 0.02f && atPresent) ? "PRESENT" : ((speed < -0.02f && atOldest) ? "HISTORY LIMIT" : ((MathF.Abs(speed) < 0.02f) ? "PAUSED" : ((speed < 0f) ? "REWIND" : "FORWARD"))));
				float value2 = (((speed > 0f && atPresent) || (speed < 0f && atOldest)) ? 0f : MathF.Abs(speed));
				label.text = $"{value}  {value2:0.0}x\n-{behind:0.00}s";
				label.color = (Color)((speed < -0.02f) ? new Color(0.2f, 0.85f, 1f) : ((speed > 0.02f) ? new Color(1f, 0.75f, 0.2f) : Color.white));
			}
		}

		public void Dispose()
		{
			if ((Object)(object)label != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)label).gameObject);
			}
			label = null;
		}
	}
}
namespace WristRewind.Tracks
{
	internal readonly record struct AnimationLayer(int Hash, float Time);
	internal sealed record AnimationState(AnimationLayer[] Layers, float Speed, bool Enabled);
	internal sealed class AnimatorTrack : Track<AnimationState>
	{
		private readonly Animator animator;

		private AnimationState? live;

		private AnimationState? selected;

		private bool frozen;

		public AnimatorTrack(Animator animator, int capacity)
			: base(capacity, (Component)(object)animator)
		{
			this.animator = animator;
		}

		protected override AnimationState Read()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			AnimationLayer[] array = new AnimationLayer[Math.Min(animator.layerCount, 8)];
			if (animator.isInitialized)
			{
				for (int i = 0; i < array.Length; i++)
				{
					AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(i);
					array[i] = new AnimationLayer(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash, ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime);
				}
			}
			return new AnimationState(array, animator.speed, ((Behaviour)animator).enabled);
		}

		public override void Freeze()
		{
			if (base.Alive && !frozen)
			{
				live = (selected = Read());
				frozen = true;
				((Behaviour)animator).enabled = false;
			}
		}

		public override void Apply(double time)
		{
			if (base.Alive && frozen)
			{
				((Behaviour)animator).enabled = false;
				if (History.Sample(time, out AnimationState a, out AnimationState _, out float _))
				{
					selected = a;
				}
			}
		}

		public override void Release(bool commit, double time)
		{
			AnimationState animationState = (commit ? selected : live);
			if (base.Owned && frozen && animationState != null)
			{
				((Behaviour)animator).enabled = animationState.Enabled;
				animator.speed = animationState.Speed;
				if (commit && animator.isInitialized)
				{
					for (int i = 0; i < animationState.Layers.Length; i++)
					{
						if (animationState.Layers[i].Hash != 0)
						{
							animator.Play(animationState.Layers[i].Hash, i, animationState.Layers[i].Time);
						}
					}
				}
			}
			frozen = false;
			base.Release(commit, time);
		}

		public override void Dispose()
		{
			Release(commit: false, 0.0);
			base.Dispose();
		}
	}
	internal readonly record struct AudioFrame(AudioClip? Clip, int Sample, bool Playing, float Pitch);
	internal sealed class AudioTrack : Track<AudioFrame>
	{
		private readonly AudioSource source;

		private AudioFrame live;

		private AudioFrame selected;

		private bool frozen;

		public AudioTrack(AudioSource source, int capacity)
			: base(capacity, (Component)(object)source)
		{
			this.source = source;
		}

		protected override AudioFrame Read()
		{
			return new AudioFrame(source.clip, ((Object)(object)source.clip != (Object)null) ? source.timeSamples : 0, source.isPlaying, source.pitch);
		}

		public override void Freeze()
		{
			if (base.Alive && !frozen)
			{
				live = (selected = Read());
				frozen = true;
				source.Pause();
			}
		}

		public override void Apply(double time)
		{
			if (base.Alive && frozen)
			{
				source.Pause();
				selected = (History.Sample(time, out var a, out var _, out var _) ? a : default(AudioFrame));
			}
		}

		public override void Release(bool commit, double time)
		{
			if (base.Owned && frozen)
			{
				AudioFrame audioFrame = (commit ? selected : live);
				if ((Object)(object)source.clip != (Object)(object)audioFrame.Clip)
				{
					source.clip = audioFrame.Clip;
				}
				source.pitch = ((audioFrame.Pitch == 0f) ? live.Pitch : audioFrame.Pitch);
				if ((Object)(object)audioFrame.Clip != (Object)null)
				{
					source.timeSamples = Math.Clamp(audioFrame.Sample, 0, Math.Max(0, audioFrame.Clip.samples - 1));
					if (audioFrame.Playing)
					{
						source.UnPause();
						if (!source.isPlaying)
						{
							source.Play();
						}
					}
					else
					{
						source.Stop();
					}
				}
				else
				{
					source.Stop();
				}
			}
			frozen = false;
			base.Release(commit, time);
		}

		public override void Dispose()
		{
			Release(commit: false, 0.0);
			base.Dispose();
		}
	}
	internal readonly record struct BodyState(Vector3 Position, Quaternion Rotation, Vector3 Velocity, Vector3 AngularVelocity, bool Kinematic, bool Gravity, bool Collisions, bool Sleeping);
	internal sealed class BodyTrack : Track<BodyState>
	{
		private Rigidbody? body;

		private readonly MarrowBody? marrow;

		private readonly Transform root;

		private readonly MarrowEntity? entity;

		private readonly bool moveEntity;

		private readonly Vector3 entityOffset;

		private readonly Quaternion entityRotation;

		private BodyState live;

		private BodyState selected;

		private bool frozen;

		private RigidbodyInterpolation interpolation;

		private Vector3 previous;

		private bool observed;

		private double appliedTime;

		private bool sampled;

		private float poseStep;

		public Rigidbody? Body => Resolve();

		public Transform Root => root;

		public bool Frozen => frozen;

		public bool Applied { get; private set; }

		public float DistanceRecorded { get; private set; }

		public float PositionError { get; private set; }

		public double Oldest => History.Oldest;

		public double Newest => History.Newest;

		public BodyTrack(Rigidbody body, int capacity)
			: base(capacity, (Component)(object)body)
		{
			this.body = body;
			root = ((Component)body).transform;
		}

		public BodyTrack(MarrowBody body, int capacity)
			: base(capacity, (Component)(object)body)
		{
			//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_0082: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			marrow = body;
			root = ((Component)body).transform;
			entity = body.Entity;
			moveEntity = (Object)(object)entity != (Object)null && entity.Bodies != null && ((Il2CppArrayBase<MarrowBody>)(object)entity.Bodies).Length == 1;
			if (moveEntity)
			{
				entityOffset = Quaternion.Inverse(root.rotation) * (((Component)entity).transform.position - root.position);
				entityRotation = Quaternion.Inverse(root.rotation) * ((Component)entity).transform.rotation;
			}
			Resolve();
		}

		private Rigidbody? Resolve()
		{
			if ((Object)(object)marrow != (Object)null)
			{
				body = marrow._rigidbody;
			}
			if (!((Object)(object)body != (Object)null))
			{
				return null;
			}
			return body;
		}

		private void Unpack()
		{
			if ((Object)(object)marrow != (Object)null && marrow.IsPacked)
			{
				marrow.Unpack();
			}
		}

		protected override BodyState Read()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0047: 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_0034: 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_006b: 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_0081: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			Rigidbody val = Resolve();
			Vector3 val2 = (((Object)(object)val != (Object)null) ? val.position : root.position);
			if (observed)
			{
				DistanceRecorded += Vector3.Distance(previous, val2);
			}
			previous = val2;
			observed = true;
			return new BodyState(val2, ((Object)(object)val != (Object)null) ? val.rotation : root.rotation, ((Object)(object)val != (Object)null) ? val.velocity : Vector3.zero, ((Object)(object)val != (Object)null) ? val.angularVelocity : Vector3.zero, (Object)(object)val != (Object)null && val.isKinematic, (Object)(object)val == (Object)null || val.useGravity, (Object)(object)val == (Object)null || val.detectCollisions, (Object)(object)val == (Object)null || val.IsSleeping());
		}

		public override void Freeze()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (base.Alive && !frozen)
			{
				Unpack();
				Rigidbody val = Resolve();
				live = (selected = Read());
				interpolation = (RigidbodyInterpolation)(((Object)(object)val != (Object)null) ? ((int)val.interpolation) : 0);
				Applied = false;
				sampled = false;
				poseStep = 0f;
				PositionError = 0f;
				frozen = true;
				Lock(val);
			}
		}

		private static void Lock(Rigidbody? rb)
		{
			if (!((Object)(object)rb == (Object)null))
			{
				rb.interpolation = (RigidbodyInterpolation)0;
				rb.detectCollisions = false;
				rb.isKinematic = true;
			}
		}

		public override void Apply(double time)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00ea: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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_0069: 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_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: 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)
			bool applied = Applied;
			Applied = false;
			sampled = false;
			if (base.Alive && frozen && History.Sample(time, out var a, out var b, out var blend))
			{
				Unpack();
				sampled = true;
				appliedTime = time;
				if (applied)
				{
					PositionError = MathF.Max(PositionError, Vector3.Distance(root.position, selected.Position));
				}
				Vector3 position = selected.Position;
				selected = a with
				{
					Position = Vector3.LerpUnclamped(a.Position, b.Position, blend),
					Rotation = Quaternion.SlerpUnclamped(a.Rotation, b.Rotation, blend),
					Velocity = Vector3.LerpUnclamped(a.Velocity, b.Velocity, blend),
					AngularVelocity = Vector3.LerpUnclamped(a.AngularVelocity, b.AngularVelocity, blend)
				};
				poseStep = Vector3.Distance(position, selected.Position);
				MoveEntity(selected);
				Rigidbody val = Resolve();
				Lock(val);
				if ((Object)(object)val != (Object)null)
				{
					val.position = selected.Position;
					val.rotation = selected.Rotation;
				}
				root.SetPositionAndRotation(selected.Position, selected.Rotation);
				PositionError = MathF.Max(PositionError, Vector3.Distance(root.position, selected.Position));
				Applied = true;
			}
		}

		public override void Release(bool commit, double time)
		{
			//IL_0044: 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_0064: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (base.Owned && frozen)
			{
				Unpack();
				BodyState state = (commit ? selected : live);
				MoveEntity(state);
				Rigidbody val = Resolve();
				root.SetPositionAndRotation(state.Position, state.Rotation);
				if ((Object)(object)val != (Object)null)
				{
					val.position = state.Position;
					val.rotation = state.Rotation;
					val.useGravity = state.Gravity;
					val.isKinematic = state.Kinematic;
					if (!state.Kinematic)
					{
						val.velocity = state.Velocity;
						val.angularVelocity = state.AngularVelocity;
						if (state.Sleeping)
						{
							val.Sleep();
						}
						else
						{
							val.WakeUp();
						}
					}
					val.detectCollisions = state.Collisions;
					val.interpolation = interpolation;
				}
			}
			frozen = false;
			Applied = false;
			sampled = false;
			base.Release(commit, time);
		}

		private void MoveEntity(BodyState state)
		{
			//IL_0038: 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_0045: 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_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_005c: 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)
			if (moveEntity && (Object)(object)entity != (Object)null && !entity.IsDespawned && !entity.IsDestroyed)
			{
				entity.Teleport(state.Position + state.Rotation * entityOffset, state.Rotation * entityRotation, false);
			}
		}

		public string Describe(bool skipped)
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			return $"Body {((Object)((Component)root).gameObject).name}: samples={base.SampleCount} range={Oldest:0.00}..{Newest:0.00} motion={DistanceRecorded:0.00}m frozen={frozen} applied={Applied} sampled={sampled} poseTime={appliedTime:0.00} step={poseStep:0.000}m toPresent={Vector3.Distance(selected.Position, live.Position):0.000}m alive={base.Alive} rigidbody={(Object)(object)Resolve() != (Object)null} packed={(Object)(object)marrow != (Object)null && marrow.IsPacked} skipped={skipped} drift={PositionError:0.000}m stableBody={(Object)(object)marrow != (Object)null}";
		}

		public override void Dispose()
		{
			Release(commit: false, 0.0);
			base.Dispose();
		}
	}
	internal readonly record struct GunState(bool Loaded, bool Charged, bool Locked, CartridgeData? Cartridge, MagazineState? Magazine, int Rounds, CartridgeData? MagazineCartridge);
	internal sealed class GunTrack : Track<GunState>
	{
		private readonly Gun gun;

		private GunState selected;

		private bool frozen;

		private bool enabled;

		public GunTrack(Gun gun, int capacity)
			: base(capacity, (Component)(object)gun)
		{
			this.gun = gun;
		}

		protected override GunState Read()
		{
			MagazineState magazineState = gun.MagazineState;
			return new GunState(gun.isLoaded, gun.isCharged, gun.isSlideLockedOnEmpty, gun.chamberedCartridge, magazineState, (magazineState != null) ? magazineState.AmmoCount : 0, (magazineState != null) ? magazineState.GetCartridgeData() : null);
		}

		public override void Freeze()
		{
			if (base.Alive && !frozen)
			{
				selected = Read();
				enabled = ((Behaviour)gun).enabled;
				frozen = true;
				((Behaviour)gun).enabled = false;
			}
		}

		public override void Apply(double time)
		{
			if (base.Alive && frozen)
			{
				((Behaviour)gun).enabled = false;
				if (History.Sample(time, out var a, out var _, out var _))
				{
					selected = a;
				}
			}
		}

		public override void Release(bool commit, double time)
		{
			if (base.Owned && frozen && commit)
			{
				gun.isLoaded = selected.Loaded;
				gun.isCharged = selected.Charged;
				gun.isSlideLockedOnEmpty = selected.Locked;
				gun.chamberedCartridge = selected.Cartridge;
				Gun obj = gun;
				Gun obj2 = gun;
				bool flag = (gun.isTriggerPulled = false);
				bool isFiring = (obj2.isTriggerPressed = flag);
				obj.isFiring = isFiring;
				MagazineState magazineState = gun.MagazineState;
				if (magazineState != null && selected.Magazine != null && ((Il2CppObjectBase)magazineState).Pointer == ((Il2CppObjectBase)selected.Magazine).Pointer && (Object)(object)selected.MagazineCartridge != (Object)null && magazineState.AmmoCount != selected.Rounds)
				{
					magazineState.Initialize(selected.MagazineCartridge, selected.Rounds);
				}
			}
			if (base.Owned && frozen)
			{
				((Behaviour)gun).enabled = enabled;
			}
			frozen = false;
			base.Release(commit, time);
		}

		public override void Dispose()
		{
			Release(commit: false, 0.0);
			base.Dispose();
		}
	}
	internal sealed class NodeTrack : Track<Pose>
	{
		private readonly Transform node;

		private Pose live;

		private bool frozen;

		public NodeTrack(Transform node, int capacity)
			: base(capacity, (Component)(object)node)
		{
			this.node = node;
		}

		protected override Pose Read()
		{
			return Pose.Read(node);
		}

		public override void Freeze()
		{
			if (base.Alive)
			{
				live = Read();
				frozen = true;
			}
		}

		public override void Apply(double time)
		{
			if (base.Alive && frozen && History.Sample(time, out var a, out var b, out var blend))
			{
				Pose.Apply(node, a, b, blend);
			}
		}

		public override void Release(bool commit, double time)
		{
			if (!commit && base.Owned && frozen)
			{
				Pose.Apply(node, live, live, 0f);
			}
			frozen = false;
			base.Release(commit, time);
		}

		public override void Dispose()
		{
			Release(commit: false, 0.0);
			base.Dispose();
		}
	}
	internal readonly record struct NpcState(float Health, float LeftArm, float RightArm, float LeftLeg, float RightLeg, bool Dead, State PuppetState)
	{
		[CompilerGenerated]
		public void Deconstruct(out float Health, out float LeftArm, out float RightArm, out float LeftLeg, out float RightLeg, out bool Dead, out State PuppetState)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected I4, but got Unknown
			Health = this.Health;
			LeftArm = this.LeftArm;
			RightArm = this.RightArm;
			LeftLeg = this.LeftLeg;
			RightLeg = this.RightLeg;
			Dead = this.Dead;
			PuppetState = (State)(int)this.PuppetState;
		}
	}
	internal sealed class NpcTrack : Track<NpcState>
	{
		private readonly AIBrain brain;

		private readonly BehaviourBaseNav? behaviour;

		private readonly PuppetMaster? puppet;

		private readonly SubBehaviourHealth? health;

		private readonly NavMeshAgent? nav;

		private bool brainEnabled;

		private bool behaviourEnabled;

		private bool puppetEnabled;

		private bool navEnabled;

		private bool frozen;

		private NpcState live;

		private NpcState selected;

		public NpcTrack(AIBrain brain, int capacity)
			: base(capacity, (Component)(object)brain)
		{
			this.brain = brain;
			behaviour = brain.behaviour;
			puppet = brain.puppetMaster;
			health = (((Object)(object)behaviour != (Object)null) ? behaviour.health : null);
			nav = (((Object)(object)behaviour != (Object)null) ? behaviour._navAgent : null);
		}

		protected override NpcState Read()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			return new NpcState((health != null) ? health.cur_hp : 0f, (health != null) ? health.cur_arm_lf : 0f, (health != null) ? health.cur_arm_rt : 0f, (health != null) ? health.cur_leg_lf : 0f, (health != null) ? health.cur_leg_rt : 0f, brain.isDead, (State)(((Object)(object)puppet != (Object)null) ? ((int)puppet.state) : 0));
		}

		public override void Freeze()
		{
			if (base.Alive && !frozen)
			{
				live = (selected = Read());
				brainEnabled = ((Behaviour)brain).enabled;
				behaviourEnabled = (Object)(object)behaviour != (Object)null && ((Behaviour)behaviour).enabled;
				puppetEnabled = (Object)(object)puppet != (Object)null && ((Behaviour)puppet).enabled;
				navEnabled = (Object)(object)nav != (Object)null && ((Behaviour)nav).enabled;
				frozen = true;
				((Behaviour)brain).enabled = false;
				if ((Object)(object)behaviour != (Object)null)
				{
					((Behaviour)behaviour).enabled = false;
				}
				if ((Object)(object)puppet != (Object)null)
				{
					((Behaviour)puppet).enabled = false;
				}
				if ((Object)(object)nav != (Object)null)
				{
					((Behaviour)nav).enabled = false;
				}
			}
		}

		public override void Apply(double time)
		{
			if (base.Alive && frozen && History.Sample(time, out var a, out var _, out var _))
			{
				selected = a;
			}
		}

		public override void Release(bool commit, double time)
		{
			//IL_0082: 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 (base.Owned && frozen)
			{
				NpcState npcState = (commit ? selected : live);
				if (commit && live.Dead && !npcState.Dead && (Object)(object)behaviour != (Object)null)
				{
					((BehaviourBase)behaviour).Resurrect();
				}
				brain.isDead = npcState.Dead;
				if ((Object)(object)puppet != (Object)null)
				{
					puppet.state = npcState.PuppetState;
				}
				if (health != null)
				{
					health.cur_hp = npcState.Health;
					health.cur_arm_lf = npcState.LeftArm;
					health.cur_arm_rt = npcState.RightArm;
					health.cur_leg_lf = npcState.LeftLeg;
					health.cur_leg_rt = npcState.RightLeg;
				}
				((Behaviour)brain).enabled = brainEnabled;
				if ((Object)(object)behaviour != (Object)null)
				{
					((Behaviour)behaviour).enabled = behaviourEnabled || (commit && live.Dead && !npcState.Dead);
				}
				if ((Object)(object)puppet != (Object)null)
				{
					((Behaviour)puppet).enabled = puppetEnabled;
				}
				if ((Object)(object)nav != (Object)null)
				{
					((Behaviour)nav).enabled = navEnabled;
					if (navEnabled && nav.isOnNavMesh)
					{
						nav.Warp(((Component)nav).transform.position);
						nav.ResetPath();
					}
				}
			}
			frozen = false;
			base.Release(commit, time);
		}

		public override void Dispose()
		{
			Release(commit: false, 0.0);
			base.Dispose();
		}
	}
	internal readonly record struct ParticleFrame(float Time, uint Seed, int Count, bool AutoSeed, bool Playing, bool Paused, bool RendererEnabled);
	internal sealed class ParticleTrack : Track<ParticleFrame>
	{
		private const float MaxReplayTime = 3f;

		private readonly ParticleSystem system;

		private readonly ParticleSystemRenderer? renderer;

		private ParticleFrame live;

		private ParticleFrame? selected;

		private bool frozen;

		private bool simulated;

		private float appliedTime;

		private uint appliedSeed;

		public ParticleTrack(ParticleSystem system, int capacity)
			: base(capacity, (Component)(object)system)
		{
			this.system = system;
			renderer = ((Component)system).GetComponent<ParticleSystemRenderer>();
		}

		protected override ParticleFrame Read()
		{
			return new ParticleFrame(system.time, system.randomSeed, system.particleCount, system.useAutoRandomSeed, system.isPlaying, system.isPaused, (Object)(object)renderer != (Object)null && ((Renderer)renderer).enabled);
		}

		public override void Freeze()
		{
			if (base.Alive && !frozen)
			{
				live = Read();
				selected = live;
				simulated = false;
				frozen = true;
				system.Pause(false);
			}
		}

		public override void Apply(double time)
		{
			if (!base.Alive || !frozen)
			{
				return;
			}
			system.Pause(false);
			if (!History.Sample(time, out var a, out var b, out var blend))
			{
				if ((Object)(object)renderer != (Object)null)
				{
					((Renderer)renderer).enabled = false;
				}
				selected = null;
				return;
			}
			float time2 = ((a.Seed == b.Seed && b.Time >= a.Time && b.Time - a.Time <= 0.25f) ? (a.Time + (b.Time - a.Time) * blend) : a.Time);
			selected = a with
			{
				Time = time2
			};
			if (!Replayable(live) || !Replayable(selected.Value))
			{
				if (simulated && (appliedSeed != live.Seed || appliedTime != live.Time))
				{
					Seek(live, force: true);
				}
				selected = live;
				if ((Object)(object)renderer != (Object)null)
				{
					((Renderer)renderer).enabled = live.RendererEnabled;
				}
			}
			else
			{
				Seek(selected.Value);
				if ((Object)(object)renderer != (Object)null)
				{
					((Renderer)renderer).enabled = a.RendererEnabled;
				}
			}
		}

		private void Seek(ParticleFrame frame, bool force = false)
		{
			if (Replayable(live) && Replayable(frame) && (simulated || frame.Seed != live.Seed || !(MathF.Abs(frame.Time - live.Time) < 1E-06f)) && (force || !simulated || frame.Seed != appliedSeed || !(MathF.Abs(frame.Time - appliedTime) < 1f / 30f)))
			{
				system.useAutoRandomSeed = false;
				system.randomSeed = frame.Seed;
				system.Simulate(frame.Time, false, true, false);
				system.Pause(false);
				simulated = true;
				appliedTime = frame.Time;
				appliedSeed = frame.Seed;
			}
		}

		private static bool Replayable(ParticleFrame frame)
		{
			if (float.IsFinite(frame.Time) && frame.Time >= 0f && frame.Time <= 3f)
			{
				int count = frame.Count;
				if (count >= 0)
				{
					return count <= 128;
				}
				return false;
			}
			return false;
		}

		public override void Release(bool commit, double time)
		{
			if (base.Owned && frozen)
			{
				ParticleFrame? particleFrame = (commit ? selected : new ParticleFrame?(live));
				if (particleFrame.HasValue)
				{
					if (simulated)
					{
						Seek(particleFrame.Value, force: true);
					}
					sy