Decompiled source of Litany v1.0.0

BepInEx/plugins/LitanyEntity.dll

Decompiled a day ago
using System;
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 GameNetcodeStuff;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("LitanyEntity")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LitanyEntity")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("21440c59-62c0-4f6c-ade7-0e1d5c5b2345")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LitanyEntity;

public class LitanyBehaviour : MonoBehaviour
{
	private enum LitanyState
	{
		Hidden,
		Preparing,
		Warning,
		EyeOpen,
		BetweenEyes,
		Screamer
	}

	private AudioSource idleSource;

	private AudioSource screamsSource;

	private AudioSource oneShotSource;

	private AudioClip idleClip;

	private AudioClip preparaClip;

	private AudioClip[] openClips = (AudioClip[])(object)new AudioClip[3];

	private AudioClip screamsClip;

	private VideoPlayer videoPlayer;

	private RawImage videoImage;

	private RenderTexture videoTexture;

	private bool videoFinished = false;

	private LitanyState currentState = LitanyState.Hidden;

	private float timer = 0f;

	private float nextAppearTime = 0f;

	private int currentEye = 0;

	private float shakeTimer = 0f;

	private float shakeSpeed = 0.08f;

	private bool isOnCooldown = false;

	private float cooldownTimer = 0f;

	private float cooldownTime = 0f;

	private GameObject rootObject;

	private Image bodyImage;

	private Image eyeImage;

	private Sprite bodySprites;

	private Sprite eyesClosedSprite;

	private Sprite[] eyeWarningSprites = (Sprite[])(object)new Sprite[3];

	private Sprite[] eyeOpenedSprites = (Sprite[])(object)new Sprite[3];

	private float eyeAnimTimer = 0f;

	private int eyeAnimFrame = 0;

	private Vector2 basePosition;

	private string modFolder;

	private void Start()
	{
		modFolder = Path.Combine(Paths.PluginPath, "LitanyEntity");
		string[] directories = Directory.GetDirectories(Paths.PluginPath, "LitanyEntity", SearchOption.AllDirectories);
		if (directories.Length != 0)
		{
			modFolder = directories[0];
		}
		LoadSprites();
		CreateOverlay();
		LoadAudio();
		nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
	}

	private void LoadAudio()
	{
		idleClip = LoadAudioClip("idle.mp3");
		preparaClip = LoadAudioClip("prepare.mp3");
		openClips[0] = LoadAudioClip("open1.mp3");
		openClips[1] = LoadAudioClip("open2.mp3");
		openClips[2] = LoadAudioClip("open3.mp3");
		screamsClip = LoadAudioClip("screams.mp3");
		idleSource = ((Component)this).gameObject.AddComponent<AudioSource>();
		idleSource.loop = true;
		idleSource.playOnAwake = false;
		screamsSource = ((Component)this).gameObject.AddComponent<AudioSource>();
		screamsSource.loop = true;
		screamsSource.playOnAwake = false;
		oneShotSource = ((Component)this).gameObject.AddComponent<AudioSource>();
		oneShotSource.loop = false;
		oneShotSource.playOnAwake = false;
	}

