Decompiled source of Eclipse AdvancedSkins v0.1.1

BepInEx/plugins/EclipseAdvancedSkins/Eclipse.AdvancedSkins.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using EntityStates;
using EntityStates.Croco;
using On.RoR2;
using RoR2;
using RoR2.ContentManagement;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Audio;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Eclipse.AdvancedSkins;

public sealed class AdvancedSkinApplicationBridge : IDisposable
{
	private static bool installed;

	private readonly HashSet<SkinDef> ownedSkins = new HashSet<SkinDef>();

	private bool disposed;

	public AdvancedSkinApplicationBridge()
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		if (installed)
		{
			throw new InvalidOperationException("Advanced skin bridge already installed");
		}
		SkinDef.ApplyAsync += new hook_ApplyAsync(Apply);
		CharacterBody.onBodyAwakeGlobal += BodyAwake;
		installed = true;
	}

	private void BodyAwake(CharacterBody body)
	{
		if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.modelLocator) && Object.op_Implicit((Object)(object)body.modelLocator.modelTransform))
		{
			AdvancedSkinModel component = ((Component)body.modelLocator.modelTransform).GetComponent<AdvancedSkinModel>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.BindBody(body);
			}
		}
	}

	public void Register(SkinDef skin, Mesh mesh)
	{
		if (disposed || !Object.op_Implicit((Object)(object)skin) || !AdvancedSkinRegistry.TryGet(mesh, out var _) || !ownedSkins.Add(skin))
		{
			throw new ArgumentException("Register a unique skin with a registered custom mesh");
		}
	}

	private IEnumerator Apply(orig_ApplyAsync orig, SkinDef skin, GameObject model, List<AssetReferenceT<Material>> materials, List<AssetReferenceT<Mesh>> meshes, AsyncReferenceHandleUnloadType unload)
	{
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		AdvancedSkinModel advancedSkinModel = (Object.op_Implicit((Object)(object)model) ? model.GetComponent<AdvancedSkinModel>() : null);
		SkinRigDriver driver = null;
		if (ownedSkins.Contains(skin) && !Object.op_Implicit((Object)(object)advancedSkinModel))
		{
			throw new InvalidOperationException("Advanced skin requires its own validated body/display template");
		}
		if (Object.op_Implicit((Object)(object)advancedSkinModel))
		{
			try
			{
				driver = advancedSkinModel.Prepare();
			}
			catch (Exception ex)
			{
				if (ownedSkins.Contains(skin))
				{
					throw;
				}
				Debug.LogError((object)("ECLIPSE_NATIVE_SNAPSHOT_FAILED " + ex));
			}
		}
		IEnumerator enumerator = orig.Invoke(skin, model, materials, meshes, unload);
		if (!Object.op_Implicit((Object)(object)driver) || enumerator == null)
		{
			return enumerator;
		}
		return SkinApplyObservation.Observe(enumerator, delegate
		{
			if (Object.op_Implicit((Object)(object)driver))
			{
				driver.SynchronizeSkin();
			}
		}, delegate(Exception ex2)
		{
			Debug.LogError((object)("ECLIPSE_SKIN_RECONCILE_FAILED " + ex2));
		});
	}

	internal void Remove(SkinDef skin)
	{
		ownedSkins.Remove(skin);
	}

	public void Dispose()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		if (!disposed)
		{
			SkinDef.ApplyAsync -= new hook_ApplyAsync(Apply);
			CharacterBody.onBodyAwakeGlobal -= BodyAwake;
			ownedSkins.Clear();
			installed = false;
			disposed = true;
		}
	}
}
public static class AdvancedSkinApplications
{
	private sealed class Registration : IDisposable
	{
		private SkinDef skin;

		private bool disposed;

		public Registration(SkinDef skin)
		{
			this.skin = skin;
		}

		public void Dispose()
		{
			if (!disposed)
			{
				bridge.Remove(skin);
				registrations--;
				if (registrations == 0)
				{
					bridge.Dispose();
					bridge = null;
				}
				skin = null;
				disposed = true;
			}
		}
	}

	private static AdvancedSkinApplicationBridge bridge;

	private static int registrations;

	public static IDisposable Register(SkinDef skin, Mesh mesh)
	{
		if (bridge == null)
		{
			bridge = new AdvancedSkinApplicationBridge();
		}
		try
		{
			bridge.Register(skin, mesh);
		}
		catch
		{
			if (registrations == 0)
			{
				bridge.Dispose();
				bridge = null;
			}
			throw;
		}
		registrations++;
		return new Registration(skin);
	}
}
public sealed class AdvancedSkinModel : MonoBehaviour
{
	[SerializeField]
	private SkinnedMeshRenderer target;

	[SerializeField]
	private Animator source;

	[SerializeField]
	private Mesh nativeMesh;

	[SerializeField]
	private Material[] nativeMaterials;

	[SerializeField]
	private Transform[] nativeBones;

	[SerializeField]
	private Transform nativeRoot;

	[SerializeField]
	private Bounds nativeBounds;

	[SerializeField]
	private bool nativeOffscreen;

	[SerializeField]
	private bool captured;

	[NonSerialized]
	private CharacterBody associatedBody;

	public CharacterBody AssociatedBody
	{
		get
		{
			if (!Object.op_Implicit((Object)(object)associatedBody) || !Object.op_Implicit((Object)(object)associatedBody.modelLocator) || !((Object)(object)associatedBody.modelLocator.modelTransform == (Object)(object)((Component)this).transform))
			{
				return null;
			}
			return associatedBody;
		}
	}

	public bool HasNativeSnapshot => captured;

	internal void BindBody(CharacterBody body)
	{
		if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.modelLocator) || (Object)(object)body.modelLocator.modelTransform != (Object)(object)((Component)this).transform)
		{
			throw new ArgumentException("Body does not own this model");
		}
		associatedBody = body;
	}

	private bool Owns(Transform value)
	{
		if (Object.op_Implicit((Object)(object)value))
		{
			if (!((Object)(object)value == (Object)(object)((Component)this).transform))
			{
				return value.IsChildOf(((Component)this).transform);
			}
			return true;
		}
		return false;
	}

	private void Validate()
	{
		if (!captured || !Object.op_Implicit((Object)(object)target) || !Object.op_Implicit((Object)(object)source) || !Object.op_Implicit((Object)(object)nativeMesh) || !Owns(((Component)target).transform) || !Owns(((Component)source).transform) || nativeBones == null || nativeBones.Length != nativeMesh.bindposes.Length || Array.Exists(nativeBones, (Transform b) => !Owns(b)) || (Object.op_Implicit((Object)(object)nativeRoot) && !Owns(nativeRoot)) || nativeMaterials == null || nativeMaterials.Length == 0 || Array.Exists(nativeMaterials, (Material m) => !Object.op_Implicit((Object)(object)m)))
		{
			throw new InvalidOperationException("Invalid or cross-instance native skin snapshot");
		}
	}

	public void CaptureNativeTemplate(SkinnedMeshRenderer renderer, Animator animator, Mesh fallback = null, Material[] fallbackMaterials = null)
	{
		//IL_00a3: 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)
		if (captured)
		{
			throw new InvalidOperationException("Native template already captured");
		}
		if (!Object.op_Implicit((Object)(object)renderer) || !Object.op_Implicit((Object)(object)animator) || (!Object.op_Implicit((Object)(object)renderer.sharedMesh) && !Object.op_Implicit((Object)(object)fallback)) || AdvancedSkinRegistry.TryGet(renderer.sharedMesh, out var _))
		{
			throw new ArgumentException("Capture the native template before advanced skin application");
		}
		target = renderer;
		source = animator;
		nativeMesh = (Object.op_Implicit((Object)(object)fallback) ? fallback : renderer.sharedMesh);
		nativeMaterials = fallbackMaterials ?? ((Renderer)renderer).sharedMaterials;
		nativeBones = renderer.bones;
		nativeRoot = renderer.rootBone;
		nativeBounds = ((Renderer)renderer).localBounds;
		nativeOffscreen = renderer.updateWhenOffscreen;
		captured = true;
		try
		{
			Validate();
		}
		catch
		{
			captured = false;
			throw;
		}
	}

	public SkinRigDriver Prepare()
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		Validate();
		SkinRigDriver skinRigDriver = ((Component)this).GetComponent<SkinRigDriver>();
		if (!Object.op_Implicit((Object)(object)skinRigDriver))
		{
			skinRigDriver = ((Component)this).gameObject.AddComponent<SkinRigDriver>();
		}
		if (!skinRigDriver.IsConfigured)
		{
			skinRigDriver.ConfigureSnapshot(target, nativeMesh, source, nativeBones, nativeRoot, nativeBounds, nativeOffscreen, nativeMaterials);
		}
		return skinRigDriver;
	}

	private void Awake()
	{
		if (captured)
		{
			try
			{
				Prepare();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("ECLIPSE_NATIVE_SNAPSHOT_FAILED " + ex));
			}
		}
	}
}
public sealed class GroundedLandingClock
{
	private bool initialized;

	private bool wasGrounded;

	private bool landing;

	private double last;

	private double started;

	private long serial;

	public long Serial => serial;

	public double Phase { get; private set; }

	public bool Observe(bool grounded, bool dead, double now, float duration)
	{
		if (!Limits.Finite(now) || !Limits.Finite(duration) || duration <= 0f)
		{
			throw new ArgumentException("Landing clock");
		}
		if (!initialized || now < last || now - last > 0.25)
		{
			initialized = true;
			wasGrounded = grounded;
			landing = false;
		}
		else if (!wasGrounded && grounded && !dead)
		{
			landing = true;
			started = now;
			serial++;
		}
		if (!grounded || dead)
		{
			landing = false;
		}
		wasGrounded = grounded;
		last = now;
		Phase = (landing ? Math.Max(0.0, (now - started) / (double)duration) : 0.0);
		if (Phase >= 1.0)
		{
			landing = false;
		}
		return landing;
	}
}
public sealed class AirPoseTransition
{
	private Pose[] previous;

	private Pose[] from;

	private bool[] bypass;

	private bool initialized;

	private bool air;

	private double lastTime;

	private double changedAt;

	private bool blending;

	public void Reset()
	{
		initialized = false;
		blending = false;
	}

	public void Apply(Pose[] pose, bool airborne, double now, float seconds, bool[] exempt = null)
	{
		if (pose == null || !Limits.Finite(now) || !Limits.Finite(seconds) || seconds <= 0f || (exempt != null && exempt.Length != pose.Length))
		{
			throw new ArgumentException("Air pose transition context");
		}
		if (!initialized || previous.Length != pose.Length || now < lastTime || now - lastTime > 0.25)
		{
			previous = (Pose[])pose.Clone();
			from = new Pose[pose.Length];
			bypass = new bool[pose.Length];
			initialized = true;
			air = airborne;
			blending = false;
		}
		else if (air != airborne)
		{
			Array.Copy(previous, from, pose.Length);
			Array.Clear(bypass, 0, bypass.Length);
			changedAt = now;
			air = airborne;
			blending = true;
		}
		if (blending)
		{
			float num = (float)Math.Max(0.0, Math.Min(1.0, (now - changedAt) / (double)seconds));
			num = num * num * (3f - 2f * num);
			for (int i = 0; i < pose.Length; i++)
			{
				ref bool reference = ref bypass[i];
				reference |= exempt != null && exempt[i];
				if (!bypass[i])
				{
					pose[i] = Pose.Blend(from[i], pose[i], num);
				}
			}
			if (now - changedAt >= (double)seconds)
			{
				blending = false;
			}
		}
		Array.Copy(pose, previous, pose.Length);
		lastTime = now;
	}
}
internal static class CrocoModelActor
{
	public static CharacterBody Resolve(Component member)
	{
		if (!Object.op_Implicit((Object)(object)member))
		{
			return null;
		}
		CharacterModel componentInParent = member.GetComponentInParent<CharacterModel>();
		if (Object.op_Implicit((Object)(object)componentInParent) && Object.op_Implicit((Object)(object)componentInParent.body) && Object.op_Implicit((Object)(object)componentInParent.body.modelLocator) && (Object)(object)componentInParent.body.modelLocator.modelTransform == (Object)(object)((Component)componentInParent).transform)
		{
			return componentInParent.body;
		}
		AdvancedSkinModel componentInParent2 = member.GetComponentInParent<AdvancedSkinModel>();
		if (Object.op_Implicit((Object)(object)componentInParent2) && Object.op_Implicit((Object)(object)componentInParent2.AssociatedBody))
		{
			return componentInParent2.AssociatedBody;
		}
		return null;
	}
}
public sealed class CrocoModelMotionSource : ISkinMotionSource, IDisposable
{
	private readonly Animator animator;

	private readonly MotionBank bank;

	private CharacterBody actor;

	private CrocoStateMotionSource source;

	private bool disposed;

	private readonly bool observeLanding;

	public CrocoModelMotionSource(Animator animator, MotionBank bank)
		: this(animator, bank, observeLanding: false)
	{
	}

	public CrocoModelMotionSource(Animator animator, MotionBank bank, bool observeLanding)
	{
		if (!Object.op_Implicit((Object)(object)animator))
		{
			throw new ArgumentException("Missing model Animator");
		}
		CrocoStateMotionSource.ValidateBank(bank);
		this.animator = animator;
		this.bank = bank;
		this.observeLanding = observeLanding;
	}

