Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Litany v1.0.0
BepInEx/plugins/LitanyEntity.dll
Decompiled 3 weeks agousing 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(); } }