using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("UKMod template")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("UKMod template")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("3d018c2f-f5bc-47be-a844-3e9888579d1f")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace SRankStalker; [BepInPlugin("com.nico.yourtakingtoolong", "YOUR TAKING TOO LONG", "0.1.0")] public class Plugin : BaseUnityPlugin { public static Plugin Instance; public ManualLogSource log; public GameObject stalkerPrefab; public static ConfigEntry BaseSpeed; public static ConfigEntry SpeedGain; public static ConfigEntry MaxSpeed; public static ConfigEntry GracePeriod; public AudioClip spawnSound; public AudioClip catchSound; public AudioClip loopSound; public AudioClip parrySound; public KeyCode debugSpawnKey = (KeyCode)287; public static ConfigEntry PunchHitboxLayer; public static ConfigEntry ParryPushDistance; public static ConfigEntry ParryHitstopDuration; public static ConfigEntry ParryStaggerDuration; private string AssetPath(string fileName) { return Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Assets", fileName); } private void Awake() { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) Instance = this; log = ((BaseUnityPlugin)this).Logger; ((BaseUnityPlugin)this).Logger.LogInfo((object)"YOUR TAKING TOO LONG"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"RAHAHAHAHAHAHAHA"); stalkerPrefab = LoadFromAssetBundle() ?? BuildStalkerTemplate(); ApplyPngOverride(stalkerPrefab); ((MonoBehaviour)this).StartCoroutine(LoadClip("spawn.ogg", delegate(AudioClip clip) { spawnSound = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadClip("catch.ogg", delegate(AudioClip clip) { catchSound = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadClip("loop.ogg", delegate(AudioClip clip) { loopSound = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadClip("parry.ogg", delegate(AudioClip clip) { parrySound = clip; })); new Harmony("com.nico.yourtakingtoolong").PatchAll(); BaseSpeed = ((BaseUnityPlugin)this).Config.Bind("Speed", "Base Speed", 5f, "The speed the stalker starts moving at."); SpeedGain = ((BaseUnityPlugin)this).Config.Bind("Speed", "Speed Gain", 1f, "How much speed the stalker gains over time."); MaxSpeed = ((BaseUnityPlugin)this).Config.Bind("Speed", "Max Speed", 999999f, "The maximum speed the stalker can reach."); GracePeriod = ((BaseUnityPlugin)this).Config.Bind("Speed", "Grace Period", 2f, "How long the stalker waits before chasing."); PunchHitboxLayer = ((BaseUnityPlugin)this).Config.Bind("Parry", "Punch Hitbox Layer", 14, "Layer index the parry hitbox sits on. Must be 14 to match the layer mask (16384 = 1<<14) the game's own Punch code checks - change only if that turns out to be wrong for your game version."); ParryPushDistance = ((BaseUnityPlugin)this).Config.Bind("Parry", "Parry Push Distance", 15f, "How far the stalker gets shoved away when successfully parried."); ParryHitstopDuration = ((BaseUnityPlugin)this).Config.Bind("Parry", "Parry Hitstop Duration", 0.08f, "How long the game briefly freezes on a successful parry, in real seconds (unaffected by the freeze itself)."); ParryStaggerDuration = ((BaseUnityPlugin)this).Config.Bind("Parry", "Parry Stagger Duration", 1.5f, "How long the stalker sits still and recovers after being parried, before resuming the chase."); } public void ReloadAfterDelay(float delay) { ((MonoBehaviour)this).StartCoroutine(ReloadCoroutine(delay)); } private IEnumerator ReloadCoroutine(float delay) { yield return (object)new WaitForSeconds(delay); SceneHelper.LoadScene(SceneHelper.CurrentScene, false); } private IEnumerator LoadClip(string fileName, Action onLoaded) { string path = AssetPath(fileName); if (!File.Exists(path)) { log.LogWarning((object)("[SRankStalker] no " + fileName + " found at " + path + " - that sound won't play until it's added.")); yield break; } UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + path, (AudioType)14); try { yield return www.SendWebRequest(); if ((int)www.result != 1) { log.LogWarning((object)("[SRankStalker] failed to load " + fileName + ": " + www.error)); yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www); log.LogInfo((object)$"[SRankStalker] loaded {fileName} ok - length {clip.length:0.00}s, {clip.channels} channel(s), {clip.frequency}Hz"); onLoaded(clip); } finally { ((IDisposable)www)?.Dispose(); } } private GameObject LoadFromAssetBundle() { string text = AssetPath("stalker"); if (!File.Exists(text)) { log.LogInfo((object)("[SRankStalker] no bundle found at " + text + ", using the placeholder template.")); return null; } AssetBundle val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { log.LogError((object)"[SRankStalker] found a bundle file but it failed to load - usually means it was built with a Unity version that doesn't match the game (needs 2022.3.x to match ULTRAKILL's current engine)."); return null; } GameObject val2 = val.LoadAsset("Stalker"); if ((Object)(object)val2 == (Object)null) { log.LogError((object)"[SRankStalker] bundle loaded, but no asset named 'Stalker' was found in it. Check the GameObject's name in Unity."); return null; } log.LogInfo((object)"[SRankStalker] using the AssetBundle prefab for everything except the sprite - stalker.png always overrides whatever sprite is baked in (see ApplyPngOverride)."); return val2; } private GameObject BuildStalkerTemplate() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown GameObject val = new GameObject("StalkerTemplate"); val.SetActive(false); val.AddComponent(); SphereCollider val2 = val.AddComponent(); ((Collider)val2).isTrigger = true; val2.radius = 1f; return val; } private void ApplyPngOverride(GameObject target) { if ((Object)(object)target == (Object)null) { return; } string text = AssetPath("stalker.png"); if (!File.Exists(text)) { log.LogWarning((object)("[SRankStalker] no stalker.png found at " + text + " - keeping whatever sprite is already on the prefab (possibly none).")); return; } Sprite sprite = LoadSpriteFromFile(text); SpriteRenderer[] componentsInChildren = target.GetComponentsInChildren(true); if (componentsInChildren.Length == 0) { log.LogWarning((object)"[SRankStalker] stalker.png found, but the prefab has no SpriteRenderer to apply it to."); return; } SpriteRenderer[] array = componentsInChildren; foreach (SpriteRenderer val in array) { val.sprite = sprite; } log.LogInfo((object)$"[SRankStalker] stalker.png applied to {componentsInChildren.Length} SpriteRenderer(s)."); } private Sprite LoadSpriteFromFile(string path) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2); ImageConversion.LoadImage(val, array); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); } } public static class PluginInfo { public const string GUID = "com.nico.yourtakingtoolong"; public const string NAME = "YOUR TAKING TOO LONG"; public const string VERSION = "0.1.0"; } public static class SRankTimes { public static readonly HashSet Blacklist = new HashSet { "uk_construct", "Endless", "MainMenu" }; public static bool TryGetHalfTime(out float halfTime) { halfTime = 0f; if (Blacklist.Contains(SceneHelper.CurrentScene)) { return false; } int[] timeRanks = MonoSingleton.Instance.timeRanks; if (timeRanks == null || timeRanks.Length == 0) { return false; } float num = timeRanks[^1]; if (num <= 0f) { return false; } halfTime = num / 2f; return true; } } [HarmonyPatch(typeof(NewMovement), "Update")] public static class StalkerSpawnController { private static bool spawnedThisLevel; private static string lastSeenScene; [HarmonyPostfix] public static void Postfix(NewMovement __instance) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (SceneHelper.CurrentScene != lastSeenScene) { lastSeenScene = SceneHelper.CurrentScene; spawnedThisLevel = false; if ((Object)(object)Stalker.Instance != (Object)null) { Stalker.Instance.Despawn(); } } if (!MonoSingleton.Instance.timer || ((Component)MonoSingleton.Instance).gameObject.activeSelf || __instance.dead) { spawnedThisLevel = false; if ((Object)(object)Stalker.Instance != (Object)null) { Stalker.Instance.Despawn(); } } else { if (spawnedThisLevel || (Object)(object)Stalker.Instance != (Object)null) { return; } bool keyDown = Input.GetKeyDown(Plugin.Instance.debugSpawnKey); if (!SRankTimes.TryGetHalfTime(out var halfTime)) { if (keyDown) { SpawnStalker(__instance, 120f); } } else if (keyDown || MonoSingleton.Instance.seconds >= halfTime) { SpawnStalker(__instance, halfTime * 2f); spawnedThisLevel = true; } } } private static void SpawnStalker(NewMovement player, float fullSRankTime) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Plugin.Instance.stalkerPrefab == (Object)null) { Plugin.Instance.log.LogWarning((object)"[SRankStalker] stalkerPrefab is not set."); return; } Vector3 val = -((Component)player).transform.forward; Vector3 val2 = ((Component)player).transform.position + val * 40f + Vector3.up * 5f; GameObject val3 = Object.Instantiate(Plugin.Instance.stalkerPrefab, val2, Quaternion.identity); val3.SetActive(true); Stalker stalker = val3.AddComponent(); stalker.fullSRankTime = fullSRankTime; ((Component)stalker).transform.position = val2; if ((Object)(object)Plugin.Instance.spawnSound != (Object)null) { GameObject val4 = new GameObject("Stalker Spawn Sound"); val4.transform.position = val2; AudioSource val5 = val4.AddComponent(); val5.clip = Plugin.Instance.spawnSound; val5.volume = 5f; val5.spatialBlend = 1f; val5.minDistance = 20f; val5.maxDistance = 200f; val5.playOnAwake = false; ((Behaviour)val5).enabled = true; val5.Play(); Object.Destroy((Object)(object)val4, val5.clip.length); } } } public class Stalker : MonoBehaviour { public static Stalker Instance; public float fullSRankTime; private float spawnGracePeriod; private Vector3 parryVelocity; private bool parryMoving; private AudioSource loopSource; private bool parrying; private bool staggered; private float staggerTimer; private bool killing; private void Awake() { Instance = this; spawnGracePeriod = Plugin.GracePeriod.Value; if ((Object)(object)Plugin.Instance.loopSound != (Object)null) { loopSource = ((Component)this).gameObject.AddComponent(); loopSource.clip = Plugin.Instance.loopSound; loopSource.loop = true; loopSource.playOnAwake = false; loopSource.spatialBlend = 1f; loopSource.minDistance = 15f; loopSource.maxDistance = 150f; loopSource.volume = 2f; ((Behaviour)loopSource).enabled = true; loopSource.Play(); } SetUpParryReceiver(); } private void SetUpParryReceiver() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown try { GameObject val = new GameObject("StalkerParryHitbox"); val.transform.SetParent(((Component)this).transform, false); int num = (val.layer = ((Plugin.PunchHitboxLayer != null) ? Plugin.PunchHitboxLayer.Value : 14)); Plugin.Instance.log.LogInfo((object)$"[SRankStalker] parry hitbox created on layer {num} ({LayerMask.LayerToName(num)})"); SphereCollider val2 = val.AddComponent(); ((Collider)val2).isTrigger = true; val2.radius = 1.2f; Plugin.Instance.log.LogInfo((object)"[SRankStalker] parry hitbox collider added"); ParryReceiver val3 = val.AddComponent(); Plugin.Instance.log.LogInfo((object)"[SRankStalker] ParryReceiver component added"); val3.parryHeal = true; val3.disappearOnParry = false; val3.onParry = new UnityEvent(); val3.onParry.AddListener(new UnityAction(OnRealParry)); Plugin.Instance.log.LogInfo((object)"[SRankStalker] ParryReceiver fully configured and listener attached"); } catch (Exception arg) { Plugin.Instance.log.LogError((object)$"[SRankStalker] SetUpParryReceiver failed: {arg}"); } } private void Update() { //IL_008c: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)MonoSingleton.Instance).transform; if (spawnGracePeriod > 0f) { spawnGracePeriod -= Time.deltaTime; if ((Object)(object)Camera.current != (Object)null) { ((Component)this).transform.rotation = Quaternion.LookRotation(((Component)Camera.current).transform.position - ((Component)this).transform.position); } } else if (parryMoving) { Transform transform2 = ((Component)this).transform; transform2.position += parryVelocity * Time.deltaTime; parryVelocity = Vector3.Lerp(parryVelocity, Vector3.zero, Time.deltaTime * 8f); if (((Vector3)(ref parryVelocity)).magnitude < 0.1f) { parryVelocity = Vector3.zero; parryMoving = false; } } else if (staggered) { staggerTimer -= Time.deltaTime; if (staggerTimer <= 0f) { staggered = false; } if ((Object)(object)Camera.current != (Object)null) { ((Component)this).transform.rotation = Quaternion.LookRotation(((Component)Camera.current).transform.position - ((Component)this).transform.position); } } else { float seconds = MonoSingleton.Instance.seconds; float num = Plugin.BaseSpeed.Value + seconds * Plugin.SpeedGain.Value; num = Mathf.Clamp(num, Plugin.BaseSpeed.Value, Plugin.MaxSpeed.Value); ((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, transform.position, num * Time.deltaTime); if ((Object)(object)Camera.current != (Object)null) { ((Component)this).transform.rotation = Quaternion.LookRotation(((Component)Camera.current).transform.position - ((Component)this).transform.position); } } } private void OnRealParry() { if (!parrying) { parrying = true; ((MonoBehaviour)this).StartCoroutine(ParrySequence()); } } private IEnumerator ParrySequence() { Transform player = ((Component)MonoSingleton.Instance).transform; Vector3 val = ((Component)this).transform.position - player.position; Vector3 pushDir = ((Vector3)(ref val)).normalized; parryVelocity = pushDir * Plugin.ParryPushDistance.Value / 0.15f; parryMoving = true; if ((Object)(object)Plugin.Instance.parrySound != (Object)null) { GameObject soundObj = new GameObject("Stalker Parry Sound"); soundObj.transform.position = ((Component)this).transform.position; AudioSource source = soundObj.AddComponent(); source.clip = Plugin.Instance.parrySound; source.volume = 5f; source.spatialBlend = 1f; source.minDistance = 15f; source.maxDistance = 150f; source.playOnAwake = false; ((Behaviour)source).enabled = true; source.Play(); Object.Destroy((Object)(object)soundObj, source.clip.length); } staggered = true; staggerTimer = Plugin.ParryStaggerDuration.Value; parrying = false; yield break; } private void OnTriggerEnter(Collider other) { if (!staggered && !parrying && ((Component)other).CompareTag("Player")) { ((MonoBehaviour)this).StartCoroutine(KillPlayer()); } } private void OnTriggerStay(Collider other) { if (!staggered && !parrying && ((Component)other).CompareTag("Player")) { ((MonoBehaviour)this).StartCoroutine(KillPlayer()); } } private IEnumerator KillPlayer() { if (!killing) { killing = true; if ((Object)(object)loopSource != (Object)null) { loopSource.Stop(); } if ((Object)(object)Plugin.Instance.catchSound != (Object)null) { GameObject soundObj = new GameObject("Stalker Catch Sound"); soundObj.transform.position = ((Component)this).transform.position; AudioSource source = soundObj.AddComponent(); source.clip = Plugin.Instance.catchSound; source.volume = 20f; source.spatialBlend = 1f; source.minDistance = 15f; source.maxDistance = 150f; source.playOnAwake = false; ((Behaviour)source).enabled = true; source.Play(); Object.Destroy((Object)(object)soundObj, source.clip.length); } MonoSingleton.Instance.GetHurt(999, false, 1f, false, false, 1f, false); Plugin.Instance.ReloadAfterDelay(1f); } yield break; } public void Despawn() { Instance = null; Object.Destroy((Object)(object)((Component)this).gameObject); } }