	public void Observe(List<NativeMotionCandidate> candidates, HashSet<string> replaceNativeClips)
	{
		if (disposed)
		{
			return;
		}
		CharacterBody val = CrocoModelActor.Resolve((Component)(object)animator);
		if ((Object)(object)val != (Object)(object)actor)
		{
			source?.Dispose();
			source = null;
			actor = val;
		}
		if (!Object.op_Implicit((Object)(object)actor))
		{
			return;
		}
		if (source == null)
		{
			if (((Component)actor).GetComponents<EntityStateMachine>().Length == 0)
			{
				return;
			}
			source = new CrocoStateMotionSource(actor, bank, observeLanding);
		}
		source.Observe(candidates, replaceNativeClips);
	}

	public void Dispose()
	{
		if (!disposed)
		{
			disposed = true;
			source?.Dispose();
			source = null;
			actor = null;
		}
	}
}
public sealed class CrocoModelGaitTiming : ISkinMotionTiming, IDisposable
{
	private readonly SkinnedMeshRenderer renderer;

	private readonly MotionBank bank;

	private readonly GaitCalibration calibration;

	private CharacterBody actor;

	private CrocoGaitTiming timing;

	private bool disposed;

	public CrocoModelGaitTiming(SkinnedMeshRenderer renderer, MotionBank bank, GaitCalibration calibration)
	{
		if (!Object.op_Implicit((Object)(object)renderer) || calibration == null)
		{
			throw new ArgumentException("Missing model gait context");
		}
		calibration.Validate(bank);
		this.renderer = renderer;
		this.bank = bank;
		this.calibration = calibration;
	}

	public void Retime(MotionLayer[] composed, IEnumerable<NativeMotionCandidate> observed)
	{
		if (disposed)
		{
			return;
		}
		CharacterBody val = CrocoModelActor.Resolve((Component)(object)renderer);
		if ((Object)(object)val != (Object)(object)actor)
		{
			timing?.Dispose();
			timing = null;
			actor = val;
		}
		if (Object.op_Implicit((Object)(object)actor) && Object.op_Implicit((Object)(object)actor.characterMotor))
		{
			if (timing == null)
			{
				timing = new CrocoGaitTiming(actor, ((Component)renderer).transform, bank, calibration);
			}
			timing.Retime(composed, observed);
		}
	}

	public bool OwnsCues(MotionClip clip)
	{
		if (!disposed && timing != null)
		{
			return timing.OwnsCues(clip);
		}
		return false;
	}

	public void AdvanceCues(Action<string> emit)
	{
		if (!disposed)
		{
			timing?.AdvanceCues(emit);
		}
	}

	public void Dispose()
	{
		if (!disposed)
		{
			disposed = true;
			timing?.Dispose();
			timing = null;
			actor = null;
		}
	}
}
public sealed class GaitCalibration
{
	public int ReferenceFoot;

	public readonly Dictionary<string, V3> TravelPerCycle = new Dictionary<string, V3>();

	public Cue[] ContactCues;

	public readonly Dictionary<string, float> CyclesPerWorldMetre = new Dictionary<string, float>();

	public double MaximumCyclesPerSecond = 12.0;

	public double Acceleration;

	public double EffectiveCadence(IEnumerable<MotionLayer> layers, double worldSpeed)
	{
		if (!Limits.Finite(worldSpeed) || worldSpeed < 0.0)
		{
			throw new ArgumentException("World gait speed");
		}
		double num = 0.0;
		foreach (MotionLayer item in layers.OrderBy((MotionLayer l) => l.Priority))
		{
			if (item.BoneMask != null && Array.IndexOf(item.BoneMask, ReferenceFoot) < 0)
			{
				continue;
			}
			float num2 = 0f;
			double num3 = 0.0;
			WeightedMotion[] samples = item.Samples;
			foreach (WeightedMotion weightedMotion in samples)
			{
				if (weightedMotion.Clip.Tracks.Any((Track t) => t.Bone == ReferenceFoot))
				{
					if (!Limits.Finite(weightedMotion.Weight) || weightedMotion.Weight < 0f)
					{
						throw new ArgumentException("Gait weight");
					}
					num2 += weightedMotion.Weight;
					if (CyclesPerWorldMetre.TryGetValue(weightedMotion.Clip.Id, out var value))
					{
						num3 += (double)value * worldSpeed * (double)weightedMotion.Weight;
					}
				}
			}
			if (!(num2 <= 0f))
			{
				double num5 = Math.Min(1f, num2);
				if (item.Blend == MotionBlend.Override)
				{
					num = num * (1.0 - num5) + num3 * (num5 / (double)num2);
				}
				else if (num3 != 0.0)
				{
					throw new ArgumentException("Additive gait cadence unsupported");
				}
			}
		}
		return num;
	}

	public void Validate(MotionBank bank)
	{
		if (bank == null || ReferenceFoot < 0 || ReferenceFoot >= bank.Bones.Length || TravelPerCycle.Count == 0 || ContactCues == null || ContactCues.Length == 0)
		{
			throw new ArgumentException("Gait calibration");
		}
		new GaitContactClock(ContactCues);
		if (!Limits.Finite(MaximumCyclesPerSecond) || MaximumCyclesPerSecond <= 0.0 || !Limits.Finite(Acceleration) || Acceleration < 0.0)
		{
			throw new ArgumentException("Gait cadence limits");
		}
		if (CyclesPerWorldMetre.Count > 0 && (CyclesPerWorldMetre.Count != TravelPerCycle.Count || CyclesPerWorldMetre.Any((KeyValuePair<string, float> p) => !TravelPerCycle.ContainsKey(p.Key) || !Limits.Finite(p.Value) || p.Value <= 0f)))
		{
			throw new ArgumentException("Complete visual gait calibration required");
		}
		foreach (KeyValuePair<string, V3> item in TravelPerCycle)
		{
			MotionClip motionClip = bank.Find(item.Key);
			V3 value = item.Value;
			if (!motionClip.Loop || !Limits.Finite(value.X) || !Limits.Finite(value.Y) || !Limits.Finite(value.Z) || value.X * value.X + value.Y * value.Y + value.Z * value.Z < 0.0001f)
			{
				throw new ArgumentException("Gait stride");
			}
			if (motionClip.Cues.Length != ContactCues.Length || motionClip.Cues.Where((Cue c, int i) => c.Phase != ContactCues[i].Phase || c.Sound != ContactCues[i].Sound).Any())
			{
				throw new ArgumentException("Gaits require a shared contact cue schedule");
			}
		}
	}

	public V3 EffectiveTravel(IEnumerable<MotionLayer> layers)
	{
		V3 v = default(V3);
		foreach (MotionLayer item in layers.OrderBy((MotionLayer l) => l.Priority))
		{
			if (item.BoneMask != null && Array.IndexOf(item.BoneMask, ReferenceFoot) < 0)
			{
				continue;
			}
			float num = 0f;
			V3 v2 = default(V3);
			WeightedMotion[] samples = item.Samples;
			foreach (WeightedMotion weightedMotion in samples)
			{
				if (weightedMotion.Clip.Tracks.Any((Track t) => t.Bone == ReferenceFoot))
				{
					if (!Limits.Finite(weightedMotion.Weight) || weightedMotion.Weight < 0f)
					{
						throw new ArgumentException("Gait weight");
					}
					num += weightedMotion.Weight;
					if (TravelPerCycle.TryGetValue(weightedMotion.Clip.Id, out var value))
					{
						v2 += value * weightedMotion.Weight;
					}
				}
			}
			if (!(num <= 0f))
			{
				float num3 = Math.Min(1f, num);
				if (item.Blend == MotionBlend.Override)
				{
					v = v * (1f - num3) + v2 * (num3 / num);
				}
				else if (v2.X != 0f || v2.Y != 0f || v2.Z != 0f)
				{
					throw new ArgumentException("Additive gait calibration unsupported");
				}
			}
		}
		return v;
	}
}
public sealed class GaitContactClock
{
	private readonly CueCursor cursor = new CueCursor();

	private readonly MotionClip schedule;

	public GaitContactClock(Cue[] cues)
	{
		if (cues == null || cues.Length == 0 || cues.Length > 128)
		{
			throw new ArgumentException("Contact cues");
		}
		float num = -1f;
		foreach (Cue cue in cues)
		{
			if (cue == null || !Limits.Finite(cue.Phase) || cue.Phase < 0f || cue.Phase >= 1f || cue.Phase < num || string.IsNullOrWhiteSpace(cue.Sound))
			{
				throw new ArgumentException("Contact cue schedule");
			}
			num = cue.Phase;
		}
		schedule = new MotionClip
		{
			Id = "gait_contacts",
			Loop = true,
			Cues = cues.Select((Cue c) => new Cue
			{
				Phase = c.Phase,
				Sound = c.Sound
			}).ToArray()
		};
	}

	public void Advance(double phase, bool audible, Action<string> emit)
	{
		cursor.Advance(schedule, "gait_contacts", phase, delegate(string id)
		{
			if (audible)
			{
				emit(id);
			}
		});
	}
}
public sealed class GaitCycleClock
{
	private bool initialized;

	private double previous;

	public double MaximumCyclesPerSecond = 12.0;

	public double Phase { get; private set; } = 1.0;

	public double CyclesPerSecond { get; private set; }

	public double UnmatchedSpeed { get; private set; }

	public bool RateLimited { get; private set; }

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

	public double AdvanceAtRate(double desiredRate, double now, bool grounded, double acceleration = 0.0)
	{
		if (!Limits.Finite(desiredRate) || desiredRate < 0.0 || !Limits.Finite(now) || !Limits.Finite(MaximumCyclesPerSecond) || MaximumCyclesPerSecond <= 0.0 || !Limits.Finite(acceleration) || acceleration < 0.0)
		{
			throw new ArgumentException("Visual gait clock input");
		}
		double num = (initialized ? (now - previous) : 0.0);
		initialized = true;
		previous = now;
		RateLimited = desiredRate > MaximumCyclesPerSecond;
		UnmatchedSpeed = 0.0;
		if (!grounded || desiredRate == 0.0 || num < 0.0 || num > 0.25)
		{
			CyclesPerSecond = 0.0;
			return Phase;
		}
		double num2 = Math.Min(MaximumCyclesPerSecond, desiredRate);
		double cyclesPerSecond = CyclesPerSecond;
		CyclesPerSecond = ((acceleration == 0.0) ? num2 : Math.Max(cyclesPerSecond - acceleration * num, Math.Min(cyclesPerSecond + acceleration * num, num2)));
		Phase += num * ((acceleration == 0.0) ? num2 : ((cyclesPerSecond + CyclesPerSecond) * 0.5));
		return Phase;
	}

	public double Advance(V3 localVelocity, V3 travel, double now, bool grounded)
	{
		if (!Limits.Finite(now) || !Limits.Finite(Dot(localVelocity, localVelocity)) || !Limits.Finite(Dot(travel, travel)) || !Limits.Finite(MaximumCyclesPerSecond) || MaximumCyclesPerSecond <= 0.0)
		{
			throw new ArgumentException("Gait clock input");
		}
		double num = (initialized ? (now - previous) : 0.0);
		initialized = true;
		previous = now;
		CyclesPerSecond = 0.0;
		RateLimited = false;
		UnmatchedSpeed = Math.Sqrt(Dot(localVelocity, localVelocity));
		if (!grounded || num < 0.0 || num > 0.25)
		{
			return Phase;
		}
		double num2 = Dot(travel, travel);
		if (num2 < 0.0001)
		{
			return Phase;
		}
		double num3 = Math.Max(0.0, Dot(localVelocity, travel) / num2);
		CyclesPerSecond = Math.Min(MaximumCyclesPerSecond, num3);
		RateLimited = num3 > MaximumCyclesPerSecond;
		V3 v = localVelocity - travel * (float)CyclesPerSecond;
		UnmatchedSpeed = Math.Sqrt(Dot(v, v));
		Phase += num * CyclesPerSecond;
		return Phase;
	}
}
public struct NativeSoundPlayback
{
	public readonly SoundResult Result;

	public readonly uint PlayingId;

	public NativeSoundPlayback(SoundResult result, uint playingId = 0u)
	{
		if (result == SoundResult.Played != (playingId != 0))
		{
			throw new ArgumentException("Played native sound requires an engine playing ID");
		}
		Result = result;
		PlayingId = playingId;
	}
}
public struct NativeSoundDecision
{
	public readonly bool SuppressNative;

	public readonly uint PlayingId;

	public static NativeSoundDecision Forward => new NativeSoundDecision(suppressNative: false);

	public NativeSoundDecision(bool suppressNative, uint playingId = 0u)
	{
		SuppressNative = suppressNative;
		PlayingId = playingId;
	}
}
public sealed class NativeSoundLoop
{
	public string StartEvent;

	public string StopEvent;

	public string Cue;
}
public sealed class NativeSoundRouter
{
	private sealed class LoopState
	{
		public string Cue;

		public bool NativeFallback;

		public bool HasStart;

		public readonly HashSet<uint> Playing = new HashSet<uint>();

		public readonly HashSet<uint> PendingStops = new HashSet<uint>();
	}

	private readonly Dictionary<string, string> oneShots = new Dictionary<string, string>(StringComparer.Ordinal);

