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;
}
}
}