using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ClassLibrary4")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ClassLibrary4")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("43afb890-208d-4528-aedf-46e3838ce5a8")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.user.goi.ghostchaser", "Ghost Chaser Pro", "1.8.0")]
public class GhostChaser : BaseUnityPlugin
{
[Serializable]
public class FrameData
{
public List<PartData> parts = new List<PartData>();
}
[Serializable]
public class PartData
{
public string name;
public Vector3 wPos;
public Quaternion wRot;
public List<PartData> children = new List<PartData>();
}
[Serializable]
private class RunWrapper
{
public List<FrameData> frames;
}
[CompilerGenerated]
private sealed class <InitiateSequence>d__26 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public bool recording;
public GhostChaser <>4__this;
private Joint2D[] <>s__1;
private int <>s__2;
private Joint2D <joint>5__3;
private MonoBehaviour[] <>s__4;
private int <>s__5;
private MonoBehaviour <comp>5__6;
private string <compName>5__7;
private Animator[] <>s__8;
private int <>s__9;
private Animator <anim>5__10;
private Rigidbody2D[] <>s__11;
private int <>s__12;
private Rigidbody2D <rb>5__13;
private Transform[] <>s__14;
private int <>s__15;
private Transform <t>5__16;
private string <parentName>5__17;
private string <uniqueId>5__18;
private Renderer[] <>s__19;
private int <>s__20;
private Renderer <rend>5__21;
private Material[] <>s__22;
private int <>s__23;
private Material <mat>5__24;
private Color <c>5__25;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <InitiateSequence>d__26(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>s__1 = null;
<joint>5__3 = null;
<>s__4 = null;
<comp>5__6 = null;
<compName>5__7 = null;
<>s__8 = null;
<anim>5__10 = null;
<>s__11 = null;
<rb>5__13 = null;
<>s__14 = null;
<t>5__16 = null;
<parentName>5__17 = null;
<uniqueId>5__18 = null;
<>s__19 = null;
<rend>5__21 = null;
<>s__22 = null;
<mat>5__24 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Expected O, but got Unknown
//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
//IL_05ea: Unknown result type (might be due to invalid IL or missing references)
//IL_0606: Unknown result type (might be due to invalid IL or missing references)
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>4__this.startTimer = 6.5f;
<>2__current = (object)new WaitForSeconds(6.5f);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
if (recording)
{
<>4__this.currentRun.Clear();
<>4__this.isRecording = true;
<>4__this.isPlaying = false;
}
else
{
<>4__this.isPlaying = true;
<>4__this.isRecording = false;
<>4__this.playbackIndex = 0;
}
if ((Object)(object)<>4__this.player != (Object)null)
{
<>4__this.ghost = Object.Instantiate<GameObject>(<>4__this.player);
((Object)<>4__this.ghost).name = "GhostVisualClone";
<>4__this.ghostPartCache.Clear();
<>s__1 = <>4__this.ghost.GetComponentsInChildren<Joint2D>(true);
for (<>s__2 = 0; <>s__2 < <>s__1.Length; <>s__2++)
{
<joint>5__3 = <>s__1[<>s__2];
Object.Destroy((Object)(object)<joint>5__3);
<joint>5__3 = null;
}
<>s__1 = null;
<>s__4 = <>4__this.ghost.GetComponentsInChildren<MonoBehaviour>(true);
for (<>s__5 = 0; <>s__5 < <>s__4.Length; <>s__5++)
{
<comp>5__6 = <>s__4[<>s__5];
if (!((Object)(object)<comp>5__6 == (Object)(object)<>4__this))
{
<compName>5__7 = ((object)<comp>5__6).GetType().Name;
if (<compName>5__7.Contains("IK") || <compName>5__7.Contains("Control") || <compName>5__7.Contains("Animator") || <compName>5__7.Contains("PlayerControl") || <compName>5__7.Contains("Saviour") || <compName>5__7.Contains("PlayerSounds") || <compName>5__7.Contains("Sounds"))
{
Object.DestroyImmediate((Object)(object)<comp>5__6);
}
<compName>5__7 = null;
<comp>5__6 = null;
}
}
<>s__4 = null;
<>s__8 = <>4__this.ghost.GetComponentsInChildren<Animator>(true);
for (<>s__9 = 0; <>s__9 < <>s__8.Length; <>s__9++)
{
<anim>5__10 = <>s__8[<>s__9];
Object.DestroyImmediate((Object)(object)<anim>5__10);
<anim>5__10 = null;
}
<>s__8 = null;
<>s__11 = <>4__this.ghost.GetComponentsInChildren<Rigidbody2D>();
for (<>s__12 = 0; <>s__12 < <>s__11.Length; <>s__12++)
{
<rb>5__13 = <>s__11[<>s__12];
<rb>5__13.isKinematic = true;
<rb>5__13.simulated = false;
<rb>5__13 = null;
}
<>s__11 = null;
<>s__14 = <>4__this.ghost.GetComponentsInChildren<Transform>(true);
for (<>s__15 = 0; <>s__15 < <>s__14.Length; <>s__15++)
{
<t>5__16 = <>s__14[<>s__15];
<parentName>5__17 = (((Object)(object)<t>5__16.parent != (Object)null) ? ((Object)<t>5__16.parent).name : "");
if (<parentName>5__17 == "GhostVisualClone" || <parentName>5__17 == "Player(Clone)")
{
<parentName>5__17 = "Player";
}
<uniqueId>5__18 = (((Object)(object)<t>5__16.parent != (Object)null) ? (<parentName>5__17 + "_" + ((Object)<t>5__16).name + "_" + <t>5__16.GetSiblingIndex()) : ((Object)<t>5__16).name);
if ((Object)(object)<t>5__16 == (Object)(object)<>4__this.ghost.transform)
{
<uniqueId>5__18 = "Player";
}
if (!<>4__this.ghostPartCache.ContainsKey(<uniqueId>5__18))
{
<>4__this.ghostPartCache.Add(<uniqueId>5__18, <t>5__16);
}
<parentName>5__17 = null;
<uniqueId>5__18 = null;
<t>5__16 = null;
}
<>s__14 = null;
<>s__19 = <>4__this.ghost.GetComponentsInChildren<Renderer>();
for (<>s__20 = 0; <>s__20 < <>s__19.Length; <>s__20++)
{
<rend>5__21 = <>s__19[<>s__20];
if (recording)
{
<rend>5__21.enabled = false;
}
else
{
<>s__22 = <rend>5__21.materials;
for (<>s__23 = 0; <>s__23 < <>s__22.Length; <>s__23++)
{
<mat>5__24 = <>s__22[<>s__23];
if (<mat>5__24.HasProperty("_Color"))
{
<c>5__25 = <mat>5__24.color;
<c>5__25.a = 0.4f;
<mat>5__24.color = <c>5__25;
}
<mat>5__24 = null;
}
<>s__22 = null;
}
<rend>5__21 = null;
}
<>s__19 = null;
}
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <WaitAndShowMenu>d__23 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public GhostChaser <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WaitAndShowMenu>d__23(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if ((Object)(object)<>4__this.player == (Object)null)
{
<>4__this.player = GameObject.Find("Player");
<>2__current = null;
<>1__state = 1;
return true;
}
<>4__this.showMenu = true;
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private GameObject player;
private GameObject ghost;
private bool isRecording = false;
private bool isPlaying = false;
private bool showMenu = false;
private float startTimer = -1f;
private Dictionary<string, Transform> ghostPartCache = new Dictionary<string, Transform>();
private bool showSaveSelection = false;
private Vector2 scrollPosition = Vector2.zero;
private string selectedFilePath = "";
private string renameString = "";
private List<FrameData> currentRun = new List<FrameData>();
private int playbackIndex = 0;
private string SavesFolder => Path.Combine(Application.persistentDataPath, "ghost_saves");
private string DefaultSavePath => Path.Combine(SavesFolder, "ghost_run.json");
private void Awake()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Cleanup();
((MonoBehaviour)this).StartCoroutine(WaitAndShowMenu());
}
private void Cleanup()
{
if ((Object)(object)ghost != (Object)null)
{
Object.Destroy((Object)(object)ghost);
}
ghostPartCache.Clear();
isRecording = false;
isPlaying = false;
showSaveSelection = false;
selectedFilePath = "";
startTimer = -1f;
Time.timeScale = 1f;
if (!Directory.Exists(SavesFolder))
{
Directory.CreateDirectory(SavesFolder);
}
}
[IteratorStateMachine(typeof(<WaitAndShowMenu>d__23))]
private IEnumerator WaitAndShowMenu()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WaitAndShowMenu>d__23(0)
{
<>4__this = this
};
}
private void Update()
{
if (showMenu)
{
Cursor.visible = true;
Cursor.lockState = (CursorLockMode)0;
}
else
{
if ((Object)(object)player == (Object)null)
{
return;
}
if (startTimer > 0f)
{
startTimer -= Time.deltaTime;
return;
}
if (isRecording)
{
RecordFrame();
}
if (isPlaying && (Object)(object)ghost != (Object)null && currentRun.Count > 0)
{
ApplyFrame(playbackIndex);
playbackIndex = (playbackIndex + 1) % currentRun.Count;
}
}
}
private void OnGUI()
{
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
//IL_01db: Unknown result type (might be due to invalid IL or missing references)
//IL_01de: Unknown result type (might be due to invalid IL or missing references)
//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
//IL_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_0052: 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_04fe: Unknown result type (might be due to invalid IL or missing references)
//IL_054a: Unknown result type (might be due to invalid IL or missing references)
//IL_0240: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Unknown result type (might be due to invalid IL or missing references)
//IL_045a: Unknown result type (might be due to invalid IL or missing references)
//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
//IL_0300: Unknown result type (might be due to invalid IL or missing references)
//IL_0339: Unknown result type (might be due to invalid IL or missing references)
//IL_0499: Unknown result type (might be due to invalid IL or missing references)
//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
if (showMenu)
{
Time.timeScale = 0f;
if (!showSaveSelection)
{
Rect val = default(Rect);
((Rect)(ref val))..ctor((float)(Screen.width / 2 - 120), (float)(Screen.height / 2 - 80), 240f, 160f);
GUI.Box(val, "GHOST MENU");
if (GUI.Button(new Rect(((Rect)(ref val)).x + 20f, ((Rect)(ref val)).y + 30f, 200f, 35f), "RECORD NEW RUN"))
{
Cursor.visible = false;
Cursor.lockState = (CursorLockMode)1;
showMenu = false;
Time.timeScale = 1f;
((MonoBehaviour)this).StartCoroutine(InitiateSequence(recording: true));
}
string[] files = Directory.GetFiles(SavesFolder, "*.json");
if ((files.Length != 0 || File.Exists(DefaultSavePath)) && GUI.Button(new Rect(((Rect)(ref val)).x + 20f, ((Rect)(ref val)).y + 75f, 200f, 35f), "PLAY LAST RUN"))
{
showSaveSelection = true;
}
}
else
{
Rect val2 = default(Rect);
((Rect)(ref val2))..ctor((float)(Screen.width / 2 - 250), (float)(Screen.height / 2 - 180), 500f, 360f);
GUI.Box(val2, "SELECT OR RENAME RUN TRACKS");
string[] files2 = Directory.GetFiles(SavesFolder, "*.json");
Rect val3 = default(Rect);
((Rect)(ref val3))..ctor(((Rect)(ref val2)).x + 15f, ((Rect)(ref val2)).y + 30f, 470f, 200f);
Rect val4 = default(Rect);
((Rect)(ref val4))..ctor(0f, 0f, 450f, (float)Mathf.Max(200, files2.Length * 32));
scrollPosition = GUI.BeginScrollView(val3, scrollPosition, val4);
for (int i = 0; i < files2.Length; i++)
{
string text = files2[i];
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
string text2 = ((selectedFilePath == text) ? ("> " + fileNameWithoutExtension + " <") : fileNameWithoutExtension);
if (GUI.Button(new Rect(5f, (float)(i * 32), 440f, 28f), text2))
{
selectedFilePath = text;
renameString = fileNameWithoutExtension;
}
}
GUI.EndScrollView();
if (!string.IsNullOrEmpty(selectedFilePath) && File.Exists(selectedFilePath))
{
GUI.Label(new Rect(((Rect)(ref val2)).x + 20f, ((Rect)(ref val2)).y + 245f, 100f, 25f), "Rename to:");
renameString = GUI.TextField(new Rect(((Rect)(ref val2)).x + 110f, ((Rect)(ref val2)).y + 245f, 240f, 25f), renameString);
if (GUI.Button(new Rect(((Rect)(ref val2)).x + 360f, ((Rect)(ref val2)).y + 245f, 120f, 25f), "Apply Name") && !string.IsNullOrEmpty(renameString.Trim()))
{
string text3 = Path.Combine(SavesFolder, renameString.Trim() + ".json");
if (!File.Exists(text3))
{
File.Move(selectedFilePath, text3);
selectedFilePath = text3;
}
}
if (GUI.Button(new Rect(((Rect)(ref val2)).x + 20f, ((Rect)(ref val2)).y + 290f, 210f, 40f), "LOAD SELECTED RUN"))
{
Cursor.visible = false;
Cursor.lockState = (CursorLockMode)1;
LoadRunDirect(selectedFilePath);
showSaveSelection = false;
showMenu = false;
Time.timeScale = 1f;
((MonoBehaviour)this).StartCoroutine(InitiateSequence(recording: false));
}
}
else
{
GUI.Label(new Rect(((Rect)(ref val2)).x + 20f, ((Rect)(ref val2)).y + 250f, 460f, 30f), "Select a file from the list above to proceed...", GUI.skin.box);
}
if (GUI.Button(new Rect(((Rect)(ref val2)).x + 260f, ((Rect)(ref val2)).y + 290f, 220f, 40f), "BACK TO MENU"))
{
showSaveSelection = false;
selectedFilePath = "";
}
}
}
if (!showMenu)
{
if (startTimer > 0f)
{
GUI.Label(new Rect(20f, 20f, 200f, 30f), $"GHOST STARTING: {startTimer:F1}s");
}
if (isRecording && GUI.Button(new Rect((float)(Screen.width - 140), (float)(Screen.height - 45), 130f, 35f), "SAVE RUN"))
{
SaveRun();
}
}
}
[IteratorStateMachine(typeof(<InitiateSequence>d__26))]
private IEnumerator InitiateSequence(bool recording)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <InitiateSequence>d__26(0)
{
<>4__this = this,
recording = recording
};
}
private void RecordFrame()
{
if (!((Object)(object)player == (Object)null))
{
FrameData frameData = new FrameData();
frameData.parts.Add(RecordTransformRecursive(player.transform));
currentRun.Add(frameData);
}
}
private PartData RecordTransformRecursive(Transform current)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Expected O, but got Unknown
PartData partData = new PartData
{
name = ((Object)current).name,
wPos = current.position,
wRot = current.rotation
};
foreach (Transform item in current)
{
Transform current2 = item;
partData.children.Add(RecordTransformRecursive(current2));
}
return partData;
}
private void ApplyFrame(int index)
{
if (!((Object)(object)ghost == (Object)null) && index < currentRun.Count)
{
FrameData frameData = currentRun[index];
if (frameData.parts.Count > 0)
{
ApplyTransformRecursive(ghost.transform, frameData.parts[0]);
}
}
}
private void ApplyTransformRecursive(Transform current, PartData data)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)current == (Object)null || data == null)
{
return;
}
current.position = data.wPos;
current.rotation = data.wRot;
for (int i = 0; i < data.children.Count; i++)
{
if (i < current.childCount)
{
ApplyTransformRecursive(current.GetChild(i), data.children[i]);
}
}
}
private void SaveRun()
{
if (currentRun.Count != 0)
{
string text = $"run_{DateTime.Now:yyyyMMdd_HHmmss}.json";
string path = Path.Combine(SavesFolder, text);
File.WriteAllText(path, JsonUtility.ToJson((object)new RunWrapper
{
frames = currentRun
}));
Debug.Log((object)("Ghost Run Saved to: " + text));
}
}
private void LoadRunDirect(string fullPath)
{
if (File.Exists(fullPath))
{
RunWrapper runWrapper = JsonUtility.FromJson<RunWrapper>(File.ReadAllText(fullPath));
if (runWrapper != null && runWrapper.frames != null)
{
currentRun = runWrapper.frames;
}
}
}
}