Decompiled source of GhostBuster v0.0.6

GhostBusterMod/GhostBusterMod.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Xml;
using BepInEx;
using BepInEx.Configuration;
using GameEvent;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyInformationalVersion("0.0.6")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.6.0")]
[module: UnverifiableCode]
namespace GhostBuster
{
	[BepInPlugin("GhostBuster", "GhostBuster", "0.0.6")]
	public class GhostBusterMod : BaseUnityPlugin
	{
		public enum GhostMode
		{
			Fastest,
			All,
			Last,
			Stored
		}

		[HarmonyPatch(typeof(Character), "SetupReplay")]
		private static class CharacterSetupReplayPatch
		{
			private static bool Prefix(Character __instance, GhostData data)
			{
				//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)
				if (!GhostFraid.ContainsValue(__instance))
				{
					return false;
				}
				if (InputReplay.Value)
				{
					__instance.Enable(false);
					__instance.replayData = data;
					__instance.isReplay = true;
					((Component)__instance).transform.position = data.GetDataForTime(0f, false).position;
					__instance.SetOutfitsFromArray(data.Outfits);
					__instance.nameTag.setNameBoxText(data.PlayerName, __instance);
					return false;
				}
				__instance.SetPlayerPlayerColliders(false, (ColliderStates)2);
				return true;
			}
		}

		[HarmonyPatch(typeof(Character), "SetNonLocalColliderMode")]
		private static class CharacterReplaySetNonLocalColliderModePatch
		{
			private static void Prefix(Character __instance, ref NonLocalColliderMode mode)
			{
				if (InputReplay.Value && __instance.replayData != null)
				{
					mode = (NonLocalColliderMode)1;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Update")]
		private static class CharacterUpdatePatch
		{
			private static void Postfix(Character __instance)
			{
				//IL_0116: Unknown result type (might be due to invalid IL or missing references)
				if (__instance.replayData == null)
				{
					return;
				}
				string text = "notime";
				if (__instance.replayData.lastTime != float.PositiveInfinity && __instance.replayData.lastTime > 0f)
				{
					text = HighscoreDisplayEntry.GetTimeString(__instance.replayData.lastTime);
					if (text.StartsWith("00:"))
					{
						text = text.Substring(3);
					}
				}
				else
				{
					float winTime = GetWinTime(__instance.replayData);
					if (winTime > 0f)
					{
						text = HighscoreDisplayEntry.GetTimeString(winTime);
						if (text.StartsWith("00:"))
						{
							text = text.Substring(3);
						}
						text = "~" + text;
					}
				}
				__instance.nameTag.setNameBoxText(__instance.replayData.PlayerName + " (" + text + ")", __instance);
				__instance.nameTag.currentAlpha = (ShowGhostText.Value ? 1f : 0f);
				((Graphic)__instance.nameTag.nameBox).color = Color.white;
			}
		}

		[HarmonyPatch]
		private static class CharacterReplayUpdateInvalidNowValid
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Character), "replayUpdate", (Type[])null, (Type[])null);
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> e)
			{
				bool done = false;
				bool start = false;
				foreach (CodeInstruction inst in e)
				{
					if (inst.opcode == OpCodes.Ldstr)
					{
						Debug.Log((object)("OpCodes.Ldstr " + inst.operand.ToString()));
					}
					if (!done && inst.opcode == OpCodes.Ldstr && inst.operand.ToString() == "Playing back an invalid keyframe!")
					{
						start = true;
						inst.opcode = OpCodes.Nop;
						inst.operand = null;
					}
					else if (start)
					{
						start = false;
						inst.opcode = OpCodes.Nop;
						done = true;
					}
					yield return inst;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "replayUpdate")]
		private static class CharacterReplayUpdatePatch
		{
			private static bool Prefix(Character __instance)
			{
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_0086: Unknown result type (might be due to invalid IL or missing references)
				if (InputReplay.Value)
				{
					if (__instance.replayData != null && !__instance.replayPaused)
					{
						float num = Time.realtimeSinceStartup - __instance.replayStartTime;
						for (int i = __instance.replayData.lastIndex; i < __instance.replayData.dataPoints.Count; i++)
						{
							if (__instance.replayData.dataPoints[i].timestamp > num)
							{
								__instance.replayData.lastIndex = i;
								break;
							}
							ReplayDataPoint(__instance, __instance.replayData.dataPoints[i]);
						}
						__instance.fullUpdate();
					}
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Character), "StartReplay")]
		private static class CharacterStartReplayPatch
		{
			private static bool Prefix(Character __instance)
			{
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				__instance.isGhost = true;
				if (GhostOutfis.Value)
				{
					__instance.SetOutfitsFromArray(__instance.replayData.outfits);
				}
				else
				{
					__instance.SetOutfitsFromArray(new int[6] { -1, -1, -1, -1, -1, -1 });
				}
				if (InputReplay.Value)
				{
					__instance.replayData.lastIndex = 0;
					((Component)__instance).transform.position = __instance.replayData.GetDataForTime(0f, false).position;
					__instance.isReplaying = false;
					__instance.replayStartTime = Time.realtimeSinceStartup;
					return false;
				}
				return true;
			}

