Decompiled source of YOURTAKINGTOOLONG v0.1.2

YOUR TAKING TOO LONG.dll

Decompiled 10 hours ago
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<float> BaseSpeed;

	public static ConfigEntry<float> SpeedGain;

	public static ConfigEntry<float> MaxSpeed;

	public static ConfigEntry<float> GracePeriod;

	public AudioClip spawnSound;

	public AudioClip catchSound;

	public AudioClip loopSound;

	public AudioClip parrySound;

	public KeyCode debugSpawnKey = (KeyCode)287;

	public static ConfigEntry<int> PunchHitboxLayer;

	public static ConfigEntry<float> ParryPushDistance;

	public static ConfigEntry<float> ParryHitstopDuration;

	public static ConfigEntry<float> 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<float>("Speed", "Base Speed", 5f, "The speed the stalker starts moving at.");
		SpeedGain = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "Speed Gain", 1f, "How much speed the stalker gains over time.");
		MaxSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "Max Speed", 999999f, "The maximum speed the stalker can reach.");
		GracePeriod = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "Grace Period", 2f, "How long the stalker waits before chasing.");
		PunchHitboxLayer = ((BaseUnityPlugin)this).Config.Bind<int>("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<float>("Parry", "Parry Push Distance", 15f, "How far the stalker gets shoved away when successfully parried.");
		ParryHitstopDuration = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>("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<AudioClip> 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<GameObject>("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<SpriteRenderer>();
		SphereCollider val2 = val.AddComponent<SphereCollider>();
		((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<SpriteRenderer>(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<string> Blacklist = new HashSet<string> { "uk_construct", "Endless", "MainMenu" };

	public static bool TryGetHalfTime(out float halfTime)
	{
		halfTime = 0f;
		if (Blacklist.Contains(SceneHelper.CurrentScene))
		{
			return false;
		}
		int[] timeRanks = MonoSingleton<StatsManager>.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<StatsManager>.Instance.timer || ((Component)MonoSingleton<FinalRank>.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<StatsManager>.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<GameObject>(Plugin.Instance.stalkerPrefab, val2, Quaternion.identity);
		val3.SetActive(true);
		Stalker stalker = val3.AddComponent<Stalker>();
		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<AudioSource>();
			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<AudioSource>();
			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<SphereCollider>();
			((Collider)val2).isTrigger = true;
			val2.radius = 1.2f;
			Plugin.Instance.log.LogInfo((object)"[SRankStalker] parry hitbox collider added");
			ParryReceiver val3 = val.AddComponent<ParryReceiver>();
			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<NewMovement>.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<StatsManager>.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<NewMovement>.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<AudioSource>();
			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<AudioSource>();
				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<NewMovement>.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);
	}
}