	private readonly Dictionary<string, LoopState> starts = new Dictionary<string, LoopState>(StringComparer.Ordinal);

	private readonly Dictionary<string, LoopState> stops = new Dictionary<string, LoopState>(StringComparer.Ordinal);

	private readonly Func<string, NativeSoundPlayback> play;

	private readonly Func<uint, bool> stop;

	public int PendingStopCount
	{
		get
		{
			int num = 0;
			foreach (LoopState value in starts.Values)
			{
				foreach (uint pendingStop in value.PendingStops)
				{
					if (pendingStop != 0)
					{
						num++;
					}
				}
			}
			return num;
		}
	}

	public NativeSoundRouter(IDictionary<string, string> replacements, IEnumerable<NativeSoundLoop> loops, Func<string, NativeSoundPlayback> play, Func<uint, bool> stop)
	{
		this.play = play ?? throw new ArgumentNullException("play");
		this.stop = stop ?? throw new ArgumentNullException("stop");
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		foreach (KeyValuePair<string, string> replacement in replacements)
		{
			if (string.IsNullOrWhiteSpace(replacement.Key) || string.IsNullOrWhiteSpace(replacement.Value) || !hashSet.Add(replacement.Key))
			{
				throw new ArgumentException("Invalid native sound mapping");
			}
			oneShots.Add(replacement.Key, replacement.Value);
		}
		foreach (NativeSoundLoop loop in loops)
		{
			if (loop == null || string.IsNullOrWhiteSpace(loop.StartEvent) || string.IsNullOrWhiteSpace(loop.StopEvent) || string.IsNullOrWhiteSpace(loop.Cue) || !hashSet.Add(loop.StartEvent) || !hashSet.Add(loop.StopEvent))
			{
				throw new ArgumentException("Ambiguous native sound loop pair");
			}
			LoopState value = new LoopState
			{
				Cue = loop.Cue
			};
			starts.Add(loop.StartEvent, value);
			stops.Add(loop.StopEvent, value);
		}
	}

	private static NativeSoundDecision Decision(NativeSoundPlayback result)
	{
		return new NativeSoundDecision(result.Result != SoundResult.Unavailable, result.PlayingId);
	}

	public NativeSoundDecision Route(string exactEvent)
	{
		if (exactEvent == null)
		{
			return NativeSoundDecision.Forward;
		}
		if (stops.TryGetValue(exactEvent, out var value))
		{
			foreach (uint item in value.Playing)
			{
				value.PendingStops.Add(item);
			}
			value.Playing.Clear();
			value.NativeFallback = false;
			value.HasStart = false;
			Retry(value);
			return NativeSoundDecision.Forward;
		}
		if (starts.TryGetValue(exactEvent, out value))
		{
			NativeSoundPlayback result = play(value.Cue);
			value.HasStart = true;
			if (result.Result == SoundResult.Unavailable)
			{
				value.NativeFallback = true;
			}
			else if (result.Result == SoundResult.Played)
			{
				value.Playing.Add(result.PlayingId);
			}
			return Decision(result);
		}
		if (!oneShots.TryGetValue(exactEvent, out var value2))
		{
			return NativeSoundDecision.Forward;
		}
		return Decision(play(value2));
	}

	private void Retry(LoopState state)
	{
		foreach (uint item in new List<uint>(state.PendingStops))
		{
			try
			{
				if (stop(item))
				{
					state.PendingStops.Remove(item);
				}
			}
			catch
			{
			}
		}
	}

	public void RetryPendingStops()
	{
		foreach (LoopState value in starts.Values)
		{
			Retry(value);
		}
	}

	public void StopOwned()
	{
		foreach (LoopState value in starts.Values)
		{
			foreach (uint item in value.Playing)
			{
				value.PendingStops.Add(item);
			}
			value.Playing.Clear();
			Retry(value);
		}
	}
}
public sealed class OwnedNativeVoices
{
	public sealed class Ticket
	{
		internal readonly OwnedNativeVoices Owner;

		internal int completed;

		public readonly string Cue;

		public readonly object Emitter;

		public uint PlayingId { get; internal set; }

		public bool StopRequested { get; internal set; }

		public bool Completed => Volatile.Read(in completed) != 0;

		internal Ticket(OwnedNativeVoices owner, string cue, object emitter)
		{
			Owner = owner;
			Cue = cue;
			Emitter = emitter;
		}

		public void Complete()
		{
			Interlocked.Exchange(ref completed, 1);
		}
	}

	private readonly int capacity;

	private readonly List<Ticket> tickets = new List<Ticket>();

	public int Count
	{
		get
		{
			Drain();
			return tickets.Count;
		}
	}

	public OwnedNativeVoices(int capacity = 8)
	{
		if (capacity < 1 || capacity > 32)
		{
			throw new ArgumentOutOfRangeException("capacity");
		}
		this.capacity = capacity;
	}

	public Ticket Reserve(string cue, int cueLimit, object emitter)
	{
		if (string.IsNullOrWhiteSpace(cue) || cueLimit < 1 || cueLimit > 4 || emitter == null)
		{
			throw new ArgumentException("Invalid owned voice request");
		}
		Drain();
		if (tickets.Count >= capacity || tickets.Count((Ticket t) => t.Cue == cue) >= cueLimit)
		{
			return null;
		}
		Ticket ticket = new Ticket(this, cue, emitter);
		tickets.Add(ticket);
		return ticket;
	}

	private void Require(Ticket ticket)
	{
		if (ticket == null || ticket.Owner != this || !tickets.Contains(ticket))
		{
			throw new ArgumentException("Voice is not owned by this ledger");
		}
	}

	public void Commit(Ticket ticket, uint playingId)
	{
		Require(ticket);
		if (playingId == 0 || ticket.PlayingId != 0)
		{
			throw new ArgumentException("Invalid native playing ID");
		}
		ticket.PlayingId = playingId;
	}

	public void Abort(Ticket ticket)
	{
		Require(ticket);
		ticket.Complete();
		Drain();
	}

	public void MarkStopRequested(Ticket ticket)
	{
		Require(ticket);
		if (ticket.PlayingId == 0)
		{
			throw new InvalidOperationException("Cannot stop an unposted voice");
		}
		ticket.StopRequested = true;
	}

	public void Drain()
	{
		tickets.RemoveAll((Ticket t) => t.Completed);
	}

	public Ticket[] Active()
	{
		Drain();
		return tickets.ToArray();
	}

	public void EngineStopped()
	{
		foreach (Ticket ticket in tickets)
		{
			ticket.Complete();
		}
		tickets.Clear();
	}
}
public sealed class RetryDisposalQueue
{
	private sealed class Entry
	{
		public IDisposable Value;

		public double Next;

		public int Attempts;
	}

	private readonly List<Entry> entries = new List<Entry>();

	private bool draining;

	public int Count => entries.Count;

	public void Add(IDisposable value)
	{
		if (value == null)
		{
			return;
		}
		foreach (Entry entry in entries)
		{
			if (entry.Value == value)
			{
				return;
			}
		}
		entries.Add(new Entry
		{
			Value = value
		});
	}

	public void Tick(double now, Action<Exception> failed = null)
	{
		if (draining || double.IsNaN(now) || double.IsInfinity(now))
		{
			return;
		}
		draining = true;
		try
		{
			Entry[] array = entries.ToArray();
			foreach (Entry entry in array)
			{
				if (now < entry.Next)
				{
					continue;
				}
				entry.Attempts++;
				entry.Next = now + Math.Min(30.0, Math.Pow(2.0, Math.Min(5, entry.Attempts)));
				try
				{
					entry.Value.Dispose();
					entries.Remove(entry);
				}
				catch (Exception obj)
				{
					if (failed != null)
					{
						try
						{
							failed(obj);
						}
						catch
						{
						}
					}
				}
			}
		}
		finally
		{
			draining = false;
		}
	}
}
public static class SkinApplyObservation
{
	private sealed class Observed : IEnumerator, IDisposable
	{
		private readonly IEnumerator source;

		private readonly Action after;

		private readonly Action<Exception> failed;

		private Observed nested;

		private object current;

		private bool disposed;

		public object Current => current;

		public Observed(IEnumerator source, Action after, Action<Exception> failed)
		{
			this.source = source;
			this.after = after;
			this.failed = failed;
		}

		private void Report(Exception ex)
		{
			try
			{
				failed?.Invoke(ex);
			}
			catch
			{
			}
		}

		private void Notify()
		{
			try
			{
				after?.Invoke();
			}
			catch (Exception ex)
			{
				Report(ex);
			}
		}

		public bool MoveNext()
		{
			if (disposed)
			{
				return false;
			}
			try
			{
				if (nested != null)
				{
					nested.Dispose();
					nested = null;
				}
				if (source == null || !source.MoveNext())
				{
					Dispose();
					return false;
				}
				Notify();
				current = source.Current;
				if (current is IEnumerator enumerator)
				{
					nested = new Observed(enumerator, after, failed);
					current = nested;
				}
				return true;
			}
			catch
			{
				try
				{
					Dispose();
				}
				catch (Exception ex)
				{
					Report(ex);
				}
				throw;
			}
		}

		public void Reset()
		{
			throw new NotSupportedException();
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			try
			{
				nested?.Dispose();
			}
			finally
			{
				nested = null;
				try
				{
					(source as IDisposable)?.Dispose();
				}
				finally
				{
					current = null;
					Notify();
				}
			}
		}
	}

	public static IEnumerator Observe(IEnumerator source, Action after, Action<Exception> failed)
	{
		return new Observed(source, after, failed);
	}
}
public abstract class SkinSoundAsset
{
}
public sealed class UnityClipSoundAsset : SkinSoundAsset
{
	public AudioClip[] Clips;
}
public sealed class WwiseEventSoundAsset : SkinSoundAsset
{
	public string BankId;

	public string[] Events;
}
public sealed class SkinAudioContextUnavailableException : Exception
{
	public SkinAudioContextUnavailableException(string message)
		: base(message)
	{
	}
}
public sealed class SkinSound
{
	public string Id;

	public string Anchor;

	public string Provenance;

	public SkinSoundAsset Asset;

	public float Gain = 0.7f;

	public float Cooldown = 0.15f;

	public float MinDistance = 3f;

	public float MaxDistance = 60f;

	public int MaxVoices = 2;

	public bool Loop;
}
public sealed class SkinSoundProfile
{
	public readonly Dictionary<string, SkinSound> Cues = new Dictionary<string, SkinSound>();

	public readonly Dictionary<string, string> NativeEventReplacements = new Dictionary<string, string>();

	public readonly List<NativeSoundLoop> NativeLoops = new List<NativeSoundLoop>();

	public void Validate()
	{
		foreach (KeyValuePair<string, SkinSound> cue in Cues)
		{
			SkinSound value = cue.Value;
			if (value == null || cue.Key != value.Id || string.IsNullOrWhiteSpace(value.Anchor) || string.IsNullOrWhiteSpace(value.Provenance) || value.Asset == null || !Limits.Finite(value.Gain) || value.Gain < 0f || value.Gain > 1f || !Limits.Finite(value.Cooldown) || value.Cooldown < 0f || !Limits.Finite(value.MinDistance) || value.MinDistance <= 0f || !Limits.Finite(value.MaxDistance) || value.MaxDistance <= value.MinDistance || value.MaxVoices < 1 || value.MaxVoices > 4)
			{
				throw new ArgumentException("Invalid species sound " + cue.Key);
			}
		}
		foreach (KeyValuePair<string, string> nativeEventReplacement in NativeEventReplacements)
		{
			if (!Cues.ContainsKey(nativeEventReplacement.Value) || Cues[nativeEventReplacement.Value].Loop)
			{
				throw new ArgumentException("Missing/non-one-shot replacement cue " + nativeEventReplacement.Key);
			}
		}
		foreach (NativeSoundLoop nativeLoop in NativeLoops)
		{
			if (nativeLoop == null || !Cues.ContainsKey(nativeLoop.Cue) || !Cues[nativeLoop.Cue].Loop)
			{
				throw new ArgumentException("Loop replacement requires an explicit loop cue");
			}
		}
	}
}
public interface ISkinSoundBackend : IDisposable
{
	bool Available { get; }

	void Validate(SkinSound sound);

	SoundResult Play(SkinSound sound, Transform anchor);
}
public interface ISkinNativeSoundBackend
{
	NativeSoundPlayback PlayNative(SkinSound sound, Transform anchor);

	bool StopNative(uint playingId);
}
public interface ISkinSoundBackendUpdate
{
	void Update();
}
public sealed class SkinAudioRouter : IDisposable
{
	private readonly SkinSoundProfile profile;

	private readonly ISkinSoundBackend backend;

	private readonly Func<string, Transform> anchor;

	private readonly SoundGate gate = new SoundGate();

	private readonly NativeSoundRouter native;

	private double nativeNow;

	private bool disposed;

	public SkinAudioRouter(SkinSoundProfile profile, ISkinSoundBackend backend, Func<string, Transform> anchor)
	{
		SkinAudioRouter skinAudioRouter = this;
		if (profile == null || backend == null || anchor == null)
		{
			throw new ArgumentNullException("Audio routing context");
		}
		try
		{
			profile.Validate();
			foreach (SkinSound value in profile.Cues.Values)
			{
				backend.Validate(value);
			}
			native = new NativeSoundRouter(profile.NativeEventReplacements, profile.NativeLoops, (string id) => skinAudioRouter.NativeCue(id, skinAudioRouter.nativeNow), (uint id) => ((ISkinNativeSoundBackend)backend).StopNative(id));
		}
		catch
		{
			SkinAudioCleanupPump.Release(backend);
			throw;
		}
		this.profile = profile;
		this.backend = backend;
		this.anchor = anchor;
	}