			private static void Postfix(Character __instance)
			{
				if (!InputReplay.Value)
				{
					__instance.Disable(false);
					__instance.Visible = true;
				}
			}
		}

		[HarmonyPatch(typeof(GhostRecorder), "Update")]
		private static class GhostRecorderUpdatePatch
		{
			private static bool Prefix(GhostRecorder __instance, out object[] __state)
			{
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Invalid comparison between Unknown and I4
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				if (__instance.previousValues != null && __instance.ghostData != null)
				{
					if (!__instance.previousValues.ContainsKey((GhostEvent)0))
					{
						__instance.previousValues[(GhostEvent)0] = "";
					}
					__state = new object[2]
					{
						__instance.ghostData.dataPoints.Count,
						__instance.previousValues[(GhostEvent)0]
					};
					__instance.previousValues.Remove((GhostEvent)0);
					object obj = __instance.previousValues[(GhostEvent)2];
					if (obj != null && (int)(AnimState)obj == 8 && __instance.ghostData.lastTime == 0f)
					{
						__instance.ghostData.lastTime = __instance.ghostData.dataPoints[__instance.ghostData.dataPoints.Count - 1].timestamp;
					}
				}
				else
				{
					__state = new object[2] { 0, "" };
				}
				return true;
			}

			private static void Postfix(GhostRecorder __instance, object[] __state)
			{
				//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_0178: Unknown result type (might be due to invalid IL or missing references)
				//IL_017d: Unknown result type (might be due to invalid IL or missing references)
				if (!__instance.tracking || __instance.paused || !((Object)(object)__instance.trackedChar != (Object)null) || __instance.ghostData.lastTime != 0f)
				{
					return;
				}
				Character trackedChar = __instance.trackedChar;
				string text = trackedChar.up + "|" + trackedChar.down + "|" + trackedChar.leftInput + "|" + trackedChar.rightInput + "|" + (trackedChar.jump ? 1 : 0) + "|" + (trackedChar.suicide ? 1 : 0) + "|" + (trackedChar.sprint ? 1 : 0) + "|" + (trackedChar.dance ? 1 : 0);
				if (__state[1] as string != text)
				{
					if ((int)__state[0] != __instance.ghostData.dataPoints.Count)
					{
						GhostDataPoint val = __instance.ghostData.dataPoints[__instance.ghostData.dataPoints.Count - 1];
						((GhostDataPoint)(ref val)).AddData((GhostEvent)0, (object)text);
					}
					else
					{
						((GhostDataPoint)(ref __instance.nextDataPoint)).AddData((GhostEvent)0, (object)text);
						__instance.ghostData.AddGhostData(new GhostDataPoint(__instance.nextDataPoint));
					}
				}
				__instance.previousValues[(GhostEvent)0] = text;
			}
		}

		[HarmonyPatch(typeof(ChallengeScoreboard), "ShowNewResult")]
		private static class ChallengeScoreboardShowNewResultPatch
		{
			private static void Prefix(ChallengeScoreboard __instance)
			{
				foreach (GamePlayer item in ((GameControl)__instance.challengeController).PlayerQueue)
				{
					GhostRecorder val = item?.CharacterInstance?.ReplayRecorder;
					if (!((Object)(object)val != (Object)null))
					{
						continue;
					}
					string currentLevelName = GetCurrentLevelName();
					GhostData copy = val.ghostData.GetCopy();
					if (__instance.challengeController.playerEndTimes.ContainsKey(item))
					{
						copy.lastTime = __instance.challengeController.playerEndTimes[item];
					}
					Debug.Log((object)("add replay for level: " + currentLevelName));
					if (!Replays.ContainsKey(currentLevelName))
					{
						Replays.Add(currentLevelName, new List<GhostData> { copy });
					}
					else
					{
						Replays[currentLevelName].Add(copy);
					}
					List<GhostData> list = Replays[currentLevelName].ToList();
					if (Replays[currentLevelName].Count > MaxGhostNumber.Value)
					{
						IOrderedEnumerable<GhostData> source = SortGhostByTime(Replays[currentLevelName]);
						GhostData val2 = source.Last();
						if (SelectedGhostMode.Value == GhostMode.Last)
						{
							val2 = Replays[currentLevelName][0];
						}
						int key = Animator.StringToHash(val2.printData());
						if (GhostFraid.ContainsKey(key))
						{
							Object.Destroy((Object)(object)((Component)GhostFraid[key]).gameObject);
							GhostFraid.Remove(key);
						}
						Replays[currentLevelName].Remove(val2);
					}
				}
			}
		}

		[HarmonyPatch(typeof(ChallengeControl), "SetupStart")]
		private static class ChallengeControlSetupStartPatch
		{
			private static void Prefix(ChallengeControl __instance)
			{
				GhostFraid = new Dictionary<int, Character>();
			}

