Decompiled source of WristRewind v1.0.0
Mods/WristRewind.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BoneLib; using BoneLib.BoneMenu; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.AI; using Il2CppSLZ.Marrow.Data; using Il2CppSLZ.Marrow.Input; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.PuppetMasta; using Il2CppSLZ.Marrow.Utilities; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.AI; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.XR; using WristRewind; using WristRewind.Core; using WristRewind.Tracks; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(RewindMod), "WristRewind", "1.0.0", "MarrowByte", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("MarrowByte")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Wrist controlled world rewind for BONELAB.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WristRewind")] [assembly: AssemblyTitle("WristRewind")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace WristRewind { internal static class ControllerInput { public static ControlInput Read(bool right, bool held) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) XRApi xr = MarrowGame.xr; XRController val = ((xr == null) ? null : (right ? xr.RightController : xr.LeftController)); if (val != null && ((XRDevice)val).IsConnected) { return ControlInput.Create(InputSource.Marrow, val.Grip, val.GripButton, ((XRDevice)val).IsTracking, Convert(((XRDevice)val).Rotation), held); } InputDevice deviceAtXRNode = InputDevices.GetDeviceAtXRNode((XRNode)(right ? 5 : 4)); BaseController val2 = (right ? Player.RightController : Player.LeftController); float num = 0f; bool flag = false; float num2 = ((((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.grip, ref num)) ? num : (((Object)(object)val2 != (Object)null) ? val2.GetSecondaryInteractionButtonAxis() : 0f)); bool flag2 = default(bool); bool button = ((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.gripButton, ref flag2) && flag2; bool flag3 = ((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.isTracked, ref flag); Quaternion value = default(Quaternion); if (((InputDevice)(ref deviceAtXRNode)).isValid && ((InputDevice)(ref deviceAtXRNode)).TryGetFeatureValue(CommonUsages.deviceRotation, ref value)) { return ControlInput.Create(InputSource.UnityXR, num2, button, !flag3 || flag, Convert(value), held); } if (val == null && (Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy) { return ControlInput.Create(InputSource.Controller, MathF.Max(num2, val2.GetGripForce()), val2.GetSecondaryInteractionButton(), tracked: true, Convert(((Component)val2).transform.rotation), held); } return default(ControlInput); } private static Quaternion Convert(Quaternion value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(value.x, value.y, value.z, value.w); } } internal readonly record struct BulletPose(Vector3 Position, Quaternion Rotation, Vector3 Direction, float Speed); internal sealed class Projectiles : IDisposable { private sealed class Shot { public readonly History<BulletPose> History = new History<BulletPose>(256); public Projectile? Projectile; public int Key; public double Born; public double End = double.PositiveInfinity; public LineRenderer? Line; public Material? Material; public BulletPose Live; public bool TrailEnabled; } private sealed class Observed { public Projectile Projectile; public readonly ProjectileLifetime Lifetime = new ProjectileLifetime(); public Vector3 Position; public Vector3 TrailTip; public int TrailCount; public double Seen; } private sealed record FrozenProjectile(Projectile Projectile, bool Enabled); private readonly List<Shot> shots = new List<Shot>(512); private readonly Dictionary<int, Shot> active = new Dictionary<int, Shot>(); private readonly List<Shot> retired = new List<Shot>(); private readonly Dictionary<int, Observed> observed = new Dictionary<int, Observed>(); private readonly List<int> missing = new List<int>(); private readonly List<FrozenProjectile> frozenProjectiles = new List<FrozenProjectile>(); private readonly Func<double> clock; private readonly Func<bool> scrubbing; private Material? fallback; private bool frozen; private double nextDiscovery; public int Count => shots.Count; public int Captured { get; private set; } public Projectiles(Func<double> clock, Func<bool> scrubbing) { this.clock = clock; this.scrubbing = scrubbing; } private static BulletPose Read(Projectile p) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) return new BulletPose(((Component)p).transform.position, ((Component)p).transform.rotation, p._direction, p.currentSpeed); } public void Poll() { //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) if (frozen || scrubbing()) { return; } double num = clock(); if (num >= nextDiscovery) { nextDiscovery = num + 0.25; Discover(); } missing.Clear(); foreach (KeyValuePair<int, Observed> item in observed) { Observed value = item.Value; Projectile projectile = value.Projectile; if ((Object)(object)projectile == (Object)null) { if (active.Remove(item.Key, out Shot value2)) { value2.End = num; } missing.Add(item.Key); continue; } bool flag = active.ContainsKey(item.Key); bool activeInHierarchy = ((Component)projectile).gameObject.activeInHierarchy; BulletPose bulletPose = Read(projectile); double num2 = Math.Clamp(num - value.Seen, 0.001, 0.1); float num3 = MathF.Max(1f, MathF.Abs(bulletPose.Speed)) * (float)num2 * 2f + 1f; Vector3 val; int num4; if (flag) { val = bulletPose.Position - value.Position; num4 = ((((Vector3)(ref val)).sqrMagnitude > num3 * num3) ? 1 : 0); } else { num4 = 0; } bool movedBackToEmitter = (byte)num4 != 0; bool flag2 = value.Lifetime.Observe(activeInHierarchy, projectile.isFinished, movedBackToEmitter, projectile._lastFrameUdate, num); if (flag2) { Spawn(projectile); } if (activeInHierarchy && !projectile.isFinished) { Moved(projectile); } else if (flag) { End(projectile); } TrailRenderer trail = projectile.trail; int num5 = (((Object)(object)trail != (Object)null) ? trail.positionCount : 0); if (num5 > 1) { Vector3 position = trail.GetPosition(0); Vector3 position2 = trail.GetPosition(num5 - 1); val = position - bulletPose.Position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; val = position2 - bulletPose.Position; Vector3 val2 = ((sqrMagnitude < ((Vector3)(ref val)).sqrMagnitude) ? position : position2); bool flag3 = num5 > value.TrailCount || val2 != value.TrailTip; if (!flag && !flag2 && projectile.isFinished && flag3) { ArchiveTrail(projectile, trail, num5); } value.TrailTip = val2; } value.TrailCount = num5; value.Position = bulletPose.Position; value.Seen = num; } foreach (int item2 in missing) { observed.Remove(item2); } } private void Discover() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) foreach (Projectile item in Object.FindObjectsOfType<Projectile>(true)) { if ((Object)(object)item == (Object)null) { continue; } Scene scene = ((Component)item).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { int instanceID = ((Object)item).GetInstanceID(); if (!observed.ContainsKey(instanceID) && observed.Count < 1024) { observed.Add(instanceID, new Observed { Projectile = item, Position = ((Component)item).transform.position, Seen = clock() }); } } } } private void ArchiveTrail(Projectile p, TrailRenderer trail, int length) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) if (shots.Count >= 512) { Remove(shots[0]); } int num = Math.Min(length, 128); double num2 = clock(); double num3 = Math.Min(num2, Math.Clamp(trail.time, 0.001f, 0.12f)); Shot shot = new Shot { Key = ((Object)p).GetInstanceID(), Born = num2 - num3, End = num2 }; if ((Object)(object)((Renderer)trail).sharedMaterial != (Object)null) { shot.Material = new Material(((Renderer)trail).sharedMaterial); } Vector3 val = trail.GetPosition(0) - ((Component)p).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; val = trail.GetPosition(length - 1) - ((Component)p).transform.position; bool flag = sqrMagnitude < ((Vector3)(ref val)).sqrMagnitude; BulletPose bulletPose = Read(p); for (int i = 0; i < num; i++) { int num4 = (int)((long)i * (long)(length - 1) / (num - 1)); if (flag) { num4 = length - 1 - num4; } shot.History.Add(shot.Born + num3 * (double)i / (double)(num - 1), bulletPose with { Position = trail.GetPosition(num4) }); } shots.Add(shot); Captured++; } public void Spawn(Projectile projectile) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown if (!((Object)(object)projectile == (Object)null) && !scrubbing()) { int instanceID = ((Object)projectile).GetInstanceID(); if (active.Remove(instanceID, out Shot value)) { value.End = clock(); } if (shots.Count >= 512) { Remove(shots[0]); } Shot shot = new Shot { Projectile = projectile, Key = instanceID, Born = clock() }; shot.History.Add(shot.Born, Read(projectile)); if ((Object)(object)projectile.trail != (Object)null && (Object)(object)((Renderer)projectile.trail).sharedMaterial != (Object)null) { shot.Material = new Material(((Renderer)projectile.trail).sharedMaterial); } shots.Add(shot); active[instanceID] = shot; Captured++; } } public void Moved(Projectile projectile) { if (!((Object)(object)projectile == (Object)null) && !scrubbing() && active.TryGetValue(((Object)projectile).GetInstanceID(), out Shot value)) { value.History.Add(Math.Max(clock(), value.Born + 1E-06), Read(projectile)); if (projectile.isFinished || !((Component)projectile).gameObject.activeInHierarchy) { End(projectile); } } } public void End(Projectile projectile) { if (!((Object)(object)projectile == (Object)null) && !scrubbing() && active.Remove(((Object)projectile).GetInstanceID(), out Shot value)) { double num = Math.Max(clock(), value.Born + 1E-06); value.History.Add(num, Read(projectile)); value.End = num; } } public void Trim(double oldest) { retired.Clear(); foreach (Shot shot in shots) { Shot value; if (shot.End < oldest) { retired.Add(shot); } else if (active.TryGetValue(shot.Key, out value) && value == shot && ((Object)(object)shot.Projectile == (Object)null || !((Component)shot.Projectile).gameObject.activeInHierarchy || shot.Projectile.isFinished)) { shot.End = clock(); active.Remove(shot.Key); } } foreach (Shot item in retired) { Remove(item); } } public void Freeze() { frozen = true; Discover(); foreach (Observed value in observed.Values) { Projectile projectile = value.Projectile; if (!((Object)(object)projectile == (Object)null) && ((Component)projectile).gameObject.activeInHierarchy) { frozenProjectiles.Add(new FrozenProjectile(projectile, ((Behaviour)projectile).enabled)); ((Behaviour)projectile).enabled = false; } } foreach (Shot value2 in active.Values) { Projectile projectile2 = value2.Projectile; if (!((Object)(object)projectile2 == (Object)null)) { value2.Live = Read(projectile2); value2.TrailEnabled = (Object)(object)projectile2.trail != (Object)null && ((Renderer)projectile2.trail).enabled; if ((Object)(object)projectile2.trail != (Object)null) { ((Renderer)projectile2.trail).enabled = false; } } } } private LineRenderer View(Shot shot) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)shot.Line != (Object)null) { return shot.Line; } LineRenderer val = (shot.Line = new GameObject("WristRewind bullet").AddComponent<LineRenderer>()); val.useWorldSpace = true; ((Renderer)val).sharedMaterial = shot.Material ?? Fallback(); val.startWidth = 0.008f; val.endWidth = 0.002f; val.startColor = new Color(1f, 0.83f, 0.28f, 1f); val.endColor = new Color(1f, 0.42f, 0.1f, 0.1f); val.numCapVertices = 2; ((Renderer)val).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val).receiveShadows = false; return val; } private Material Fallback() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_004b: Expected O, but got Unknown if ((Object)(object)fallback != (Object)null) { return fallback; } Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? throw new InvalidOperationException("No bullet trail shader was found."); Material val2 = new Material(val); Material result = val2; fallback = val2; return result; } public void Apply(double time) { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) if (!frozen) { return; } foreach (FrozenProjectile frozenProjectile in frozenProjectiles) { if ((Object)(object)frozenProjectile.Projectile != (Object)null) { ((Behaviour)frozenProjectile.Projectile).enabled = false; } } foreach (Shot shot in shots) { if (!(time >= shot.Born) || !(time <= shot.End) || !shot.History.Sample(time, out var _, out var _, out var _)) { if ((Object)(object)shot.Line != (Object)null) { ((Renderer)shot.Line).enabled = false; } continue; } LineRenderer val = View(shot); ((Renderer)val).enabled = true; val.positionCount = 9; for (int i = 0; i < 9; i++) { double time2 = Math.Max(shot.Born, time - 0.035 + (double)i * 0.035 / 8.0); shot.History.Sample(time2, out var a2, out var b2, out var blend2); val.SetPosition(i, Vector3.LerpUnclamped(a2.Position, b2.Position, blend2)); } } } public void Release(bool commit, double time) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) if (!frozen) { return; } foreach (Shot value in active.Values) { Projectile projectile = value.Projectile; if ((Object)(object)projectile == (Object)null) { continue; } BulletPose bulletPose = value.Live; if (commit) { if (time < value.Born || time > value.End || !value.History.Sample(time, out var a, out var b, out var blend)) { projectile.isFinished = true; if ((Object)(object)projectile.trail != (Object)null) { projectile.trail.Clear(); ((Renderer)projectile.trail).enabled = value.TrailEnabled; } continue; } bulletPose = a with { Position = Vector3.LerpUnclamped(a.Position, b.Position, blend), Rotation = Quaternion.SlerpUnclamped(a.Rotation, b.Rotation, blend), Direction = Vector3.LerpUnclamped(a.Direction, b.Direction, blend), Speed = a.Speed + (b.Speed - a.Speed) * blend }; } ((Component)projectile).transform.SetPositionAndRotation(bulletPose.Position, bulletPose.Rotation); projectile._direction = bulletPose.Direction; projectile.currentSpeed = bulletPose.Speed; if ((Object)(object)projectile.trail != (Object)null) { projectile.trail.Clear(); ((Renderer)projectile.trail).enabled = value.TrailEnabled; } } retired.Clear(); foreach (Shot shot in shots) { if ((Object)(object)shot.Line != (Object)null) { ((Renderer)shot.Line).enabled = false; } if (!commit) { continue; } if (shot.Born > time) { retired.Add(shot); continue; } shot.History.CutAfter(time); if (shot.End > time && !active.ContainsKey(shot.Key)) { shot.End = time; } } foreach (Shot item in retired) { Remove(item); } foreach (FrozenProjectile frozenProjectile in frozenProjectiles) { if ((Object)(object)frozenProjectile.Projectile != (Object)null) { ((Behaviour)frozenProjectile.Projectile).enabled = frozenProjectile.Enabled; } } frozenProjectiles.Clear(); foreach (Observed value2 in observed.Values) { value2.Seen = time; value2.Lifetime.Rebase(time); } nextDiscovery = 0.0; frozen = false; } private void Remove(Shot shot) { if (active.TryGetValue(shot.Key, out Shot value) && value == shot) { active.Remove(shot.Key); } shots.Remove(shot); shot.History.Clear(); if ((Object)(object)shot.Line != (Object)null) { Object.Destroy((Object)(object)((Component)shot.Line).gameObject); } if ((Object)(object)shot.Material != (Object)null) { Object.Destroy((Object)(object)shot.Material); } } public void Dispose() { Release(commit: false, 0.0); while (shots.Count > 0) { List<Shot> list = shots; Remove(list[list.Count - 1]); } observed.Clear(); if ((Object)(object)fallback != (Object)null) { Object.Destroy((Object)(object)fallback); } fallback = null; } } public sealed class RewindMod : MelonMod { private Settings settings; private RewindWorld? world; private readonly LevelGate level = new LevelGate(); private readonly WristDial dial = new WristDial(); private readonly WristDisplay display = new WristDisplay(); private readonly TimeEffects effects = new TimeEffects(); private readonly TimeAudio audio = new TimeAudio(); private bool held; private bool waitForRelease; private bool failed; private bool displayFailed; private float rate; private double menuAttempt; private Page? menuParent; private static double nextWarning; private ControlInput input; private InputSource activeInput; private bool keyboardHeld; private bool effectsFailed; private bool audioFailed; private float forwardTimer; private int forwardSamples; public override void OnInitializeMelon() { settings = new Settings(); Hooking.OnLevelLoading += Loading; Hooking.OnLevelLoaded += Loaded; Hooking.OnLevelUnloaded += Unloaded; MelonLogger.Msg("[WristRewind] 1.0.0. Time sounds and wrist rings. Waiting for level loaded."); } public override void OnUpdate() { if (failed) { return; } try { try { Menu(); } catch (Exception ex) { Warn("BoneMenu", ex); } if (!level.IsLoaded) { return; } bool flag = HelperMethods.IsLoading(); RigManager rigManager = Player.RigManager; bool flag2 = !flag && (Object)(object)rigManager != (Object)null && (Object)(object)rigManager.physicsRig != (Object)null && (Object)(object)Player.Head != (Object)null && ((Component)rigManager).gameObject.activeInHierarchy && ((Component)Player.Head).gameObject.activeInHierarchy; if (!level.Ready(flag, flag2 ? ((Object)rigManager).GetInstanceID() : 0, Time.realtimeSinceStartupAsDouble)) { if (world != null) { ResetWorld(); } return; } if (world == null) { world = new RewindWorld(settings); MelonLogger.Msg("[WristRewind] Player rig stable. Starting scene discovery."); } float unscaledDeltaTime = Time.unscaledDeltaTime; world.Tick(unscaledDeltaTime); Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current.f9Key).wasPressedThisFrame) { world.End(commit: false); dial.Reset(); waitForRelease = true; } input = ControllerInput.Read(settings.RightHand, held); bool flag3 = current != null && ((ButtonControl)current.f6Key).isPressed; if (flag3 && !keyboardHeld) { world.End(commit: false); held = (waitForRelease = false); dial.Reset(); } else if (!flag3 && keyboardHeld) { world.End(commit: true); held = false; waitForRelease = input.Grip; dial.Reset(); activeInput = InputSource.None; rate = 0f; } keyboardHeld = flag3; if (flag3) { input = ControlInput.Create(InputSource.Keyboard, 1f, button: true, tracked: true, Quaternion.Identity, held); } Quaternion rotation = input.Rotation; if (!input.Tracked) { if (world.Scrubbing) { world.End(commit: false); } if (held) { waitForRelease = true; } held |= input.Grip; dial.Reset(); rate = 0f; } else if (!input.Grip) { if (held && world.Scrubbing) { MelonLogger.Msg($"[WristRewind] Time control released: -{world.Behind:0.00}s."); world.End(commit: true); } held = (waitForRelease = false); dial.Reset(); rate = 0f; } else if (!settings.Enabled || waitForRelease) { if (world.Scrubbing) { world.End(commit: false); } held = true; rate = 0f; } else { if (!held) { held = true; activeInput = input.Source; if (!dial.Calibrate(rotation) || !world.Begin()) { waitForRelease = true; } else { MelonLogger.Msg($"[WristRewind] Time control active: {input.Source}."); } } if (world.Scrubbing) { if (activeInput != input.Source) { dial.Calibrate(rotation); activeInput = input.Source; } if (input.Source == InputSource.Keyboard) { rate = -1f; world.Seek(rate, unscaledDeltaTime); } else if (!dial.Read(rotation, unscaledDeltaTime, settings.Deadzone, 70f, settings.Speed, settings.Invert, out rate)) { world.End(commit: false); waitForRelease = true; } else { world.Seek(rate, unscaledDeltaTime); } } } if (current != null && ((ButtonControl)current.f8Key).wasPressedThisFrame) { PrintStatus(); } } catch (Exception ex2) { Warn("Time control stopped", ex2); ResetWorld(); failed = true; } } public override void OnLateUpdate() { if (world == null || failed) { return; } try { if (world.Scrubbing) { world.Apply(); } else { world.Capture(); } TraceForward(); if (!audioFailed) { try { audio.Update(world.Scrubbing, world.PlaybackRate, settings.Sound, settings.SoundVolume, Time.unscaledDeltaTime); } catch (Exception ex) { Warn("Time audio", ex); audio.Dispose(); audioFailed = true; } } if (!effectsFailed) { try { effects.Update(world.Scrubbing && settings.Magic, settings.RightHand, world.Behind, rate); } catch (Exception ex2) { Warn("Time effects", ex2); effects.Dispose(); effectsFailed = true; } } if (!displayFailed) { try { display.Update(settings.Hud && world.Scrubbing, settings.RightHand, world.Behind, rate, world.AtPresent, world.AtOldest); return; } catch (Exception ex3) { Warn("Wrist display", ex3); display.Dispose(); displayFailed = true; return; } } } catch (Exception ex4) { Warn("World playback stopped", ex4); ResetWorld(); failed = true; } } private void PrintStatus() { world?.PrintStatus($"input={input.Source} grip={input.Grip} axis={input.Axis:0.00} tracked={input.Tracked} roll={dial.Roll:0.0} rate={rate:0.00} waitingForRelease={waitForRelease} enabled={settings.Enabled}"); } private void TraceForward() { if (world == null || !world.Scrubbing || rate <= 0.02f) { forwardTimer = 0f; forwardSamples = 0; return; } forwardTimer += Math.Clamp(Time.unscaledDeltaTime, 0f, 0.1f); if (forwardSamples < 2 && !(forwardTimer < 0.2f + (float)forwardSamples)) { forwardSamples++; MelonLogger.Msg($"[WristRewind] Forward playback sample {forwardSamples}."); PrintStatus(); } } private void Menu() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (realtimeSinceStartupAsDouble < menuAttempt) { return; } menuAttempt = realtimeSinceStartupAsDouble + 3.0; Page root = Page.Root; if (root != null && root != menuParent) { Page obj = root.CreatePage("WristRewind", Color.cyan, 0, true); obj.CreateBool("Enabled", Color.green, settings.Enabled, (Action<bool>)delegate(bool value) { world?.End(commit: false); settings.Enabled = value; settings.Save(); waitForRelease = true; }); obj.CreateBool("Use right hand", Color.white, settings.RightHand, (Action<bool>)delegate(bool value) { world?.End(commit: false); settings.RightHand = value; settings.Save(); waitForRelease = true; held = false; dial.Reset(); }); obj.CreateBool("Invert wrist twist", Color.white, settings.Invert, (Action<bool>)delegate(bool value) { settings.Invert = value; settings.Save(); }); obj.CreateBool("Wrist display", Color.white, settings.Hud, (Action<bool>)delegate(bool value) { settings.Hud = value; settings.Save(); }); obj.CreateBool("Wrist magic", Color.green, settings.Magic, (Action<bool>)delegate(bool value) { settings.Magic = value; settings.Save(); }); obj.CreateBool("Time sounds", Color.white, settings.Sound, (Action<bool>)delegate(bool value) { settings.Sound = value; settings.Save(); }); obj.CreateFloat("Time sound volume", Color.white, settings.SoundVolume, 0.05f, 0f, 1f, (Action<float>)delegate(float value) { settings.SoundVolume = value; settings.Save(); }); obj.CreateFloat("Maximum speed", Color.white, settings.Speed, 0.25f, 0.25f, 12f, (Action<float>)delegate(float value) { settings.Speed = value; settings.Save(); }); obj.CreateFunction("Cancel rewind", Color.yellow, (Action)delegate { world?.End(commit: false); waitForRelease = true; }); obj.CreateFunction("Clear history", Color.yellow, (Action)ResetWorld); obj.CreateFunction("Print status", Color.white, (Action)PrintStatus); menuParent = root; } } private void Loading(LevelInfo info) { level.Reset(); ResetWorld(); failed = false; } private void Loaded(LevelInfo info) { ResetWorld(); failed = false; level.Loaded(); MelonLogger.Msg("[WristRewind] Level loaded. Waiting for a stable player rig."); } private void Unloaded() { level.Reset(); ResetWorld(); } private void ResetWorld() { try { world?.Dispose(); } catch (Exception ex) { Warn("World cleanup", ex); } world = null; display.Dispose(); effects.Dispose(); audio.Dispose(); dial.Reset(); held = false; waitForRelease = true; rate = 0f; displayFailed = false; input = default(ControlInput); activeInput = InputSource.None; keyboardHeld = false; effectsFailed = false; audioFailed = false; forwardTimer = 0f; forwardSamples = 0; } internal static void Warn(string context, Exception ex) { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (!(realtimeSinceStartupAsDouble < nextWarning)) { nextWarning = realtimeSinceStartupAsDouble + 5.0; MelonLogger.Warning($"[WristRewind] {context}: {ex.GetType().Name}: {ex.Message}"); } } public override void OnDeinitializeMelon() { Hooking.OnLevelLoading -= Loading; Hooking.OnLevelLoaded -= Loaded; Hooking.OnLevelUnloaded -= Unloaded; ResetWorld(); } } internal sealed class RewindWorld : IDisposable { private readonly Settings settings; private readonly TimeWindow window; private readonly Dictionary<(int Kind, int Id), ITrack> tracks = new Dictionary<(int, int), ITrack>(); private readonly int[] counts = new int[9]; private readonly HashSet<ITrack> skipped = new HashSet<ITrack>(); private readonly HashSet<int> heldBodies = new HashSet<int>(); private readonly List<Transform> heldRoots = new List<Transform>(); private readonly HashSet<int> marrowBodies = new HashSet<int>(); private readonly List<(int Kind, int Id)> stale = new List<(int, int)>(); private readonly List<ParticleSystem> pausedParticles = new List<ParticleSystem>(); private readonly List<ITrack> releaseOrder = new List<ITrack>(); private double nextScan; private double nextSample; private readonly HashSet<int> warned = new HashSet<int>(); private static readonly string[] categories = new string[9] { "physics bodies", "animation nodes", "NPCs", "animators", "guns", "particle effects", "trails", "lines", "audio sources" }; private bool discovered; private readonly int capacity; public bool Scrubbing => window.Scrubbing; public double Now => window.Now; public double Cursor => window.Cursor; public double Oldest => window.Oldest; public double Behind => Math.Max(0.0, Now - Cursor); public float PlaybackRate => window.PlaybackRate; public bool AtPresent => Behind < 0.001; public bool AtOldest => Cursor - Oldest < 0.001; public int Count => tracks.Count; public Projectiles Bullets { get; } public RewindWorld(Settings settings) { this.settings = settings; window = new TimeWindow(settings.Seconds); capacity = (int)Math.Ceiling(settings.Seconds * 30f) + 4; Bullets = new Projectiles(() => Now, () => Scrubbing); } public void Tick(float dt) { if (!Scrubbing) { window.Advance(dt); if (Now >= nextScan) { nextScan = Now + 0.5; Discover(); } } } public void Capture() { if (Scrubbing) { return; } Bullets.Poll(); if (Now < nextSample) { return; } nextSample = Now + 1.0 / 30.0; foreach (ITrack value in tracks.Values) { try { value.Capture(Now, Oldest); } catch (Exception ex) { RewindMod.Warn(value.GetType().Name, ex); } } Bullets.Trim(Oldest); } private bool Eligible(Component component) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)component == (Object)null || !component.gameObject.activeInHierarchy || (Object)(object)Player.Head == (Object)null) { return false; } if ((Object)(object)component.GetComponentInParent<RigManager>() != (Object)null) { return false; } if (((Object)component.gameObject).name.StartsWith("WristRewind", StringComparison.Ordinal)) { return false; } Vector3 val = component.transform.position - Player.Head.position; return ((Vector3)(ref val)).sqrMagnitude <= settings.Radius * settings.Radius; } private bool Add(int kind, Component component, int limit, Func<ITrack> create) { (int, int) key = (kind, ((Object)component).GetInstanceID()); if (tracks.ContainsKey(key)) { return false; } if (counts[kind] >= limit) { if (warned.Add(kind)) { MelonLogger.Warning($"[WristRewind] Recording limit: {categories[kind]} {counts[kind]}/{limit}. Other recording remains active."); } return false; } ITrack track = create(); tracks.Add(key, track); counts[kind]++; Safe(track, delegate { track.Capture(Now, Oldest); }); return true; } private void Node(Transform node) { if (!((Object)(object)node == (Object)null) && !((Object)(object)((Component)node).GetComponent<Rigidbody>() != (Object)null) && Eligible((Component)(object)node)) { Add(1, (Component)(object)node, 1536, () => new NodeTrack(node, capacity)); } } public void Discover() { Phase("cleanup"); stale.Clear(); foreach (KeyValuePair<(int, int), ITrack> track in tracks) { if (!track.Value.Alive || !Eligible(track.Value.Source)) { stale.Add(track.Key); } } foreach (var item in stale) { Safe(tracks[item], tracks[item].Dispose); tracks.Remove(item); counts[item.Kind]--; } Phase("physics"); marrowBodies.Clear(); foreach (MarrowBody body in Object.FindObjectsOfType<MarrowBody>()) { if (Eligible((Component)(object)body)) { if ((Object)(object)body._rigidbody != (Object)null) { marrowBodies.Add(((Object)body._rigidbody).GetInstanceID()); } Add(0, (Component)(object)body, 768, () => new BodyTrack(body, capacity)); } } foreach (Rigidbody body2 in Object.FindObjectsOfType<Rigidbody>()) { if (Eligible((Component)(object)body2) && !marrowBodies.Contains(((Object)body2).GetInstanceID())) { Add(0, (Component)(object)body2, 768, () => new BodyTrack(body2, capacity)); } } Phase("NPCs"); foreach (AIBrain npc in Object.FindObjectsOfType<AIBrain>()) { if (Eligible((Component)(object)npc)) { Add(2, (Component)(object)npc, 48, () => new NpcTrack(npc, capacity)); Node(((Component)npc).transform); if ((Object)(object)npc.puppetMaster != (Object)null && (Object)(object)npc.puppetMaster.targetRoot != (Object)null) { Node(npc.puppetMaster.targetRoot); } } } Phase("animation"); foreach (Animator animator in Object.FindObjectsOfType<Animator>()) { if (!Eligible((Component)(object)animator)) { continue; } Add(3, (Component)(object)animator, 64, () => new AnimatorTrack(animator, capacity)); foreach (Transform componentsInChild in ((Component)animator).GetComponentsInChildren<Transform>(true)) { Node(componentsInChild); } } Phase("guns"); foreach (Gun gun in Object.FindObjectsOfType<Gun>()) { if (Eligible((Component)(object)gun)) { Add(4, (Component)(object)gun, 64, () => new GunTrack(gun, capacity)); } } Phase("particles"); foreach (ParticleSystem system in Object.FindObjectsOfType<ParticleSystem>()) { bool flag = !Eligible((Component)(object)system); if (!flag) { int particleCount = system.particleCount; bool flag2 = ((particleCount <= 0 || particleCount > 128) ? true : false); flag = flag2; } if (!flag) { Add(5, (Component)(object)system, 16, () => new ParticleTrack(system, capacity)); Node(((Component)system).transform); } } Phase("trails"); foreach (TrailRenderer trail in Object.FindObjectsOfType<TrailRenderer>()) { if (Eligible((Component)(object)trail) && !((Object)(object)((Component)trail).GetComponentInParent<Projectile>() != (Object)null)) { Add(6, (Component)(object)trail, 48, () => new TrailTrack(trail, capacity)); } } foreach (LineRenderer line in Object.FindObjectsOfType<LineRenderer>()) { if (Eligible((Component)(object)line)) { Add(7, (Component)(object)line, 32, () => new TrailTrack(line, capacity)); } } Phase("audio"); foreach (AudioSource source in Object.FindObjectsOfType<AudioSource>()) { if (Eligible((Component)(object)source)) { Add(8, (Component)(object)source, 96, () => new AudioTrack(source, capacity)); } } if (!discovered) { MelonLogger.Msg("[WristRewind] Scene discovery complete. Recording active."); discovered = true; } } private void Phase(string name) { if (!discovered) { MelonLogger.Msg("[WristRewind] Discovery: " + name + "."); } } public bool Begin() { if (Scrubbing || Now < 0.15) { return false; } nextSample = 0.0; Capture(); if (!window.Begin()) { return false; } heldBodies.Clear(); heldRoots.Clear(); RememberHeld(Player.LeftHand); RememberHeld(Player.RightHand); skipped.Clear(); foreach (ITrack value in tracks.Values) { BodyTrack body = value as BodyTrack; if (body != null && (((Object)(object)body.Body != (Object)null && heldBodies.Contains(((Object)body.Body).GetInstanceID())) || heldRoots.Any((Transform root) => (Object)(object)body.Root == (Object)(object)root || body.Root.IsChildOf(root)))) { skipped.Add(value); } else { Safe(value, value.Freeze); } } foreach (ParticleSystem item in Object.FindObjectsOfType<ParticleSystem>()) { if (Eligible((Component)(object)item) && !tracks.ContainsKey((5, ((Object)item).GetInstanceID())) && item.isPlaying) { pausedParticles.Add(item); item.Pause(false); } } Bullets.Freeze(); return true; } private void RememberHeld(Hand hand) { if ((Object)(object)hand == (Object)null) { return; } GameObject objectInHand = Player.GetObjectInHand(hand); if ((Object)(object)objectInHand == (Object)null) { return; } InteractableHost componentInParent = objectInHand.GetComponentInParent<InteractableHost>(); GameObject val = (((Object)(object)componentInParent != (Object)null) ? componentInParent.GetHostGameObject() : objectInHand); if ((Object)(object)val == (Object)null) { return; } heldRoots.Add(val.transform); foreach (Rigidbody componentsInChild in val.GetComponentsInChildren<Rigidbody>(true)) { heldBodies.Add(((Object)componentsInChild).GetInstanceID()); } Rigidbody componentInParent2 = objectInHand.GetComponentInParent<Rigidbody>(); if ((Object)(object)componentInParent2 != (Object)null) { heldBodies.Add(((Object)componentInParent2).GetInstanceID()); } } public void Seek(float speed, float dt) { if (Scrubbing) { window.Seek(speed, dt); } } public void Apply() { if (!Scrubbing) { return; } foreach (ITrack value in tracks.Values) { if (!(value is BodyTrack) && !skipped.Contains(value)) { try { value.Apply(Cursor); } catch (Exception ex) { RewindMod.Warn(value.GetType().Name, ex); } } } foreach (ITrack value2 in tracks.Values) { if (value2 is BodyTrack && !skipped.Contains(value2)) { try { value2.Apply(Cursor); } catch (Exception ex2) { RewindMod.Warn(value2.GetType().Name, ex2); } } } Physics.SyncTransforms(); Bullets.Apply(Cursor); } public void End(bool commit) { if (!Scrubbing) { return; } releaseOrder.Clear(); releaseOrder.AddRange(tracks.Values); releaseOrder.Sort((ITrack a, ITrack b) => Rank(a).CompareTo(Rank(b))); foreach (ITrack track in releaseOrder) { if (!skipped.Contains(track) && track is BodyTrack && commit) { Safe(track, delegate { track.Apply(Cursor); }); } Safe(track, delegate { track.Release(commit, Cursor); }); } foreach (ParticleSystem pausedParticle in pausedParticles) { if ((Object)(object)pausedParticle != (Object)null) { pausedParticle.Play(false); } } pausedParticles.Clear(); Bullets.Release(commit, Cursor); window.End(commit); if (commit) { nextSample = (nextScan = Now); } skipped.Clear(); Physics.SyncTransforms(); } private static int Rank(ITrack track) { if (!(track is NpcTrack)) { if (!(track is AnimatorTrack)) { if (!(track is BodyTrack)) { return 2; } return 3; } return 1; } return 0; } private static void Safe(ITrack track, Action action) { try { action(); } catch (Exception ex) { RewindMod.Warn(track.GetType().Name, ex); } } public void PrintStatus(string input = "") { BodyTrack[] source = tracks.Values.OfType<BodyTrack>().ToArray(); MelonLogger.Msg($"[WristRewind] history={settings.Seconds:0}s objects={Count} bodies={counts[0]} recordedBodies={source.Count((BodyTrack t) => t.SampleCount > 1)} movingBodies={source.Count((BodyTrack t) => t.DistanceRecorded > 0.05f)} appliedBodies={source.Count((BodyTrack t) => t.Applied)} nodes={counts[1]} particles={counts[5]} trails={counts[6] + counts[7]} bulletHistory={Bullets.Count} capturedBullets={Bullets.Captured} scrubbing={Scrubbing} behind={Behind:0.00}s cursor={Cursor:0.00} now={Now:0.00} playbackRate={PlaybackRate:0.00} atPresent={AtPresent} {input}"); foreach (BodyTrack item in source.Where((BodyTrack t) => ((Object)((Component)t.Root).gameObject).name.Contains("cone", StringComparison.OrdinalIgnoreCase)).Take(4)) { MelonLogger.Msg("[WristRewind] " + item.Describe(skipped.Contains(item))); } foreach (BodyTrack item2 in (from t in source where !((Object)((Component)t.Root).gameObject).name.Contains("cone", StringComparison.OrdinalIgnoreCase) orderby t.DistanceRecorded descending select t).Take(2)) { MelonLogger.Msg("[WristRewind] " + item2.Describe(skipped.Contains(item2))); } } public void Dispose() { End(commit: false); foreach (ITrack value in tracks.Values) { Safe(value, value.Dispose); } tracks.Clear(); Bullets.Dispose(); } } internal sealed class Settings { private readonly MelonPreferences_Category category; private readonly MelonPreferences_Entry<bool> enabled; private readonly MelonPreferences_Entry<bool> rightHand; private readonly MelonPreferences_Entry<bool> invert; private readonly MelonPreferences_Entry<bool> hud; private readonly MelonPreferences_Entry<float> seconds; private readonly MelonPreferences_Entry<float> radius; private readonly MelonPreferences_Entry<float> speed; private readonly MelonPreferences_Entry<float> deadzone; private readonly MelonPreferences_Entry<bool> magic; private readonly MelonPreferences_Entry<bool> sound; private readonly MelonPreferences_Entry<float> soundVolume; public bool Enabled { get { return enabled.Value; } set { enabled.Value = value; } } public bool RightHand { get { return rightHand.Value; } set { rightHand.Value = value; } } public bool Invert { get { return invert.Value; } set { invert.Value = value; } } public bool Hud { get { return hud.Value; } set { hud.Value = value; } } public float Seconds => Clamp(seconds.Value, 2f, 20f, 10f); public float Radius => Clamp(radius.Value, 10f, 250f, 100f); public float Speed { get { return Clamp(speed.Value, 0.25f, 12f, 4f); } set { speed.Value = value; } } public float Deadzone => Clamp(deadzone.Value, 2f, 25f, 8f); public bool Magic { get { return magic.Value; } set { magic.Value = value; } } public bool Sound { get { return sound.Value; } set { sound.Value = value; } } public float SoundVolume { get { return Clamp(soundVolume.Value, 0f, 1f, 0.35f); } set { soundVolume.Value = value; } } public Settings() { category = MelonPreferences.CreateCategory("WristRewind"); enabled = category.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); rightHand = category.CreateEntry<bool>("RightHand", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); invert = category.CreateEntry<bool>("InvertTwist", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); hud = category.CreateEntry<bool>("ShowWristDisplay", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); seconds = category.CreateEntry<float>("HistorySeconds", 10f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); radius = category.CreateEntry<float>("RecordRadius", 100f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); speed = category.CreateEntry<float>("MaximumSpeed", 4f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); deadzone = category.CreateEntry<float>("DeadzoneDegrees", 8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); magic = category.CreateEntry<bool>("WristMagic", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); sound = category.CreateEntry<bool>("TimeSounds", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); soundVolume = category.CreateEntry<float>("TimeSoundVolume", 0.35f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); } public void Save() { category.SaveToFile(false); } private static float Clamp(float value, float min, float max, float fallback) { if (!float.IsFinite(value)) { return fallback; } return Math.Clamp(value, min, max); } } internal sealed class TimeAudio : IDisposable { private readonly List<AudioClip> clips = new List<AudioClip>(); private GameObject? root; private AudioSource? rewind; private AudioSource? forward; private AudioSource? cues; private AudioClip? start; private AudioClip? stop; private bool wasActive; private float rewindGain; private float forwardGain; public void Update(bool active, float rate, bool enabled, float volume, float dt) { volume = (float.IsFinite(volume) ? Math.Clamp(volume, 0f, 1f) : 0f); if (!enabled || volume == 0f) { Silence(); return; } if ((Object)(object)root == (Object)null) { if (!active) { return; } Create(); } dt = (float.IsFinite(dt) ? Math.Clamp(dt, 0f, 0.1f) : 0f); rate = (float.IsFinite(rate) ? rate : 0f); cues.volume = volume; if (active && !wasActive) { cues.PlayOneShot(start, 0.8f); } else if (!active && wasActive) { cues.PlayOneShot(stop, 0.8f); } wasActive = active; float num = 1f - MathF.Exp((0f - dt) / 0.09f); rewindGain += ((float)((active && rate < -0.02f) ? 1 : 0) - rewindGain) * num; forwardGain += ((float)((active && rate > 0.02f) ? 1 : 0) - forwardGain) * num; float num2 = Math.Clamp(MathF.Abs(rate), 0f, 4f); Loop(rewind, rewindGain * volume, 0.8f + num2 * 0.08f); Loop(forward, forwardGain * volume, 0.95f + num2 * 0.08f); } private static void Loop(AudioSource source, float volume, float pitch) { source.pitch = pitch; source.volume = ((volume < 0.0005f) ? 0f : volume); if (source.volume == 0f) { if (source.isPlaying) { source.Stop(); } } else if (!source.isPlaying) { source.Play(); } } private AudioClip Load(string name) { using Stream stream = typeof(TimeAudio).Assembly.GetManifestResourceStream("WristRewind.Audio." + name + ".wav") ?? throw new FileNotFoundException("Missing time audio: " + name + "."); WaveData waveData = WaveData.Read(stream); AudioClip val = AudioClip.Create("WristRewind " + name, waveData.Frames, waveData.Channels, waveData.Rate, false); clips.Add(val); if (!val.SetData(Il2CppStructArray<float>.op_Implicit(waveData.Samples), 0)) { throw new InvalidOperationException("Time audio could not be loaded."); } return val; } private AudioSource Source(AudioClip? clip, bool loop) { AudioSource obj = root.AddComponent<AudioSource>(); obj.playOnAwake = false; obj.loop = loop; obj.clip = clip; obj.volume = 0f; obj.spatialBlend = 0f; obj.ignoreListenerPause = true; obj.bypassListenerEffects = true; obj.bypassReverbZones = true; obj.priority = 64; return obj; } private void Create() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown root = new GameObject("WristRewind time audio"); rewind = Source(Load("rewind"), loop: true); forward = Source(Load("forward"), loop: true); start = Load("start"); stop = Load("stop"); cues = Source(null, loop: false); } private void Silence() { if ((Object)(object)rewind != (Object)null) { rewind.Stop(); rewind.volume = 0f; } if ((Object)(object)forward != (Object)null) { forward.Stop(); forward.volume = 0f; } if ((Object)(object)cues != (Object)null) { cues.Stop(); cues.volume = 0f; } wasActive = false; rewindGain = (forwardGain = 0f); } public void Dispose() { Silence(); if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } foreach (AudioClip clip in clips) { Object.Destroy((Object)(object)clip); } clips.Clear(); root = null; rewind = (forward = (cues = null)); start = (stop = null); } } internal sealed class TimeEffects : IDisposable { private GameObject? wrist; private Transform? outer; private Transform? glyphs; private LineRenderer? hand; private LineRenderer? sweep; private Material? ink; private float phase; private float fade; public void Update(bool active, bool right, double behind, float speed) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) float num = Math.Clamp(Time.unscaledDeltaTime, 0f, 0.1f); fade = Mathf.MoveTowards(fade, (float)(active ? 1 : 0), num * 6f); if (fade <= 0f) { if ((Object)(object)wrist != (Object)null) { wrist.SetActive(false); } return; } Hand val = (right ? Player.RightHand : Player.LeftHand); if (!((Object)(object)val == (Object)null) && !((Object)(object)Player.Head == (Object)null)) { if ((Object)(object)wrist == (Object)null) { CreateWrist(); } wrist.SetActive(true); wrist.transform.SetPositionAndRotation(((Component)val).transform.position + ((Component)val).transform.forward * 0.13f, ((Component)val).transform.rotation); wrist.transform.localScale = Vector3.one * (0.8f + fade * 0.2f); phase += num * ((MathF.Abs(speed) < 0.02f) ? 12f : ((0f - speed) * 55f)); outer.localRotation = Quaternion.Euler(0f, 0f, phase); glyphs.localRotation = Quaternion.Euler(0f, 0f, (0f - phase) * 0.55f); float num2 = 0.88f + 0.12f * MathF.Sin(Time.unscaledTime * 3.5f); ink.color = new Color(0.22f * num2, 1.5f * num2, 0.48f * num2, fade); float x = (float)(behind * Math.PI * 2.0 / 10.0); hand.SetPosition(0, Vector3.zero); hand.SetPosition(1, new Vector3(MathF.Sin(x) * 0.055f, MathF.Cos(x) * 0.055f, 0f)); sweep.SetPosition(0, Vector3.zero); sweep.SetPosition(1, new Vector3(MathF.Sin((0f - phase) * ((float)Math.PI / 180f)) * 0.036f, MathF.Cos((0f - phase) * ((float)Math.PI / 180f)) * 0.036f, 0f)); } } private Material Material() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown Material val = new Material(Shader.Find("Sprites/Default") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? throw new InvalidOperationException("No time effect shader was found.")) { renderQueue = 3100 }; if (val.HasProperty("_ZWrite")) { val.SetInt("_ZWrite", 0); } if (val.HasProperty("_Cull")) { val.SetInt("_Cull", 0); } if (val.HasProperty("_Surface")) { val.SetFloat("_Surface", 1f); } if (val.HasProperty("_SrcBlend")) { val.SetInt("_SrcBlend", 5); } if (val.HasProperty("_DstBlend")) { val.SetInt("_DstBlend", 10); } val.SetOverrideTag("RenderType", "Transparent"); val.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); return val; } private static GameObject Child(Transform parent, string name) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown GameObject val = new GameObject("WristRewind " + name); val.transform.SetParent(parent, false); return val; } private LineRenderer Line(Transform parent, float width, Color color, params Vector3[] points) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) LineRenderer val = Child(parent, "sigil").AddComponent<LineRenderer>(); val.useWorldSpace = false; ((Renderer)val).sharedMaterial = ink; val.widthMultiplier = width; Color startColor = (val.endColor = color); val.startColor = startColor; val.numCapVertices = 2; val.numCornerVertices = 2; ((Renderer)val).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val).receiveShadows = false; val.positionCount = points.Length; for (int i = 0; i < points.Length; i++) { val.SetPosition(i, points[i]); } return val; } private void Arc(Transform parent, float radius, float from, float to, float width, Color color, float z = 0f) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) int num = Math.Max(3, (int)(MathF.Abs(to - from) / 4f) + 1); Vector3[] array = (Vector3[])(object)new Vector3[num]; for (int i = 0; i < num; i++) { float x = Mathf.Lerp(from, to, (float)i / (float)(num - 1)) * ((float)Math.PI / 180f); array[i] = new Vector3(MathF.Sin(x) * radius, MathF.Cos(x) * radius, z); } Line(parent, width, color, array); } private void CreateWrist() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_0452: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Unknown result type (might be due to invalid IL or missing references) //IL_0468: Unknown result type (might be due to invalid IL or missing references) //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_049e: Unknown result type (might be due to invalid IL or missing references) //IL_04a8: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) ink = Material(); wrist = new GameObject("WristRewind time sigil"); outer = Child(wrist.transform, "outer ring").transform; glyphs = Child(wrist.transform, "runes").transform; Color color = default(Color); ((Color)(ref color))..ctor(0.55f, 1f, 0.65f, 0.95f); Color color2 = default(Color); ((Color)(ref color2))..ctor(0.2f, 1f, 0.5f, 0.45f); Arc(wrist.transform, 0.061f, 0f, 360f, 0.0018f, color); Arc(wrist.transform, 0.067f, 0f, 360f, 0.0006f, color2); Arc(outer, 0.099f, 0f, 360f, 0.0018f, color); Arc(outer, 0.106f, 0f, 360f, 0.0007f, color2); Arc(outer, 0.117f, 0f, 360f, 0.0012f, color); for (int i = 0; i < 48; i++) { float x = (float)i * 7.5f * ((float)Math.PI / 180f); float num = ((i % 4 == 0) ? 0.107f : 0.112f); Line(outer, 0.0009f, color, new Vector3(MathF.Sin(x) * num, MathF.Cos(x) * num, 0f), new Vector3(MathF.Sin(x) * 0.116f, MathF.Cos(x) * 0.116f, 0f)); } for (int j = 0; j < 12; j++) { Transform transform = Child(glyphs, "rune").transform; transform.localRotation = Quaternion.Euler(0f, 0f, (float)(j * 30)); float num2 = ((j % 2 == 0) ? 1 : (-1)); Line(transform, 0.0011f, color, new Vector3(-0.005f, 0.075f, 0f), new Vector3(0.004f, 0.084f, 0f), new Vector3(-0.003f, 0.092f, 0f), new Vector3(0.005f, 0.089f, 0f)); Line(transform, 0.0007f, color, new Vector3(-0.006f * num2, 0.087f, 0f), new Vector3(0.006f * num2, 0.08f, 0f)); } for (int k = 0; k < 2; k++) { Transform transform2 = Child(wrist.transform, "geometry").transform; transform2.localRotation = Quaternion.Euler(0f, 0f, (float)(k * 45)); Line(transform2, 0.0009f, color2, new Vector3(-0.043f, -0.043f, 0f), new Vector3(0.043f, -0.043f, 0f), new Vector3(0.043f, 0.043f, 0f), new Vector3(-0.043f, 0.043f, 0f), new Vector3(-0.043f, -0.043f, 0f)); } Arc(wrist.transform, 0.009f, 0f, 360f, 0.0012f, color); hand = Line(wrist.transform, 0.0014f, color, Vector3.zero, Vector3.up * 0.055f); sweep = Line(wrist.transform, 0.0008f, color2, Vector3.zero, Vector3.up * 0.036f); } public void Dispose() { if ((Object)(object)wrist != (Object)null) { Object.Destroy((Object)(object)wrist); } if ((Object)(object)ink != (Object)null) { Object.Destroy((Object)(object)ink); } wrist = null; ink = null; hand = (sweep = null); outer = (glyphs = null); fade = (phase = 0f); } } internal sealed class WristDisplay : IDisposable { private TextMesh? label; public void Update(bool visible, bool right, double behind, float speed, bool atPresent, bool atOldest) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) if (!visible) { if ((Object)(object)label != (Object)null) { ((Component)label).gameObject.SetActive(false); } return; } Hand val = (right ? Player.RightHand : Player.LeftHand); if (!((Object)(object)val == (Object)null) && !((Object)(object)Player.Head == (Object)null)) { if ((Object)(object)label == (Object)null) { GameObject val2 = new GameObject("WristRewind display"); label = val2.AddComponent<TextMesh>(); label.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); ((Renderer)((Component)label).GetComponent<MeshRenderer>()).sharedMaterial = label.font.material; label.fontSize = 44; label.characterSize = 0.002f; label.anchor = (TextAnchor)4; label.alignment = (TextAlignment)1; label.richText = false; } ((Component)label).gameObject.SetActive(true); ((Component)label).transform.position = ((Component)val).transform.position + Vector3.up * 0.12f; ((Component)label).transform.rotation = Quaternion.LookRotation(((Component)label).transform.position - Player.Head.position, Player.Head.up); string value = ((speed > 0.02f && atPresent) ? "PRESENT" : ((speed < -0.02f && atOldest) ? "HISTORY LIMIT" : ((MathF.Abs(speed) < 0.02f) ? "PAUSED" : ((speed < 0f) ? "REWIND" : "FORWARD")))); float value2 = (((speed > 0f && atPresent) || (speed < 0f && atOldest)) ? 0f : MathF.Abs(speed)); label.text = $"{value} {value2:0.0}x\n-{behind:0.00}s"; label.color = (Color)((speed < -0.02f) ? new Color(0.2f, 0.85f, 1f) : ((speed > 0.02f) ? new Color(1f, 0.75f, 0.2f) : Color.white)); } } public void Dispose() { if ((Object)(object)label != (Object)null) { Object.Destroy((Object)(object)((Component)label).gameObject); } label = null; } } } namespace WristRewind.Tracks { internal readonly record struct AnimationLayer(int Hash, float Time); internal sealed record AnimationState(AnimationLayer[] Layers, float Speed, bool Enabled); internal sealed class AnimatorTrack : Track<AnimationState> { private readonly Animator animator; private AnimationState? live; private AnimationState? selected; private bool frozen; public AnimatorTrack(Animator animator, int capacity) : base(capacity, (Component)(object)animator) { this.animator = animator; } protected override AnimationState Read() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) AnimationLayer[] array = new AnimationLayer[Math.Min(animator.layerCount, 8)]; if (animator.isInitialized) { for (int i = 0; i < array.Length; i++) { AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(i); array[i] = new AnimationLayer(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash, ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime); } } return new AnimationState(array, animator.speed, ((Behaviour)animator).enabled); } public override void Freeze() { if (base.Alive && !frozen) { live = (selected = Read()); frozen = true; ((Behaviour)animator).enabled = false; } } public override void Apply(double time) { if (base.Alive && frozen) { ((Behaviour)animator).enabled = false; if (History.Sample(time, out AnimationState a, out AnimationState _, out float _)) { selected = a; } } } public override void Release(bool commit, double time) { AnimationState animationState = (commit ? selected : live); if (base.Owned && frozen && animationState != null) { ((Behaviour)animator).enabled = animationState.Enabled; animator.speed = animationState.Speed; if (commit && animator.isInitialized) { for (int i = 0; i < animationState.Layers.Length; i++) { if (animationState.Layers[i].Hash != 0) { animator.Play(animationState.Layers[i].Hash, i, animationState.Layers[i].Time); } } } } frozen = false; base.Release(commit, time); } public override void Dispose() { Release(commit: false, 0.0); base.Dispose(); } } internal readonly record struct AudioFrame(AudioClip? Clip, int Sample, bool Playing, float Pitch); internal sealed class AudioTrack : Track<AudioFrame> { private readonly AudioSource source; private AudioFrame live; private AudioFrame selected; private bool frozen; public AudioTrack(AudioSource source, int capacity) : base(capacity, (Component)(object)source) { this.source = source; } protected override AudioFrame Read() { return new AudioFrame(source.clip, ((Object)(object)source.clip != (Object)null) ? source.timeSamples : 0, source.isPlaying, source.pitch); } public override void Freeze() { if (base.Alive && !frozen) { live = (selected = Read()); frozen = true; source.Pause(); } } public override void Apply(double time) { if (base.Alive && frozen) { source.Pause(); selected = (History.Sample(time, out var a, out var _, out var _) ? a : default(AudioFrame)); } } public override void Release(bool commit, double time) { if (base.Owned && frozen) { AudioFrame audioFrame = (commit ? selected : live); if ((Object)(object)source.clip != (Object)(object)audioFrame.Clip) { source.clip = audioFrame.Clip; } source.pitch = ((audioFrame.Pitch == 0f) ? live.Pitch : audioFrame.Pitch); if ((Object)(object)audioFrame.Clip != (Object)null) { source.timeSamples = Math.Clamp(audioFrame.Sample, 0, Math.Max(0, audioFrame.Clip.samples - 1)); if (audioFrame.Playing) { source.UnPause(); if (!source.isPlaying) { source.Play(); } } else { source.Stop(); } } else { source.Stop(); } } frozen = false; base.Release(commit, time); } public override void Dispose() { Release(commit: false, 0.0); base.Dispose(); } } internal readonly record struct BodyState(Vector3 Position, Quaternion Rotation, Vector3 Velocity, Vector3 AngularVelocity, bool Kinematic, bool Gravity, bool Collisions, bool Sleeping); internal sealed class BodyTrack : Track<BodyState> { private Rigidbody? body; private readonly MarrowBody? marrow; private readonly Transform root; private readonly MarrowEntity? entity; private readonly bool moveEntity; private readonly Vector3 entityOffset; private readonly Quaternion entityRotation; private BodyState live; private BodyState selected; private bool frozen; private RigidbodyInterpolation interpolation; private Vector3 previous; private bool observed; private double appliedTime; private bool sampled; private float poseStep; public Rigidbody? Body => Resolve(); public Transform Root => root; public bool Frozen => frozen; public bool Applied { get; private set; } public float DistanceRecorded { get; private set; } public float PositionError { get; private set; } public double Oldest => History.Oldest; public double Newest => History.Newest; public BodyTrack(Rigidbody body, int capacity) : base(capacity, (Component)(object)body) { this.body = body; root = ((Component)body).transform; } public BodyTrack(MarrowBody body, int capacity) : base(capacity, (Component)(object)body) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) marrow = body; root = ((Component)body).transform; entity = body.Entity; moveEntity = (Object)(object)entity != (Object)null && entity.Bodies != null && ((Il2CppArrayBase<MarrowBody>)(object)entity.Bodies).Length == 1; if (moveEntity) { entityOffset = Quaternion.Inverse(root.rotation) * (((Component)entity).transform.position - root.position); entityRotation = Quaternion.Inverse(root.rotation) * ((Component)entity).transform.rotation; } Resolve(); } private Rigidbody? Resolve() { if ((Object)(object)marrow != (Object)null) { body = marrow._rigidbody; } if (!((Object)(object)body != (Object)null)) { return null; } return body; } private void Unpack() { if ((Object)(object)marrow != (Object)null && marrow.IsPacked) { marrow.Unpack(); } } protected override BodyState Read() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = Resolve(); Vector3 val2 = (((Object)(object)val != (Object)null) ? val.position : root.position); if (observed) { DistanceRecorded += Vector3.Distance(previous, val2); } previous = val2; observed = true; return new BodyState(val2, ((Object)(object)val != (Object)null) ? val.rotation : root.rotation, ((Object)(object)val != (Object)null) ? val.velocity : Vector3.zero, ((Object)(object)val != (Object)null) ? val.angularVelocity : Vector3.zero, (Object)(object)val != (Object)null && val.isKinematic, (Object)(object)val == (Object)null || val.useGravity, (Object)(object)val == (Object)null || val.detectCollisions, (Object)(object)val == (Object)null || val.IsSleeping()); } public override void Freeze() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (base.Alive && !frozen) { Unpack(); Rigidbody val = Resolve(); live = (selected = Read()); interpolation = (RigidbodyInterpolation)(((Object)(object)val != (Object)null) ? ((int)val.interpolation) : 0); Applied = false; sampled = false; poseStep = 0f; PositionError = 0f; frozen = true; Lock(val); } } private static void Lock(Rigidbody? rb) { if (!((Object)(object)rb == (Object)null)) { rb.interpolation = (RigidbodyInterpolation)0; rb.detectCollisions = false; rb.isKinematic = true; } } public override void Apply(double time) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) bool applied = Applied; Applied = false; sampled = false; if (base.Alive && frozen && History.Sample(time, out var a, out var b, out var blend)) { Unpack(); sampled = true; appliedTime = time; if (applied) { PositionError = MathF.Max(PositionError, Vector3.Distance(root.position, selected.Position)); } Vector3 position = selected.Position; selected = a with { Position = Vector3.LerpUnclamped(a.Position, b.Position, blend), Rotation = Quaternion.SlerpUnclamped(a.Rotation, b.Rotation, blend), Velocity = Vector3.LerpUnclamped(a.Velocity, b.Velocity, blend), AngularVelocity = Vector3.LerpUnclamped(a.AngularVelocity, b.AngularVelocity, blend) }; poseStep = Vector3.Distance(position, selected.Position); MoveEntity(selected); Rigidbody val = Resolve(); Lock(val); if ((Object)(object)val != (Object)null) { val.position = selected.Position; val.rotation = selected.Rotation; } root.SetPositionAndRotation(selected.Position, selected.Rotation); PositionError = MathF.Max(PositionError, Vector3.Distance(root.position, selected.Position)); Applied = true; } } public override void Release(bool commit, double time) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (base.Owned && frozen) { Unpack(); BodyState state = (commit ? selected : live); MoveEntity(state); Rigidbody val = Resolve(); root.SetPositionAndRotation(state.Position, state.Rotation); if ((Object)(object)val != (Object)null) { val.position = state.Position; val.rotation = state.Rotation; val.useGravity = state.Gravity; val.isKinematic = state.Kinematic; if (!state.Kinematic) { val.velocity = state.Velocity; val.angularVelocity = state.AngularVelocity; if (state.Sleeping) { val.Sleep(); } else { val.WakeUp(); } } val.detectCollisions = state.Collisions; val.interpolation = interpolation; } } frozen = false; Applied = false; sampled = false; base.Release(commit, time); } private void MoveEntity(BodyState state) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (moveEntity && (Object)(object)entity != (Object)null && !entity.IsDespawned && !entity.IsDestroyed) { entity.Teleport(state.Position + state.Rotation * entityOffset, state.Rotation * entityRotation, false); } } public string Describe(bool skipped) { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) return $"Body {((Object)((Component)root).gameObject).name}: samples={base.SampleCount} range={Oldest:0.00}..{Newest:0.00} motion={DistanceRecorded:0.00}m frozen={frozen} applied={Applied} sampled={sampled} poseTime={appliedTime:0.00} step={poseStep:0.000}m toPresent={Vector3.Distance(selected.Position, live.Position):0.000}m alive={base.Alive} rigidbody={(Object)(object)Resolve() != (Object)null} packed={(Object)(object)marrow != (Object)null && marrow.IsPacked} skipped={skipped} drift={PositionError:0.000}m stableBody={(Object)(object)marrow != (Object)null}"; } public override void Dispose() { Release(commit: false, 0.0); base.Dispose(); } } internal readonly record struct GunState(bool Loaded, bool Charged, bool Locked, CartridgeData? Cartridge, MagazineState? Magazine, int Rounds, CartridgeData? MagazineCartridge); internal sealed class GunTrack : Track<GunState> { private readonly Gun gun; private GunState selected; private bool frozen; private bool enabled; public GunTrack(Gun gun, int capacity) : base(capacity, (Component)(object)gun) { this.gun = gun; } protected override GunState Read() { MagazineState magazineState = gun.MagazineState; return new GunState(gun.isLoaded, gun.isCharged, gun.isSlideLockedOnEmpty, gun.chamberedCartridge, magazineState, (magazineState != null) ? magazineState.AmmoCount : 0, (magazineState != null) ? magazineState.GetCartridgeData() : null); } public override void Freeze() { if (base.Alive && !frozen) { selected = Read(); enabled = ((Behaviour)gun).enabled; frozen = true; ((Behaviour)gun).enabled = false; } } public override void Apply(double time) { if (base.Alive && frozen) { ((Behaviour)gun).enabled = false; if (History.Sample(time, out var a, out var _, out var _)) { selected = a; } } } public override void Release(bool commit, double time) { if (base.Owned && frozen && commit) { gun.isLoaded = selected.Loaded; gun.isCharged = selected.Charged; gun.isSlideLockedOnEmpty = selected.Locked; gun.chamberedCartridge = selected.Cartridge; Gun obj = gun; Gun obj2 = gun; bool flag = (gun.isTriggerPulled = false); bool isFiring = (obj2.isTriggerPressed = flag); obj.isFiring = isFiring; MagazineState magazineState = gun.MagazineState; if (magazineState != null && selected.Magazine != null && ((Il2CppObjectBase)magazineState).Pointer == ((Il2CppObjectBase)selected.Magazine).Pointer && (Object)(object)selected.MagazineCartridge != (Object)null && magazineState.AmmoCount != selected.Rounds) { magazineState.Initialize(selected.MagazineCartridge, selected.Rounds); } } if (base.Owned && frozen) { ((Behaviour)gun).enabled = enabled; } frozen = false; base.Release(commit, time); } public override void Dispose() { Release(commit: false, 0.0); base.Dispose(); } } internal sealed class NodeTrack : Track<Pose> { private readonly Transform node; private Pose live; private bool frozen; public NodeTrack(Transform node, int capacity) : base(capacity, (Component)(object)node) { this.node = node; } protected override Pose Read() { return Pose.Read(node); } public override void Freeze() { if (base.Alive) { live = Read(); frozen = true; } } public override void Apply(double time) { if (base.Alive && frozen && History.Sample(time, out var a, out var b, out var blend)) { Pose.Apply(node, a, b, blend); } } public override void Release(bool commit, double time) { if (!commit && base.Owned && frozen) { Pose.Apply(node, live, live, 0f); } frozen = false; base.Release(commit, time); } public override void Dispose() { Release(commit: false, 0.0); base.Dispose(); } } internal readonly record struct NpcState(float Health, float LeftArm, float RightArm, float LeftLeg, float RightLeg, bool Dead, State PuppetState) { [CompilerGenerated] public void Deconstruct(out float Health, out float LeftArm, out float RightArm, out float LeftLeg, out float RightLeg, out bool Dead, out State PuppetState) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected I4, but got Unknown Health = this.Health; LeftArm = this.LeftArm; RightArm = this.RightArm; LeftLeg = this.LeftLeg; RightLeg = this.RightLeg; Dead = this.Dead; PuppetState = (State)(int)this.PuppetState; } } internal sealed class NpcTrack : Track<NpcState> { private readonly AIBrain brain; private readonly BehaviourBaseNav? behaviour; private readonly PuppetMaster? puppet; private readonly SubBehaviourHealth? health; private readonly NavMeshAgent? nav; private bool brainEnabled; private bool behaviourEnabled; private bool puppetEnabled; private bool navEnabled; private bool frozen; private NpcState live; private NpcState selected; public NpcTrack(AIBrain brain, int capacity) : base(capacity, (Component)(object)brain) { this.brain = brain; behaviour = brain.behaviour; puppet = brain.puppetMaster; health = (((Object)(object)behaviour != (Object)null) ? behaviour.health : null); nav = (((Object)(object)behaviour != (Object)null) ? behaviour._navAgent : null); } protected override NpcState Read() { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) return new NpcState((health != null) ? health.cur_hp : 0f, (health != null) ? health.cur_arm_lf : 0f, (health != null) ? health.cur_arm_rt : 0f, (health != null) ? health.cur_leg_lf : 0f, (health != null) ? health.cur_leg_rt : 0f, brain.isDead, (State)(((Object)(object)puppet != (Object)null) ? ((int)puppet.state) : 0)); } public override void Freeze() { if (base.Alive && !frozen) { live = (selected = Read()); brainEnabled = ((Behaviour)brain).enabled; behaviourEnabled = (Object)(object)behaviour != (Object)null && ((Behaviour)behaviour).enabled; puppetEnabled = (Object)(object)puppet != (Object)null && ((Behaviour)puppet).enabled; navEnabled = (Object)(object)nav != (Object)null && ((Behaviour)nav).enabled; frozen = true; ((Behaviour)brain).enabled = false; if ((Object)(object)behaviour != (Object)null) { ((Behaviour)behaviour).enabled = false; } if ((Object)(object)puppet != (Object)null) { ((Behaviour)puppet).enabled = false; } if ((Object)(object)nav != (Object)null) { ((Behaviour)nav).enabled = false; } } } public override void Apply(double time) { if (base.Alive && frozen && History.Sample(time, out var a, out var _, out var _)) { selected = a; } } public override void Release(bool commit, double time) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) if (base.Owned && frozen) { NpcState npcState = (commit ? selected : live); if (commit && live.Dead && !npcState.Dead && (Object)(object)behaviour != (Object)null) { ((BehaviourBase)behaviour).Resurrect(); } brain.isDead = npcState.Dead; if ((Object)(object)puppet != (Object)null) { puppet.state = npcState.PuppetState; } if (health != null) { health.cur_hp = npcState.Health; health.cur_arm_lf = npcState.LeftArm; health.cur_arm_rt = npcState.RightArm; health.cur_leg_lf = npcState.LeftLeg; health.cur_leg_rt = npcState.RightLeg; } ((Behaviour)brain).enabled = brainEnabled; if ((Object)(object)behaviour != (Object)null) { ((Behaviour)behaviour).enabled = behaviourEnabled || (commit && live.Dead && !npcState.Dead); } if ((Object)(object)puppet != (Object)null) { ((Behaviour)puppet).enabled = puppetEnabled; } if ((Object)(object)nav != (Object)null) { ((Behaviour)nav).enabled = navEnabled; if (navEnabled && nav.isOnNavMesh) { nav.Warp(((Component)nav).transform.position); nav.ResetPath(); } } } frozen = false; base.Release(commit, time); } public override void Dispose() { Release(commit: false, 0.0); base.Dispose(); } } internal readonly record struct ParticleFrame(float Time, uint Seed, int Count, bool AutoSeed, bool Playing, bool Paused, bool RendererEnabled); internal sealed class ParticleTrack : Track<ParticleFrame> { private const float MaxReplayTime = 3f; private readonly ParticleSystem system; private readonly ParticleSystemRenderer? renderer; private ParticleFrame live; private ParticleFrame? selected; private bool frozen; private bool simulated; private float appliedTime; private uint appliedSeed; public ParticleTrack(ParticleSystem system, int capacity) : base(capacity, (Component)(object)system) { this.system = system; renderer = ((Component)system).GetComponent<ParticleSystemRenderer>(); } protected override ParticleFrame Read() { return new ParticleFrame(system.time, system.randomSeed, system.particleCount, system.useAutoRandomSeed, system.isPlaying, system.isPaused, (Object)(object)renderer != (Object)null && ((Renderer)renderer).enabled); } public override void Freeze() { if (base.Alive && !frozen) { live = Read(); selected = live; simulated = false; frozen = true; system.Pause(false); } } public override void Apply(double time) { if (!base.Alive || !frozen) { return; } system.Pause(false); if (!History.Sample(time, out var a, out var b, out var blend)) { if ((Object)(object)renderer != (Object)null) { ((Renderer)renderer).enabled = false; } selected = null; return; } float time2 = ((a.Seed == b.Seed && b.Time >= a.Time && b.Time - a.Time <= 0.25f) ? (a.Time + (b.Time - a.Time) * blend) : a.Time); selected = a with { Time = time2 }; if (!Replayable(live) || !Replayable(selected.Value)) { if (simulated && (appliedSeed != live.Seed || appliedTime != live.Time)) { Seek(live, force: true); } selected = live; if ((Object)(object)renderer != (Object)null) { ((Renderer)renderer).enabled = live.RendererEnabled; } } else { Seek(selected.Value); if ((Object)(object)renderer != (Object)null) { ((Renderer)renderer).enabled = a.RendererEnabled; } } } private void Seek(ParticleFrame frame, bool force = false) { if (Replayable(live) && Replayable(frame) && (simulated || frame.Seed != live.Seed || !(MathF.Abs(frame.Time - live.Time) < 1E-06f)) && (force || !simulated || frame.Seed != appliedSeed || !(MathF.Abs(frame.Time - appliedTime) < 1f / 30f))) { system.useAutoRandomSeed = false; system.randomSeed = frame.Seed; system.Simulate(frame.Time, false, true, false); system.Pause(false); simulated = true; appliedTime = frame.Time; appliedSeed = frame.Seed; } } private static bool Replayable(ParticleFrame frame) { if (float.IsFinite(frame.Time) && frame.Time >= 0f && frame.Time <= 3f) { int count = frame.Count; if (count >= 0) { return count <= 128; } return false; } return false; } public override void Release(bool commit, double time) { if (base.Owned && frozen) { ParticleFrame? particleFrame = (commit ? selected : new ParticleFrame?(live)); if (particleFrame.HasValue) { if (simulated) { Seek(particleFrame.Value, force: true); } sy