	private SoundResult Cue(string id, double now)
	{
		if (!profile.Cues.TryGetValue(id, out var sound) || sound.Loop || !backend.Available)
		{
			return SoundResult.Unavailable;
		}
		Transform target = anchor(sound.Anchor);
		if (!Object.op_Implicit((Object)(object)target))
		{
			return SoundResult.Unavailable;
		}
		return gate.Dispatch(id, now, sound.Cooldown, () => backend.Play(sound, target));
	}

	public bool TryCue(string id, double now)
	{
		return Cue(id, now) == SoundResult.Played;
	}

	private NativeSoundPlayback NativeCue(string id, double now)
	{
		ISkinNativeSoundBackend nativeBackend = backend as ISkinNativeSoundBackend;
		if (nativeBackend == null || !backend.Available || !profile.Cues.TryGetValue(id, out var sound))
		{
			return new NativeSoundPlayback(SoundResult.Unavailable);
		}
		Transform target = anchor(sound.Anchor);
		if (!Object.op_Implicit((Object)(object)target))
		{
			return new NativeSoundPlayback(SoundResult.Unavailable);
		}
		NativeSoundPlayback playback = default(NativeSoundPlayback);
		SoundResult num = gate.Dispatch(id, now, sound.Cooldown, delegate
		{
			playback = nativeBackend.PlayNative(sound, target);
			return playback.Result;
		});
		return new NativeSoundPlayback(num, (num == SoundResult.Played) ? playback.PlayingId : 0u);
	}

	public NativeSoundDecision TryReplaceNative(string exactEvent, double now)
	{
		nativeNow = now;
		return native.Route(exactEvent);
	}

	public void Update()
	{
		(backend as ISkinSoundBackendUpdate)?.Update();
		native.RetryPendingStops();
	}

	public void Dispose()
	{
		if (!disposed)
		{
			native.StopOwned();
			backend.Dispose();
			gate.Reset();
			disposed = true;
		}
	}
}
public sealed class SkinAudioCleanupPump : MonoBehaviour
{
	private static readonly RetryDisposalQueue pending = new RetryDisposalQueue();

	private static SkinAudioCleanupPump instance;

	private static bool quitting;

	public static void Release(IDisposable backend)
	{
		//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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		pending.Add(backend);
		pending.Tick(Time.unscaledTime, delegate(Exception ex2)
		{
			Debug.LogError((object)("ECLIPSE_AUDIO_CLEANUP_RETAINED " + ex2));
		});
		if (pending.Count > 0 && !Object.op_Implicit((Object)(object)instance) && !quitting)
		{
			try
			{
				GameObject val = new GameObject("EclipseSpeciesAudioCleanup")
				{
					hideFlags = (HideFlags)1
				};
				Object.DontDestroyOnLoad((Object)val);
				instance = val.AddComponent<SkinAudioCleanupPump>();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("ECLIPSE_AUDIO_CLEANUP_SCHEDULER_FAILED " + ex));
			}
		}
	}

	private void Update()
	{
		pending.Tick(Time.unscaledTime, delegate(Exception ex)
		{
			Debug.LogError((object)("ECLIPSE_AUDIO_CLEANUP_RETRY " + ex));
		});
		if (pending.Count == 0)
		{
			instance = null;
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}
	}

	private void OnApplicationQuit()
	{
		quitting = true;
	}
}
public sealed class UnitySkinSoundBackend : ISkinSoundBackend, IDisposable
{
	private readonly AudioListener listener;

	private readonly AudioMixerGroup mixer;

	private readonly List<AudioSource> voices = new List<AudioSource>();

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

	private readonly Dictionary<AudioSource, string> cueIds = new Dictionary<AudioSource, string>();

	public bool Available
	{
		get
		{
			if (Object.op_Implicit((Object)(object)listener) && ((Behaviour)listener).isActiveAndEnabled)
			{
				return Object.op_Implicit((Object)(object)mixer);
			}
			return false;
		}
	}

	public UnitySkinSoundBackend(AudioListener listener, AudioMixerGroup mixer)
	{
		if (!Object.op_Implicit((Object)(object)listener) || !Object.op_Implicit((Object)(object)mixer))
		{
			throw new SkinAudioContextUnavailableException("Explicit listener and game SFX routing required");
		}
		this.listener = listener;
		this.mixer = mixer;
	}

	public void Validate(SkinSound sound)
	{
		if (!(sound.Asset is UnityClipSoundAsset { Clips: not null } unityClipSoundAsset) || unityClipSoundAsset.Clips.Length == 0 || Array.Exists(unityClipSoundAsset.Clips, (AudioClip c) => !Object.op_Implicit((Object)(object)c)))
		{
			throw new ArgumentException("Unity backend requires loaded clips: " + sound.Id);
		}
	}

	public SoundResult Play(SkinSound s, Transform anchor)
	{
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		if (!Available)
		{
			return SoundResult.Unavailable;
		}
		AudioClip[] clips = ((UnityClipSoundAsset)s.Asset).Clips;
		voices.RemoveAll((AudioSource v) => !Object.op_Implicit((Object)(object)v));
		int num = 0;
		AudioSource val = null;
		foreach (AudioSource voice in voices)
		{
			if (voice.isPlaying && cueIds[voice] == s.Id)
			{
				num++;
			}
			if (!voice.isPlaying && (Object)(object)val == (Object)null)
			{
				val = voice;
			}
		}
		if (num >= s.MaxVoices)
		{
			return SoundResult.Limited;
		}
		if ((Object)(object)val == (Object)null)
		{
			if (voices.Count >= 8)
			{
				return SoundResult.Limited;
			}
			val = new GameObject("EclipseSpeciesVoice").AddComponent<AudioSource>();
			val.playOnAwake = false;
			voices.Add(val);
		}
		((Component)val).transform.SetParent(anchor, false);
		((Component)val).transform.localPosition = Vector3.zero;
		val.outputAudioMixerGroup = mixer;
		val.spatialBlend = 1f;
		val.dopplerLevel = 0f;
		val.rolloffMode = (AudioRolloffMode)0;
		val.minDistance = s.MinDistance;
		val.maxDistance = s.MaxDistance;
		val.volume = s.Gain;
		val.pitch = 1f;
		val.loop = false;
		variant.TryGetValue(s.Id, out var value);
		val.clip = clips[value % clips.Length];
		variant[s.Id] = (value + 1) % clips.Length;
		cueIds[val] = s.Id;
		val.Play();
		return SoundResult.Played;
	}

	public void Dispose()
	{
		foreach (AudioSource voice in voices)
		{
			if (Object.op_Implicit((Object)(object)voice))
			{
				voice.Stop();
				Object.Destroy((Object)(object)((Component)voice).gameObject);
			}
		}
		voices.Clear();
		cueIds.Clear();
		variant.Clear();
	}
}
public interface ISkinEffectDriver : IDisposable
{
	void Update();
}
public sealed class CosmeticRendererLease : IDisposable
{
	private readonly Dictionary<Renderer, bool> owned = new Dictionary<Renderer, bool>();

	public void Hide(GameObject instance)
	{
		if (!Object.op_Implicit((Object)(object)instance))
		{
			return;
		}
		Renderer[] componentsInChildren = instance.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if (!owned.ContainsKey(val))
			{
				owned.Add(val, val.enabled);
			}
			val.enabled = false;
		}
	}

	public void Dispose()
	{
		foreach (KeyValuePair<Renderer, bool> item in owned)
		{
			if (Object.op_Implicit((Object)(object)item.Key) && !item.Key.enabled)
			{
				item.Key.enabled = item.Value;
			}
		}
		owned.Clear();
	}
}
public sealed class CrocoLeapCosmetics : ISkinEffectDriver, IDisposable
{
	private static readonly FieldInfo left = Field("leftFistEffectInstance");

	private static readonly FieldInfo right = Field("rightFistEffectInstance");

	private readonly SkinnedMeshRenderer renderer;

	private CharacterBody actor;

	private EntityStateMachine[] machines;

	private readonly CosmeticRendererLease lease = new CosmeticRendererLease();

	private readonly HashSet<GameObject> previous = new HashSet<GameObject>();

	private bool disposed;

	private static FieldInfo Field(string name)
	{
		return typeof(BaseLeap).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
	}

	public CrocoLeapCosmetics(SkinnedMeshRenderer renderer)
	{
		if (!Object.op_Implicit((Object)(object)renderer) || left == null || right == null || left.FieldType != typeof(GameObject) || right.FieldType != typeof(GameObject))
		{
			throw new ArgumentException("Validated Croco leap effect fields required");
		}
		this.renderer = renderer;
	}

	public void Update()
	{
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		if (disposed)
		{
			return;
		}
		CharacterBody val = CrocoModelActor.Resolve((Component)(object)renderer);
		if ((Object)(object)val != (Object)(object)actor)
		{
			lease.Dispose();
			previous.Clear();
			actor = val;
			machines = (Object.op_Implicit((Object)(object)actor) ? ((Component)actor).GetComponents<EntityStateMachine>() : null);
		}
		if (!Object.op_Implicit((Object)(object)actor) || !Object.op_Implicit((Object)(object)actor.modelLocator) || !Object.op_Implicit((Object)(object)actor.modelLocator.modelTransform) || machines == null)
		{
			return;
		}
		HashSet<GameObject> hashSet = new HashSet<GameObject>();
		EntityStateMachine[] array = machines;
		foreach (EntityStateMachine val2 in array)
		{
			BaseLeap val3 = (BaseLeap)(Object.op_Implicit((Object)(object)val2) ? /*isinst with value type is only supported in some contexts*/: null);
			if (val3 == null)
			{
				continue;
			}
			FieldInfo[] array2 = new FieldInfo[2] { left, right };
			for (int j = 0; j < array2.Length; j++)
			{
				object? value = array2[j].GetValue(val3);
				GameObject val4 = (GameObject)((value is GameObject) ? value : null);
				if (Object.op_Implicit((Object)(object)val4))
				{
					Scene scene = val4.scene;
					if (((Scene)(ref scene)).IsValid() && val4.transform.IsChildOf(actor.modelLocator.modelTransform))
					{
						hashSet.Add(val4);
					}
				}
			}
		}
		if (!previous.SetEquals(hashSet))
		{
			lease.Dispose();
			previous.Clear();
			foreach (GameObject item in hashSet)
			{
				previous.Add(item);
			}
		}
		foreach (GameObject item2 in hashSet)
		{
			lease.Hide(item2);
		}
	}

	public void Dispose()
	{
		if (!disposed)
		{
			lease.Dispose();
			previous.Clear();
			machines = null;
			actor = null;
			disposed = true;
		}
	}
}
public interface ISkinMotionTiming : IDisposable
{
	void Retime(MotionLayer[] composed, IEnumerable<NativeMotionCandidate> observed);

	bool OwnsCues(MotionClip clip);

	void AdvanceCues(Action<string> emit);
}
public sealed class CrocoGaitTiming : ISkinMotionTiming, IDisposable
{
	private readonly CharacterMotor motor;

	private readonly Transform space;

	private readonly GaitCalibration calibration;

	private readonly GaitCycleClock clock = new GaitCycleClock();

	private bool warned;

	private readonly GaitContactClock contacts;

	private bool audible;