			private static void Postfix(ChallengeControl __instance)
			{
				//IL_0065: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
				foreach (GamePlayer item in ((GameControl)__instance).PlayerQueue)
				{
					if (!Object.op_Implicit((Object)(object)item.CharacterInstance.ReplayRecorder))
					{
						GhostRecorder component = new GameObject("Ghost recorder - " + ((object)Unsafe.As<Animals, Animals>(ref item.PickedAnimal)/*cast due to .constrained prefix*/).ToString(), new Type[1] { typeof(GhostRecorder) }).GetComponent<GhostRecorder>();
						component.TrackCharacter(item.CharacterInstance);
						item.CharacterInstance.ReplayRecorder = component;
						Character val = Object.Instantiate<Character>(((GameControl)__instance).CharacterPrefab);
						((Object)((Component)val).gameObject).name = ((object)Unsafe.As<Animals, Animals>(ref item.PickedAnimal)/*cast due to .constrained prefix*/).ToString() + " (ghost)";
						val.NetworkCharacterSprite = item.PickedAnimal;
						val.SetOutfitsFromArray(item.characterOutfitsList);
						val.NetworknetworkNumber = 0;
						val.NetworklocalNumber = 0;
						val.isReplay = true;
						val.Disable(true);
						val.NetworkFindPlayerOnSpawn = false;
						val.Networkpicked = true;
						item.CharacterInstance.ReplayCharacter = val;
					}
				}
			}
		}

		[HarmonyPatch(typeof(ChallengeControl), "DoPlayMode")]
		private static class ChallengeControlDoPlayModePatch
		{
			private static void Postfix(ChallengeControl __instance)
			{
				if (!__instance.runStarted || !GameSettings.GetInstance().DebugChallengeGhosts)
				{
					return;
				}
				foreach (GamePlayer item in ((GameControl)__instance).PlayerQueue)
				{
					GhostRecorder replayRecorder = item.CharacterInstance.ReplayRecorder;
					replayRecorder.PauseTracking();
					replayRecorder.UpdateCharacter();
					item.CharacterInstance.ReplayCharacter.SetupReplay(replayRecorder.CurrentGhostData.GetCopy());
				}
			}
		}

		[HarmonyPatch(typeof(ChallengeControl), "startRun")]
		private static class ChallengeControlStartRunPatch
		{
			private static void Postfix(ChallengeControl __instance)
			{
				if (GameSettings.GetInstance().DebugChallengeGhosts)
				{
					foreach (GamePlayer item in ((GameControl)__instance).PlayerQueue)
					{
						GhostRecorder replayRecorder = item.CharacterInstance.ReplayRecorder;
						Character replayCharacter = item.CharacterInstance.ReplayCharacter;
						if (!replayRecorder.IsTracking)
						{
							replayRecorder.StartTracking(item.CharacterInstance);
						}
						if (replayRecorder.IsPaused)
						{
							replayRecorder.ResumeTracking();
							replayRecorder.Reset();
						}
						if (replayCharacter.HasReplayData)
						{
							replayCharacter.Enable(false);
							replayCharacter.StartReplay();
						}
					}
				}
				string currentLevelName = GetCurrentLevelName();
				if (ShowGhosts.Value && ((SelectedGhostMode.Value != GhostMode.Stored && Replays.ContainsKey(currentLevelName)) || (SelectedGhostMode.Value == GhostMode.Stored && StoredReplays.ContainsKey(currentLevelName))))
				{
					switch (SelectedGhostMode.Value)
					{
					case GhostMode.All:
					{
						foreach (GhostData item2 in Replays[currentLevelName])
						{
							int key2 = Animator.StringToHash(item2.printData());
							if (GhostFraid.ContainsKey(key2))
							{
								Character val2 = GhostFraid[key2];
								val2.StartReplay();
							}
							else
							{
								AddReplayGhost(item2);
							}
						}
						break;
					}
					case GhostMode.Fastest:
						ClearReplayGhosts();
						AddReplayGhost(SortGhostByTime(Replays[currentLevelName]).First());
						break;
					case GhostMode.Last:
						ClearReplayGhosts();
						AddReplayGhost(Replays[currentLevelName][Replays[currentLevelName].Count - 1]);
						break;
					case GhostMode.Stored:
						if (StoredReplays.Count != 0)
						{
							foreach (GhostData item3 in StoredReplays[currentLevelName])
							{
								int key = Animator.StringToHash(item3.printData());
								if (GhostFraid.ContainsKey(key) && Object.op_Implicit((Object)(object)GhostFraid[key]))
								{
									Character val = GhostFraid[key];
									val.StartReplay();
								}
								else
								{
									AddReplayGhost(item3);
								}
							}
							break;
						}
						ClearReplayGhosts();
						break;
					}
				}
				else
				{
					ClearReplayGhosts();
				}
			}
		}

