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 TargetMethods() { yield return AccessTools.Method(typeof(Character), "replayUpdate", (Type[])null, (Type[])null); } private static IEnumerable Transpiler(IEnumerable 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 { copy }); } else { Replays[currentLevelName].Add(copy); } List list = Replays[currentLevelName].ToList(); if (Replays[currentLevelName].Count > MaxGhostNumber.Value) { IOrderedEnumerable 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(); } 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(ref item.PickedAnimal)/*cast due to .constrained prefix*/).ToString(), new Type[1] { typeof(GhostRecorder) }).GetComponent(); component.TrackCharacter(item.CharacterInstance); item.CharacterInstance.ReplayRecorder = component; Character val = Object.Instantiate(((GameControl)__instance).CharacterPrefab); ((Object)((Component)val).gameObject).name = ((object)Unsafe.As(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 ShowGhosts; public static ConfigEntry SelectedGhostMode; public static ConfigEntry MaxGhostNumber; public static ConfigEntry ClearStoredGhosts; public static ConfigEntry ShowGhostText; public static ConfigEntry GhostOutfis; public static ConfigEntry GhostAlpha; public static ConfigEntry InputReplay; public static ConfigEntry MsgPriority; public static ConfigEntry ToggleGhostsKey; public static ConfigEntry SwitchGhostModeKey; public static ConfigEntry StoreGhostDataKey; public static ConfigEntry LoadGhostDataKey; public static ConfigEntry ToggleGhostTextKey; public static ConfigEntry ClearAllGhostReplaysKey; public static Dictionary GhostFraid = new Dictionary(); public static Dictionary> Replays = new Dictionary>(); public static Dictionary> StoredReplays = new Dictionary>(); 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("General", "ShowGhosts", false, (ConfigDescription)null); MaxGhostNumber = ((BaseUnityPlugin)this).Config.Bind("General", "MaxGhostNumber", 10, "Maximum number of ghost replays that are kept and shown in Ghost Mode ALL"); SelectedGhostMode = ((BaseUnityPlugin)this).Config.Bind("General", "SelectedGhostMode", GhostMode.Fastest, "The selected Replay Ghost Mode"); ClearStoredGhosts = ((BaseUnityPlugin)this).Config.Bind("General", "ClearStoredGhosts", false, "Remove stored ghost replays when new one is loaded"); ShowGhostText = ((BaseUnityPlugin)this).Config.Bind("General", "ShowGhostText", true, "Display text box above ghosts with name and time"); GhostOutfis = ((BaseUnityPlugin)this).Config.Bind("General", "GhostOutfis", true, "Show outfits for ghost replays"); GhostAlpha = ((BaseUnityPlugin)this).Config.Bind("General", "GhostAlpha", 0.45f, "Set the alpha/transparency of ghosts. [1: solid, ..., 0: invisible]"); InputReplay = ((BaseUnityPlugin)this).Config.Bind("General", "InputReplay", false, "Record and replay from user input"); GameSettings.GetInstance().ghostAlpha = GhostAlpha.Value; MsgPriority = ((BaseUnityPlugin)this).Config.Bind("GUI", "MsgPriority", (UserMsgPriority)1, "Display GUI messages: hi = show in middle, lo = show bottom right"); ToggleGhostsKey = ((BaseUnityPlugin)this).Config.Bind("INPUT", "ToggleGhostsKey", (KeyCode)103, "Keybinding: Toggle Ghosts on or off"); LoadGhostDataKey = ((BaseUnityPlugin)this).Config.Bind("INPUT", "LoadGhostDataKey", (KeyCode)108, "Keybinding: Toggle Ghost Modes"); StoreGhostDataKey = ((BaseUnityPlugin)this).Config.Bind("INPUT", "StoreGhostDataKey", (KeyCode)107, "Keybinding: Load stored data from clipboard"); SwitchGhostModeKey = ((BaseUnityPlugin)this).Config.Bind("INPUT", "SwitchGhostModeKey", (KeyCode)104, "Keybinding: Store ghost data in clipboard"); ToggleGhostTextKey = ((BaseUnityPlugin)this).Config.Bind("INPUT", "ToggleGhostTextKey", (KeyCode)110, "Keybinding: Toggle text above ghosts"); ClearAllGhostReplaysKey = ((BaseUnityPlugin)this).Config.Bind("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(string name) where T : Object { T result = default(T); T[] array = Resources.FindObjectsOfTypeAll(); 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(GhostBusterMod.FindObject("NotAMeatboy")); val.CharacterSprite = data.animal; int key = Animator.StringToHash(data.printData()); GhostFraid.Add(key, val); ((Object)((Component)val).gameObject).name = ((object)Unsafe.As(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(); } public static void ClearAllReplayGhostsData() { ClearReplayGhosts(); Replays = new Dictionary>(); StoredReplays = new Dictionary>(); } 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 SortGhostByTime(List 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 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(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>(); } 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 { 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(); 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(datapoint, ((object)item/*cast due to .constrained prefix*/).ToString(), (AnimState)0)); } else if ((int)item == 3) { ghostDataPoint.eventVals.Add(item, QuickSaver.ParseAttrEnum(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") { __result = Encoding.UTF8.GetBytes(((XmlNode)doc).OuterXml); return false; } __result = null; return true; } } }