	public CrocoGaitTiming(CharacterBody body, Transform meshSpace, MotionBank bank, GaitCalibration calibration)
	{
		if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.characterMotor) || !Object.op_Implicit((Object)(object)meshSpace) || calibration == null)
		{
			throw new ArgumentException("Gait context");
		}
		calibration.Validate(bank);
		motor = body.characterMotor;
		space = meshSpace;
		this.calibration = calibration;
		contacts = new GaitContactClock(calibration.ContactCues);
	}

	public void Retime(MotionLayer[] composed, IEnumerable<NativeMotionCandidate> observed)
	{
		//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_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)motor) || !Object.op_Implicit((Object)(object)space))
		{
			return;
		}
		Vector3 val = space.InverseTransformVector(motor.velocity);
		V3 travel = calibration.EffectiveTravel(composed);
		clock.MaximumCyclesPerSecond = calibration.MaximumCyclesPerSecond;
		Vector3 velocity = motor.velocity;
		double num = Math.Sqrt(velocity.x * velocity.x + velocity.z * velocity.z);
		double time = ((calibration.CyclesPerWorldMetre.Count == 0) ? clock.Advance(new V3(val.x, val.y, 0f), travel, Time.time, motor.isGrounded) : clock.AdvanceAtRate(calibration.EffectiveCadence(composed, (num < 0.05) ? 0.0 : num), Time.time, motor.isGrounded, calibration.Acceleration));
		audible = motor.isGrounded && clock.CyclesPerSecond > 0.0;
		foreach (WeightedMotion item in composed.SelectMany((MotionLayer l) => l.Samples).Concat(observed.SelectMany((NativeMotionCandidate c) => c.Motion.Samples)).Distinct())
		{
			if (calibration.TravelPerCycle.ContainsKey(item.Clip.Id))
			{
				item.Time = time;
				item.KnownEntry = false;
			}
		}
		if (clock.RateLimited && !warned)
		{
			warned = true;
			Debug.LogWarning((object)"ECLIPSE_GAIT_RATE_LIMIT: extreme speed exceeds authored cadence; movement unchanged");
		}
	}

	public bool OwnsCues(MotionClip clip)
	{
		return calibration.TravelPerCycle.ContainsKey(clip.Id);
	}

	public void AdvanceCues(Action<string> emit)
	{
		contacts.Advance(clock.Phase, audible, emit);
	}

	public void Dispose()
	{
	}
}
public static class SkinMesh
{
	private static Vector3 V(BinaryReader r)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(Limits.ReadFloat(r), Limits.ReadFloat(r), Limits.ReadFloat(r));
	}

	public static Mesh Read(Stream stream, MotionBank bank)
	{
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0368: 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_03c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0448: 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_0459: Unknown result type (might be due to invalid IL or missing references)
		//IL_0460: 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_0470: Unknown result type (might be due to invalid IL or missing references)
		//IL_0478: Unknown result type (might be due to invalid IL or missing references)
		//IL_0480: Unknown result type (might be due to invalid IL or missing references)
		//IL_0488: Unknown result type (might be due to invalid IL or missing references)
		//IL_0490: Unknown result type (might be due to invalid IL or missing references)
		//IL_0498: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a0: Expected O, but got Unknown
		//IL_03f7: 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_03f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0286: Unknown result type (might be due to invalid IL or missing references)
		//IL_027f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0430: Unknown result type (might be due to invalid IL or missing references)
		//IL_0435: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0294: Unknown result type (might be due to invalid IL or missing references)
		//IL_0299: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02af: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
		if (!stream.CanSeek || stream.Length > 67108864)
		{
			throw new InvalidDataException("Mesh stream/size");
		}
		using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
		if (binaryReader.ReadUInt32() != 827151173)
		{
			throw new InvalidDataException("Unsupported ESM version");
		}
		int num = Limits.Count(binaryReader, 3, 200000);
		int num2 = Limits.Count(binaryReader, 3, 1200000);
		int num3 = Limits.Count(binaryReader, 1, 256);
		if (num2 % 3 != 0 || num3 != bank.Bones.Length)
		{
			throw new InvalidDataException("Mesh topology/skeleton mismatch");
		}
		if (stream.Length - stream.Position != (long)num * 64L + (long)num2 * 4L)
		{
			throw new InvalidDataException("Mesh length mismatch");
		}
		Vector3[] array = (Vector3[])(object)new Vector3[num];
		Vector3[] array2 = (Vector3[])(object)new Vector3[num];
		Vector2[] array3 = (Vector2[])(object)new Vector2[num];
		BoneWeight[] array4 = (BoneWeight[])(object)new BoneWeight[num];
		Vector4[] array5 = (Vector4[])(object)new Vector4[num];
		for (int i = 0; i < num; i++)
		{
			array[i] = V(binaryReader);
			array2[i] = V(binaryReader);
			array3[i] = new Vector2(Limits.ReadFloat(binaryReader), Limits.ReadFloat(binaryReader));
			if (Math.Abs(((Vector3)(ref array2[i])).magnitude - 1f) > 0.002f)
			{
				throw new InvalidDataException("Nonunit mesh normal");
			}
			int[] array6 = new int[4];
			float[] array7 = new float[4];
			float num4 = 0f;
			for (int j = 0; j < 4; j++)
			{
				array6[j] = binaryReader.ReadInt32();
				if (array6[j] < 0 || array6[j] >= num3)
				{
					throw new InvalidDataException("Bone index");
				}
			}
			for (int k = 0; k < 4; k++)
			{
				array7[k] = Limits.ReadFloat(binaryReader);
				if (array7[k] < 0f || array7[k] > 1f)
				{
					throw new InvalidDataException("Bone weight");
				}
				num4 += array7[k];
			}
			if ((double)Math.Abs(num4 - 1f) > 0.0001)
			{
				throw new InvalidDataException("Weight sum");
			}
			int num5 = i;
			BoneWeight val = default(BoneWeight);
			((BoneWeight)(ref val)).boneIndex0 = array6[0];
			((BoneWeight)(ref val)).boneIndex1 = array6[1];
			((BoneWeight)(ref val)).boneIndex2 = array6[2];
			((BoneWeight)(ref val)).boneIndex3 = array6[3];
			((BoneWeight)(ref val)).weight0 = array7[0];
			((BoneWeight)(ref val)).weight1 = array7[1];
			((BoneWeight)(ref val)).weight2 = array7[2];
			((BoneWeight)(ref val)).weight3 = array7[3];
			array4[num5] = val;
			Vector3 val2 = Vector3.Cross((Mathf.Abs(array2[i].y) > 0.99f) ? Vector3.right : Vector3.up, array2[i]);
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			array5[i] = new Vector4(normalized.x, normalized.y, normalized.z, 1f);
		}
		int[] array8 = new int[num2];
		for (int l = 0; l < num2; l++)
		{
			array8[l] = binaryReader.ReadInt32();
			if (array8[l] < 0 || array8[l] >= num)
			{
				throw new InvalidDataException("Triangle index");
			}
		}
		Matrix4x4[] array9 = (Matrix4x4[])(object)new Matrix4x4[num3];
		Matrix4x4[] array10 = (Matrix4x4[])(object)new Matrix4x4[num3];
		for (int m = 0; m < num3; m++)
		{
			Bone bone = bank.Bones[m];
			Pose rest = bone.Rest;
			Matrix4x4 val3 = Matrix4x4.TRS(new Vector3(rest.Position.X, rest.Position.Y, rest.Position.Z), new Quaternion(rest.Rotation.X, rest.Rotation.Y, rest.Rotation.Z, rest.Rotation.W), new Vector3(rest.Scale.X, rest.Scale.Y, rest.Scale.Z));
			array9[m] = ((bone.Parent < 0) ? val3 : (array9[bone.Parent] * val3));
			if (Mathf.Abs(((Matrix4x4)(ref array9[m])).determinant) < 1E-08f)
			{
				throw new InvalidDataException("Singular bind pose");
			}
			array10[m] = ((Matrix4x4)(ref array9[m])).inverse;
		}
		Mesh val4 = new Mesh
		{
			name = bank.SkinId,
			indexFormat = (IndexFormat)1,
			vertices = array,
			normals = array2,
			uv = array3,
			boneWeights = array4,
			bindposes = array10,
			tangents = array5,
			triangles = array8
		};
		val4.RecalculateBounds();
		return val4;
	}
}
public struct V3
{
	public float X;

	public float Y;

	public float Z;

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

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

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

	public static V3 operator *(V3 a, float t)
	{
		return new V3(a.X * t, a.Y * t, a.Z * t);
	}

	public static V3 Lerp(V3 a, V3 b, float t)
	{
		return a + (b - a) * t;
	}
}
public struct Q4
{
	public float X;

	public float Y;

	public float Z;

	public float W;

	public static Q4 Identity => new Q4(0f, 0f, 0f, 1f);

	public Q4 Inverse => new Q4(0f - X, 0f - Y, 0f - Z, W);

	public Q4(float x, float y, float z, float w)
	{
		X = x;
		Y = y;
		Z = z;
		W = w;
	}

	public float Dot(Q4 b)
	{
		return X * b.X + Y * b.Y + Z * b.Z + W * b.W;
	}

	public Q4 Normalized()
	{
		float num = (float)Math.Sqrt(Dot(this));
		if (num < 1E-08f || !Limits.Finite(num))
		{
			throw new InvalidDataException("Invalid quaternion");
		}
		return new Q4(X / num, Y / num, Z / num, W / num);
	}

	public static Q4 operator *(Q4 a, Q4 b)
	{
		return new Q4(a.W * b.X + a.X * b.W + a.Y * b.Z - a.Z * b.Y, a.W * b.Y - a.X * b.Z + a.Y * b.W + a.Z * b.X, a.W * b.Z + a.X * b.Y - a.Y * b.X + a.Z * b.W, a.W * b.W - a.X * b.X - a.Y * b.Y - a.Z * b.Z);
	}

	public static Q4 Blend(Q4 a, Q4 b, float t)
	{
		float num = ((!(a.Dot(b) < 0f)) ? 1 : (-1));
		return new Q4(a.X * (1f - t) + b.X * t * num, a.Y * (1f - t) + b.Y * t * num, a.Z * (1f - t) + b.Z * t * num, a.W * (1f - t) + b.W * t * num).Normalized();
	}
}
public struct Pose
{
	public V3 Position;

	public V3 Scale;

	public Q4 Rotation;

	public Pose(V3 p, Q4 r, V3 s)
	{
		Position = p;
		Rotation = r;
		Scale = s;
	}

	public static Pose Blend(Pose a, Pose b, float t)
	{
		return new Pose(V3.Lerp(a.Position, b.Position, t), Q4.Blend(a.Rotation, b.Rotation, t), V3.Lerp(a.Scale, b.Scale, t));
	}

	public static Pose Add(Pose basis, Pose value, Pose rest, float weight)
	{
		V3 v = new V3(value.Scale.X / rest.Scale.X, value.Scale.Y / rest.Scale.Y, value.Scale.Z / rest.Scale.Z);
		return new Pose(basis.Position + (value.Position - rest.Position) * weight, (basis.Rotation * Q4.Blend(Q4.Identity, rest.Rotation.Inverse * value.Rotation, weight)).Normalized(), new V3(basis.Scale.X * (1f + (v.X - 1f) * weight), basis.Scale.Y * (1f + (v.Y - 1f) * weight), basis.Scale.Z * (1f + (v.Z - 1f) * weight)));
	}
}
public sealed class Bone
{
	public string Name;

	public int Parent;

	public Pose Rest;
}
public sealed class Track
{
	public int Bone;

	public Pose[] Frames;
}
public sealed class Cue
{
	public float Phase;

	public string Sound;
}
public sealed class MotionClip
{
	public string Id;

	public float Duration;

	public bool Loop;

	public int FrameCount;

	public Track[] Tracks;

	public Cue[] Cues;

	public Pose Sample(Track track, double normalizedTime)
	{
		if (!Limits.Finite(normalizedTime))
		{
			throw new InvalidDataException("Invalid sample time");
		}
		double num = (Loop ? (normalizedTime - Math.Floor(normalizedTime)) : Math.Max(0.0, Math.Min(1.0, normalizedTime))) * (double)(FrameCount - 1);
		int num2 = Math.Min(FrameCount - 1, (int)num);
		int num3 = Math.Min(num2 + 1, FrameCount - 1);
		return Pose.Blend(track.Frames[num2], track.Frames[num3], (float)(num - (double)num2));
	}
}
public enum MotionBlend : byte
{
	Override,
	Additive
}
public enum MotionClock : byte
{
	NativeState,
	Elapsed,
	Parameter
}
public sealed class MotionOccurrence
{
	public long Serial;

	public int Hash;

	public double ObservedAt;

	public double EntryPhase;

	public double LastPhase;

	public double Elapsed(double now, float duration)
	{
		return EntryPhase + Math.Max(0.0, now - ObservedAt) / (double)duration;
	}
}
public sealed class ObservedStateClock
{
	private object previous;

	private double age;

	private double lastObserved;

	private long serial;

	private bool initialized;

	public bool BeganSinceLastObservation { get; private set; }

	public long Observe(object state, double stateAge, double now = 0.0)
	{
		if (!Limits.Finite(stateAge) || stateAge < 0.0 || !Limits.Finite(now))
		{
			throw new ArgumentException("State age");
		}
		bool flag = state != previous || stateAge < age - 0.0001;
		double num = now - lastObserved;
		BeganSinceLastObservation = flag && state != null && initialized && num >= 0.0 && num <= 0.25 && stateAge <= num + 0.05;
		if (flag)
		{
			serial++;
		}
		previous = state;
		age = stateAge;
		lastObserved = now;
		initialized = true;
		return serial;
	}
}
public sealed class ObservedMotionChannel
{
	private string token;

	private double changedAt;

	private float fade;

	private bool initialized;

	private WeightedMotion incoming;

	private WeightedMotion[] outgoing = new WeightedMotion[0];

	private static WeightedMotion Copy(WeightedMotion source, float weight)
	{
		return new WeightedMotion
		{
			Clip = source.Clip,
			Time = source.Time,
			Token = source.Token,
			Weight = weight,
			OriginGroup = source.OriginGroup,
			OriginPriority = source.OriginPriority,
			KnownEntry = source.KnownEntry
		};
	}

	private WeightedMotion[] Evaluate(double now)
	{
		float num = ((fade == 0f) ? 1f : ((float)Math.Max(0.0, Math.Min(1.0, (now - changedAt) / (double)fade))));
		List<WeightedMotion> list = new List<WeightedMotion>();
		WeightedMotion[] array = outgoing;
		foreach (WeightedMotion weightedMotion in array)
		{
			float num2 = weightedMotion.Weight * (1f - num);
			if (num2 > 0f)
			{
				list.Add(Copy(weightedMotion, num2));
			}
		}
		if (incoming != null)
		{
			list.Add(Copy(incoming, num));
		}
		return list.ToArray();
	}

