using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GlobalEnums; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using InControl; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using SpeedrunGym.Source.Moves; using SpeedrunGym.Source.WorldToasts; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SpeedrunGym")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.2.0+02730305992a1b7c27d0e63f319a0af21f69692d")] [assembly: AssemblyProduct("SpeedrunGym")] [assembly: AssemblyTitle("SpeedrunGym")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/jakobhellermann/SpeedrunGym")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.2.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace Microsoft.CodeAnalysis { [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace SpeedrunGym.Source { [PublicAPI] internal static class Log { private static ManualLogSource? logSource; internal static void Init(ManualLogSource logSource) { Log.logSource = logSource; } internal static void Debug(object data) { ManualLogSource? obj = logSource; if (obj != null) { obj.LogDebug(data); } } internal static void Error(object data) { ManualLogSource? obj = logSource; if (obj != null) { obj.LogError(data); } } internal static void Fatal(object data) { ManualLogSource? obj = logSource; if (obj != null) { obj.LogFatal(data); } } internal static void Info(object data) { ManualLogSource? obj = logSource; if (obj != null) { obj.LogInfo(data); } } internal static void Message(object data) { ManualLogSource? obj = logSource; if (obj != null) { obj.LogMessage(data); } } internal static void Warning(object data) { ManualLogSource? obj = logSource; if (obj != null) { obj.LogWarning(data); } } } [BepInPlugin("io.github.jakobhellermann.speedrungym", "SpeedrunGym", "0.2.0")] public class SpeedrunGymPlugin : BaseUnityPlugin { private Harmony harmony; private WorldToastManager worldToasts; public const string Id = "io.github.jakobhellermann.speedrungym"; public static string Name => "SpeedrunGym"; public static string Version => "0.2.0"; private void Awake() { Log.Init(((BaseUnityPlugin)this).Logger); Log.Info("Plugin " + Name + " (io.github.jakobhellermann.speedrungym) has loaded!"); ForceCrawPogo.BindConfig(((BaseUnityPlugin)this).Config); PogoEndlagDetector.BindConfig(((BaseUnityPlugin)this).Config); worldToasts = WorldToastManager.Create(); SceneManager.sceneLoaded += OnSceneLoaded; try { harmony = Harmony.CreateAndPatchAll(((object)this).GetType().Assembly, (string)null); } catch (Exception arg) { Log.Info(string.Format("Plugin {0} ({1}) failed to initialize: {2}", Name, "io.github.jakobhellermann.speedrungym", arg)); } } private void LateUpdate() { try { PogoEndlagDetector.LateUpdate(); worldToasts.Update(); } catch (Exception arg) { Log.Error($"Error during LateUpdate: {arg}"); } } private void OnDestroy() { try { SceneManager.sceneLoaded -= OnSceneLoaded; harmony.UnpatchSelf(); ForceCrawPogo.Cleanup(); worldToasts.Destroy(); } catch (Exception arg) { Log.Info(string.Format("Plugin {0} ({1}) failed to clean up: {2}", Name, "io.github.jakobhellermann.speedrungym", arg)); } Log.Info("Plugin " + Name + " (io.github.jakobhellermann.speedrungym) has been unloaded!"); } private static void OnSceneLoaded(Scene scene, LoadSceneMode loadMode) { try { ForceCrawPogo.OnSceneLoaded(); } catch (Exception arg) { Log.Error($"Error during OnSceneLoaded: {arg}"); } } } [HarmonyPatch] internal static class FsmStatePatches { [HarmonyPrefix] [HarmonyPatch(typeof(Fsm), "EnterState", new Type[] { typeof(FsmState) })] private static void EnterState(Fsm __instance, FsmState state) { try { ForceCrawPogo.OnEnterState(__instance, state); } catch (Exception data) { Log.Error(data); } } } } namespace SpeedrunGym.Source.WorldToasts { internal record struct WorldToastEntry(GameObject Go, Text Text, Image Backdrop, float StartTime, Vector3 WorldPos, Color Color, bool Fade, bool MoveUp); internal class WorldToastManager { private const float MaxAge = 3f; private const float FloatSpeed = 0.5f; private const float BackdropAlpha = 0.5f; private const int DefaultFontSize = 10; private static WorldToastManager? instance; private readonly Canvas canvas; private readonly List entries = new List(); private WorldToastManager(Canvas canvas) { this.canvas = canvas; } internal static WorldToastManager Create() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0029: Expected O, but got Unknown GameObject val = new GameObject("SpeedrunGymToastCanvas"); Canvas val2 = val.AddComponent(); val2.renderMode = (RenderMode)0; val.AddComponent().uiScaleMode = (ScaleMode)1; Object.DontDestroyOnLoad((Object)val); return instance = new WorldToastManager(val2); } internal void Destroy() { if (Object.op_Implicit((Object)(object)canvas)) { Object.Destroy((Object)(object)((Component)canvas).gameObject); } if (instance == this) { instance = null; } } internal static void Show(string message, Vector3 worldPos, Color? color = null, int fontSize = 10, bool fade = true, bool moveUp = true) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) instance?.ShowInner(message, worldPos, color, fontSize, fade, moveUp); } private void ShowInner(string message, Vector3 worldPos, Color? color, int fontSize, bool fade, bool moveUp) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) Color color2 = (Color)(((??)color) ?? Color.white); GameObject val = new GameObject("WorldToast"); val.transform.SetParent(((Component)canvas).transform, false); Image val2 = val.AddComponent(); ((Graphic)val2).color = new Color(0f, 0f, 0f, 0.5f); ((Graphic)val2).raycastTarget = false; HorizontalLayoutGroup obj = val.AddComponent(); ((LayoutGroup)obj).padding = new RectOffset(4, 4, 2, 2); ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ContentSizeFitter obj2 = val.AddComponent(); obj2.horizontalFit = (FitMode)2; obj2.verticalFit = (FitMode)2; GameObject val3 = new GameObject("Text"); val3.transform.SetParent(val.transform, false); Text val4 = val3.AddComponent(); val4.text = message; val4.fontSize = fontSize; ((Graphic)val4).color = color2; val4.alignment = (TextAnchor)4; val4.font = Resources.GetBuiltinResource("Arial.ttf"); ((Graphic)val4).raycastTarget = false; val4.horizontalOverflow = (HorizontalWrapMode)1; entries.Add(new WorldToastEntry(val, val4, val2, Time.time, worldPos, color2, fade, moveUp)); List list = entries; UpdatePosition(list[list.Count - 1]); } internal void Update() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; for (int num = entries.Count - 1; num >= 0; num--) { WorldToastEntry entry = entries[num]; float num2 = time - entry.StartTime; if (num2 > 3f) { Object.Destroy((Object)(object)entry.Go); entries.RemoveAt(num); } else { UpdatePosition(entry); float num3 = (entry.Fade ? (1f - num2 / 3f) : 1f); ((Graphic)entry.Text).color = new Color(entry.Color.r, entry.Color.g, entry.Color.b, entry.Color.a * num3); ((Graphic)entry.Backdrop).color = new Color(0f, 0f, 0f, 0.5f * num3); } } } private void UpdatePosition(WorldToastEntry entry) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if (!((Object)(object)main == (Object)null)) { float num = Time.time - entry.StartTime; Vector3 val = (entry.MoveUp ? (entry.WorldPos + Vector3.up * (num * 0.5f)) : entry.WorldPos); Vector3 val2 = main.WorldToScreenPoint(val); Vector2 val3 = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle(((Component)canvas).GetComponent(), Vector2.op_Implicit(val2), ((int)canvas.renderMode == 0) ? null : canvas.worldCamera, ref val3); ((Transform)entry.Go.GetComponent()).localPosition = Vector2.op_Implicit(val3); } } } } namespace SpeedrunGym.Source.Moves { internal static class ForceCrawPogo { private enum Mode { Normal, Early, Late1, Late2 } private static ConfigEntry mode = null; private static int decisionCount; private static SendRandomEventV4? patchedAction; private static float[] origWeights = Array.Empty(); private static int[] origEventMax = Array.Empty(); private static int[] origMissedMax = Array.Empty(); internal static void BindConfig(ConfigFile config) { mode = config.Bind("Normalization", "Force craw pogo", Mode.Normal, "Force how often the craw in Greymoor_15 flaps before diving."); mode.SettingChanged += delegate { decisionCount = 0; Restore(); }; } internal static void Cleanup() { Restore(); } internal static void OnSceneLoaded() { decisionCount = 0; patchedAction = null; } private static void Restore() { if (patchedAction != null) { for (int i = 0; i < origWeights.Length; i++) { patchedAction.weights[i].Value = origWeights[i]; } for (int j = 0; j < origEventMax.Length; j++) { patchedAction.eventMax[j].Value = origEventMax[j]; } for (int k = 0; k < origMissedMax.Length; k++) { patchedAction.missedMax[k].Value = origMissedMax[k]; } patchedAction = null; } } internal static void OnEnterState(Fsm fsm, FsmState state) { if (mode.Value == Mode.Normal || state.Name != "Flap Move" || fsm.Name != "Behaviour") { return; } GameObject gameObject = fsm.GameObject; if (!Object.op_Implicit((Object)(object)gameObject) || ((Object)gameObject).name != "Crow (3)") { return; } FsmStateAction? obj = Array.Find(state.Actions, (FsmStateAction a) => a is SendRandomEventV4); SendRandomEventV4 val = (SendRandomEventV4)(object)((obj is SendRandomEventV4) ? obj : null); if (val == null) { return; } int num = Array.FindIndex(val.events, (FsmEvent e) => e.Name == "ATTACK"); int num2 = Array.FindIndex(val.events, (FsmEvent e) => e.Name == "SHIFT"); if (num < 0 || num2 < 0) { return; } if (patchedAction != val) { patchedAction = val; origWeights = Array.ConvertAll(val.weights, (FsmFloat w) => w.Value); origEventMax = Array.ConvertAll(val.eventMax, (FsmInt m) => m.Value); origMissedMax = Array.ConvertAll(val.missedMax, (FsmInt m) => m.Value); } int num3 = mode.Value switch { Mode.Early => 1, Mode.Late1 => 2, Mode.Late2 => 3, _ => 0, }; decisionCount++; bool flag = decisionCount >= num3; Log.Info(string.Format("[craw] normalize flap-move decision #{0} (target {1}) -> {2}", decisionCount, num3, flag ? "ATTACK" : "SHIFT")); if (flag) { decisionCount = 0; } int num4 = (flag ? num : num2); for (int num5 = 0; num5 < val.weights.Length; num5++) { val.weights[num5].Value = ((num5 == num4) ? 1f : 0f); } FsmInt[] eventMax = val.eventMax; for (int num6 = 0; num6 < eventMax.Length; num6++) { eventMax[num6].Value = int.MaxValue; } eventMax = val.missedMax; for (int num6 = 0; num6 < eventMax.Length; num6++) { eventMax[num6].Value = int.MaxValue; } } } internal static class PogoEndlagDetector { internal record struct FrameTime { public static FrameTime Now => new FrameTime { frame = Time.frameCount, Time = Time.realtimeSinceStartup }; private int frame; private float Time; public static FrameTimeDelta operator -(FrameTime a, FrameTime b) { return new FrameTimeDelta { Frames = a.frame - b.frame, Ms = (a.Time - b.Time) * 1000f }; } [CompilerGenerated] private readonly bool PrintMembers(StringBuilder builder) { return false; } } internal record struct FrameTimeDelta { public int Frames; public float Ms; public override string ToString() { if (!showFrameCounts.Value) { return $"{Ms:0}ms"; } return $"{Frames}f ({Ms:0}ms)"; } } private record CurrentDownspike { public FrameTime? DirectionPress; public FrameTime? FirstCanJump; public FrameTime? JumpExecuted; public FrameTime LastInput; public FrameTime? NeutralDeadline; public bool ReleasedNeutral; public FrameTime? RelevantInput; public FrameTimeDelta? RepressDelta; public bool Success; public bool WasJumping; } private const string Section = "Pogo Endlag"; private static ConfigEntry enabled = null; private static ConfigEntry showGood = null; private static ConfigEntry slowRepressThresholdMs = null; private static ConfigEntry slowJumpThresholdMs = null; private static ConfigEntry showFrameCounts = null; private static readonly Color GoodColor = new Color(0.4f, 1f, 0.4f); private static readonly Color SlowColor = new Color(1f, 1f, 0.4f); private static readonly Color FailColor = new Color(1f, 0.3f, 0.4f); private static CurrentDownspike? startedDownspike; internal static void BindConfig(ConfigFile config) { enabled = config.Bind("Pogo Endlag", "Enabled", false, "Detect pogo (downspike) endlag cancels and show feedback popups next to Hornet."); showGood = config.Bind("Pogo Endlag", "Show good", true, "Show a popup on a successful endlag cancel."); slowRepressThresholdMs = config.Bind("Pogo Endlag", "Slow repress threshold", 60f, "Repress slower than this many ms turns the popup yellow."); slowJumpThresholdMs = config.Bind("Pogo Endlag", "Slow jump threshold", 60f, "Jump slower than this many ms turns the popup yellow."); showFrameCounts = config.Bind("Pogo Endlag", "Show frame counts", false, "Include the frame count in popups (e.g. '3f (50ms)') instead of just milliseconds."); } internal static void OnDownAttack(HeroController hero) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (enabled.Value && (int)hero.Config.DownSlashType == 0 && !hero.cState.shuttleCock) { startedDownspike = new CurrentDownspike(); } } internal static void OnUpdateState(HeroAnimationController anim, ActorStates newState) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected I4, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if ((object)startedDownspike == null) { return; } FrameTime? relevantInput = startedDownspike.RelevantInput; if (relevantInput.HasValue) { return; } HeroControllerStates cState = HeroController.SilentInstance.cState; if (!cState.downSpiking && !cState.downSpikeRecovery) { return; } FrameTime now = FrameTime.Now; switch (newState - 1) { case 0: startedDownspike = startedDownspike with { RelevantInput = now }; startedDownspike.Success = true; startedDownspike.WasJumping = cState.jumping; break; case 1: startedDownspike = startedDownspike with { RelevantInput = now, NeutralDeadline = startedDownspike.LastInput, Success = false }; relevantInput = startedDownspike.DirectionPress; if (relevantInput.HasValue) { FrameTime valueOrDefault = relevantInput.GetValueOrDefault(); HeroToast($"dir repress {now - valueOrDefault} early", FailColor); } break; case 2: break; } } private static void HeroToast(string message, Color color) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) HeroController silentInstance = HeroController.SilentInstance; if (Object.op_Implicit((Object)(object)silentInstance)) { WorldToastManager.Show(message, silentInstance.transform.position + Vector3.up * 1.5f + Vector3.right * (float)(silentInstance.cState.facingRight ? 1 : (-1)), color, 10, fade: true, moveUp: false); } } internal static void LateUpdate() { //IL_003c: 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_023e: Unknown result type (might be due to invalid IL or missing references) HeroController silentInstance = HeroController.SilentInstance; if (!Object.op_Implicit((Object)(object)silentInstance)) { return; } CurrentDownspike currentDownspike = startedDownspike; if ((object)currentDownspike == null) { return; } if (silentInstance.cState.dashing) { startedDownspike = null; return; } float x = ((TwoAxisInputControl)ManagerSingleton.SilentInstance.inputActions.MoveVector).Vector.x; FrameTime? directionPress; if (x == 0f) { currentDownspike.ReleasedNeutral = true; } else if ((object)currentDownspike != null && currentDownspike.ReleasedNeutral) { directionPress = currentDownspike.DirectionPress; if (!directionPress.HasValue) { currentDownspike.DirectionPress = FrameTime.Now; } } currentDownspike.LastInput = FrameTime.Now; if ((object)currentDownspike == null) { return; } directionPress = currentDownspike.RelevantInput; if (!directionPress.HasValue) { return; } FrameTime valueOrDefault = directionPress.GetValueOrDefault(); FrameTimeDelta value = FrameTime.Now - valueOrDefault; bool flag = value.Ms > 300f; if (currentDownspike.Success) { if (!showGood.Value) { startedDownspike = null; return; } FrameTimeDelta? repressDelta = currentDownspike.RepressDelta; if (!repressDelta.HasValue && x != 0f) { currentDownspike.RepressDelta = value; } FrameTime? firstCanJump; if ((object)currentDownspike != null) { directionPress = currentDownspike.JumpExecuted; if (!directionPress.HasValue) { firstCanJump = currentDownspike.FirstCanJump; if (!firstCanJump.HasValue && silentInstance.CanJump()) { currentDownspike.FirstCanJump = FrameTime.Now; } } } firstCanJump = currentDownspike.JumpExecuted; if (!firstCanJump.HasValue && silentInstance.cState.jumping && !currentDownspike.WasJumping) { currentDownspike.JumpExecuted = FrameTime.Now; } currentDownspike.WasJumping = silentInstance.cState.jumping; if ((object)currentDownspike != null) { repressDelta = currentDownspike.RepressDelta; if (repressDelta.HasValue) { firstCanJump = currentDownspike.JumpExecuted; if (firstCanJump.HasValue) { goto IL_01c3; } } } if (!flag) { return; } goto IL_01c3; } if (flag) { FrameTime? firstCanJump = currentDownspike.DirectionPress; if (!firstCanJump.HasValue) { HeroToast("no neutral", FailColor); } startedDownspike = null; } else if (x == 0f) { FrameTime? firstCanJump = currentDownspike.DirectionPress; if (!firstCanJump.HasValue) { FrameTime frameTime = currentDownspike.NeutralDeadline ?? valueOrDefault; HeroToast($"neutral {FrameTime.Now - frameTime} late", FailColor); } startedDownspike = null; } return; IL_01c3: ShowSuccessResult(currentDownspike); startedDownspike = null; } private static void ShowSuccessResult(CurrentDownspike d) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) bool flag = false; string text = null; FrameTimeDelta? repressDelta = d.RepressDelta; if (repressDelta.HasValue) { FrameTimeDelta valueOrDefault = repressDelta.GetValueOrDefault(); if (valueOrDefault.Ms >= slowRepressThresholdMs.Value) { flag = true; } text = $"repress +{valueOrDefault}"; } string text2 = null; FrameTime? jumpExecuted = d.JumpExecuted; if (jumpExecuted.HasValue) { FrameTime valueOrDefault2 = jumpExecuted.GetValueOrDefault(); jumpExecuted = d.FirstCanJump; FrameTimeDelta obj; if (jumpExecuted.HasValue) { FrameTime valueOrDefault3 = jumpExecuted.GetValueOrDefault(); obj = valueOrDefault2 - valueOrDefault3; } else { obj = default(FrameTimeDelta); } FrameTimeDelta frameTimeDelta = obj; if (frameTimeDelta.Ms >= slowJumpThresholdMs.Value) { flag = true; } text2 = $"jump +{frameTimeDelta}"; } string text3 = ((text == null) ? text2 : ((text2 == null) ? text : (text + "\n" + text2))); if (text3 != null) { HeroToast(text3, flag ? SlowColor : GoodColor); } } } [HarmonyPatch] internal static class PogoEndlagPatches { [HarmonyPrefix] [HarmonyPatch(typeof(HeroController), "DownAttack")] private static void DownAttack(HeroController __instance) { try { PogoEndlagDetector.OnDownAttack(__instance); } catch (Exception data) { Log.Error(data); } } [HarmonyPrefix] [HarmonyPatch(typeof(HeroAnimationController), "UpdateState")] private static void UpdateState(HeroAnimationController __instance, ActorStates newState) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) try { PogoEndlagDetector.OnUpdateState(__instance, newState); } catch (Exception data) { Log.Error(data); } } } }