	private AudioClip LoadAudioClip(string filename)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		string text = Path.Combine(modFolder, filename);
		if (!File.Exists(text))
		{
			Debug.LogError((object)("Litany Entity: звук не найден — " + text));
			return null;
		}
		WWW val = new WWW("file://" + text);
		try
		{
			while (!val.isDone)
			{
			}
			return val.GetAudioClip(false, false, (AudioType)13);
		}
		finally
		{
			((IDisposable)val)?.Dispose();
		}
	}

	private void LoadSprites()
	{
		bodySprites = LoadSprite("body.png");
		eyesClosedSprite = LoadSprite("eyes_closed.png");
		for (int i = 0; i < 3; i++)
		{
			eyeWarningSprites[i] = LoadSprite($"eye{i + 1}_warning.png");
			eyeOpenedSprites[i] = LoadSprite($"eye{i + 1}_opened.png");
		}
	}

	private Sprite LoadSprite(string filename)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		string text = Path.Combine(modFolder, filename);
		if (!File.Exists(text))
		{
			Debug.LogError((object)("Litany Entity: file not found — " + text));
			return null;
		}
		byte[] array = File.ReadAllBytes(text);
		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));
	}

	private void Update()
	{
		PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		if (currentState != 0)
		{
			AnimateBody();
		}
		if (isOnCooldown)
		{
			cooldownTimer += Time.deltaTime;
			if (cooldownTimer >= cooldownTime)
			{
				isOnCooldown = false;
				cooldownTimer = 0f;
				timer = 0f;
				nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
			}
		}
		else if (val.isPlayerDead)
		{
			ForceHide();
		}
		else
		{
			if (!IsPlayerInGame())
			{
				return;
			}
			timer += Time.deltaTime;
			switch (currentState)
			{
			case LitanyState.Hidden:
				if (timer >= nextAppearTime)
				{
					Appear();
				}
				break;
			case LitanyState.Preparing:
				if (timer >= Plugin.PrepareDuration.Value)
				{
					StartWarning();
				}
				break;
			case LitanyState.Warning:
				AnimateWarning();
				if (timer >= Plugin.WarningDuration.Value)
				{
					OpenEye();
				}
				break;
			case LitanyState.EyeOpen:
				if (!IsPlayerCrouching())
				{
					TriggerDeath();
				}
				else if (timer >= Plugin.OpenEyeDuration.Value)
				{
					CloseEye();
				}
				break;
			case LitanyState.BetweenEyes:
				if (timer >= Plugin.BetweenEyesDuration.Value)
				{
					if (currentEye >= 3)
					{
						Hide();
					}
					else
					{
						StartWarning();
					}
				}
				break;
			case LitanyState.Screamer:
				Debug.Log((object)$"Litany: Screamer state, videoFinished={videoFinished}");
				if (videoFinished)
				{
					KillPlayer();
				}
				break;
			}
		}
	}

	private void AnimateBody()
	{
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		shakeTimer += Time.deltaTime;
		if (shakeTimer >= shakeSpeed)
		{
			shakeTimer = 0f;
			float num = Random.Range(-10f, 10f);
			float num2 = Random.Range(-10f, 10f);
			float num3 = Random.Range(-6f, 6f);
			rootObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(basePosition.x + num, basePosition.y + num2);
			((Transform)rootObject.GetComponent<RectTransform>()).localRotation = Quaternion.Euler(0f, 0f, num3);
		}
	}

	private void AnimateWarning()
	{
		eyeAnimTimer += Time.deltaTime;
		if (eyeAnimTimer >= 0.15f)
		{
			eyeAnimTimer = 0f;
			eyeAnimFrame = (eyeAnimFrame + 1) % 2;
			eyeImage.sprite = ((eyeAnimFrame == 0) ? eyesClosedSprite : eyeWarningSprites[currentEye]);
		}
	}

	private void Appear()
	{
		//IL_0047: 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_005a: Unknown result type (might be due to invalid IL or missing references)
		currentState = LitanyState.Preparing;
		timer = 0f;
		currentEye = 0;
		float num = Random.Range(-500f, 500f);
		float num2 = Random.Range(-250f, 250f);
		rootObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(num, num2);
		basePosition = new Vector2(num, num2);
		rootObject.SetActive(true);
		if ((Object)(object)bodySprites != (Object)null)
		{
			bodyImage.sprite = bodySprites;
		}
		if ((Object)(object)eyesClosedSprite != (Object)null)
		{
			eyeImage.sprite = eyesClosedSprite;
		}
		if ((Object)(object)idleClip != (Object)null)
		{
			idleSource.clip = idleClip;
			idleSource.Play();
		}
	}

	private void StartWarning()
	{
		currentState = LitanyState.Warning;
		timer = 0f;
		eyeAnimFrame = 0;
		eyeAnimTimer = 0f;
		if ((Object)(object)preparaClip != (Object)null)
		{
			oneShotSource.clip = preparaClip;
			oneShotSource.Play();
		}
	}

	private void OpenEye()
	{
		currentState = LitanyState.EyeOpen;
		timer = 0f;
		if ((Object)(object)eyeOpenedSprites[currentEye] != (Object)null)
		{
			eyeImage.sprite = eyeOpenedSprites[currentEye];
		}
		if ((Object)(object)openClips[currentEye] != (Object)null)
		{
			oneShotSource.Stop();
			oneShotSource.clip = openClips[currentEye];
			oneShotSource.Play();
		}
		if (currentEye == 0 && (Object)(object)screamsClip != (Object)null)
		{
			screamsSource.clip = screamsClip;
			screamsSource.Play();
		}
	}

	private void CloseEye()
	{
		//IL_0078: 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)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		currentEye++;
		if (currentEye >= 3)
		{
			Hide();
			return;
		}
		if ((Object)(object)eyesClosedSprite != (Object)null)
		{
			eyeImage.sprite = eyesClosedSprite;
		}
		float num = Random.Range(-500f, 500f);
		float num2 = Random.Range(-250f, 250f);
		rootObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(num, num2);
		basePosition = new Vector2(num, num2);
		currentState = LitanyState.BetweenEyes;
		timer = 0f;
	}

	private void TriggerDeath()
	{
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		currentState = LitanyState.Screamer;
		timer = 0f;
		videoFinished = false;
		rootObject.SetActive(false);
		string text = Path.Combine(modFolder, "Death.mp4");
		Debug.Log((object)("Litany Entity: видео путь — " + text));
		Debug.Log((object)$"Litany Entity: файл существует — {File.Exists(text)}");
		videoPlayer.url = "file://" + text;
		videoPlayer.Play();
		((Graphic)videoImage).color = new Color(1f, 1f, 1f, 1f);
	}

	private void KillPlayer()
	{
		//IL_0067: 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_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		Debug.Log((object)"Litany Entity: KillPlayer вызван!");
		PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
		if ((Object)(object)val != (Object)null)
		{
			val.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 1, default(Vector3));
		}
		videoPlayer.Stop();
		((Graphic)videoImage).color = new Color(1f, 1f, 1f, 0f);
		rootObject.SetActive(false);
		Hide();
		idleSource.Stop();
		screamsSource.Stop();
		oneShotSource.Stop();
	}

	private void Hide()
	{
		currentState = LitanyState.Hidden;
		timer = 0f;
		rootObject.SetActive(false);
		isOnCooldown = true;
		cooldownTimer = 0f;
		cooldownTime = Plugin.CooldownTime.Value;
		idleSource.Stop();
		screamsSource.Stop();
		oneShotSource.Stop();
	}

	private void ForceHide()
	{
		currentState = LitanyState.Hidden;
		timer = 0f;
		rootObject.SetActive(false);
		isOnCooldown = false;
		nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
	}

	public void ResetLitany()
	{
		currentState = LitanyState.Hidden;
		timer = 0f;
		nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
		rootObject.SetActive(false);
		isOnCooldown = false;
		cooldownTimer = 0f;
	}

	private bool IsPlayerCrouching()
	{
		PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
		if ((Object)(object)val == (Object)null)
		{
			return true;
		}
		return val.isCrouching;
	}

	private void OnVideoFinished(VideoPlayer vp)
	{
		Debug.Log((object)"Litany Entity: видео закончилось!");
		videoFinished = true;
	}

	private bool IsPlayerInGame()
	{
		PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		if (val.isPlayerDead)
		{
			return false;
		}
		return !val.isInHangarShipRoom && (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded;
	}

	private void CreateOverlay()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Expected O, but got Unknown
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Expected O, but got Unknown
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Expected O, but got Unknown
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Expected O, but got Unknown
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_021e: Expected O, but got Unknown
		//IL_0224: Unknown result type (might be due to invalid IL or missing references)
		//IL_022b: Expected O, but got Unknown
		//IL_0278: Unknown result type (might be due to invalid IL or missing references)
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("LitanyCanvas");
		Canvas val2 = val.AddComponent<Canvas>();
		val2.renderMode = (RenderMode)0;
		val2.sortingOrder = 100;
		Object.DontDestroyOnLoad((Object)(object)val);
		rootObject = new GameObject("LitanyRoot");
		rootObject.transform.SetParent(val.transform, false);
		RectTransform val3 = rootObject.AddComponent<RectTransform>();
		val3.anchorMin = new Vector2(0.5f, 0.5f);
		val3.anchorMax = new Vector2(0.5f, 0.5f);
		val3.sizeDelta = new Vector2(500f, 500f);
		val3.anchoredPosition = Vector2.zero;
		GameObject val4 = new GameObject("LitanyBody");
		val4.transform.SetParent(rootObject.transform, false);
		bodyImage = val4.AddComponent<Image>();
		RectTransform component = val4.GetComponent<RectTransform>();
		component.anchorMin = Vector2.zero;
		component.anchorMax = Vector2.one;
		component.sizeDelta = Vector2.zero;
		GameObject val5 = new GameObject("LitanyEyes");
		val5.transform.SetParent(rootObject.transform, false);
		eyeImage = val5.AddComponent<Image>();
		RectTransform component2 = val5.GetComponent<RectTransform>();
		component2.anchorMin = new Vector2(0.5f, 0.5f);
		component2.anchorMax = new Vector2(0.5f, 0.5f);
		component2.sizeDelta = new Vector2(500f, 500f);
		component2.anchoredPosition = Vector2.zero;
		GameObject val6 = new GameObject("LitanyVideoPlayer");
		val6.transform.SetParent(val.transform, false);
		videoPlayer = val6.AddComponent<VideoPlayer>();
		videoPlayer.playOnAwake = false;
		videoPlayer.renderMode = (VideoRenderMode)2;
		videoTexture = new RenderTexture(1920, 1080, 0);
		videoPlayer.targetTexture = videoTexture;
		videoPlayer.loopPointReached += new EventHandler(OnVideoFinished);
		GameObject val7 = new GameObject("LitanyVideo");
		val7.transform.SetParent(val.transform, false);
		videoImage = val7.AddComponent<RawImage>();
		videoImage.texture = (Texture)(object)videoTexture;
		((Graphic)videoImage).color = new Color(1f, 1f, 1f, 0f);
		RectTransform component3 = val7.GetComponent<RectTransform>();
		component3.anchorMin = Vector2.zero;
		component3.anchorMax = Vector2.one;
		component3.sizeDelta = Vector2.zero;
		rootObject.SetActive(false);
	}
}
[HarmonyPatch(typeof(PlayerControllerB), "Start")]
public class LitanyPatch
{
	private static void Postfix(PlayerControllerB __instance)
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Expected O, but got Unknown
		if (((NetworkBehaviour)__instance).IsOwner && !((Object)(object)Object.FindObjectOfType<LitanyBehaviour>() != (Object)null))
		{
			int num = Random.Range(0, 100);
			if (num >= Plugin.SpawnChance.Value)
			{
				Debug.Log((object)$"Litany Entity: won't appear this round (roll {num})");
				return;
			}
			GameObject val = new GameObject("LitanyManager");
			val.AddComponent<LitanyBehaviour>();
			Object.DontDestroyOnLoad((Object)(object)val);
			Debug.Log((object)"Litany Entity: manager created!");
		}
	}
}
[HarmonyPatch(typeof(PlayerControllerB), "SpawnDeadAnimation")]
public class LitanyRespawnPatch
{
	private static void Postfix(PlayerControllerB __instance)
	{
		if (((NetworkBehaviour)__instance).IsOwner)
		{
			LitanyBehaviour litanyBehaviour = Object.FindObjectOfType<LitanyBehaviour>();
			if ((Object)(object)litanyBehaviour != (Object)null)
			{
				litanyBehaviour.ResetLitany();
				Debug.Log((object)"Litany Entity: reset after respawn!");
			}
		}
	}
}
[BepInPlugin("com.yourname.litanyentity", "Litany Entity", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
	private readonly Harmony harmony = new Harmony("com.yourname.litanyentity");

	public static ConfigEntry<int> SpawnChance;

	public static ConfigEntry<float> PrepareDuration;

	public static ConfigEntry<float> WarningDuration;

	public static ConfigEntry<float> OpenEyeDuration;

	public static ConfigEntry<float> BetweenEyesDuration;

	public static ConfigEntry<float> MinSpawnTime;

	public static ConfigEntry<float> MaxSpawnTime;

	public static ConfigEntry<float> CooldownTime;

	private void Awake()
	{
		SpawnChance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpawnChance", 35, "Chance of Litany appearing each round (0-100)");
		PrepareDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "PrepareDuration", 4f, "Time before first eye warning");
		WarningDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "WarningDuration", 1.5f, "Duration of warning animation");
		OpenEyeDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "OpenEyeDuration", 0.5f, "How long eye stays open");
		BetweenEyesDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "BetweenEyesDuration", 2f, "Time between each eye attack");
		MinSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinSpawnTime", 10f, "Minimum time before Litany appears");
		MaxSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxSpawnTime", 30f, "Maximum time before Litany appears");
		CooldownTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CooldownTime", 60f, "Cooldown time in seconds before Litany can appear again");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Litany Entity loaded!");
		harmony.PatchAll();
	}
}