	private static WeightedMotion[] Bounded(WeightedMotion[] history)
	{
		float num = history.Sum((WeightedMotion s) => s.Weight);
		WeightedMotion[] array = (from s in history
			where s.Weight > 1E-06f
			orderby s.Weight descending
			select s).Take(32).ToArray();
		float num2 = array.Sum((WeightedMotion s) => s.Weight);
		if (num2 > 0f)
		{
			WeightedMotion[] array2 = array;
			for (int num3 = 0; num3 < array2.Length; num3++)
			{
				array2[num3].Weight *= num / num2;
			}
		}
		return array;
	}

	public WeightedMotion[] Sample(MotionClip clip, string occurrence, double phase, double now, float fadeSeconds, string group = null, int priority = 0, bool entered = false)
	{
		if (!Limits.Finite(now) || !Limits.Finite(phase) || phase < 0.0 || !Limits.Finite(fadeSeconds) || fadeSeconds < 0f)
		{
			throw new ArgumentException("Observed motion time");
		}
		if (!initialized)
		{
			changedAt = now - (double)fadeSeconds;
			token = occurrence;
			initialized = true;
		}
		else if (token != occurrence)
		{
			outgoing = Bounded(Evaluate(now));
			changedAt = now;
			token = occurrence;
		}
		fade = fadeSeconds;
		incoming = ((clip == null) ? null : new WeightedMotion
		{
			Clip = clip,
			Time = phase,
			Token = occurrence,
			OriginGroup = group,
			OriginPriority = priority,
			KnownEntry = entered
		});
		WeightedMotion[] result = Evaluate(now);
		if (now - changedAt >= (double)fade)
		{
			outgoing = new WeightedMotion[0];
		}
		return result;
	}
}
public sealed class LayerMotionClock
{
	private long serial;

	private bool transitioning;

	private double lastNow;

	public MotionOccurrence Current { get; private set; }

	public MotionOccurrence Next { get; private set; }

	private MotionOccurrence New(int hash, double phase, double now)
	{
		return new MotionOccurrence
		{
			Serial = ++serial,
			Hash = hash,
			ObservedAt = now,
			EntryPhase = phase - Math.Floor(phase),
			LastPhase = phase
		};
	}

	public void Observe(int currentHash, double currentPhase, bool transition, int nextHash, double nextPhase, double now)
	{
		if (!Limits.Finite(currentPhase) || !Limits.Finite(nextPhase) || !Limits.Finite(now))
		{
			throw new ArgumentException("State clock");
		}
		if (now < lastNow)
		{
			Current = null;
			Next = null;
			transitioning = false;
		}
		if (Next != null && Next.Hash == currentHash && (Current == null || Current.Hash != currentHash || (transitioning && !transition && Math.Abs(currentPhase - Next.LastPhase) < Math.Abs(currentPhase - Current.LastPhase))))
		{
			Current = Next;
			Next = null;
		}
		else if (Current == null || Current.Hash != currentHash || currentPhase < Current.LastPhase - 0.0001)
		{
			Current = New(currentHash, currentPhase, now);
		}
		Current.LastPhase = currentPhase;
		if (transition)
		{
			if (Next == null || Next.Hash != nextHash || nextPhase < Next.LastPhase - 0.0001)
			{
				Next = New(nextHash, nextPhase, now);
			}
			Next.LastPhase = nextPhase;
		}
		else
		{
			Next = null;
		}
		transitioning = transition;
		lastNow = now;
	}
}
public sealed class MotionBinding
{
	public string NativeClip;

	public string Clip;

	public string Layer;

	public string Group;

	public string Parameter;

	public int Priority;

	public MotionBlend Blend;

	public MotionClock Clock;
}
public static class Limits
{
	public static bool Finite(double x)
	{
		if (!double.IsNaN(x))
		{
			return !double.IsInfinity(x);
		}
		return false;
	}

	public static float ReadFloat(BinaryReader r)
	{
		float num = r.ReadSingle();
		if (!Finite(num))
		{
			throw new InvalidDataException("Nonfinite value");
		}
		return num;
	}

	public static int Count(BinaryReader r, int min, int max)
	{
		int num = r.ReadInt32();
		if (num < min || num > max)
		{
			throw new InvalidDataException("Invalid count " + num);
		}
		return num;
	}

	public static string Text(BinaryReader r)
	{
		int num = Count(r, 1, 256);
		byte[] array = r.ReadBytes(num);
		if (array.Length != num)
		{
			throw new EndOfStreamException();
		}
		string text = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(array);
		if (text.Any(char.IsControl))
		{
			throw new InvalidDataException("Control character");
		}
		return text;
	}

	private static V3 Vec(BinaryReader r)
	{
		return new V3(ReadFloat(r), ReadFloat(r), ReadFloat(r));
	}

	public static Pose ReadPose(BinaryReader r)
	{
		V3 p = Vec(r);
		Q4 b = new Q4(ReadFloat(r), ReadFloat(r), ReadFloat(r), ReadFloat(r));
		V3 s = Vec(r);
		if ((double)Math.Abs(b.Dot(b) - 1f) > 0.002 || s.X <= 0f || s.Y <= 0f || s.Z <= 0f)
		{
			throw new InvalidDataException("Invalid rest/frame transform");
		}
		return new Pose(p, b.Normalized(), s);
	}
}
public sealed class MotionBank
{
	public string SkinId;

	public Bone[] Bones;

	public MotionClip[] Clips;

	public MotionBinding[] Bindings;

	public V3 BoundsCenter;

	public V3 BoundsSize;

	public MotionClip Find(string id)
	{
		return Clips.First((MotionClip c) => c.Id == id);
	}

	public static MotionBank Read(Stream stream)
	{
		if (!stream.CanSeek || stream.Length > 67108864)
		{
			throw new InvalidDataException("Motion bank size/stream");
		}
		using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
		if (binaryReader.ReadUInt32() != 827020101 || binaryReader.ReadInt32() != 1)
		{
			throw new InvalidDataException("Unsupported ESK version");
		}
		MotionBank motionBank = new MotionBank
		{
			SkinId = Limits.Text(binaryReader)
		};
		motionBank.BoundsCenter = new V3(Limits.ReadFloat(binaryReader), Limits.ReadFloat(binaryReader), Limits.ReadFloat(binaryReader));
		motionBank.BoundsSize = new V3(Limits.ReadFloat(binaryReader), Limits.ReadFloat(binaryReader), Limits.ReadFloat(binaryReader));
		if (motionBank.BoundsSize.X <= 0f || motionBank.BoundsSize.Y <= 0f || motionBank.BoundsSize.Z <= 0f)
		{
			throw new InvalidDataException("Invalid bounds");
		}
		motionBank.Bones = new Bone[Limits.Count(binaryReader, 1, 256)];
		HashSet<string> hashSet = new HashSet<string>();
		int num = 0;
		for (int i = 0; i < motionBank.Bones.Length; i++)
		{
			string text = Limits.Text(binaryReader);
			int num2 = binaryReader.ReadInt32();
			if (!hashSet.Add(text) || num2 < -1 || num2 >= i)
			{
				throw new InvalidDataException("Bone order/name");
			}
			if (num2 == -1)
			{
				num++;
			}
			motionBank.Bones[i] = new Bone
			{
				Name = text,
				Parent = num2,
				Rest = Limits.ReadPose(binaryReader)
			};
		}
		if (num != 1)
		{
			throw new InvalidDataException("Exactly one visual root required");
		}
		motionBank.Clips = new MotionClip[Limits.Count(binaryReader, 1, 256)];
		hashSet.Clear();
		long num3 = 0L;
		for (int j = 0; j < motionBank.Clips.Length; j++)
		{
			MotionClip motionClip = new MotionClip
			{
				Id = Limits.Text(binaryReader),
				Duration = Limits.ReadFloat(binaryReader)
			};
			byte b = binaryReader.ReadByte();
			if (!hashSet.Add(motionClip.Id) || motionClip.Duration <= 0f || motionClip.Duration > 120f || b > 1)
			{
				throw new InvalidDataException("Clip metadata");
			}
			motionClip.Loop = b == 1;
			motionClip.FrameCount = Limits.Count(binaryReader, 2, 3601);
			motionClip.Tracks = new Track[Limits.Count(binaryReader, 1, motionBank.Bones.Length)];
			HashSet<int> hashSet2 = new HashSet<int>();
			num3 += (long)motionClip.FrameCount * (long)motionClip.Tracks.Length;
			if (num3 > 1000000)
			{
				throw new InvalidDataException("Motion bank allocation budget");
			}
			for (int k = 0; k < motionClip.Tracks.Length; k++)
			{
				int num4 = binaryReader.ReadInt32();
				if (num4 < 0 || num4 >= motionBank.Bones.Length || !hashSet2.Add(num4))
				{
					throw new InvalidDataException("Track bone");
				}
				Track track = new Track
				{
					Bone = num4,
					Frames = new Pose[motionClip.FrameCount]
				};
				for (int l = 0; l < motionClip.FrameCount; l++)
				{
					track.Frames[l] = Limits.ReadPose(binaryReader);
				}
				motionClip.Tracks[k] = track;
			}
			motionClip.Cues = new Cue[Limits.Count(binaryReader, 0, 128)];
			float num5 = -1f;
			for (int m = 0; m < motionClip.Cues.Length; m++)
			{
				float num6 = Limits.ReadFloat(binaryReader);
				if (num6 < 0f || num6 >= 1f || num6 < num5)
				{
					throw new InvalidDataException("Cue phase/order");
				}
				num5 = num6;
				motionClip.Cues[m] = new Cue
				{
					Phase = num6,
					Sound = Limits.Text(binaryReader)
				};
			}
			motionBank.Clips[j] = motionClip;
		}
		motionBank.Bindings = new MotionBinding[Limits.Count(binaryReader, 1, 1024)];
		HashSet<string> hashSet3 = new HashSet<string>();
		for (int n = 0; n < motionBank.Bindings.Length; n++)
		{
			MotionBinding motionBinding = new MotionBinding
			{
				NativeClip = Limits.Text(binaryReader),
				Clip = Limits.Text(binaryReader),
				Layer = Limits.Text(binaryReader),
				Group = Limits.Text(binaryReader),
				Priority = binaryReader.ReadInt32(),
				Blend = (MotionBlend)binaryReader.ReadByte(),
				Clock = (MotionClock)binaryReader.ReadByte(),
				Parameter = Limits.Text(binaryReader)
			};
			if (!hashSet.Contains(motionBinding.Clip) || motionBinding.Priority < 0 || motionBinding.Priority > 1000 || (int)motionBinding.Blend > 1 || (int)motionBinding.Clock > 2 || !hashSet3.Add(motionBinding.Layer + "\n" + motionBinding.NativeClip))
			{
				throw new InvalidDataException("Binding metadata");
			}
			motionBank.Bindings[n] = motionBinding;
		}
		if (stream.Position != stream.Length)
		{
			throw new InvalidDataException("Trailing motion data");
		}
		return motionBank;
	}
}
public sealed class WeightedMotion
{
	public MotionClip Clip;

	public double Time;

	public float Weight;

	public string Token;

	public string OriginGroup;

	public int OriginPriority;

	public bool KnownEntry;
}
public sealed class MotionLayer
{
	public string Group;

	public int Priority;

	public MotionBlend Blend;

	public WeightedMotion[] Samples;

	public int[] BoneMask;
}
public sealed class NativeMotionCandidate
{
	public int SourceLayer;

	public int SourceRank;

	public MotionLayer Motion;
}
public static class NativeMotionComposition
{
	public static NativeMotionCandidate[] ResolveSources(IEnumerable<NativeMotionCandidate> candidates)
	{
		return (from c in candidates
			where c.Motion.Samples.Any((WeightedMotion s) => s.Weight > 0f)
			group c by c.Motion.Group).SelectMany(delegate(IGrouping<string, NativeMotionCandidate> g)
		{
			NativeMotionCandidate selected = (from c in g
				orderby c.SourceRank descending, c.SourceLayer descending
				select c).First();
			return g.Where((NativeMotionCandidate c) => c.SourceRank == selected.SourceRank && c.SourceLayer == selected.SourceLayer);
		}).ToArray();
	}

