Decompiled source of GangBeastsSandevistan v0.9.0

Mods\GangBeastsSandevistan.dll

Decompiled 17 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using GangBeastsSandevistan;
using HarmonyLib;
using Il2CppFemur;
using Il2CppGB.Game;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MelonLoader;
using MelonLoader.NativeUtils;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Users;
using UnityEngine.InputSystem.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Gang Beasts Sandevistan", "0.9.0-beta.1", "Delan", null)]
[assembly: MelonGame("Boneloaf", "Gang Beasts")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("GangBeastsSandevistan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.9.0.0")]
[assembly: AssemblyInformationalVersion("0.9.0-beta.1+c28132d28914e626b16ebebf1c3a0678c245bac4")]
[assembly: AssemblyProduct("GangBeastsSandevistan")]
[assembly: AssemblyTitle("GangBeastsSandevistan")]
[assembly: AssemblyVersion("0.9.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace GangBeastsSandevistan
{
	public sealed class AbilityState
	{
		public bool Active { get; private set; }

		public double EndsAt { get; private set; }

		public double ReadyAt { get; private set; }

		public bool TryStart(double now, bool allowed)
		{
			if (Active || !allowed || now < ReadyAt)
			{
				return false;
			}
			Active = true;
			EndsAt = now + 3.0;
			return true;
		}

		public void Stop(double now)
		{
			if (Active)
			{
				Active = false;
				ReadyAt = now + 6.0;
			}
		}

		public bool Expired(double now)
		{
			if (Active)
			{
				return now >= EndsAt;
			}
			return false;
		}

		public void Reset()
		{
			Active = false;
			double endsAt = (ReadyAt = 0.0);
			EndsAt = endsAt;
		}
	}
	public sealed class ButtonEdges
	{
		private readonly Dictionary<int, (bool left, bool right)> previous = new Dictionary<int, (bool, bool)>();

		public (bool cycle, bool toggle) Poll(int device, bool left, bool right)
		{
			(bool, bool) value;
			bool num = previous.TryGetValue(device, out value);
			previous[device] = (left, right);
			if (!num)
			{
				return (cycle: false, toggle: false);
			}
			return (cycle: left && !value.Item1, toggle: right && !value.Item2);
		}

		public void KeepOnly(HashSet<int> connected)
		{
			int[] array = previous.Keys.Where((int id) => !connected.Contains(id)).ToArray();
			foreach (int key in array)
			{
				previous.Remove(key);
			}
		}

		public void Reset()
		{
			previous.Clear();
		}
	}
	public static class LocalTimeHooks
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate float Scalar();

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void Force(IntPtr body, ref Vector3 value, ForceMode mode);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void VectorAccess(IntPtr body, ref Vector3 value);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void BodyScalarSet(IntPtr body, float value);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate float BodyScalarGet(IntPtr body);

		private static NativeHook<Scalar>? delta;

		private static NativeHook<Scalar>? fixedDelta;

		private static NativeHook<Force>? force;

		private static NativeHook<Force>? torque;

		private static NativeHook<Force>? relativeForce;

		private static NativeHook<Force>? relativeTorque;

		private static NativeHook<BodyScalarSet>? setMaxAngular;

		private static NativeHook<BodyScalarSet>? setDrag;

		private static NativeHook<BodyScalarSet>? setAngularDrag;

		private static NativeHook<BodyScalarGet>? getMaxAngular;

		private static NativeHook<BodyScalarGet>? getDrag;

		private static NativeHook<BodyScalarGet>? getAngularDrag;

		private static NativeHook<VectorAccess>? getVelocity;

		private static NativeHook<VectorAccess>? setVelocity;

		private static NativeHook<VectorAccess>? getAngular;

		private static NativeHook<VectorAccess>? setAngular;

		private static readonly List<Delegate> callbacks = new List<Delegate>();

		private static readonly List<Action> detach = new List<Action>();

		public static readonly HashSet<IntPtr> Bodies = new HashSet<IntPtr>();

		[ThreadStatic]
		public static int ScopeDepth;

		public static float Factor = 1f;

		public static long ForceCalls;

		public static long TimeCalls;

		public static long LimitCalls;

		public static long RelativeCalls;

		private static bool Scoped
		{
			get
			{
				if (ScopeDepth > 0)
				{
					return Factor > 1f;
				}
				return false;
			}
		}

		private static bool Applies(IntPtr body)
		{
			if (Scoped)
			{
				return Bodies.Contains(body);
			}
			return false;
		}

		private static void Attach<T>(ref NativeHook<T>? field, string name, T callback) where T : Delegate
		{
			IntPtr intPtr = IL2CPP.il2cpp_resolve_icall(name);
			if (intPtr == IntPtr.Zero)
			{
				throw new InvalidOperationException("Missing native call " + name);
			}
			callbacks.Add(callback);
			NativeHook<T> val = (field = new NativeHook<T>(intPtr, Marshal.GetFunctionPointerForDelegate(callback)));
			val.Attach();
			detach.Add((Action)val.Detach);
		}

		public static void Install()
		{
			try
			{
				Attach(ref delta, "UnityEngine.Time::get_deltaTime()", Delta);
				Attach(ref fixedDelta, "UnityEngine.Time::get_fixedDeltaTime()", FixedDelta);
				Attach(ref force, "UnityEngine.Rigidbody::AddForce_Injected(UnityEngine.Vector3&,UnityEngine.ForceMode)", AddForce);
				Attach(ref torque, "UnityEngine.Rigidbody::AddTorque_Injected(UnityEngine.Vector3&,UnityEngine.ForceMode)", AddTorque);
				Attach(ref relativeForce, "UnityEngine.Rigidbody::AddRelativeForce_Injected(UnityEngine.Vector3&,UnityEngine.ForceMode)", AddRelativeForce);
				Attach(ref relativeTorque, "UnityEngine.Rigidbody::AddRelativeTorque_Injected(UnityEngine.Vector3&,UnityEngine.ForceMode)", AddRelativeTorque);
				Attach(ref setMaxAngular, "UnityEngine.Rigidbody::set_maxAngularVelocity(System.Single)", SetMaxAngular);
				Attach(ref getMaxAngular, "UnityEngine.Rigidbody::get_maxAngularVelocity()", GetMaxAngular);
				Attach(ref setDrag, "UnityEngine.Rigidbody::set_drag(System.Single)", SetDrag);
				Attach(ref getDrag, "UnityEngine.Rigidbody::get_drag()", GetDrag);
				Attach(ref setAngularDrag, "UnityEngine.Rigidbody::set_angularDrag(System.Single)", SetAngularDrag);
				Attach(ref getAngularDrag, "UnityEngine.Rigidbody::get_angularDrag()", GetAngularDrag);
				Attach(ref getVelocity, "UnityEngine.Rigidbody::get_velocity_Injected(UnityEngine.Vector3&)", GetVelocity);
				Attach(ref setVelocity, "UnityEngine.Rigidbody::set_velocity_Injected(UnityEngine.Vector3&)", SetVelocity);
				Attach(ref getAngular, "UnityEngine.Rigidbody::get_angularVelocity_Injected(UnityEngine.Vector3&)", GetAngular);
				Attach(ref setAngular, "UnityEngine.Rigidbody::set_angularVelocity_Injected(UnityEngine.Vector3&)", SetAngular);
			}
			catch
			{
				Uninstall();
				throw;
			}
		}

		public static void Uninstall()
		{
			ScopeDepth = 0;
			Factor = 1f;
			Bodies.Clear();
			for (int num = detach.Count - 1; num >= 0; num--)
			{
				detach[num]();
			}
			detach.Clear();
		}

		private static float Delta()
		{
			float num = delta.Trampoline();
			if (Scoped)
			{
				TimeCalls++;
				return num * Factor;
			}
			return num;
		}

		private static float FixedDelta()
		{
			float num = fixedDelta.Trampoline();
			if (!Scoped)
			{
				return num;
			}
			return num * Factor;
		}

		public static float ForceFactor(ForceMode mode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)mode != 1 && (int)mode != 2)
			{
				return Factor * Factor;
			}
			return Factor;
		}

		private static void AddForce(IntPtr body, ref Vector3 value, ForceMode mode)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 value2 = value;
			if (Applies(body))
			{
				value2 *= ForceFactor(mode);
				ForceCalls++;
			}
			force.Trampoline(body, ref value2, mode);
		}

		private static void AddTorque(IntPtr body, ref Vector3 value, ForceMode mode)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 value2 = value;
			if (Applies(body))
			{
				value2 *= ForceFactor(mode);
				ForceCalls++;
			}
			torque.Trampoline(body, ref value2, mode);
		}

		private static void AddRelativeForce(IntPtr body, ref Vector3 value, ForceMode mode)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 value2 = value;
			if (Applies(body))
			{
				value2 *= ForceFactor(mode);
				RelativeCalls++;
			}
			relativeForce.Trampoline(body, ref value2, mode);
		}

		private static void AddRelativeTorque(IntPtr body, ref Vector3 value, ForceMode mode)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 value2 = value;
			if (Applies(body))
			{
				value2 *= ForceFactor(mode);
				RelativeCalls++;
			}
			relativeTorque.Trampoline(body, ref value2, mode);
		}

		private static void SetMaxAngular(IntPtr body, float value)
		{
			if (Applies(body))
			{
				value *= Factor;
				LimitCalls++;
			}
			setMaxAngular.Trampoline(body, value);
		}

		private static float GetMaxAngular(IntPtr body)
		{
			float num = getMaxAngular.Trampoline(body);
			if (!Applies(body))
			{
				return num;
			}
			return num / Factor;
		}

		private static void SetDrag(IntPtr body, float value)
		{
			setDrag.Trampoline(body, Applies(body) ? (value * Factor) : value);
		}

		private static float GetDrag(IntPtr body)
		{
			float num = getDrag.Trampoline(body);
			if (!Applies(body))
			{
				return num;
			}
			return num / Factor;
		}

		private static void SetAngularDrag(IntPtr body, float value)
		{
			setAngularDrag.Trampoline(body, Applies(body) ? (value * Factor) : value);
		}

		private static float GetAngularDrag(IntPtr body)
		{
			float num = getAngularDrag.Trampoline(body);
			if (!Applies(body))
			{
				return num;
			}
			return num / Factor;
		}

		private static void GetVelocity(IntPtr body, ref Vector3 value)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			getVelocity.Trampoline(body, ref value);
			if (Applies(body))
			{
				value /= Factor;
			}
		}

		private static void SetVelocity(IntPtr body, ref Vector3 value)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Vector3 value2 = (Applies(body) ? (value * Factor) : value);
			setVelocity.Trampoline(body, ref value2);
		}

		private static void GetAngular(IntPtr body, ref Vector3 value)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			getAngular.Trampoline(body, ref value);
			if (Applies(body))
			{
				value /= Factor;
			}
		}

		private static void SetAngular(IntPtr body, ref Vector3 value)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Vector3 value2 = (Applies(body) ? (value * Factor) : value);
			setAngular.Trampoline(body, ref value2);
		}
	}
	public sealed class Mod : MelonMod
	{
		private sealed class Session
		{
			public Actor Actor;

			public PlayerClock? Clock;

			public MotionEcho? Echo;

			public int? TriggerDevice;

			public float Started;
		}

		private const float SlowFactor = 0.2f;

		private readonly PlayerAbilities abilities = new PlayerAbilities();

		private readonly Dictionary<int, Session> sessions = new Dictionary<int, Session>();

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

		private readonly ButtonEdges buttons = new ButtonEdges();

		private WorldDilation? dilation;

		private float startedGameTime;

		private float startedRealTime;

		private Actor? selected;

		private string message = "Each controller: R3 activates or cancels its paired Beast.";

		private MelonPreferences_Entry<bool> showHud;

		private bool inputErrorReported;

		public override void OnInitializeMelon()
		{
			showHud = MelonPreferences.CreateCategory("Sandevistan").CreateEntry<bool>("ShowHUD", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			((MelonBase)this).LoggerInstance.Msg("Independent local players: R3 toggles the paired Beast. F7 cycles the keyboard target, F8 toggles it. Three seconds active, six seconds cooldown per Beast.");
		}

		private static bool Usable(Actor? a)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			if ((Object)(object)a != (Object)null && ((Component)a).gameObject.activeInHierarchy && a.initialized && (Object)(object)a.bodyHandeler != (Object)null)
			{
				return (int)a.actorState != 1;
			}
			return false;
		}

		private static bool HasRemoteHuman(IEnumerable<Actor> actors)
		{
			return actors.Any((Actor a) => !a.IsAI && !a.IsLocal);
		}

		private static List<Actor> LocalActors(IEnumerable<Actor> actors)
		{
			return (from a in actors
				where Usable(a) && a.IsLocal && !a.IsAI
				orderby a.playerID, ((Object)a).GetInstanceID()
				select a).ToList();
		}

		private Session SessionFor(Actor actor)
		{
			int instanceID = ((Object)actor).GetInstanceID();
			if (!sessions.TryGetValue(instanceID, out Session value))
			{
				Dictionary<int, Session> dictionary = sessions;
				Session obj = new Session
				{
					Actor = actor
				};
				value = obj;
				dictionary[instanceID] = obj;
			}
			return value;
		}

		private Actor? Owner(Gamepad pad, List<Actor> locals)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			List<Actor> list = new List<Actor>();
			foreach (Actor local in locals)
			{
				InputUser inputPlayer = local.InputPlayer;
				if (!((InputUser)(ref inputPlayer)).valid)
				{
					continue;
				}
				ReadOnlyArray<InputDevice> pairedDevices = ((InputUser)(ref inputPlayer)).pairedDevices;
				for (int i = 0; i < pairedDevices.Count; i++)
				{
					if (pairedDevices[i].deviceId == ((InputDevice)pad).deviceId)
					{
						list.Add(local);
						break;
					}
				}
			}
			if (list.Count != 1)
			{
				deviceOwners.Remove(((InputDevice)pad).deviceId);
				return null;
			}
			Actor val = list[0];
			int instanceID = ((Object)val).GetInstanceID();
			if (!deviceOwners.TryGetValue(((InputDevice)pad).deviceId, out var value) || value != instanceID)
			{
				deviceOwners[((InputDevice)pad).deviceId] = instanceID;
				((MelonBase)this).LoggerInstance.Msg($"CONTROLLER OWNER device={((InputDevice)pad).deviceId} player={val.playerID} actor={instanceID}");
			}
			return val;
		}

		public override void OnUpdate()
		{
			double now = Time.realtimeSinceStartup;
			try
			{
				Actor[] actors = ((IEnumerable<Actor>)Object.FindObjectsOfType<Actor>()).ToArray();
				bool flag = HasRemoteHuman(actors);
				if (!Application.isFocused || flag)
				{
					StopAll(now);
					buttons.Reset();
					if (flag)
					{
						message = "Local play only: remote human detected.";
					}
					return;
				}
				List<Actor> list = LocalActors(actors);
				foreach (Actor item in list)
				{
					SessionFor(item);
				}
				KeyValuePair<int, Session>[] array = sessions.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					KeyValuePair<int, Session> keyValuePair = array[i];
					Session value = keyValuePair.Value;
					if (abilities.IsActive(keyValuePair.Key) && (abilities.For(keyValuePair.Key).Expired(now) || !Usable(value.Actor) || !value.Actor.IsLocal || value.Actor.IsAI))
					{
						StopSession(keyValuePair.Key, now);
					}
				}
				if (Time.timeScale <= 0f)
				{
					StopAll(now);
					buttons.Reset();
					return;
				}
				HashSet<int> connected = new HashSet<int>();
				ReadOnlyArray<Gamepad> all = Gamepad.all;
				for (int j = 0; j < all.Count; j++)
				{
					Gamepad val = all[j];
					connected.Add(((InputDevice)val).deviceId);
					Actor val2 = Owner(val, list);
					if (buttons.Poll(((InputDevice)val).deviceId, left: false, val.rightStickButton.isPressed).toggle)
					{
						if ((Object)(object)val2 == (Object)null)
						{
							message = "This controller has no uniquely paired local Beast. Join the match first.";
							continue;
						}
						((MelonBase)this).LoggerInstance.Msg($"Gamepad device {((InputDevice)val).deviceId}: player={val2.playerID} R3=true");
						Toggle(val2, now, ((InputDevice)val).deviceId);
					}
				}
				Keyboard current2 = Keyboard.current;
				if (current2 != null)
				{
					connected.Add(((InputDevice)current2).deviceId);
					(bool, bool) tuple = buttons.Poll(((InputDevice)current2).deviceId, ((ButtonControl)current2.f7Key).isPressed, ((ButtonControl)current2.f8Key).isPressed);
					if (tuple.Item1)
					{
						Cycle(list);
					}
					else if (tuple.Item2)
					{
						if (!Usable(selected))
						{
							selected = list.FirstOrDefault();
						}
						if ((Object)(object)selected != (Object)null)
						{
							Toggle(selected, now, ((InputDevice)current2).deviceId);
						}
					}
				}
				array = sessions.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					KeyValuePair<int, Session> keyValuePair2 = array[i];
					if (!abilities.IsActive(keyValuePair2.Key))
					{
						continue;
					}
					int? triggerDevice = keyValuePair2.Value.TriggerDevice;
					if (triggerDevice.HasValue)
					{
						int valueOrDefault = triggerDevice.GetValueOrDefault();
						if (!connected.Contains(valueOrDefault))
						{
							StopSession(keyValuePair2.Key, now);
						}
					}
				}
				buttons.KeepOnly(connected);
				int[] array2 = deviceOwners.Keys.Where((int d) => !connected.Contains(d)).ToArray();
				foreach (int key in array2)
				{
					deviceOwners.Remove(key);
				}
			}
			catch (Exception ex)
			{
				StopAll(now);
				message = "Sandevistan stopped: see MelonLoader log.";
				if (!inputErrorReported)
				{
					((MelonBase)this).LoggerInstance.Error(ex.ToString());
					inputErrorReported = true;
				}
			}
		}

		private void Cycle(List<Actor> locals)
		{
			if (locals.Count == 0)
			{
				selected = null;
				message = "Start a local match first.";
				return;
			}
			int num = (((Object)(object)selected == (Object)null) ? (-1) : locals.FindIndex((Actor a) => ((Object)a).GetInstanceID() == ((Object)selected).GetInstanceID()));
			selected = locals[(num + 1) % locals.Count];
			message = $"Keyboard target: Beast {selected.playerID + 1}. F8 activates/cancels this Beast.";
		}

		private void Toggle(Actor actor, double now, int? device)
		{
			int instanceID = ((Object)actor).GetInstanceID();
			if (abilities.IsActive(instanceID))
			{
				StopSession(instanceID, now);
			}
			else
			{
				StartActor(actor, now, device);
			}
		}

		private void Start(double now)
		{
			if (!Usable(selected))
			{
				selected = LocalActors((IEnumerable<Actor>)Object.FindObjectsOfType<Actor>()).FirstOrDefault();
			}
			if ((Object)(object)selected != (Object)null)
			{
				StartActor(selected, now, null);
			}
		}

		private void Stop(double now)
		{
			StopAll(now);
		}

		private void StartActor(Actor actor, double now, int? device)
		{
			if (!Usable(actor) || !actor.IsLocal || actor.IsAI || HasRemoteHuman((IEnumerable<Actor>)Object.FindObjectsOfType<Actor>()) || Time.timeScale <= 0f)
			{
				return;
			}
			int instanceID = ((Object)actor).GetInstanceID();
			Session session = SessionFor(actor);
			if (!abilities.For(instanceID).TryStart(now, allowed: true))
			{
				message = $"Beast {actor.playerID + 1} is cooling down.";
				return;
			}
			session.TriggerDevice = device;
			session.Started = Time.realtimeSinceStartup;
			try
			{
				if (dilation == null)
				{
					dilation = new WorldDilation(actor, 0.2f);
					dilation.Apply();
					startedGameTime = Time.time;
					startedRealTime = Time.realtimeSinceStartup;
				}
				session.Clock = new PlayerClock(actor, 0.2f);
				session.Clock.Apply();
				NativeLocomotion.Start(actor);
				try
				{
					Rigidbody[] source = ((IEnumerable<Rigidbody>)actor.bodyHandeler.GetAllRigidbodies()).Where((Rigidbody b) => (Object)(object)b != (Object)null).ToArray();
					Rigidbody val = ((IEnumerable<Rigidbody>)source).FirstOrDefault((Func<Rigidbody, bool>)((Rigidbody b) => ((Object)b).name.Contains("chest", StringComparison.OrdinalIgnoreCase))) ?? source.FirstOrDefault();
					List<Transform> list = new List<Transform> { ((Object)(object)val == (Object)null) ? ((Component)actor).transform : ((Component)val).transform };
					list.AddRange(from b in source.Where((Rigidbody b) => ((Object)b).name.Contains("hand", StringComparison.OrdinalIgnoreCase)).Take(2)
						select ((Component)b).transform);
					session.Echo = new MotionEcho(list);
				}
				catch (Exception ex)
				{
					((MelonBase)this).LoggerInstance.Warning("Echo unavailable: " + ex.Message);
				}
				message = $"Beast {actor.playerID + 1} active. {abilities.ActiveCount} active player(s).";
				((MelonBase)this).LoggerInstance.Msg($"SANDE START player={actor.playerID} actor={instanceID} device={device} active={abilities.ActiveCount} world={Time.timeScale:F3}");
			}
			catch
			{
				StopSession(instanceID, now);
				throw;
			}
		}

		private void StopSession(int id, double now)
		{
			if (!sessions.TryGetValue(id, out Session value))
			{
				return;
			}
			bool flag = abilities.IsActive(id);
			abilities.For(id).Stop(now);
			foreach (string item in NativeLocomotion.Stop(id))
			{
				((MelonBase)this).LoggerInstance.Msg(item);
			}
			try
			{
				value.Clock?.Dispose();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Body cleanup: " + ex);
			}
			finally
			{
				value.Clock = null;
				value.Echo = null;
				value.TriggerDevice = null;
			}
			if (flag)
			{
				((MelonBase)this).LoggerInstance.Msg($"SANDE END actor={id} real={Time.realtimeSinceStartup - value.Started:F3}s remaining={abilities.ActiveCount} cooldown=6s");
			}
			if (abilities.ActiveCount == 0 && dilation != null)
			{
				float num = Time.realtimeSinceStartup - startedRealTime;
				((MelonBase)this).LoggerInstance.Msg($"World clock evidence: real={num:F3}s, game={Time.time - startedGameTime:F3}s, ratio={((num > 0f) ? ((Time.time - startedGameTime) / num) : 0f):F3}, scale={Time.timeScale:F3}");
				try
				{
					dilation.Dispose();
				}
				finally
				{
					dilation = null;
				}
				((MelonBase)this).LoggerInstance.Msg($"World restored: scale={Time.timeScale:F3}");
			}
		}

		private void StopAll(double now)
		{
			KeyValuePair<int, Session>[] array = sessions.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<int, Session> keyValuePair = array[i];
				if (abilities.IsActive(keyValuePair.Key) || keyValuePair.Value.Clock != null)
				{
					StopSession(keyValuePair.Key, now);
				}
			}
			if (abilities.ActiveCount == 0 && dilation != null)
			{
				try
				{
					dilation.Dispose();
				}
				finally
				{
					dilation = null;
				}
			}
		}

		public override void OnFixedUpdate()
		{
			KeyValuePair<int, Session>[] array = sessions.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<int, Session> keyValuePair = array[i];
				if (abilities.IsActive(keyValuePair.Key))
				{
					try
					{
						keyValuePair.Value.Clock?.FixedUpdate();
					}
					catch (Exception ex)
					{
						StopSession(keyValuePair.Key, Time.realtimeSinceStartup);
						((MelonBase)this).LoggerInstance.Error(ex.ToString());
					}
				}
			}
		}

		public override void OnLateUpdate()
		{
			KeyValuePair<int, Session>[] array = sessions.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<int, Session> keyValuePair = array[i];
				if (!abilities.IsActive(keyValuePair.Key))
				{
					continue;
				}
				Session value = keyValuePair.Value;
				try
				{
					PlayerClock? clock = value.Clock;
					if (clock != null && clock.ExcessiveSpeed())
					{
						((MelonBase)this).LoggerInstance.Error($"Player clock safety stop: actor={keyValuePair.Key} excessive real body speed.");
						StopSession(keyValuePair.Key, Time.realtimeSinceStartup);
					}
					else
					{
						value.Echo?.Update(Time.realtimeSinceStartup);
					}
				}
				catch (Exception ex)
				{
					StopSession(keyValuePair.Key, Time.realtimeSinceStartup);
					((MelonBase)this).LoggerInstance.Error(ex.ToString());
				}
			}
		}

		private void ResetScene()
		{
			StopAll(Time.realtimeSinceStartup);
			sessions.Clear();
			abilities.Reset();
			deviceOwners.Clear();
			buttons.Reset();
			selected = null;
			inputErrorReported = false;
			message = "Each controller: R3 activates or cancels its paired Beast.";
		}

		public override void OnSceneWasUnloaded(int index, string name)
		{
			ResetScene();
		}

		public override void OnSceneWasInitialized(int index, string name)
		{
			ResetScene();
			try
			{
				PlayerClock.Prepare();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Player clock preparation failed: " + ex);
			}
		}

		public override void OnDeinitializeMelon()
		{
			ResetScene();
			PlayerClock.Unload();
		}

		public override void OnGUI()
		{
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Camera main = Camera.main;
			foreach (KeyValuePair<int, Session> session in sessions)
			{
				if (abilities.IsActive(session.Key) && session.Value.Echo != null && (Object)(object)main != (Object)null)
				{
					try
					{
						session.Value.Echo.Draw(main, realtimeSinceStartup);
					}
					catch (Exception ex)
					{
						session.Value.Echo = null;
						((MelonBase)this).LoggerInstance.Warning("Echo draw: " + ex.Message);
					}
				}
			}
			if (showHud == null || !showHud.Value)
			{
				return;
			}
			KeyValuePair<int, Session>[] array = (from p in sessions
				where Usable(p.Value.Actor)
				orderby p.Value.Actor.playerID
				select p).ToArray();
			List<string> list = new List<string> { "SANDEVISTAN | Independent local players", "R3: your ability   F7/F8: keyboard target" };
			KeyValuePair<int, Session>[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				KeyValuePair<int, Session> keyValuePair = array2[num];
				AbilityState abilityState = abilities.For(keyValuePair.Key);
				Actor actor = keyValuePair.Value.Actor;
				string value = (abilityState.Active ? $"ACTIVE {Math.Max(0.0, abilityState.EndsAt - (double)realtimeSinceStartup):0.0}s" : (((double)realtimeSinceStartup < abilityState.ReadyAt) ? $"Cooldown {abilityState.ReadyAt - (double)realtimeSinceStartup:0.0}s" : "Ready"));
				string value2 = (((Object)(object)selected != (Object)null && ((Object)selected).GetInstanceID() == keyValuePair.Key) ? " [keyboard]" : "");
				list.Add($"Beast {actor.playerID + 1}: {value}{value2}");
				if (abilityState.Active && (Object)(object)main != (Object)null)
				{
					Vector3 val = main.WorldToScreenPoint(((Component)actor).transform.position + Vector3.up * 1.4f);
					if (val.z > 0f)
					{
						GUI.Label(new Rect(val.x - 60f, (float)Screen.height - val.y, 180f, 30f), $"▼ SANDEVISTAN P{actor.playerID + 1}");
					}
				}
			}
			list.Add(message);
			GUI.Box(new Rect(16f, 16f, 580f, (float)(26 + list.Count * 18)), string.Join("\n", list));
		}
	}
	public sealed class MotionEcho
	{
		private readonly Transform[] anchors;

		private readonly Queue<(float time, Vector3[] points)> history = new Queue<(float, Vector3[])>();

		private float nextSample;

		private static GUIStyle? strokeStyle;

		public int Samples => history.Count;

		public MotionEcho(IEnumerable<Transform> anchors)
		{
			this.anchors = anchors.Where((Transform t) => (Object)(object)t != (Object)null).Take(3).ToArray();
		}

		public void Update(float now)
		{
			while (history.Count > 0 && now - history.Peek().time > 0.45f)
			{
				history.Dequeue();
			}
			if (now < nextSample)
			{
				return;
			}
			nextSample = now + 0.025f;
			if (anchors.Any((Transform t) => (Object)(object)t == (Object)null))
			{
				history.Clear();
				return;
			}
			history.Enqueue((now, anchors.Select((Transform t) => t.position).ToArray()));
			while (history.Count > 20)
			{
				history.Dequeue();
			}
		}

		public void Draw(Camera camera, float now)
		{
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: 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_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			(float, Vector3[])[] array = history.ToArray();
			if (array.Length == 0 || anchors.Length == 0)
			{
				return;
			}
			Color color = GUI.color;
			Matrix4x4 matrix = GUI.matrix;
			try
			{
				Vector2 val3 = default(Vector2);
				Vector2 val4 = default(Vector2);
				for (int i = 1; i < array.Length; i++)
				{
					float num = Mathf.Clamp01(1f - (now - array[i].Item1) / 0.45f);
					for (int j = 0; j < array[i].Item2.Length; j++)
					{
						Vector3 val = camera.WorldToScreenPoint(array[i - 1].Item2[j]);
						Vector3 val2 = camera.WorldToScreenPoint(array[i].Item2[j]);
						if (!(val.z <= 0f) && !(val2.z <= 0f))
						{
							((Vector2)(ref val3))..ctor(val.x, (float)Screen.height - val.y);
							((Vector2)(ref val4))..ctor(val2.x, (float)Screen.height - val2.y);
							if (!(Vector2.Distance(val3, val4) > (float)Screen.width * 0.3f))
							{
								GUI.color = ((j == 0) ? new Color(0f, 1f, 1f, num * 0.8f) : new Color(1f, 0.1f, 0.35f, num * 0.8f));
								Stroke(val3, val4, (j == 0) ? 9 : 5);
							}
						}
					}
				}
				Vector3 val5 = camera.WorldToScreenPoint(array[^1].Item2[0]);
				if (val5.z > 0f)
				{
					Vector2 val6 = default(Vector2);
					((Vector2)(ref val6))..ctor(val5.x, (float)Screen.height - val5.y);
					float num2 = 25f + Mathf.Sin(now * 14f) * 4f;
					GUI.color = new Color(0f, 1f, 1f, 0.9f);
					for (int k = 0; k < 20; k++)
					{
						float num3 = (float)k * (float)Math.PI / 10f;
						float num4 = (float)(k + 1) * (float)Math.PI / 10f;
						Stroke(val6 + new Vector2(Mathf.Cos(num3), Mathf.Sin(num3)) * num2, val6 + new Vector2(Mathf.Cos(num4), Mathf.Sin(num4)) * num2, 3f);
					}
				}
			}
			finally
			{
				GUI.color = color;
				GUI.matrix = matrix;
			}
		}

		private static void Stroke(Vector2 a, Vector2 b, float width)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			float num = Vector2.Distance(a, b);
			if (num < 0.1f)
			{
				return;
			}
			Matrix4x4 matrix = GUI.matrix;
			try
			{
				GUIUtility.RotateAroundPivot(Mathf.Atan2(b.y - a.y, b.x - a.x) * 57.29578f, a);
				if (strokeStyle == null)
				{
					strokeStyle = new GUIStyle(GUI.skin.box);
					strokeStyle.normal.background = Texture2D.whiteTexture;
					strokeStyle.border = new RectOffset(0, 0, 0, 0);
				}
				GUI.Box(new Rect(a.x, a.y - width / 2f, num, width), GUIContent.none, strokeStyle);
			}
			finally
			{
				GUI.matrix = matrix;
			}
		}
	}
	internal sealed class MutationJournal
	{
		private readonly Stack<Action> undo = new Stack<Action>();

		public void Set(Action change, Action restore)
		{
			undo.Push(restore);
			change();
		}

		public void Commit()
		{
			undo.Clear();
		}

		public void Rollback()
		{
			List<Exception> list = new List<Exception>();
			Action result;
			while (undo.TryPop(out result))
			{
				try
				{
					result();
				}
				catch (Exception item)
				{
					list.Add(item);
				}
			}
			if (list.Count != 0)
			{
				throw new AggregateException("Body restoration failed", list);
			}
		}
	}
	public static class NativeLocomotion
	{
		[HarmonyPatch(typeof(MovementHandeler_HumanoidMediumEctomorph), "RunCycleRotateBall")]
		private static class SpeedPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(MovementHandeler_HumanoidMediumEctomorph __instance, out float? __state)
			{
				__state = null;
				if (Active && !((Object)(object)((MovementHandeler)__instance).actor == (Object)null))
				{
					Actor actor = ((MovementHandeler)__instance).actor;
					float cycleModifer = __instance._cycleModifer;
					if (float.IsFinite(cycleModifer) && !(cycleModifer < 0f))
					{
						int instanceID = ((Object)actor).GetInstanceID();
						float num = (activeActors.Contains(instanceID) ? (cycleModifer * Math.Clamp(SelectedWalkMultiplier, 1f, 4f)) : cycleModifer);
						__state = cycleModifer;
						__instance._cycleModifer = num;
						evidence.TryGetValue(instanceID, out (bool, int, float, float) value);
						evidence[instanceID] = (actor.IsAI, value.Item2 + 1, cycleModifer, num);
					}
				}
			}

			private static void Postfix(MovementHandeler_HumanoidMediumEctomorph __instance, float? __state)
			{
				if (__state.HasValue)
				{
					__instance._cycleModifer = __state.Value;
				}
			}

			private static Exception? Finalizer(MovementHandeler_HumanoidMediumEctomorph __instance, float? __state, Exception? __exception)
			{
				if (__state.HasValue)
				{
					__instance._cycleModifer = __state.Value;
				}
				return __exception;
			}
		}

		private static readonly HashSet<int> activeActors = new HashSet<int>();

		private static readonly Dictionary<int, (bool ai, int calls, float before, float during)> evidence = new Dictionary<int, (bool, int, float, float)>();

		public static bool Active => activeActors.Count > 0;

		public static float SelectedWalkMultiplier { get; set; } = 1.5f;

		public static void Start(Actor selected)
		{
			activeActors.Add(((Object)selected).GetInstanceID());
		}

		public static IEnumerable<string> Stop(int actorId)
		{
			activeActors.Remove(actorId);
			string[] result = (from x in evidence
				where x.Key == actorId
				select $"Native locomotion: actor={x.Key}, ai={x.Value.ai}, calls={x.Value.calls}, baseline={x.Value.before:F3}, effective={x.Value.during:F3}").ToArray();
			evidence.Remove(actorId);
			if (activeActors.Count == 0)
			{
				evidence.Clear();
			}
			return result;
		}
	}
	public sealed class PlayerAbilities
	{
		private readonly Dictionary<int, AbilityState> players = new Dictionary<int, AbilityState>();

		public int ActiveCount => players.Values.Count((AbilityState s) => s.Active);

		public AbilityState For(int actorId)
		{
			if (!players.TryGetValue(actorId, out AbilityState value))
			{
				value = (players[actorId] = new AbilityState());
			}
			return value;
		}

		public bool IsActive(int actorId)
		{
			if (players.TryGetValue(actorId, out AbilityState value))
			{
				return value.Active;
			}
			return false;
		}

		public void Reset()
		{
			players.Clear();
		}
	}
	public sealed class PlayerClock : IDisposable
	{
		[HarmonyPatch(typeof(Actor), "FixedUpdate")]
		private static class FixedScope
		{
			private static void Prefix(Actor __instance, out int __state)
			{
				__state = LocalTimeHooks.ScopeDepth;
				LocalTimeHooks.ScopeDepth = (IsActive(((Object)__instance).GetInstanceID()) ? 1 : 0);
			}

			private static Exception? Finalizer(int __state, Exception? __exception)
			{
				LocalTimeHooks.ScopeDepth = __state;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Actor), "Update")]
		private static class UpdateScope
		{
			private static void Prefix(Actor __instance, out int __state)
			{
				__state = LocalTimeHooks.ScopeDepth;
				LocalTimeHooks.ScopeDepth = (IsActive(((Object)__instance).GetInstanceID()) ? 1 : 0);
			}

			private static Exception? Finalizer(int __state, Exception? __exception)
			{
				LocalTimeHooks.ScopeDepth = __state;
				return __exception;
			}
		}

		private readonly List<(Rigidbody body, float drag, float angularDrag, float maxAngular, bool gravity)> bodies = new List<(Rigidbody, float, float, float, bool)>();

		private readonly float factor;

		private readonly int actorId;

		private bool applied;

		private bool disposed;

		private static bool hooksReady;

		private static readonly HashSet<int> activeActors = new HashSet<int>();

		public static bool IsActive(int id)
		{
			return activeActors.Contains(id);
		}

		public static void Prepare()
		{
			if (!hooksReady)
			{
				LocalTimeHooks.Install();
				hooksReady = true;
			}
		}

		public PlayerClock(Actor actor, float worldScale)
		{
			Prepare();
			factor = 1f / worldScale;
			actorId = ((Object)actor).GetInstanceID();
			foreach (Rigidbody item in (Il2CppArrayBase<Rigidbody>)(object)actor.bodyHandeler.GetAllRigidbodies())
			{
				if ((Object)(object)item != (Object)null && !item.isKinematic)
				{
					bodies.Add((item, item.drag, item.angularDrag, item.maxAngularVelocity, item.useGravity));
				}
			}
		}

		public void Apply()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (disposed)
			{
				throw new ObjectDisposedException("PlayerClock");
			}
			if (applied)
			{
				return;
			}
			MutationJournal mutationJournal = new MutationJournal();
			try
			{
				foreach (var item in bodies)
				{
					Rigidbody body = item.body;
					Vector3 velocity = body.velocity;
					Vector3 angular = body.angularVelocity;
					mutationJournal.Set(delegate
					{
						//IL_0007: Unknown result type (might be due to invalid IL or missing references)
						//IL_0017: Unknown result type (might be due to invalid IL or missing references)
						body.velocity = velocity * factor;
					}, delegate
					{
						//IL_0015: Unknown result type (might be due to invalid IL or missing references)
						if ((Object)(object)body != (Object)null)
						{
							body.velocity = velocity;
						}
					});
					mutationJournal.Set(delegate
					{
						//IL_0007: Unknown result type (might be due to invalid IL or missing references)
						//IL_0017: Unknown result type (might be due to invalid IL or missing references)
						body.angularVelocity = angular * factor;
					}, delegate
					{
						//IL_0015: Unknown result type (might be due to invalid IL or missing references)
						if ((Object)(object)body != (Object)null)
						{
							body.angularVelocity = angular;
						}
					});
					mutationJournal.Set(delegate
					{
						body.drag = item.drag * factor;
					}, delegate
					{
						if ((Object)(object)body != (Object)null)
						{
							body.drag = item.drag;
						}
					});
					mutationJournal.Set(delegate
					{
						body.angularDrag = item.angularDrag * factor;
					}, delegate
					{
						if ((Object)(object)body != (Object)null)
						{
							body.angularDrag = item.angularDrag;
						}
					});
					mutationJournal.Set(delegate
					{
						body.maxAngularVelocity = item.maxAngular * factor;
					}, delegate
					{
						if ((Object)(object)body != (Object)null)
						{
							body.maxAngularVelocity = item.maxAngular;
						}
					});
				}
				foreach (var body2 in bodies)
				{
					LocalTimeHooks.Bodies.Add(((Il2CppObjectBase)body2.body).Pointer);
				}
				LocalTimeHooks.Factor = factor;
				activeActors.Add(actorId);
				applied = true;
				mutationJournal.Commit();
			}
			catch (Exception ex)
			{
				foreach (var body3 in bodies)
				{
					LocalTimeHooks.Bodies.Remove(((Il2CppObjectBase)body3.body).Pointer);
				}
				activeActors.Remove(actorId);
				if (activeActors.Count == 0)
				{
					LocalTimeHooks.Factor = 1f;
				}
				try
				{
					mutationJournal.Rollback();
				}
				catch (Exception ex2)
				{
					throw new AggregateException(ex, ex2);
				}
				throw;
			}
		}

		public void FixedUpdate()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!applied || disposed)
			{
				return;
			}
			foreach (var body in bodies)
			{
				if ((Object)(object)body.body != (Object)null && body.body.useGravity && !body.body.isKinematic)
				{
					body.body.AddForce(Physics.gravity * (factor * factor - 1f), (ForceMode)5);
				}
			}
		}

		public bool ExcessiveSpeed()
		{
			return bodies.Any<(Rigidbody, float, float, float, bool)>(delegate((Rigidbody body, float drag, float angularDrag, float maxAngular, bool gravity) x)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)x.body != (Object)null)
				{
					Vector3 velocity = x.body.velocity;
					return ((Vector3)(ref velocity)).magnitude * Time.timeScale > 25f;
				}
				return false;
			});
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			activeActors.Remove(actorId);
			foreach (var body in bodies)
			{
				LocalTimeHooks.Bodies.Remove(((Il2CppObjectBase)body.body).Pointer);
			}
			if (activeActors.Count == 0)
			{
				LocalTimeHooks.Factor = 1f;
				LocalTimeHooks.ScopeDepth = 0;
			}
			if (!applied)
			{
				return;
			}
			MutationJournal mutationJournal = new MutationJournal();
			foreach (var item in bodies)
			{
				if ((Object)(object)item.body == (Object)null)
				{
					continue;
				}
				mutationJournal.Set(delegate
				{
				}, delegate
				{
					//IL_001f: Unknown result type (might be due to invalid IL or missing references)
					//IL_002f: Unknown result type (might be due to invalid IL or missing references)
					if ((Object)(object)item.body != (Object)null)
					{
						Rigidbody item2 = item.body;
						item2.velocity /= factor;
					}
				});
				mutationJournal.Set(delegate
				{
				}, delegate
				{
					//IL_001f: Unknown result type (might be due to invalid IL or missing references)
					//IL_002f: Unknown result type (might be due to invalid IL or missing references)
					if ((Object)(object)item.body != (Object)null)
					{
						Rigidbody item2 = item.body;
						item2.angularVelocity /= factor;
					}
				});
				mutationJournal.Set(delegate
				{
				}, delegate
				{
					if ((Object)(object)item.body != (Object)null)
					{
						item.body.drag = item.drag;
					}
				});
				mutationJournal.Set(delegate
				{
				}, delegate
				{
					if ((Object)(object)item.body != (Object)null)
					{
						item.body.angularDrag = item.angularDrag;
					}
				});
				mutationJournal.Set(delegate
				{
				}, delegate
				{
					if ((Object)(object)item.body != (Object)null)
					{
						item.body.maxAngularVelocity = item.maxAngular;
					}
				});
			}
			mutationJournal.Rollback();
		}

		public static void Unload()
		{
			if (hooksReady)
			{
				LocalTimeHooks.Uninstall();
			}
			hooksReady = false;
			activeActors.Clear();
		}
	}
	public sealed class WorldDilation : IDisposable
	{
		private readonly float scale;

		private readonly float savedScale;

		private readonly float savedStep;

		private readonly float savedGameplay;

		private readonly TimeManager? timeManager;

		private bool applied;

		private bool disposed;

		public WorldDilation(Actor actor, float scale)
		{
			if (scale <= 0f || scale > 1f)
			{
				throw new ArgumentOutOfRangeException("scale");
			}
			this.scale = scale;
			savedScale = Time.timeScale;
			savedStep = Time.fixedDeltaTime;
			timeManager = Object.FindObjectOfType<TimeManager>();
			savedGameplay = (((Object)(object)timeManager == (Object)null) ? savedScale : timeManager.GamePlayTime);
		}

		public void Apply()
		{
			applied = true;
			if ((Object)(object)timeManager != (Object)null)
			{
				timeManager.GamePlayTime = savedGameplay * scale;
			}
			Time.fixedDeltaTime = savedStep * scale;
			Time.timeScale = savedScale * scale;
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			if (applied)
			{
				if ((Object)(object)timeManager != (Object)null && Mathf.Approximately(timeManager.GamePlayTime, savedGameplay * scale))
				{
					timeManager.GamePlayTime = savedGameplay;
				}
				if (Mathf.Approximately(Time.timeScale, savedScale * scale))
				{
					Time.timeScale = savedScale;
				}
				if (Mathf.Approximately(Time.fixedDeltaTime, savedStep * scale))
				{
					Time.fixedDeltaTime = savedStep;
				}
			}
		}
	}
}