		[HarmonyPatch(typeof(Character), "ReceiveEvent")]
		private static class CharacterReceiveEventPatch
		{
			private static void Prefix(Character __instance, InputEvent e)
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: 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)
				//IL_0117: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)__instance.ReplayRecorder) && __instance.ReplayRecorder.IsTracking)
				{
					GhostDataPoint val = default(GhostDataPoint);
					((GhostDataPoint)(ref val))..ctor(Time.realtimeSinceStartup - __instance.ReplayRecorder.timeOffset, ((Component)__instance).transform.position, true);
					val.eventVals.Add((GhostEvent)0, ((object)e.Key/*cast due to .constrained prefix*/).ToString() + ":" + e.Valuef + ":" + e.Valueb + ":" + e.Changed);
					val.eventVals.Add((GhostEvent)2, __instance.CurrentAnim);
					val.eventVals.Add((GhostEvent)3, __instance.SecondaryAnim);
					val.eventVals.Add((GhostEvent)4, __instance.FlipSpriteX);
					__instance.ReplayRecorder.ghostData.AddGhostData(val);
				}
			}
		}

		[HarmonyPatch(typeof(ChallengeControl), "handleEvent")]
		private static class ChallengeControlHandleEventPatch
		{
			private static void PostFix(ChallengeControl __instance, GameEvent e)
			{
				if (!(((object)e).GetType() == typeof(PauseEvent)))
				{
					return;
				}
				PauseEvent val = (PauseEvent)(object)((e is PauseEvent) ? e : null);
				if (!GameSettings.GetInstance().DebugChallengeGhosts)
				{
					return;
				}
				if (((PauseEvent)((e is PauseEvent) ? e : null)).Paused)
				{
					foreach (GamePlayer item in ((GameControl)__instance).PlayerQueue)
					{
						Character characterInstance = item.CharacterInstance;
						if (characterInstance.ReplayRecorder.IsTracking)
						{
							characterInstance.ReplayRecorder.PauseTracking();
						}
						if (characterInstance.isReplaying)
						{
							characterInstance.PauseReplay();
						}
					}
					return;
				}
				foreach (GamePlayer item2 in ((GameControl)__instance).PlayerQueue)
				{
					Character characterInstance2 = item2.CharacterInstance;
					if (characterInstance2.ReplayRecorder.IsTracking && characterInstance2.ReplayRecorder.IsPaused)
					{
						characterInstance2.ReplayRecorder.ResumeTracking();
					}
					if (characterInstance2.isReplaying && characterInstance2.replayPaused)
					{
						characterInstance2.ResumeReplay();
					}
				}
			}
		}

		public static ConfigEntry<bool> ShowGhosts;

		public static ConfigEntry<GhostMode> SelectedGhostMode;

		public static ConfigEntry<int> MaxGhostNumber;

		public static ConfigEntry<bool> ClearStoredGhosts;

		public static ConfigEntry<bool> ShowGhostText;

		public static ConfigEntry<bool> GhostOutfis;

		public static ConfigEntry<float> GhostAlpha;

		public static ConfigEntry<bool> InputReplay;

		public static ConfigEntry<UserMsgPriority> MsgPriority;

		public static ConfigEntry<KeyCode> ToggleGhostsKey;

		public static ConfigEntry<KeyCode> SwitchGhostModeKey;

		public static ConfigEntry<KeyCode> StoreGhostDataKey;

		public static ConfigEntry<KeyCode> LoadGhostDataKey;

		public static ConfigEntry<KeyCode> ToggleGhostTextKey;

		public static ConfigEntry<KeyCode> ClearAllGhostReplaysKey;

		public static Dictionary<int, Character> GhostFraid = new Dictionary<int, Character>();

		public static Dictionary<string, List<GhostData>> Replays = new Dictionary<string, List<GhostData>>();

		public static Dictionary<string, List<GhostData>> StoredReplays = new Dictionary<string, List<GhostData>>();

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Debug.Log((object)"Let the Ghosts replay");
			GameSettings.GetInstance().DebugChallengeGhosts = true;
			new Harmony("GhostBuster").PatchAll();
			ShowGhosts = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowGhosts", false, (ConfigDescription)null);
			MaxGhostNumber = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxGhostNumber", 10, "Maximum number of ghost replays that are kept and shown in Ghost Mode ALL");
			SelectedGhostMode = ((BaseUnityPlugin)this).Config.Bind<GhostMode>("General", "SelectedGhostMode", GhostMode.Fastest, "The selected Replay Ghost Mode");
			ClearStoredGhosts = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClearStoredGhosts", false, "Remove stored ghost replays when new one is loaded");
			ShowGhostText = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowGhostText", true, "Display text box above ghosts with name and time");
			GhostOutfis = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "GhostOutfis", true, "Show outfits for ghost replays");
			GhostAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GhostAlpha", 0.45f, "Set the alpha/transparency of ghosts. [1: solid, ..., 0: invisible]");
			InputReplay = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "InputReplay", false, "Record and replay from user input");
			GameSettings.GetInstance().ghostAlpha = GhostAlpha.Value;
			MsgPriority = ((BaseUnityPlugin)this).Config.Bind<UserMsgPriority>("GUI", "MsgPriority", (UserMsgPriority)1, "Display GUI messages: hi = show in middle, lo = show bottom right");
			ToggleGhostsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("INPUT", "ToggleGhostsKey", (KeyCode)103, "Keybinding: Toggle Ghosts on or off");
			LoadGhostDataKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("INPUT", "LoadGhostDataKey", (KeyCode)108, "Keybinding: Toggle Ghost Modes");
			StoreGhostDataKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("INPUT", "StoreGhostDataKey", (KeyCode)107, "Keybinding: Load stored data from clipboard");
			SwitchGhostModeKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("INPUT", "SwitchGhostModeKey", (KeyCode)104, "Keybinding: Store ghost data in clipboard");
			ToggleGhostTextKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("INPUT", "ToggleGhostTextKey", (KeyCode)110, "Keybinding: Toggle text above ghosts");
			ClearAllGhostReplaysKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("INPUT", "ClearAllGhostReplays", (KeyCode)120, "Keybinding: Clear all GhostReplay data");
		}

		private static float GetWinTime(GhostData data)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Invalid comparison between Unknown and I4
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			foreach (GhostDataPoint dataPoint in data.dataPoints)
			{
				if (dataPoint.eventVals.ContainsKey((GhostEvent)2))
				{
					object obj = dataPoint.eventVals[(GhostEvent)2];
					if (obj != null && (int)(AnimState)obj == 8)
					{
						return dataPoint.timestamp;
					}
				}
			}
			return -1f;
		}

		public static T FindObject<T>(string name) where T : Object
		{
			T result = default(T);
			T[] array = Resources.FindObjectsOfTypeAll<T>();
			foreach (T val in array)
			{
				if (((Object)val).name == name)
				{
					return val;
				}
			}
			return result;
		}

		private static Character AddReplayGhost(GhostData data)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			Character val = Object.Instantiate<Character>(GhostBusterMod.FindObject<Character>("NotAMeatboy"));
			val.CharacterSprite = data.animal;
			int key = Animator.StringToHash(data.printData());
			GhostFraid.Add(key, val);
			((Object)((Component)val).gameObject).name = ((object)Unsafe.As<Animals, Animals>(ref data.animal)/*cast due to .constrained prefix*/).ToString() + " (ghost)";
			val.SetSprites(data.animal);
			int[] array = new int[6];
			for (int i = 0; i < array.Length; i++)
			{
				if (i < data.outfits.Length)
				{
					array[i] = data.outfits[i];
				}
				else
				{
					array[i] = -1;
				}
				Debug.Log((object)("clean_outfits[i] " + array[i]));
			}
			data.outfits = array;
			val.SetOutfitsFromArray(data.outfits);
			val.NetworknetworkNumber = 0;
			val.NetworklocalNumber = 0;
			val.isReplay = true;
			val.NetworkFindPlayerOnSpawn = false;
			val.Networkpicked = true;
			val.SetupReplay(data);
			val.Enable(false);
			val.StartReplay();
			return val;
		}

		public static void ClearReplayGhosts()
		{
			foreach (Character value in GhostFraid.Values)
			{
				if (Object.op_Implicit((Object)(object)value) && Object.op_Implicit((Object)(object)((Component)value).gameObject))
				{
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
			}
			GhostFraid = new Dictionary<int, Character>();
		}

		public static void ClearAllReplayGhostsData()
		{
			ClearReplayGhosts();
			Replays = new Dictionary<string, List<GhostData>>();
			StoredReplays = new Dictionary<string, List<GhostData>>();
		}

		public static void LookUpGhostId(string ghostId)
		{
		}

		public static string GetCurrentLevelName()
		{
			string text = GameState.GetInstance().currentSnapshotInfo.snapshotCode;
			if (text == "")
			{
				text = "local_" + GameState.GetInstance().currentSnapshotInfo.snapshotName;
			}
			return text;
		}

		public static IOrderedEnumerable<GhostData> SortGhostByTime(List<GhostData> replays)
		{
			return replays.OrderBy((GhostData d) => (d.lastTime != 0f) ? d.lastTime : float.PositiveInfinity);
		}

		private static void ReplayDataPoint(Character __instance, GhostDataPoint datapoint)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0072: 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_009c: Expected O, but got Unknown
			foreach (KeyValuePair<GhostEvent, object> eventVal in datapoint.eventVals)
			{
				if ((int)eventVal.Key == 0)
				{
					string text = eventVal.Value as string;
					if (text.Contains(":"))
					{
						string[] array = text.Split(new char[1] { ':' });
						__instance.ReceiveEvent(new InputEvent(-1, (InputKey)Enum.Parse(typeof(InputKey), array[0]), float.Parse(array[1]), bool.Parse(array[2]), bool.Parse(array[3])));
					}
				}
			}
		}
	}
}
namespace GhostBuster.Patches
{
	[HarmonyPatch(typeof(DancingMoverCharacterTrigger), "ProcessLocalCharacterState")]
	internal static class DancingMoverIgnoreReplayCharacterPatch
	{
		private static bool Prefix(Character character)
		{
			return (Object)(object)character == (Object)null || !character.isReplay;
		}
	}
	[HarmonyPatch(typeof(DancingMoverCharacterTrigger), "ProcessServerMessage")]
	internal static class DancingMoverIgnoreAggregateServerMessagePatch
	{
		private static bool Prefix(MsgPlatformDancing message)
		{
			if (message == null || message.PlayerNumber != 0)
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(DancingMoverCharacterTrigger), "CleanUpDisconnectedCharacters")]
	internal static class DancingMoverReplayAwareCleanupPatch
	{
		private static void Postfix(DancingMoverCharacterTrigger __instance)
		{
			int[] array = (from networkNumber in __instance.activeDancerNetworkNumbers.Concat(__instance.activeOnPlatformNetworkNumbers).Distinct()
				where !Character.AllCharacters.Any((Character character) => (Object)(object)character != (Object)null && !character.isReplay && character.networkNumber == networkNumber)
				select networkNumber).ToArray();
			bool flag = false;
			int[] array2 = array;
			foreach (int item in array2)
			{
				flag |= __instance.activeDancerNetworkNumbers.Remove(item);
				flag |= __instance.activeOnPlatformNetworkNumbers.Remove(item);
			}
			if (flag)
			{
				__instance.UpdatePlatformStateAndBroadcast();
			}
		}
	}
	[HarmonyPatch(typeof(GameState), "Update")]
	internal static class GameStateUpdatePatch
	{
		private static void Prefix(GameState __instance)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_002b: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if ((int)GameSettings.GetInstance().GameMode == 3 && !GameState.ChatSystem.ChatMode)
			{
				if (Input.GetKeyDown(GhostBusterMod.ToggleGhostsKey.Value))
				{
					ToggleGhosts();
				}
				if (Input.GetKeyDown(GhostBusterMod.SwitchGhostModeKey.Value))
				{
					SwitchGhostMode();
				}
				if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown(GhostBusterMod.StoreGhostDataKey.Value))
				{
					StoreGhostData();
				}
				if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown(GhostBusterMod.LoadGhostDataKey.Value))
				{
					LoadGhostData();
				}
				if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown(GhostBusterMod.ToggleGhostTextKey.Value))
				{
					ToggleGhostText();
				}
				if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown(GhostBusterMod.ClearAllGhostReplaysKey.Value))
				{
					ClearAllGhostReplays();
				}
			}
		}

		private static void ToggleGhosts()
		{
			GhostBusterMod.ShowGhosts.Value = !GhostBusterMod.ShowGhosts.Value;
			DisplayMessage("Show Replay Ghosts: " + (GhostBusterMod.ShowGhosts.Value ? "Enabled" : "Disabled"));
		}

		private static void ToggleGhostText()
		{
			GhostBusterMod.ShowGhostText.Value = !GhostBusterMod.ShowGhostText.Value;
			DisplayMessage("Show Ghost text: " + (GhostBusterMod.ShowGhostText.Value ? "Enabled" : "Disabled"));
		}

		private static void ClearAllGhostReplays()
		{
			GhostBusterMod.ClearAllReplayGhostsData();
			DisplayMessage("Cleared all GhostReplays!");
		}

		public static void StoreGhostData()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			XmlDocument val = new XmlDocument();
			XmlElement val2 = val.CreateElement("GhostDataExport");
			XmlAttribute val3 = val.CreateAttribute("level");
			((XmlNode)val3).Value = GhostBusterMod.GetCurrentLevelName();
			((XmlNode)val2).Attributes.Append(val3);
			((XmlNode)val).AppendChild((XmlNode)(object)val2);
			foreach (Character value in GhostBusterMod.GhostFraid.Values)
			{
				GhostRecorder val4 = new GhostRecorder();
				val4.ghostData = value.replayData;
				val4.trackedChar = value;
				byte[] bytes = val4.SerializeData();
				XmlDocument val5 = XMLFromBytes(bytes);
				if (val5 != null)
				{
					XmlNode val6 = val.ImportNode(((XmlNode)val5).FirstChild, true);
					((XmlNode)val2).AppendChild(val6);
				}
			}
			Debug.Log((object)((XmlNode)val).OuterXml);
			GUIUtility.systemCopyBuffer = ((XmlNode)val).OuterXml;
			DisplayMessage("Ghost Data stored to clipboard!");
		}

		public static XmlDocument XMLFromBytes(byte[] bytes)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			string text = Encoding.UTF8.GetString(bytes);
			Debug.Log((object)("@string " + text));
			XmlDocument val = new XmlDocument();
			val.XmlResolver = null;
			try
			{
				val.LoadXml(text);
			}
			catch (Exception ex)
			{
				Debug.LogError((object)ex.Message);
			}
			return val;
		}

		public static void EnhanceXMLData(ref XmlDocument xmlDocument, GhostData gdata)
		{
			if (xmlDocument != null && gdata != null)
			{
				XmlNodeList elementsByTagName = xmlDocument.GetElementsByTagName("CharacterInfo");
				if (elementsByTagName.Count > 0)
				{
					XmlAttribute val = xmlDocument.CreateAttribute("level");
					((XmlNode)val).Value = GhostBusterMod.GetCurrentLevelName();
					elementsByTagName[0].Attributes.Append(val);
					XmlAttribute val2 = xmlDocument.CreateAttribute("animal");
					((XmlNode)val2).Value = ((object)Unsafe.As<Animals, Animals>(ref gdata.animal)/*cast due to .constrained prefix*/).ToString();
					elementsByTagName[0].Attributes.Append(val2);
					XmlAttribute val3 = xmlDocument.CreateAttribute("lastTime");
					((XmlNode)val3).Value = gdata.lastTime.ToString();
					elementsByTagName[0].Attributes.Append(val3);
				}
			}
			else
			{
				Debug.LogError((object)("EnhanceXMLData missing xmlDocument " + ((object)xmlDocument)?.ToString() + " or c " + (object)gdata));
			}
		}

		private static void LoadGhostData()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			DisplayMessage("Loading Ghost Data from clipboard!");
			if (GhostBusterMod.ClearStoredGhosts.Value)
			{
				GhostBusterMod.StoredReplays = new Dictionary<string, List<GhostData>>();
			}
			try
			{
				XmlDocument val = new XmlDocument();
				val.XmlResolver = null;
				val.LoadXml(GUIUtility.systemCopyBuffer);
				XmlElement documentElement = val.DocumentElement;
				string text = QuickSaver.ParseAttrStr((XmlNode)(object)documentElement, "level", string.Empty);
				Debug.Log((object)("add replay for level: " + text));
				foreach (XmlElement item in documentElement.GetElementsByTagName("GhostInfo"))
				{
					XmlElement val2 = item;
					GhostData gd = new GhostData();
					XmlNodeList elementsByTagName = val2.GetElementsByTagName("CharacterInfo");
					if (elementsByTagName.Count > 0)
					{
						ParseCharacterInfo(ref gd, elementsByTagName[0]);
					}
					XmlNodeList elementsByTagName2 = val2.GetElementsByTagName("GhostData");
					if (elementsByTagName2.Count == 1)
					{
						ParseGhostDataPoints(ref gd, elementsByTagName2[0]);
						if (!GhostBusterMod.StoredReplays.ContainsKey(text))
						{
							GhostBusterMod.StoredReplays.Add(text, new List<GhostData> { gd });
						}
						else
						{
							GhostBusterMod.StoredReplays[text].Add(gd);
						}
					}
					GhostBusterMod.ClearReplayGhosts();
				}
			}
			catch (Exception ex)
			{
				DisplayMessage("Failed to parse Ghost Data: " + ex.GetType());
				Debug.LogError((object)("Failed to parse Ghost Data: " + ex));
			}
		}

		public static void ParseCharacterInfo(ref GhostData gd, XmlNode CharacterInfo)
		{
			//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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			gd.playerName = QuickSaver.ParseAttrStr(CharacterInfo, "PlayerName", string.Empty);
			Array values = Enum.GetValues(typeof(OutfitType));
			gd.outfits = new int[values.Length];
			foreach (OutfitType item in values)
			{
				gd.outfits[item] = QuickSaver.ParseAttrInt(CharacterInfo, ((object)item/*cast due to .constrained prefix*/).ToString(), -1);
			}
			string value = QuickSaver.ParseAttrStr(CharacterInfo, "animal", string.Empty);
			gd.animal = (Animals)Enum.Parse(typeof(Animals), value);
			float lastTime = QuickSaver.ParseAttrFloat(CharacterInfo, "lastTime", float.PositiveInfinity);
			gd.lastTime = lastTime;
		}

		public static void ParseGhostDataPoints(ref GhostData gd, XmlNode ghostdataelement)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0027: 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)
			foreach (XmlElement item in ghostdataelement.SelectNodes("datapoint"))
			{
				XmlElement val = item;
				GhostDataPoint ghostDataPoint = new GhostDataPoint
				{
					valid = true,
					timestamp = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "timestamp", 0f),
					frameTimestamp = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "frameTimestamp", 0f),
					position = 
					{
						x = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "posX", 0f)
					},
					position = 
					{
						y = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "posY", 0f)
					},
					position = 
					{
						z = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "posZ", 0f)
					},
					framePosition = 
					{
						x = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "framePosX", 0f)
					},
					framePosition = 
					{
						y = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "framePosY", 0f)
					},
					framePosition = 
					{
						z = QuickSaver.ParseAttrFloat((XmlNode)(object)val, "framePosZ", 0f)
					},
					interpolated = QuickSaver.ParseAttrBool((XmlNode)(object)val, "interpolated", true)
				};
				ParseGhostEvents(ref ghostDataPoint, (XmlNode)(object)val);
				gd.AddGhostData(ghostDataPoint);
			}
		}

		public static void ParseGhostEvents(ref GhostDataPoint ghostDataPoint, XmlNode datapoint)
		{
			//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)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Invalid comparison between Unknown and I4
			//IL_006a: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Invalid comparison between Unknown and I4
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: 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_01f1: Unknown result type (might be due to invalid IL or missing references)
			ghostDataPoint.eventVals = new Dictionary<GhostEvent, object>();
			Array values = Enum.GetValues(typeof(GhostEvent));
			foreach (GhostEvent item in values)
			{
				if (datapoint.Attributes[((object)item/*cast due to .constrained prefix*/).ToString()] != null)
				{
					if ((int)item == 0)
					{
						ghostDataPoint.eventVals.Add(item, ((XmlNode)datapoint.Attributes[((object)(GhostEvent)0/*cast due to .constrained prefix*/).ToString()]).Value);
					}
					else if ((int)item == 2)
					{
						ghostDataPoint.eventVals.Add(item, QuickSaver.ParseAttrEnum<AnimState>(datapoint, ((object)item/*cast due to .constrained prefix*/).ToString(), (AnimState)0));
					}
					else if ((int)item == 3)
					{
						ghostDataPoint.eventVals.Add(item, QuickSaver.ParseAttrEnum<SecondaryAnimState>(datapoint, ((object)item/*cast due to .constrained prefix*/).ToString(), (SecondaryAnimState)0));
					}
					else if (GhostData.GetEventDataType(item) == typeof(bool))
					{
						ghostDataPoint.eventVals.Add(item, QuickSaver.ParseAttrBool(datapoint, ((object)item/*cast due to .constrained prefix*/).ToString(), false));
					}
					else if (GhostData.GetEventDataType(item) == typeof(int))
					{
						ghostDataPoint.eventVals.Add(item, QuickSaver.ParseAttrInt(datapoint, ((object)item/*cast due to .constrained prefix*/).ToString(), 0));
					}
					else if (GhostData.GetEventDataType(item) == typeof(Vector3))
					{
						float num = QuickSaver.ParseAttrFloat(datapoint, "X", 0f);
						float num2 = QuickSaver.ParseAttrFloat(datapoint, "Y", 0f);
						float num3 = QuickSaver.ParseAttrFloat(datapoint, "Z", 0f);
						ghostDataPoint.eventVals.Add(item, (object)new Vector3(num, num2, num3));
					}
				}
			}
		}

		private static void SwitchGhostMode(bool reverse = false)
		{
			int num = Enum.GetNames(typeof(GhostBusterMod.GhostMode)).Length;
			GhostBusterMod.SelectedGhostMode.Value = (GhostBusterMod.GhostMode)((int)(GhostBusterMod.SelectedGhostMode.Value + ((!reverse) ? 1 : (-1))) % num);
			if (GhostBusterMod.SelectedGhostMode.Value < GhostBusterMod.GhostMode.Fastest)
			{
				GhostBusterMod.SelectedGhostMode.Value = (GhostBusterMod.GhostMode)(num - 1);
			}
			DisplayMessage("Replay Ghost Mode: " + GhostBusterMod.SelectedGhostMode.Value);
		}

		private static void DisplayMessage(string message)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			UserMessageManager.Instance.UserMessage(message, 2f, GhostBusterMod.MsgPriority.Value, false);
		}
	}
	[HarmonyPatch(typeof(GhostRecorder), "SerializeData")]
	internal static class GhostRecorderSerializeDataPatch
	{
		private static void Postfix(GhostRecorder __instance, ref byte[] __result)
		{
			StackFrame frame = new StackTrace().GetFrame(2);
			string name = frame.GetMethod().Name;
			Debug.Log((object)("methodName2 " + name));
			XmlDocument xmlDocument = GameStateUpdatePatch.XMLFromBytes(__result);
			GameStateUpdatePatch.EnhanceXMLData(ref xmlDocument, __instance.ghostData);
			if (name == "StoreGhostData")
			{
				__result = Encoding.UTF8.GetBytes(((XmlNode)xmlDocument).OuterXml);
			}
			else
			{
				__result = QuickSaver.GetCompressedBytesFromXmlString(((XmlNode)xmlDocument).OuterXml);
			}
		}
	}
	[HarmonyPatch(typeof(QuickSaver), "GetCompressedBytesFromXmlDoc")]
	internal static class QuickSaverGetCompressedBytesFromXmlDocPatch
	{
		private static bool Prefix(QuickSaver __instance, ref XmlDocument doc, out byte[] __result)
		{
			StackFrame frame = new StackTrace().GetFrame(2);
			string name = frame.GetMethod().Name;
			Debug.Log((object)("methodName " + name));
			if (name == "SerializeData" || name == "DMD<GhostRecorder::SerializeData>")
			{
				__result = Encoding.UTF8.GetBytes(((XmlNode)doc).OuterXml);
				return false;
			}
			__result = null;
			return true;
		}
	}
}