	public static MotionLayer[] ComposeSources(IEnumerable<NativeMotionCandidate> chosen)
	{
		List<MotionLayer> list = new List<MotionLayer>();
		foreach (var item in from c in chosen
			group c by new
			{
				c.SourceLayer,
				c.Motion.Blend
			})
		{
			Dictionary<int, int> priorities = new Dictionary<int, int>();
			Dictionary<int, List<int>> dictionary = new Dictionary<int, List<int>>();
			List<WeightedMotion> samples = new List<WeightedMotion>();
			foreach (NativeMotionCandidate item2 in item)
			{
				foreach (WeightedMotion item3 in item2.Motion.Samples.Where((WeightedMotion s) => s.Weight > 0f))
				{
					int count = samples.Count;
					samples.Add(item3);
					Track[] tracks = item3.Clip.Tracks;
					foreach (Track track in tracks)
					{
						if (item2.Motion.BoneMask == null || Array.IndexOf(item2.Motion.BoneMask, track.Bone) >= 0)
						{
							if (!priorities.TryGetValue(track.Bone, out var value) || item2.Motion.Priority > value)
							{
								priorities[track.Bone] = item2.Motion.Priority;
							}
							if (!dictionary.TryGetValue(track.Bone, out var value2))
							{
								value2 = (dictionary[track.Bone] = new List<int>());
							}
							value2.Add(count);
						}
					}
				}
			}
			foreach (IGrouping<string, KeyValuePair<int, List<int>>> item4 in from p in dictionary
				group p by priorities[p.Key] + ":" + string.Join(",", p.Value))
			{
				int key = item4.First().Key;
				list.Add(new MotionLayer
				{
					Group = "native-layer-" + item.Key.SourceLayer + "-" + item4.Key,
					Blend = item.Key.Blend,
					Priority = priorities[key],
					BoneMask = item4.Select((KeyValuePair<int, List<int>> p) => p.Key).ToArray(),
					Samples = dictionary[key].Select((int i) => samples[i]).ToArray()
				});
			}
		}
		return list.ToArray();
	}
}
public static class ObservedMotionComposition
{
	private static Dictionary<int, float> Weights(IEnumerable<NativeMotionCandidate> candidates)
	{
		Dictionary<int, float> dictionary = new Dictionary<int, float>();
		foreach (NativeMotionCandidate candidate in candidates)
		{
			WeightedMotion[] samples = candidate.Motion.Samples;
			foreach (WeightedMotion weightedMotion in samples)
			{
				Track[] tracks = weightedMotion.Clip.Tracks;
				foreach (Track track in tracks)
				{
					if (candidate.Motion.BoneMask == null || Array.IndexOf(candidate.Motion.BoneMask, track.Bone) >= 0)
					{
						dictionary.TryGetValue(track.Bone, out var value);
						dictionary[track.Bone] = value + weightedMotion.Weight;
					}
				}
			}
		}
		return dictionary;
	}

	public static NativeMotionCandidate[] Choose(List<NativeMotionCandidate> native, NativeMotionCandidate[] observed, HashSet<string> replacedClips)
	{
		List<NativeMotionCandidate> list = new List<NativeMotionCandidate>();
		foreach (var item in from c in native
			group c by new
			{
				c.SourceLayer,
				c.Motion.Blend
			})
		{
			Dictionary<int, float> before = Weights(item);
			foreach (NativeMotionCandidate item2 in item)
			{
				item2.Motion.Samples = item2.Motion.Samples.Where((WeightedMotion s) => !replacedClips.Contains(s.Clip.Id)).ToArray();
			}
			Dictionary<int, float> after = Weights(item);
			foreach (NativeMotionCandidate candidate in item)
			{
				foreach (IGrouping<float, int> item3 in from b in candidate.Motion.Samples.SelectMany((WeightedMotion s) => s.Clip.Tracks.Select((Track t) => t.Bone)).Distinct()
					where candidate.Motion.BoneMask == null || Array.IndexOf(candidate.Motion.BoneMask, b) >= 0
					group b by (!(after[b] > 0f)) ? 1f : (before[b] / after[b]))
				{
					float factor = item3.Key;
					list.Add(new NativeMotionCandidate
					{
						SourceLayer = candidate.SourceLayer,
						SourceRank = candidate.SourceRank,
						Motion = new MotionLayer
						{
							Group = candidate.Motion.Group,
							Priority = candidate.Motion.Priority,
							Blend = candidate.Motion.Blend,
							BoneMask = item3.ToArray(),
							Samples = candidate.Motion.Samples.Select((WeightedMotion s) => new WeightedMotion
							{
								Clip = s.Clip,
								Time = s.Time,
								Weight = Math.Min(1f, s.Weight * factor),
								Token = s.Token,
								KnownEntry = s.KnownEntry
							}).ToArray()
						}
					});
				}
			}
		}
		native.Clear();
		native.AddRange(list);
		return NativeMotionComposition.ResolveSources(native).Concat(observed).ToArray();
	}
}
public sealed class PoseMixer
{
	private readonly MotionBank bank;

	private readonly Pose[] scratch;

	private readonly float[] weights;

	private readonly MotionClip fallback;

	public PoseMixer(MotionBank bank, string fallbackClip = null)
	{
		this.bank = bank;
		scratch = new Pose[bank.Bones.Length];
		weights = new float[scratch.Length];
		fallback = ((fallbackClip == null) ? null : bank.Find(fallbackClip));
	}

	public void Evaluate(IEnumerable<MotionLayer> layers, Pose[] destination)
	{
		if (destination.Length != bank.Bones.Length)
		{
			throw new ArgumentException("Pose size");
		}
		for (int i = 0; i < destination.Length; i++)
		{
			destination[i] = bank.Bones[i].Rest;
		}
		if (fallback != null)
		{
			Track[] tracks = fallback.Tracks;
			foreach (Track track in tracks)
			{
				destination[track.Bone] = fallback.Sample(track, 0.0);
			}
		}
		foreach (MotionLayer item in layers.OrderBy((MotionLayer l) => l.Priority))
		{
			Array.Clear(weights, 0, weights.Length);
			WeightedMotion[] samples = item.Samples;
			foreach (WeightedMotion weightedMotion in samples)
			{
				if (!Limits.Finite(weightedMotion.Weight) || weightedMotion.Weight < 0f || weightedMotion.Weight > 1f)
				{
					throw new InvalidDataException("Blend weight");
				}
				if (weightedMotion.Weight == 0f)
				{
					continue;
				}
				Track[] tracks = weightedMotion.Clip.Tracks;
				foreach (Track track2 in tracks)
				{
					int bone = track2.Bone;
					if (item.BoneMask == null || Array.IndexOf(item.BoneMask, bone) >= 0)
					{
						float num2 = weights[bone] + weightedMotion.Weight;
						Pose pose = weightedMotion.Clip.Sample(track2, weightedMotion.Time);
						scratch[bone] = ((weights[bone] == 0f) ? pose : Pose.Blend(scratch[bone], pose, weightedMotion.Weight / num2));
						weights[bone] = num2;
					}
				}
			}
			for (int num3 = 0; num3 < weights.Length; num3++)
			{
				if (weights[num3] > 0f)
				{
					float num4 = Math.Min(1f, weights[num3]);
					destination[num3] = ((item.Blend == MotionBlend.Additive) ? Pose.Add(destination[num3], scratch[num3], bank.Bones[num3].Rest, num4) : Pose.Blend(destination[num3], scratch[num3], num4));
				}
			}
		}
	}
}
public sealed class CueCursor
{
	private string token;

	private double previous;

	public void Reset()
	{
		token = null;
		previous = 0.0;
	}

	public void Advance(MotionClip clip, string stateToken, double time, Action<string> emit, bool knownEntry = false)
	{
		if (!Limits.Finite(time) || time < 0.0)
		{
			throw new InvalidDataException("Audio clock");
		}
		double num = (clip.Loop ? time : Math.Min(1.0, time));
		if (token != stateToken || num < previous)
		{
			token = stateToken;
			previous = ((num <= 0.15 || (knownEntry && time <= 1.0)) ? (-1E-07) : num);
		}
		if (num - previous > 1.0)
		{
			previous = num;
			return;
		}
		long num2 = Math.Max(0L, (long)Math.Floor(previous));
		long num3 = (clip.Loop ? ((long)Math.Floor(num)) : 0);
		for (long num4 = num2; num4 <= num3; num4++)
		{
			Cue[] cues = clip.Cues;
			foreach (Cue cue in cues)
			{
				double num5 = (float)num4 + cue.Phase;
				if (num5 > previous && num5 <= num)
				{
					emit(cue.Sound);
				}
			}
		}
		previous = num;
	}
}
public sealed class MotionCueRouter
{
	private sealed class Stream
	{
		public readonly CueCursor Cursor = new CueCursor();

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

		public double Time;

		public double ZeroSince;

		public bool Zero;

		public bool Expired;
	}

	private readonly Dictionary<string, Stream> streams = new Dictionary<string, Stream>();

	public void Clear()
	{
		streams.Clear();
	}

	public void Advance(IEnumerable<NativeMotionCandidate> observed, IEnumerable<NativeMotionCandidate> chosen, Action<string> emit, Func<MotionClip, bool> suppress = null)
	{
		HashSet<NativeMotionCandidate> hashSet = new HashSet<NativeMotionCandidate>(chosen);
		HashSet<string> hashSet2 = new HashSet<string>();
		foreach (NativeMotionCandidate item in observed)
		{
			WeightedMotion[] samples = item.Motion.Samples;
			foreach (WeightedMotion sample in samples)
			{
				if (suppress != null && suppress(sample.Clip))
				{
					continue;
				}
				string text = item.Motion.Group + ":" + sample.Token;
				hashSet2.Add(text);
				if (!streams.TryGetValue(text, out var stream))
				{
					streams[text] = (stream = new Stream());
				}
				if (sample.Time < stream.Time || sample.Time - stream.Time > 1.0)
				{
					stream.Pending.Clear();
				}
				if (sample.Weight == 0f && !stream.Zero)
				{
					stream.Zero = true;
					stream.ZeroSince = sample.Time;
					stream.Expired = false;
				}
				if (stream.Zero && sample.Time - stream.ZeroSince > 1.0)
				{
					stream.Pending.Clear();
					stream.Expired = true;
				}
				bool audible = hashSet.Contains(item) && sample.Weight > 0f;
				if (audible)
				{
					foreach (string item2 in stream.Pending)
					{
						emit(item2);
					}
					stream.Pending.Clear();
				}
				else if (sample.Weight > 0f)
				{
					stream.Pending.Clear();
				}
				stream.Cursor.Advance(sample.Clip, sample.Token, sample.Time, delegate(string id)
				{
					if (audible)
					{
						emit(id);
					}
					else if (sample.Weight == 0f && !stream.Expired && stream.Pending.Count < 128)
					{
						stream.Pending.Add(id);
					}
				}, sample.KnownEntry);
				if (sample.Weight > 0f)
				{
					stream.Zero = false;
					stream.Expired = false;
				}
				stream.Time = sample.Time;
			}
		}
		string[] array = streams.Keys.Except(hashSet2).ToArray();
		foreach (string key in array)
		{
			streams.Remove(key);
		}
	}
}
public sealed class SoundGate
{
	private readonly Dictionary<string, double> last = new Dictionary<string, double>();

	public bool Admit(string id, double now, double cooldown)
	{
		if (!Limits.Finite(now) || !Limits.Finite(cooldown) || cooldown < 0.0)
		{
			throw new ArgumentException("Sound timing");
		}
		if (last.TryGetValue(id, out var value) && now - value < cooldown)
		{
			return false;
		}
		last[id] = now;
		return true;
	}

	public SoundResult Dispatch(string id, double now, double cooldown, Func<SoundResult> play)
	{
		if (!Limits.Finite(now) || !Limits.Finite(cooldown) || cooldown < 0.0)
		{
			throw new ArgumentException("Sound timing");
		}
		if (last.TryGetValue(id, out var value) && now >= value && now - value < cooldown)
		{
			return SoundResult.Limited;
		}
		SoundResult soundResult = play();
		if (soundResult == SoundResult.Played || soundResult == SoundResult.Limited)
		{
			last[id] = now;
		}
		return soundResult;
	}

	public void Reset()
	{
		last.Clear();
	}
}
public enum SoundResult
{
	Unavailable,
	Played,
	Limited
}
public sealed class AdvancedSkinDefinition
{
	public Mesh Mesh;

	public MotionBank Motion;

	public string FallbackClip = "idle";

	public Func<Func<string, Transform>, SkinAudioRouter> CreateAudio;

	public Func<Animator, MotionBank, ISkinMotionSource> CreateMotionSource;

	public float AirTransitionSeconds;

	public Func<SkinnedMeshRenderer, ISkinEffectDriver> CreateEffects;

	public Func<SkinnedMeshRenderer, MotionBank, ISkinMotionTiming> CreateMotionTiming;

	public readonly Dictionary<string, string[]> NativeLayerPreference = new Dictionary<string, string[]>();
}
public static class AdvancedSkinRegistry
{
	private static readonly Dictionary<Mesh, AdvancedSkinDefinition> definitions = new Dictionary<Mesh, AdvancedSkinDefinition>();

	public static void Register(AdvancedSkinDefinition definition)
	{
		if (definition == null || !Object.op_Implicit((Object)(object)definition.Mesh) || definition.Motion == null || definition.Mesh.bindposes.Length != definition.Motion.Bones.Length)
		{
			throw new ArgumentException("Incomplete advanced skin");
		}
		definition.Motion.Find(definition.FallbackClip);
		if (definitions.ContainsKey(definition.Mesh))
		{
			throw new ArgumentException("Mesh already registered");
		}
		definitions.Add(definition.Mesh, definition);
	}

	public static bool TryGet(Mesh mesh, out AdvancedSkinDefinition definition)
	{
		definition = null;
		if (Object.op_Implicit((Object)(object)mesh))
		{
			return definitions.TryGetValue(mesh, out definition);
		}
		return false;
	}
}
[DefaultExecutionOrder(32000)]
public sealed class SkinRigDriver : MonoBehaviour
{
	[SerializeField]
	private SkinnedMeshRenderer target;

	[SerializeField]
	private Mesh stockMesh;

	[SerializeField]
	private Animator animator;

	private Transform[] stockBones;

	private Transform[] bones;

	private Transform stockRoot;

	private Bounds stockBounds;

	private bool stockOffscreen;

	private Material[] stockMaterials;

	private GameObject container;

	private AdvancedSkinDefinition active;

	private Mesh failedMesh;

	private PoseMixer mixer;

	private Pose[] pose;

	private bool configured;

	private readonly AirPoseTransition airTransition = new AirPoseTransition();

	private bool[] transitionExempt;

	private SkinAudioRouter audio;

	private bool audioAttempted;

	private int audioAttempts;

	private float nextAudioAttempt;

	private ISkinMotionSource motionSource;

	private ISkinEffectDriver effects;

	private bool effectsAttempted;

	private ISkinMotionTiming motionTiming;

	private bool motionSourcesInitialized;

	private readonly Dictionary<int, LayerMotionClock> stateClocks = new Dictionary<int, LayerMotionClock>();

	private readonly List<AnimatorClipInfo> current = new List<AnimatorClipInfo>();

	private readonly List<AnimatorClipInfo> next = new List<AnimatorClipInfo>();

	private readonly MotionCueRouter cues = new MotionCueRouter();

	private readonly HashSet<string> unknown = new HashSet<string>();

	public bool IsConfigured => configured;

	public void Configure(SkinnedMeshRenderer renderer, Mesh fallback, Animator source, Material[] fallbackMaterials = null)
	{
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		if (active != null)
		{
			throw new InvalidOperationException("Cannot configure active rig");
		}
		if (!Object.op_Implicit((Object)(object)renderer) || !Object.op_Implicit((Object)(object)fallback) || !Object.op_Implicit((Object)(object)source))
		{
			throw new ArgumentException("Missing native rig context");
		}
		if (AdvancedSkinRegistry.TryGet(renderer.sharedMesh, out var _))
		{
			throw new ArgumentException("Configure the native renderer before applying an advanced skin");
		}
		Transform[] array = renderer.bones;
		if (array == null || array.Length != fallback.bindposes.Length || Array.Exists(array, (Transform b) => !Object.op_Implicit((Object)(object)b)))
		{
			throw new ArgumentException("Invalid native skeleton snapshot");
		}
		if (fallbackMaterials == null && (Object)(object)renderer.sharedMesh != (Object)(object)fallback)
		{
			throw new ArgumentException("Capture native materials before applying a custom skin");
		}
		Material[] array2 = fallbackMaterials ?? ((Renderer)renderer).sharedMaterials;
		if (array2.Length == 0 || Array.Exists(array2, (Material m) => !Object.op_Implicit((Object)(object)m)))
		{
			throw new ArgumentException("Missing native fallback materials");
		}
		ConfigureSnapshot(renderer, fallback, source, array, renderer.rootBone, ((Renderer)renderer).localBounds, renderer.updateWhenOffscreen, array2);
	}

	internal void ConfigureSnapshot(SkinnedMeshRenderer renderer, Mesh fallback, Animator source, Transform[] nativeBones, Transform root, Bounds bounds, bool offscreen, Material[] materials)
	{
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		if (active != null)
		{
			throw new InvalidOperationException("Cannot replace an active native snapshot");
		}
		if (!Object.op_Implicit((Object)(object)renderer) || !Object.op_Implicit((Object)(object)fallback) || !Object.op_Implicit((Object)(object)source) || nativeBones == null || nativeBones.Length != fallback.bindposes.Length || Array.Exists(nativeBones, (Transform b) => !Object.op_Implicit((Object)(object)b)) || materials == null || materials.Length == 0 || Array.Exists(materials, (Material m) => !Object.op_Implicit((Object)(object)m)))
		{
			throw new ArgumentException("Incomplete native rig snapshot");
		}
		target = renderer;
		stockMesh = fallback;
		animator = source;
		stockMaterials = (Material[])materials.Clone();
		stockBones = (Transform[])nativeBones.Clone();
		stockRoot = root;
		stockBounds = bounds;
		stockOffscreen = offscreen;
		configured = true;
		failedMesh = null;
	}

	private static Vector3 V(V3 v)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(v.X, v.Y, v.Z);
	}

	private static Quaternion Q(Q4 q)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		return new Quaternion(q.X, q.Y, q.Z, q.W);
	}

	private void Activate(AdvancedSkinDefinition definition)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Expected O, but got Unknown
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		if (!configured || !Object.op_Implicit((Object)(object)target) || !Object.op_Implicit((Object)(object)stockMesh) || !Object.op_Implicit((Object)(object)animator))
		{
			throw new InvalidOperationException("Native renderer must be configured before activating a custom rig");
		}
		active = definition;
		container = new GameObject("EclipseVisualRig_" + definition.Motion.SkinId);
		container.transform.SetParent(((Component)target).transform, false);
		MotionBank motion = definition.Motion;
		bones = (Transform[])(object)new Transform[motion.Bones.Length];
		pose = new Pose[bones.Length];
		mixer = new PoseMixer(motion, definition.FallbackClip);
		for (int i = 0; i < bones.Length; i++)
		{
			Bone bone = motion.Bones[i];
			bones[i] = new GameObject(bone.Name).transform;
			bones[i].SetParent((bone.Parent < 0) ? container.transform : bones[bone.Parent], false);
			pose[i] = bone.Rest;
		}
		mixer.Evaluate(new MotionLayer[0], pose);
		ApplyPose();
		target.bones = bones;
		target.rootBone = bones[0];
		((Renderer)target).localBounds = new Bounds(V(motion.BoundsCenter), V(motion.BoundsSize));
		target.updateWhenOffscreen = true;
		stateClocks.Clear();
		unknown.Clear();
		cues.Clear();
		ResetAudioAttempts();
		EnsureAudio();
	}

	private void ResetAudioAttempts()
	{
		audioAttempted = false;
		audioAttempts = 0;
		nextAudioAttempt = 0f;
	}

	private void DeferAudio()
	{
		StopAudio();
		audioAttempted = audioAttempts >= 6;
		nextAudioAttempt = Time.unscaledTime + Mathf.Min(16f, Mathf.Pow(2f, (float)(audioAttempts - 1)));
	}

	private void EnsureAudio()
	{
		if (!((Behaviour)this).isActiveAndEnabled || audioAttempted || active == null || audio != null || Time.unscaledTime < nextAudioAttempt)
		{
			return;
		}
		audioAttempted = true;
		audioAttempts++;
		try
		{
			if (active.CreateAudio != null)
			{
				audio = active.CreateAudio(FindAnchor);
				if (audio == null)
				{
					DeferAudio();
				}
			}
		}
		catch (SkinAudioContextUnavailableException)
		{
			DeferAudio();
		}
		catch (Exception ex2)
		{
			Debug.LogError((object)("ECLIPSE_CUSTOM_AUDIO_UNAVAILABLE " + ex2));
			StopAudio();
		}
	}

	private void StopAudio()
	{
		SkinAudioRouter skinAudioRouter = audio;
		audio = null;
		if (skinAudioRouter != null)
		{
			SkinAudioCleanupPump.Release(skinAudioRouter);
		}
		cues.Clear();
	}

	private void EmitCue(string id)
	{
		if (audio != null)
		{
			try
			{
				audio.TryCue(id, Time.unscaledTime);
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("ECLIPSE_CUSTOM_AUDIO_FAILED " + ex));
				StopAudio();
			}
		}
	}

	public NativeSoundDecision TryReplaceNativeSound(string exactEvent)
	{
		if (!((Behaviour)this).isActiveAndEnabled || audio == null)
		{
			return NativeSoundDecision.Forward;
		}
		try
		{
			return audio.TryReplaceNative(exactEvent, Time.unscaledTime);
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("ECLIPSE_CUSTOM_AUDIO_FAILED " + ex));
			StopAudio();
			return NativeSoundDecision.Forward;
		}
	}

	private Transform FindAnchor(string name)
	{
		if (active == null)
		{
			return null;
		}
		for (int i = 0; i < bones.Length; i++)
		{
			if (active.Motion.Bones[i].Name == name)
			{
				return bones[i];
			}
		}
		return null;
	}

	private void ApplyPose()
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < bones.Length; i++)
		{
			bones[i].localPosition = V(pose[i].Position);
			bones[i].localRotation = Q(pose[i].Rotation);
			bones[i].localScale = V(pose[i].Scale);
		}
	}

	private int SourceRank(string group, string layer)
	{
		if (!active.NativeLayerPreference.TryGetValue(group, out var value))
		{
			return 0;
		}
		int num = Array.IndexOf(value, layer);
		if (num >= 0)
		{
			return value.Length - num;
		}
		return 0;
	}

	private void Collect(List<AnimatorClipInfo> infos, int layer, AnimatorStateInfo state, MotionOccurrence occurrence, float transitionWeight, List<NativeMotionCandidate> candidates)
	{
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		float num = infos.Sum((AnimatorClipInfo i) => Math.Max(0f, ((AnimatorClipInfo)(ref i)).weight));
		if ((double)num <= 1E-06)
		{
			return;
		}
		string layerName = animator.GetLayerName(layer);
		float num2 = ((layer == 0) ? 1f : animator.GetLayerWeight(layer));
		if (num2 <= 0f)
		{
			return;
		}
		foreach (AnimatorClipInfo info2 in infos)
		{
			AnimatorClipInfo info = info2;
			if (!Object.op_Implicit((Object)(object)((AnimatorClipInfo)(ref info)).clip) || ((AnimatorClipInfo)(ref info)).weight <= 0f)
			{
				continue;
			}
			MotionBinding binding = active.Motion.Bindings.FirstOrDefault((MotionBinding b) => b.NativeClip == ((Object)((AnimatorClipInfo)(ref info)).clip).name && (b.Layer == "*" || b.Layer == layerName));
			if (binding == null)
			{
				string text = layerName + "/" + ((Object)((AnimatorClipInfo)(ref info)).clip).name;
				if (unknown.Add(text))
				{
					Debug.LogWarning((object)("ECLIPSE_UNMAPPED_MOTION " + active.Motion.SkinId + " " + text));
				}
				continue;
			}
			MotionClip motionClip = active.Motion.Find(binding.Clip);
			double time = ((AnimatorStateInfo)(ref state)).normalizedTime;
			if (binding.Clock == MotionClock.Elapsed)
			{
				time = occurrence.Elapsed(Time.time, motionClip.Duration);
			}
			else if (binding.Clock == MotionClock.Parameter)
			{
				time = animator.GetFloat(binding.Parameter);
			}
			WeightedMotion weightedMotion = new WeightedMotion();
			weightedMotion.Clip = motionClip;
			weightedMotion.Time = time;
			weightedMotion.Weight = ((AnimatorClipInfo)(ref info)).weight / num * transitionWeight * num2;
			weightedMotion.Token = layer + ":" + occurrence.Serial + ":" + motionClip.Id;
			WeightedMotion weightedMotion2 = weightedMotion;
			NativeMotionCandidate nativeMotionCandidate = candidates.FirstOrDefault((NativeMotionCandidate c) => c.SourceLayer == layer && c.Motion.Group == binding.Group);
			if (nativeMotionCandidate == null)
			{
				nativeMotionCandidate = new NativeMotionCandidate
				{
					SourceLayer = layer,
					SourceRank = SourceRank(binding.Group, layerName),
					Motion = new MotionLayer
					{
						Group = binding.Group,
						Priority = binding.Priority,
						Blend = binding.Blend,
						Samples = new WeightedMotion[0]
					}
				};
				candidates.Add(nativeMotionCandidate);
			}
			nativeMotionCandidate.Motion.Samples = nativeMotionCandidate.Motion.Samples.Concat(new WeightedMotion[1] { weightedMotion2 }).ToArray();
		}
	}

	private void EnsureMotionSources()
	{
		if (((Behaviour)this).isActiveAndEnabled && active != null && !motionSourcesInitialized)
		{
			if (active.CreateMotionSource != null)
			{
				motionSource = active.CreateMotionSource(animator, active.Motion);
			}
			if (active.CreateMotionTiming != null)
			{
				motionTiming = active.CreateMotionTiming(target, active.Motion);
			}
			motionSourcesInitialized = true;
		}
	}

	private void UpdateEffects()
	{
		if (!effectsAttempted)
		{
			effectsAttempted = true;
			if (active.CreateEffects != null)
			{
				effects = active.CreateEffects(target);
			}
		}
		effects?.Update();
	}

	private void StopEffects()
	{
		ISkinEffectDriver skinEffectDriver = effects;
		effects = null;
		effectsAttempted = false;
		if (skinEffectDriver != null)
		{
			try
			{
				skinEffectDriver.Dispose();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("ECLIPSE_CUSTOM_EFFECTS_CLEANUP_FAILED " + ex));
			}
		}
	}

	private void UpdateMotion()
	{
		//IL_0030: 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_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: 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_0060: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		List<NativeMotionCandidate> list = new List<NativeMotionCandidate>();
		for (int i = 0; i < animator.layerCount; i++)
		{
			bool flag = animator.IsInTransition(i);
			float num;
			if (!flag)
			{
				num = 0f;
			}
			else
			{
				AnimatorTransitionInfo animatorTransitionInfo = animator.GetAnimatorTransitionInfo(i);
				num = Mathf.Clamp01(((AnimatorTransitionInfo)(ref animatorTransitionInfo)).normalizedTime);
			}
			float num2 = num;
			AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimator