

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;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("DozerEntity")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DozerEntity")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("707b5311-bc50-4ec1-ac25-901818a7c6a1")]
[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 DozerEntity;
public class DozerBehaviour : MonoBehaviour
{
private enum DozerState
{
Hidden,
ClosedEyes,
OpenEyes,
Screamer
}
private DozerState currentState = DozerState.Hidden;
private float timer = 0f;
private float nextAppearTime = 0f;
private float cooldownTimer = 0f;
private float cooldownTime = 0f;
private bool isOnCooldown = false;
private float previousVolume = 1f;
private GameObject overlayObject;
private Image overlayImage;
private Image blackBackground;
private GameObject textObject;
private Image textImage;
private Sprite[] textSprites = (Sprite[])(object)new Sprite[25];
private float textAnimTimer = 0f;
private int textAnimFrame = 0;
private bool textAnimStarted = false;
private AudioClip openSound;
private AudioClip killSound;
private AudioSource audioSource;
private AudioClip closedSound;
private Sprite[] closedSprites = (Sprite[])(object)new Sprite[14];
private Sprite[] openSprites = (Sprite[])(object)new Sprite[14];
private Sprite[] screamerSprites = (Sprite[])(object)new Sprite[50];
private float animTimer = 0f;
private int animFrame = 0;
private float animSpeed = 0.0667f;
private string modFolder;
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)("Dozer Entity: звук не найден — " + text));
return null;
}
WWW val = new WWW("file://" + text);
try
{
while (!val.isDone)
{
}
return val.GetAudioClip(false, false, (AudioType)20);
}
finally
{
((IDisposable)val)?.Dispose();
}
}
private void Start()
{
string pluginPath = Paths.PluginPath;
string[] directories = Directory.GetDirectories(pluginPath, "DozerEntity", SearchOption.AllDirectories);
if (directories.Length != 0)
{
modFolder = directories[0];
}
else
{
modFolder = Path.Combine(pluginPath, "DozerEntity");
}
Debug.Log((object)("Dozer Entity: папка мода — " + modFolder));
LoadSprites();
closedSound = LoadAudioClip("closed_sound.wav");
openSound = LoadAudioClip("open_sound.wav");
killSound = LoadAudioClip("kill_sound.wav");
audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
audioSource.loop = true;
audioSource.volume = 1f;
CreateOverlay();
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
}
private void LoadSprites()
{
for (int i = 0; i < 14; i++)
{
closedSprites[i] = LoadSprite("Closed_eyes" + (i + 1).ToString("D4") + ".png");
}
for (int j = 0; j < 14; j++)
{
openSprites[j] = LoadSprite("Opened_eyes" + (j + 1).ToString("D4") + ".png");
}
for (int k = 0; k < 50; k++)
{
screamerSprites[k] = LoadSprite("Kill" + (k + 1).ToString("D4") + ".png");
}
for (int l = 0; l < 25; l++)
{
textSprites[l] = LoadSprite($"text{l + 1}.png");
}
}
public void ForceHide()
{
currentState = DozerState.Hidden;
timer = 0f;
overlayObject.SetActive(false);
((Component)blackBackground).gameObject.SetActive(false);
AudioListener.volume = previousVolume;
isOnCooldown = false;
audioSource.Stop();
}
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)("Dozer Entity: файл не найден — " + 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 == DozerState.Hidden || currentState == DozerState.ClosedEyes || currentState == DozerState.OpenEyes)
{
previousVolume = AudioListener.volume;
}
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)
{
if (currentState != 0)
{
overlayObject.SetActive(false);
((Component)blackBackground).gameObject.SetActive(false);
audioSource.Stop();
currentState = DozerState.Hidden;
timer = 0f;
isOnCooldown = false;
}
}
else
{
if (!IsPlayerInGame())
{
return;
}
if (val.quickMenuManager.isMenuOpen)
{
overlayObject.SetActive(false);
((Component)blackBackground).gameObject.SetActive(false);
return;
}
timer += Time.deltaTime;
switch (currentState)
{
case DozerState.Hidden:
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 (timer >= nextAppearTime)
{
ShowClosedEyes();
}
break;
case DozerState.ClosedEyes:
AnimateClosed();
if (timer >= Plugin.EyeOpenDelay.Value)
{
OpenEyes();
}
break;
case DozerState.OpenEyes:
AnimateOpen();
if (timer >= 0.1f && !IsPlayerSafe())
{
TriggerScreamer();
}
else if (timer >= Plugin.OpenEyesDuration.Value)
{
Hide();
}
break;
case DozerState.Screamer:
AnimateScreamer();
AnimateText();
if (timer >= 2.05f)
{
KillPlayer();
}
break;
}
}
}
private void AnimateClosed()
{
if (currentState != DozerState.ClosedEyes)
{
return;
}
animTimer += Time.deltaTime;
if (animTimer >= animSpeed)
{
animTimer = 0f;
animFrame = (animFrame + 1) % 14;
if ((Object)(object)closedSprites[animFrame] != (Object)null)
{
overlayImage.sprite = closedSprites[animFrame];
}
}
}
private void AnimateScreamer()
{
animTimer += Time.deltaTime;
if (animTimer >= 0.05f)
{
animTimer = 0f;
animFrame++;
if (animFrame >= 50)
{
overlayObject.SetActive(false);
}
else if ((Object)(object)screamerSprites[animFrame] != (Object)null)
{
overlayImage.sprite = screamerSprites[animFrame];
}
}
}
private void AnimateOpen()
{
animTimer += Time.deltaTime;
if (animTimer >= 0.05f)
{
animTimer = 0f;
animFrame = (animFrame + 1) % 14;
if ((Object)(object)openSprites[animFrame] != (Object)null)
{
overlayImage.sprite = openSprites[animFrame];
}
}
}
private void ShowClosedEyes()
{
currentState = DozerState.ClosedEyes;
timer = 0f;
animFrame = 0;
animTimer = 0f;
overlayObject.SetActive(true);
if ((Object)(object)closedSprites[0] != (Object)null)
{
overlayImage.sprite = closedSprites[0];
}
if ((Object)(object)closedSound != (Object)null)
{
audioSource.loop = true;
audioSource.clip = closedSound;
audioSource.Play();
}
}
private void OpenEyes()
{
currentState = DozerState.OpenEyes;
timer = 0f;
animFrame = 0;
animTimer = 0f;
if ((Object)(object)openSound != (Object)null)
{
audioSource.loop = false;
audioSource.clip = openSound;
audioSource.Play();
}
if ((Object)(object)openSprites[0] != (Object)null)
{
overlayImage.sprite = openSprites[0];
}
}
private void TriggerScreamer()
{
currentState = DozerState.Screamer;
timer = 0f;
animFrame = 0;
animTimer = 0f;
textAnimFrame = 0;
textAnimTimer = 0f;
textAnimStarted = false;
if ((Object)(object)textSprites[0] != (Object)null)
{
textImage.sprite = textSprites[0];
}
textObject.SetActive(true);
AudioSource val = ((Component)this).gameObject.AddComponent<AudioSource>();
val.loop = false;
val.volume = 1f;
if ((Object)(object)killSound != (Object)null)
{
val.clip = killSound;
val.Play();
}
AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
AudioSource[] array2 = array;
foreach (AudioSource val2 in array2)
{
if ((Object)(object)val2 != (Object)(object)val && (Object)(object)val2 != (Object)(object)audioSource)
{
val2.mute = true;
}
}
((Component)blackBackground).gameObject.SetActive(true);
overlayObject.SetActive(false);
overlayObject.SetActive(true);
if ((Object)(object)screamerSprites[0] != (Object)null)
{
overlayImage.sprite = screamerSprites[0];
}
}
private void AnimateText()
{
textAnimTimer += Time.deltaTime;
if (!textAnimStarted && textAnimTimer >= 1.5f)
{
textAnimStarted = true;
textAnimTimer = 0f;
textAnimFrame = 1;
}
if (!textAnimStarted)
{
return;
}
textAnimTimer += Time.deltaTime;
if (textAnimTimer >= 0.0667f)
{
textAnimTimer = 0f;
textAnimFrame++;
if (textAnimFrame < 25 && (Object)(object)textSprites[textAnimFrame] != (Object)null)
{
textImage.sprite = textSprites[textAnimFrame];
}
}
}
public void ResetDozer()
{
currentState = DozerState.Hidden;
timer = 0f;
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
overlayObject.SetActive(false);
}
private void Hide()
{
currentState = DozerState.Hidden;
timer = 0f;
overlayObject.SetActive(false);
((Component)blackBackground).gameObject.SetActive(false);
isOnCooldown = true;
cooldownTimer = 0f;
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
audioSource.Stop();
}
private void KillPlayer()
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController;
if ((Object)(object)localPlayerController != (Object)null)
{
localPlayerController.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 1, default(Vector3), false);
}
AudioListener.volume = previousVolume;
overlayObject.SetActive(false);
((Component)blackBackground).gameObject.SetActive(false);
audioSource.Stop();
textObject.SetActive(false);
currentState = DozerState.Hidden;
timer = 0f;
isOnCooldown = false;
AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
AudioSource[] array2 = array;
foreach (AudioSource val in array2)
{
val.mute = false;
}
}
private bool IsPlayerSafe()
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return true;
}
bool isCrouching = val.isCrouching;
Vector3 velocity = val.thisController.velocity;
bool flag = ((Vector3)(ref velocity)).magnitude > 0.1f;
return isCrouching && !flag;
}
private bool IsPlayerInGame()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.isPlayerDead)
{
return false;
}
if (StartOfRound.Instance?.currentLevel?.sceneName == "CompanyBuilding")
{
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_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: 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_00bd: Expected O, but got Unknown
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
//IL_0151: Expected O, but got Unknown
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_019e: Unknown result type (might be due to invalid IL or missing references)
//IL_01b5: 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)
GameObject val = new GameObject("DozerCanvas");
Canvas val2 = val.AddComponent<Canvas>();
val2.renderMode = (RenderMode)0;
val2.sortingOrder = 100;
Object.DontDestroyOnLoad((Object)(object)val);
GameObject val3 = new GameObject("DozerBlackBg");
val3.transform.SetParent(val.transform, false);
blackBackground = val3.AddComponent<Image>();
((Graphic)blackBackground).color = new Color(0f, 0f, 0f, 1f);
RectTransform component = val3.GetComponent<RectTransform>();
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.sizeDelta = Vector2.zero;
val3.SetActive(false);
overlayObject = new GameObject("DozerImage");
overlayObject.transform.SetParent(val.transform, false);
overlayImage = overlayObject.AddComponent<Image>();
RectTransform component2 = overlayObject.GetComponent<RectTransform>();
component2.anchorMin = new Vector2(0.5f, 0.5f);
component2.anchorMax = new Vector2(0.5f, 0.5f);
component2.sizeDelta = new Vector2(600f, 600f);
component2.anchoredPosition = Vector2.zero;
GameObject val4 = new GameObject("DozerText");
val4.transform.SetParent(val.transform, false);
textImage = val4.AddComponent<Image>();
RectTransform component3 = val4.GetComponent<RectTransform>();
component3.anchorMin = new Vector2(0.5f, 0.5f);
component3.anchorMax = new Vector2(0.5f, 0.5f);
component3.sizeDelta = new Vector2(1000f, 1000f);
component3.anchoredPosition = new Vector2(-320f, -60f);
textObject = val4;
textObject.SetActive(false);
overlayObject.SetActive(false);
}
}
[HarmonyPatch(typeof(PlayerControllerB), "Start")]
public class DozerPatch
{
private static void Postfix(PlayerControllerB __instance)
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Expected O, but got Unknown
if (((NetworkBehaviour)__instance).IsOwner && !((Object)(object)Object.FindObjectOfType<DozerBehaviour>() != (Object)null))
{
int num = Random.Range(0, 100);
if (num >= Plugin.SpawnChance.Value)
{
Debug.Log((object)$"Dozer Entity: не появится в этом раунде (бросок {num}, нужно < {Plugin.SpawnChance.Value})");
return;
}
GameObject val = new GameObject("DozerManager");
val.AddComponent<DozerBehaviour>();
Object.DontDestroyOnLoad((Object)(object)val);
Debug.Log((object)$"Dozer Entity: менеджер создан! (бросок {num})");
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "SpawnDeadAnimation")]
public class DozerRespawnPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
DozerBehaviour dozerBehaviour = Object.FindObjectOfType<DozerBehaviour>();
if ((Object)(object)dozerBehaviour != (Object)null)
{
dozerBehaviour.ResetDozer();
Debug.Log((object)"Dozer Entity: сброс после возрождения!");
}
}
}
}
[BepInPlugin("com.yourname.dozerentity", "Dozer Entity", "1.0.1")]
public class Plugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
public class DozerDeathPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
DozerBehaviour dozerBehaviour = Object.FindObjectOfType<DozerBehaviour>();
if ((Object)(object)dozerBehaviour != (Object)null)
{
dozerBehaviour.ForceHide();
}
}
}
}
private readonly Harmony harmony = new Harmony("com.yourname.dozerentity");
public static ConfigEntry<int> SpawnChance;
public static ConfigEntry<float> EyeOpenDelay;
public static ConfigEntry<float> OpenEyesDuration;
public static ConfigEntry<float> MinSpawnTime;
public static ConfigEntry<float> MaxSpawnTime;
private void Awake()
{
SpawnChance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpawnChance", 35, "Dozer appear chance each round (0-100)");
EyeOpenDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "EyeOpenDelay", 1.5f, "Time in seconds before Dozer opens it's eyes");
OpenEyesDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "OpenEyesDuration", 3f, "How long Dozer stays with open eyes in seconds");
MinSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinSpawnTime", 10f, "Minimum time in seconds before Dozer appears");
MaxSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxSpawnTime", 30f, "Maximum time in seconds before Dozer appears");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Dozer Entity загружен!");
harmony.PatchAll();
}
}using System;
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 GameNetcodeStuff;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("KookooEntity")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("KookooEntity")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("df24efe7-a262-4aa5-981a-01c019d26f0a")]
[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 KookooEntity;
public class KookooBehaviour : MonoBehaviour
{
private enum KookooState
{
Hidden,
Appearing,
Counting,
WaitingClosing,
Closing,
HoldingClose
}
private float basePitch = 0.7f;
private bool conditionChecked = false;
private KookooState currentState = KookooState.Hidden;
private float timer = 0f;
private float nextAppearTime = 0f;
private List<GameObject> deathCopies = new List<GameObject>();
private bool deathAnimPlaying = false;
private float deathTimer = 0f;
private float copyTimer = 0f;
private GameObject deathCanvas;
private Sprite lastClosingSprite;
private AudioClip rememberClip;
private AudioClip countClip;
private AudioClip checkClip;
private AudioClip killClip;
private AudioSource rememberSource;
private AudioSource countSource;
private AudioSource checkSource;
private AudioSource killSource;
private bool isOnCooldown = false;
private float cooldownTimer = 0f;
private float cooldownTime = 0f;
private GameObject rootObject;
private Image bodyImage;
private Image[] digitImages = (Image[])(object)new Image[2];
private Image particlesImage;
private Sprite[] rememberSprites = (Sprite[])(object)new Sprite[2];
private Sprite[] numSprites = (Sprite[])(object)new Sprite[10];
private Sprite particlesSprite;
private Sprite[] countSprites = (Sprite[])(object)new Sprite[6];
private Sprite[] closingSprites = (Sprite[])(object)new Sprite[4];
private Sprite errorSprite;
private bool shouldSpawnErrorSprites = false;
private float errorSpriteSpawnTimer = 0f;
private List<GameObject> errorSpriteInstances = new List<GameObject>();
private float bodyAnimTimer = 0f;
private int bodyAnimFrame = 0;
private float countAnimTimer = 0f;
private int countAnimFrame = 0;
private bool countAnimPlaying = false;
private float closingAnimTimer = 0f;
private int closingAnimFrame = 0;
private float particlesTimer = 0f;
private float particlesInterval = 0f;
private bool particlesVisible = false;
private float particlesVisibleTimer = 0f;
private int targetNumber = 0;
private int currentCount = 0;
private float countTimer = 0f;
private float shakeTimer = 0f;
private Vector2 basePosition = Vector2.zero;
private bool scanBlocked = false;
private float scanBlockTimer = 0f;
private AudioSource scanDamageSource;
private AudioSource scanErrorSource;
private AudioClip scanDamageClip;
private AudioClip scanErrorClip;
private bool scanAttackPlaying = false;
private float scanAttackTimer = 0f;
private string modFolder;
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)("Kookoo 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 Start()
{
string[] directories = Directory.GetDirectories(Paths.PluginPath, "KookooEntity", SearchOption.AllDirectories);
modFolder = ((directories.Length != 0) ? directories[0] : Path.Combine(Paths.PluginPath, "KookooEntity"));
LoadSprites();
rememberClip = LoadAudioClip("remember.mp3");
countClip = LoadAudioClip("count.mp3");
checkClip = LoadAudioClip("check.mp3");
killClip = LoadAudioClip("kill.mp3");
rememberSource = ((Component)this).gameObject.AddComponent<AudioSource>();
rememberSource.loop = false;
rememberSource.playOnAwake = false;
countSource = ((Component)this).gameObject.AddComponent<AudioSource>();
countSource.loop = false;
countSource.playOnAwake = false;
checkSource = ((Component)this).gameObject.AddComponent<AudioSource>();
checkSource.loop = false;
checkSource.playOnAwake = false;
killSource = ((Component)this).gameObject.AddComponent<AudioSource>();
killSource.loop = false;
killSource.playOnAwake = false;
scanDamageClip = LoadAudioClip("scandamage.mp3");
scanErrorClip = LoadAudioClip("scanerror.mp3");
scanDamageSource = ((Component)this).gameObject.AddComponent<AudioSource>();
scanDamageSource.loop = false;
scanDamageSource.playOnAwake = false;
scanErrorSource = ((Component)this).gameObject.AddComponent<AudioSource>();
scanErrorSource.loop = false;
scanErrorSource.playOnAwake = false;
CreateOverlay();
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
}
private void LoadSprites()
{
rememberSprites[0] = LoadSprite("remember1.png");
rememberSprites[1] = LoadSprite("remember2.png");
for (int i = 0; i < 10; i++)
{
numSprites[i] = LoadSprite($"num{i}.png");
}
particlesSprite = LoadSprite("particles.png");
for (int j = 0; j < 6; j++)
{
countSprites[j] = LoadSprite($"count{j + 1}.png");
}
for (int k = 0; k < 4; k++)
{
closingSprites[k] = LoadSprite($"closing{k + 1}.png");
}
errorSprite = LoadSprite("error.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)("Kookoo Entity: файл не найден — " + 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()
{
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_0372: Unknown result type (might be due to invalid IL or missing references)
//IL_037c: Unknown result type (might be due to invalid IL or missing references)
//IL_0382: Unknown result type (might be due to invalid IL or missing references)
if (scanBlocked)
{
scanBlockTimer += Time.deltaTime;
if (scanBlockTimer >= Plugin.ScanBlockDuration.Value)
{
scanBlocked = false;
scanBlockTimer = 0f;
}
}
if (scanAttackPlaying)
{
scanAttackTimer += Time.deltaTime;
if (!(scanAttackTimer >= 0.5f))
{
return;
}
scanAttackPlaying = false;
rootObject.SetActive(false);
((Component)particlesImage).gameObject.SetActive(false);
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val != (Object)null)
{
int num = val.health - 1;
if (num > 0)
{
val.DamagePlayer(num, true, true, (CauseOfDeath)0, 0, false, default(Vector3));
}
}
scanBlocked = true;
scanBlockTimer = 0f;
return;
}
PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val2 == (Object)null)
{
return;
}
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 (deathAnimPlaying)
{
deathTimer += Time.deltaTime;
copyTimer += Time.deltaTime;
if (shouldSpawnErrorSprites && (Object)(object)errorSprite != (Object)null && deathTimer >= 1f)
{
errorSpriteSpawnTimer += Time.deltaTime;
if (errorSpriteSpawnTimer >= 0.01f)
{
errorSpriteSpawnTimer = 0f;
SpawnErrorSprite();
}
}
if (copyTimer >= 0.01f)
{
copyTimer = 0f;
CreateDeathCopy();
}
if (!(deathTimer >= 2f))
{
return;
}
deathAnimPlaying = false;
shouldSpawnErrorSprites = false;
deathCanvas.SetActive(false);
foreach (GameObject deathCopy in deathCopies)
{
Object.Destroy((Object)(object)deathCopy);
}
deathCopies.Clear();
foreach (GameObject errorSpriteInstance in errorSpriteInstances)
{
if ((Object)(object)errorSpriteInstance != (Object)null)
{
Object.Destroy((Object)(object)errorSpriteInstance);
}
}
errorSpriteInstances.Clear();
PlayerControllerB val3 = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val3 != (Object)null)
{
val3.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 1, default(Vector3), false);
}
Hide();
}
else if (val2.isPlayerDead)
{
ForceHide();
}
else
{
if (!IsPlayerInGame())
{
return;
}
timer += Time.deltaTime;
if (currentState != 0)
{
AnimateBody();
}
switch (currentState)
{
case KookooState.Hidden:
if (timer >= nextAppearTime)
{
Appear();
}
break;
case KookooState.Appearing:
if (timer >= Plugin.AppearDuration.Value)
{
StartCounting();
}
break;
case KookooState.Counting:
UpdateCounting();
UpdateParticles();
if (countAnimPlaying)
{
AnimateCount();
}
Shake();
break;
case KookooState.WaitingClosing:
if (countAnimPlaying)
{
AnimateCount();
}
UpdateParticles();
Shake();
if (timer >= 1f)
{
StartClosing();
}
break;
case KookooState.Closing:
AnimateClosing();
break;
case KookooState.HoldingClose:
if (!conditionChecked)
{
conditionChecked = true;
CheckCondition();
}
break;
}
}
}
private void AnimateBody()
{
if (currentState != KookooState.Appearing)
{
return;
}
bodyAnimTimer += Time.deltaTime;
if (bodyAnimTimer >= 0.0667f)
{
bodyAnimTimer = 0f;
bodyAnimFrame = (bodyAnimFrame + 1) % 2;
if ((Object)(object)rememberSprites[bodyAnimFrame] != (Object)null)
{
bodyImage.sprite = rememberSprites[bodyAnimFrame];
}
}
}
public bool IsScanBlocked()
{
return scanBlocked;
}
public void PlayScanError()
{
if ((Object)(object)scanErrorClip != (Object)null)
{
scanErrorSource.clip = scanErrorClip;
scanErrorSource.Play();
}
}
public void TryTriggerScanAttack()
{
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
if (!scanAttackPlaying && Random.Range(0, Plugin.ScanAttackChance.Value) == 0)
{
scanAttackPlaying = true;
scanAttackTimer = 0f;
rootObject.SetActive(true);
if ((Object)(object)closingSprites[3] != (Object)null)
{
bodyImage.sprite = closingSprites[3];
}
((Component)particlesImage).gameObject.SetActive(true);
if ((Object)(object)scanDamageClip != (Object)null)
{
scanDamageSource.clip = scanDamageClip;
scanDamageSource.Play();
}
float num = Random.Range(-500f, 500f);
float num2 = Random.Range(-250f, 250f);
rootObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(num, num2);
}
}
private void AnimateCount()
{
countAnimTimer += Time.deltaTime;
if (!(countAnimTimer >= 0.0667f))
{
return;
}
countAnimTimer = 0f;
countAnimFrame++;
if (countAnimFrame >= 6)
{
countAnimPlaying = false;
countAnimFrame = 0;
if ((Object)(object)countSprites[5] != (Object)null)
{
bodyImage.sprite = countSprites[5];
}
}
else if ((Object)(object)countSprites[countAnimFrame] != (Object)null)
{
bodyImage.sprite = countSprites[countAnimFrame];
}
}
private void AnimateClosing()
{
closingAnimTimer += Time.deltaTime;
if (closingAnimTimer >= 0.0667f)
{
closingAnimTimer = 0f;
closingAnimFrame++;
if (closingAnimFrame >= 4)
{
currentState = KookooState.HoldingClose;
timer = 0f;
}
else if ((Object)(object)closingSprites[closingAnimFrame] != (Object)null)
{
bodyImage.sprite = closingSprites[closingAnimFrame];
}
}
}
private void UpdateCounting()
{
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: 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)
countTimer += Time.deltaTime;
if (countTimer >= Plugin.CountInterval.Value)
{
countTimer = 0f;
currentCount++;
if ((Object)(object)countClip != (Object)null)
{
countSource.clip = countClip;
countSource.Play();
countSource.pitch = basePitch + (float)currentCount / (float)targetNumber * 0.6f;
}
UpdateDigits(currentCount);
float num = Random.Range(-500f, 500f);
float num2 = Random.Range(-250f, 250f);
basePosition = new Vector2(num, num2);
rootObject.GetComponent<RectTransform>().anchoredPosition = basePosition;
countAnimPlaying = true;
countAnimFrame = 0;
countAnimTimer = 0f;
if ((Object)(object)countSprites[0] != (Object)null)
{
bodyImage.sprite = countSprites[0];
}
if (currentCount >= targetNumber)
{
currentState = KookooState.WaitingClosing;
timer = 0f;
}
}
}
private void UpdateParticles()
{
if (particlesVisible)
{
particlesVisibleTimer += Time.deltaTime;
if (particlesVisibleTimer >= 0.2f)
{
particlesVisible = false;
((Component)particlesImage).gameObject.SetActive(false);
}
return;
}
particlesTimer += Time.deltaTime;
if (particlesTimer >= particlesInterval)
{
particlesTimer = 0f;
particlesInterval = Random.Range(0.1f, 1f);
particlesVisible = true;
particlesVisibleTimer = 0f;
((Component)particlesImage).gameObject.SetActive(true);
}
}
private void Shake()
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
shakeTimer += Time.deltaTime;
if (shakeTimer >= 0.05f)
{
shakeTimer = 0f;
float num = Random.Range(-5f, 5f);
float num2 = Random.Range(-5f, 5f);
rootObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(basePosition.x + num, basePosition.y + num2);
}
}
private void UpdateDigits(int number)
{
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
if (number < 10)
{
((Component)digitImages[0]).gameObject.SetActive(false);
((Component)digitImages[1]).gameObject.SetActive(true);
digitImages[1].sprite = numSprites[number];
((Component)digitImages[1]).GetComponent<RectTransform>().anchoredPosition = Vector2.zero;
return;
}
int num = number / 10;
int num2 = number % 10;
((Component)digitImages[0]).gameObject.SetActive(true);
((Component)digitImages[1]).gameObject.SetActive(true);
digitImages[0].sprite = numSprites[num];
digitImages[1].sprite = numSprites[num2];
((Component)digitImages[0]).GetComponent<RectTransform>().anchoredPosition = new Vector2(-40f, 0f);
((Component)digitImages[1]).GetComponent<RectTransform>().anchoredPosition = new Vector2(40f, 0f);
}
private void Appear()
{
//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)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
currentState = KookooState.Appearing;
timer = 0f;
currentCount = 0;
targetNumber = Random.Range(1, 31);
float num = Random.Range(-500f, 500f);
float num2 = Random.Range(-250f, 250f);
basePosition = new Vector2(num, num2);
rootObject.GetComponent<RectTransform>().anchoredPosition = basePosition;
if ((Object)(object)rememberClip != (Object)null)
{
rememberSource.clip = rememberClip;
rememberSource.Play();
}
rootObject.SetActive(true);
if ((Object)(object)rememberSprites[0] != (Object)null)
{
bodyImage.sprite = rememberSprites[0];
}
UpdateDigits(targetNumber);
}
private void StartCounting()
{
currentState = KookooState.Counting;
timer = 0f;
countTimer = 0f;
currentCount = 0;
UpdateDigits(0);
countSource.pitch = basePitch;
}
private void StartClosing()
{
conditionChecked = false;
currentState = KookooState.Closing;
closingAnimFrame = 0;
closingAnimTimer = 0f;
countAnimPlaying = false;
((Component)digitImages[0]).gameObject.SetActive(false);
((Component)digitImages[1]).gameObject.SetActive(false);
((Component)particlesImage).gameObject.SetActive(false);
if ((Object)(object)checkClip != (Object)null)
{
checkSource.clip = checkClip;
checkSource.Play();
}
if ((Object)(object)closingSprites[0] != (Object)null)
{
bodyImage.sprite = closingSprites[0];
}
}
public void CheckCondition()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
Hide();
}
else if ((Object)(object)val.currentlyHeldObjectServer != (Object)null)
{
if ((Object)(object)killClip != (Object)null)
{
killSource.clip = killClip;
killSource.Play();
}
deathAnimPlaying = true;
shouldSpawnErrorSprites = true;
errorSpriteSpawnTimer = 0f;
deathTimer = 0f;
copyTimer = 0f;
lastClosingSprite = closingSprites[3];
deathCanvas.SetActive(true);
rootObject.SetActive(true);
}
else
{
Hide();
}
}
private void SpawnErrorSprite()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Expected O, but got Unknown
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: 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)
GameObject val = GameObject.Find("KookooErrorSpriteCanvas");
Canvas val2;
if ((Object)(object)val == (Object)null)
{
val = new GameObject("KookooErrorSpriteCanvas");
val2 = val.AddComponent<Canvas>();
val2.renderMode = (RenderMode)0;
val2.sortingOrder = 10000;
Object.DontDestroyOnLoad((Object)(object)val);
}
else
{
val2 = val.GetComponent<Canvas>();
}
GameObject val3 = new GameObject("KookooErrorSprite");
val3.transform.SetParent(((Component)val2).transform, false);
Image val4 = val3.AddComponent<Image>();
val4.sprite = errorSprite;
RectTransform component = val3.GetComponent<RectTransform>();
Rect rect = errorSprite.rect;
float width = ((Rect)(ref rect)).width;
rect = errorSprite.rect;
component.sizeDelta = new Vector2(width, ((Rect)(ref rect)).height);
component.anchoredPosition = new Vector2((float)Random.Range(-Screen.width / 2 + 160, Screen.width / 2 - 160), (float)Random.Range(-Screen.height / 2 + 80, Screen.height / 2 - 80));
errorSpriteInstances.Add(val3);
}
private void CreateDeathCopy()
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Expected O, but got Unknown
//IL_008a: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
float num = Random.Range((float)(-Screen.width) * 0.5f, (float)Screen.width * 0.5f);
float num2 = Random.Range((float)(-Screen.height) * 0.5f, (float)Screen.height * 0.5f);
GameObject val = new GameObject("KookooCopy");
val.transform.SetParent(deathCanvas.transform, false);
Image val2 = val.AddComponent<Image>();
val2.sprite = lastClosingSprite;
RectTransform component = val.GetComponent<RectTransform>();
component.anchorMin = new Vector2(0.5f, 0.5f);
component.anchorMax = new Vector2(0.5f, 0.5f);
component.sizeDelta = new Vector2(800f, 800f);
component.anchoredPosition = new Vector2(num, num2);
deathCopies.Add(val);
}
private void Hide()
{
currentState = KookooState.Hidden;
timer = 0f;
rootObject.SetActive(false);
isOnCooldown = true;
cooldownTimer = 0f;
cooldownTime = Plugin.CooldownTime.Value;
((Component)particlesImage).gameObject.SetActive(false);
rememberSource.Stop();
countSource.Stop();
checkSource.Stop();
killSource.Stop();
}
public void ForceHide()
{
currentState = KookooState.Hidden;
timer = 0f;
rootObject.SetActive(false);
isOnCooldown = false;
((Component)particlesImage).gameObject.SetActive(false);
((MonoBehaviour)this).CancelInvoke("StartClosing");
deathAnimPlaying = false;
shouldSpawnErrorSprites = false;
if ((Object)(object)deathCanvas != (Object)null)
{
deathCanvas.SetActive(false);
}
foreach (GameObject deathCopy in deathCopies)
{
Object.Destroy((Object)(object)deathCopy);
}
deathCopies.Clear();
foreach (GameObject errorSpriteInstance in errorSpriteInstances)
{
if ((Object)(object)errorSpriteInstance != (Object)null)
{
Object.Destroy((Object)(object)errorSpriteInstance);
}
}
errorSpriteInstances.Clear();
rememberSource.Stop();
countSource.Stop();
checkSource.Stop();
killSource.Stop();
}
public void ResetKookoo()
{
ForceHide();
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
cooldownTimer = 0f;
}
private bool IsPlayerInGame()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.isPlayerDead)
{
return false;
}
if (StartOfRound.Instance?.currentLevel?.sceneName == "CompanyBuilding")
{
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_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Expected O, but got Unknown
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Expected O, but got Unknown
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_0164: 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)
//IL_018a: Unknown result type (might be due to invalid IL or missing references)
//IL_0191: Expected O, but got Unknown
//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_0208: Expected O, but got Unknown
//IL_0246: Unknown result type (might be due to invalid IL or missing references)
//IL_025d: Unknown result type (might be due to invalid IL or missing references)
//IL_0274: Unknown result type (might be due to invalid IL or missing references)
//IL_0295: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("KookooCanvas");
Canvas val2 = val.AddComponent<Canvas>();
val2.renderMode = (RenderMode)0;
val2.sortingOrder = 100;
Object.DontDestroyOnLoad((Object)(object)val);
deathCanvas = new GameObject("KookooDeathCanvas");
Canvas val3 = deathCanvas.AddComponent<Canvas>();
val3.renderMode = (RenderMode)0;
val3.sortingOrder = 200;
Object.DontDestroyOnLoad((Object)(object)deathCanvas);
deathCanvas.SetActive(false);
rootObject = new GameObject("KookooRoot");
rootObject.transform.SetParent(val.transform, false);
RectTransform val4 = rootObject.AddComponent<RectTransform>();
val4.anchorMin = new Vector2(0.5f, 0.5f);
val4.anchorMax = new Vector2(0.5f, 0.5f);
val4.sizeDelta = new Vector2(800f, 800f);
val4.anchoredPosition = Vector2.zero;
GameObject val5 = new GameObject("KookooParticles");
val5.transform.SetParent(rootObject.transform, false);
particlesImage = val5.AddComponent<Image>();
if ((Object)(object)particlesSprite != (Object)null)
{
particlesImage.sprite = particlesSprite;
}
RectTransform component = val5.GetComponent<RectTransform>();
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.sizeDelta = Vector2.zero;
val5.SetActive(false);
GameObject val6 = new GameObject("KookooBody");
val6.transform.SetParent(rootObject.transform, false);
bodyImage = val6.AddComponent<Image>();
RectTransform component2 = val6.GetComponent<RectTransform>();
component2.anchorMin = Vector2.zero;
component2.anchorMax = Vector2.one;
component2.sizeDelta = Vector2.zero;
for (int i = 0; i < 2; i++)
{
GameObject val7 = new GameObject($"KookooDigit_{i}");
val7.transform.SetParent(rootObject.transform, false);
digitImages[i] = val7.AddComponent<Image>();
RectTransform component3 = val7.GetComponent<RectTransform>();
component3.anchorMin = new Vector2(0.5f, 0.5f);
component3.anchorMax = new Vector2(0.5f, 0.5f);
component3.sizeDelta = new Vector2(800f, 800f);
component3.anchoredPosition = new Vector2(((float)i - 0.5f) * 35f, 0f);
val7.SetActive(false);
}
rootObject.SetActive(false);
}
}
[HarmonyPatch(typeof(PlayerControllerB), "Start")]
public class KookooPatch
{
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<KookooBehaviour>() != (Object)null))
{
int num = Random.Range(0, 100);
if (num >= Plugin.SpawnChance.Value)
{
Debug.Log((object)$"Kookoo Entity: не появится в этом раунде (бросок {num})");
return;
}
GameObject val = new GameObject("KookooManager");
val.AddComponent<KookooBehaviour>();
Object.DontDestroyOnLoad((Object)(object)val);
Debug.Log((object)"Kookoo Entity: менеджер создан!");
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "SpawnDeadAnimation")]
public class KookooRespawnPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
KookooBehaviour kookooBehaviour = Object.FindObjectOfType<KookooBehaviour>();
if ((Object)(object)kookooBehaviour != (Object)null)
{
kookooBehaviour.ResetKookoo();
}
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
public class KookooDeathPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
KookooBehaviour kookooBehaviour = Object.FindObjectOfType<KookooBehaviour>();
if ((Object)(object)kookooBehaviour != (Object)null)
{
kookooBehaviour.ForceHide();
}
}
}
}
[HarmonyPatch(typeof(HUDManager), "CanPlayerScan")]
public class TestPatch
{
private static void Postfix()
{
Debug.Log((object)"MethodFinder: CanPlayerScan!");
}
}
public class KookooScanPatch
{
public static void OnScan()
{
Debug.Log((object)"Kookoo: скан нажат!");
KookooBehaviour kookooBehaviour = Object.FindObjectOfType<KookooBehaviour>();
if ((Object)(object)kookooBehaviour != (Object)null)
{
kookooBehaviour.TryTriggerScanAttack();
}
}
}
[BepInPlugin("com.yourname.kookooentity", "Kookoo Entity", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
private readonly Harmony harmony = new Harmony("com.yourname.kookooentity");
public static ConfigEntry<int> SpawnChance;
public static ConfigEntry<float> MinSpawnTime;
public static ConfigEntry<float> MaxSpawnTime;
public static ConfigEntry<float> CooldownTime;
public static ConfigEntry<float> AppearDuration;
public static ConfigEntry<float> CountInterval;
public static ConfigEntry<int> ScanAttackChance;
public static ConfigEntry<float> ScanBlockDuration;
private void Awake()
{
SpawnChance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpawnChance", 50, "Chance of Kookoo appearing each round (0-100)");
MinSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinSpawnTime", 30f, "Minimum time before Kookoo appears");
MaxSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxSpawnTime", 90f, "Maximum time before Kookoo appears");
CooldownTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CooldownTime", 60f, "Cooldown before Kookoo can appear again");
AppearDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "AppearDuration", 2f, "Time Kookoo shows the number before counting");
CountInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CountInterval", 1f, "Time between each count");
ScanAttackChance = ((BaseUnityPlugin)this).Config.Bind<int>("Scan", "ScanAttackChance", 5, "1 in X chance of Kookoo appearing on scan");
ScanBlockDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Scan", "ScanBlockDuration", 30f, "How long scan is blocked after Kookoo attack");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Kookoo Entity loaded!");
harmony.PatchAll();
}
}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 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("e9825634-eec7-4ae6-80c6-e82bbadddd91")]
[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 LitanyState currentState = LitanyState.Hidden;
private float timer = 0f;
private float nextAppearTime = 0f;
private int currentEye = 0;
private bool isOnCooldown = false;
private float cooldownTimer = 0f;
private float cooldownTime = 0f;
private GameObject rootObject;
private Image bodyImage;
private Image eyeImage;
private Sprite bodySprite;
private Sprite eyesClosedSprite;
private Sprite[] eyeWarningSprites = (Sprite[])(object)new Sprite[3];
private Sprite[] eyeOpenedSprites = (Sprite[])(object)new Sprite[3];
private float shakeTimer = 0f;
private float eyeAnimTimer = 0f;
private int eyeAnimFrame = 0;
private Vector2 basePosition;
private AudioSource idleSource;
private AudioSource screamsSource;
private AudioSource oneShotSource;
private AudioClip idleClip;
private AudioClip prepareClip;
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 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 LoadSprites()
{
bodySprite = 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 void LoadAudio()
{
idleClip = LoadAudioClip("idle.mp3");
prepareClip = 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 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()
{
//IL_0290: Unknown result type (might be due to invalid IL or missing references)
//IL_02be: Unknown result type (might be due to invalid IL or missing references)
//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return;
}
if (currentState != 0)
{
AnimateBody();
}
if (val.isPlayerDead)
{
ForceHide();
}
else 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 (!IsPlayerInGame())
{
return;
}
if (val.quickMenuManager.isMenuOpen)
{
rootObject.SetActive(false);
return;
}
if (currentState != 0 && currentState != LitanyState.Screamer)
{
rootObject.SetActive(true);
}
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:
if (videoFinished)
{
((Graphic)videoImage).color = new Color(1f, 1f, 1f, 0f);
PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val2 != (Object)null)
{
val2.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 1, default(Vector3), false);
}
Hide();
}
break;
}
}
}
private void AnimateBody()
{
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
shakeTimer += Time.deltaTime;
if (shakeTimer >= Plugin.ShakeSpeed.Value)
{
shakeTimer = 0f;
float num = Random.Range(-8f, 8f);
float num2 = Random.Range(-8f, 8f);
float num3 = Random.Range(-5f, 5f);
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_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_0053: 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);
basePosition = new Vector2(num, num2);
rootObject.GetComponent<RectTransform>().anchoredPosition = basePosition;
rootObject.SetActive(true);
if ((Object)(object)bodySprite != (Object)null)
{
bodyImage.sprite = bodySprite;
}
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)prepareClip != (Object)null)
{
oneShotSource.Stop();
oneShotSource.clip = prepareClip;
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_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: 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);
basePosition = new Vector2(num, num2);
rootObject.GetComponent<RectTransform>().anchoredPosition = basePosition;
currentState = LitanyState.BetweenEyes;
timer = 0f;
}
private void TriggerDeath()
{
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
currentState = LitanyState.Screamer;
videoFinished = false;
rootObject.SetActive(false);
idleSource.Stop();
screamsSource.Stop();
oneShotSource.Stop();
string text = Path.Combine(modFolder, "Death.mp4");
videoPlayer.url = "file://" + text;
videoPlayer.Play();
((Graphic)videoImage).color = new Color(1f, 1f, 1f, 1f);
}
private void OnVideoFinished(VideoPlayer vp)
{
videoFinished = true;
}
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();
}
public void ForceHide()
{
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
currentState = LitanyState.Hidden;
timer = 0f;
rootObject.SetActive(false);
isOnCooldown = false;
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
idleSource.Stop();
screamsSource.Stop();
oneShotSource.Stop();
if ((Object)(object)videoPlayer != (Object)null)
{
videoPlayer.Stop();
}
if ((Object)(object)videoImage != (Object)null)
{
((Graphic)videoImage).color = new Color(1f, 1f, 1f, 0f);
}
}
public void ResetLitany()
{
currentState = LitanyState.Hidden;
timer = 0f;
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
rootObject.SetActive(false);
isOnCooldown = false;
cooldownTimer = 0f;
idleSource.Stop();
screamsSource.Stop();
oneShotSource.Stop();
}
public void ForceAppear()
{
if (currentState == LitanyState.Hidden)
{
isOnCooldown = false;
timer = nextAppearTime + 1f;
}
}
private bool IsPlayerCrouching()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return true;
}
return val.isCrouching;
}
private bool IsPlayerInGame()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.isPlayerDead)
{
return false;
}
if (StartOfRound.Instance?.currentLevel?.sceneName == "CompanyBuilding")
{
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_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_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Expected O, but got Unknown
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: 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)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Expected O, but got Unknown
//IL_0153: Unknown result type (might be due to invalid IL or missing references)
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
//IL_0184: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Expected O, but got Unknown
//IL_0193: Unknown result type (might be due to invalid IL or missing references)
//IL_019a: Expected O, but got Unknown
//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
//IL_01fe: Expected O, but got Unknown
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
//IL_020b: Expected O, but got Unknown
//IL_0258: Unknown result type (might be due to invalid IL or missing references)
//IL_026e: Unknown result type (might be due to invalid IL or missing references)
//IL_027b: Unknown result type (might be due to invalid IL or missing references)
//IL_0288: 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;
basePosition = 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 = Vector2.zero;
component2.anchorMax = Vector2.one;
component2.sizeDelta = Vector2.zero;
videoTexture = new RenderTexture(1920, 1080, 0);
GameObject val6 = new GameObject("LitanyVideoPlayer");
val6.transform.SetParent(val.transform, false);
videoPlayer = val6.AddComponent<VideoPlayer>();
videoPlayer.playOnAwake = false;
videoPlayer.renderMode = (VideoRenderMode)2;
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);
}
}
public class LitanyDomain : MonoBehaviour
{
private enum DomainState
{
Inactive,
Active
}
private DomainState currentState = DomainState.Inactive;
private float eventTimer = 0f;
private float eventDuration = 278f;
private float commandTimer = 0f;
private float commandInterval = 5f;
private float commandDuration = 2f;
private float commandActiveTimer = 0f;
private bool commandActive = false;
private bool isJumpCommand = false;
private bool playerReacted = false;
private bool playerViolated = false;
private float scheduleTimer = 0f;
private float scheduledTime = 0f;
private bool scheduled = false;
private float litanyTimer = 0f;
private float litanyDelay = 65f;
private bool litanyPhaseStarted = false;
private GameObject commandObject;
private Image commandImage;
private Sprite jumpSprite;
private Sprite dontJumpSprite;
private AudioSource musicSource;
private AudioSource orderBGSource;
private AudioSource orderSource;
private AudioClip musicClip;
private AudioClip orderBGClip;
private AudioClip[] orderClips = (AudioClip[])(object)new AudioClip[4];
private float textShakeTimer = 0f;
private float textShakeSpeed = 0.05f;
private Vector2 textBasePosition = new Vector2(0f, -120f);
private bool orderPlayedFinal = false;
private float previousFallValue = -7f;
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];
}
LoadAssets();
CreateUI();
}
private void LoadAssets()
{
jumpSprite = LoadSprite("jump.png");
dontJumpSprite = LoadSprite("dont_jump.png");
musicClip = LoadAudioClip("OWYH.mp3");
orderBGClip = LoadAudioClip("orderBG.mp3");
for (int i = 0; i < 4; i++)
{
orderClips[i] = LoadAudioClip($"order{i + 1}.mp3");
}
musicSource = ((Component)this).gameObject.AddComponent<AudioSource>();
musicSource.loop = false;
musicSource.playOnAwake = false;
if ((Object)(object)musicClip != (Object)null)
{
musicSource.clip = musicClip;
}
orderBGSource = ((Component)this).gameObject.AddComponent<AudioSource>();
orderBGSource.loop = false;
orderBGSource.playOnAwake = false;
orderSource = ((Component)this).gameObject.AddComponent<AudioSource>();
orderSource.loop = false;
orderSource.playOnAwake = false;
}
private Sprite LoadSprite(string filename)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
string path = Path.Combine(modFolder, filename);
if (!File.Exists(path))
{
return null;
}
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));
}
private AudioClip LoadAudioClip(string filename)
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Expected O, but got Unknown
string text = Path.Combine(modFolder, filename);
if (!File.Exists(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 Update()
{
if (scheduled && currentState == DomainState.Inactive)
{
scheduleTimer += Time.deltaTime;
if (scheduleTimer >= scheduledTime)
{
scheduled = false;
StartEvent();
}
}
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return;
}
if ((Object)(object)StartOfRound.Instance == (Object)null || !StartOfRound.Instance.shipHasLanded)
{
if (currentState == DomainState.Active)
{
EndEvent();
}
}
else if (val.isInHangarShipRoom)
{
if (commandActive)
{
HideCommand();
}
}
else if (val.isPlayerDead)
{
if (commandActive)
{
HideCommand();
}
}
else
{
if (currentState == DomainState.Inactive)
{
return;
}
eventTimer += Time.deltaTime;
if (eventTimer >= eventDuration)
{
EndEvent();
return;
}
if (!litanyPhaseStarted && eventTimer >= litanyDelay)
{
litanyPhaseStarted = true;
TriggerLitany();
}
if (litanyPhaseStarted)
{
litanyTimer += Time.deltaTime;
if (litanyTimer >= Random.Range(15f, 20f))
{
litanyTimer = 0f;
TriggerLitany();
}
}
if (!commandActive)
{
commandTimer += Time.deltaTime;
if (commandTimer >= commandInterval)
{
commandTimer = 0f;
ShowCommand();
}
return;
}
ShakeCommand();
commandActiveTimer += Time.deltaTime;
if (!orderPlayedFinal && commandActiveTimer >= commandDuration - 3f)
{
orderPlayedFinal = true;
int num = Random.Range(0, 4);
if ((Object)(object)orderClips[num] != (Object)null)
{
orderSource.clip = orderClips[num];
orderSource.Play();
}
}
if (!playerReacted)
{
bool flag = IngamePlayerSettings.Instance.playerInput.actions["Jump"].WasPressedThisFrame();
if (isJumpCommand && flag)
{
playerReacted = true;
}
if (!isJumpCommand && flag)
{
playerViolated = true;
}
}
if (commandActiveTimer >= commandDuration)
{
if (isJumpCommand && !playerReacted)
{
DamagePlayer(val);
}
else if (!isJumpCommand && playerViolated)
{
DamagePlayer(val);
}
HideCommand();
}
}
}
private void ShakeCommand()
{
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
textShakeTimer += Time.deltaTime;
if (textShakeTimer >= textShakeSpeed)
{
textShakeTimer = 0f;
float num = Random.Range(-5f, 5f);
float num2 = Random.Range(-5f, 5f);
commandObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(textBasePosition.x + num, textBasePosition.y + num2);
}
}
private void TriggerLitany()
{
LitanyBehaviour litanyBehaviour = Object.FindObjectOfType<LitanyBehaviour>();
if ((Object)(object)litanyBehaviour != (Object)null)
{
litanyBehaviour.ForceAppear();
}
}
private void ShowCommand()
{
isJumpCommand = Random.Range(0, 2) == 0;
commandActive = true;
commandActiveTimer = 0f;
playerReacted = false;
playerViolated = false;
orderPlayedFinal = false;
commandImage.sprite = (isJumpCommand ? jumpSprite : dontJumpSprite);
commandObject.SetActive(true);
if ((Object)(object)orderBGClip != (Object)null)
{
orderBGSource.clip = orderBGClip;
orderBGSource.time = Random.Range(0f, orderBGClip.length * 0.7f);
orderBGSource.Play();
}
}
private void HideCommand()
{
commandActive = false;
commandActiveTimer = 0f;
orderPlayedFinal = false;
commandObject.SetActive(false);
orderBGSource.Stop();
}
private void DamagePlayer(PlayerControllerB player)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
player.DamagePlayer(20, true, true, (CauseOfDeath)0, 0, false, default(Vector3));
}
public void StartEvent()
{
currentState = DomainState.Active;
eventTimer = 0f;
commandTimer = 0f;
litanyTimer = 0f;
litanyPhaseStarted = false;
musicSource.Play();
Debug.Log((object)"Litany Domain: событие началось!");
}
private void EndEvent()
{
currentState = DomainState.Inactive;
HideCommand();
Debug.Log((object)"Litany Domain: событие закончилось!");
}
public void ScheduleEvent(float delay)
{
scheduledTime = delay;
scheduled = true;
scheduleTimer = 0f;
}
private void CreateUI()
{
//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_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("DomainCanvas");
Canvas val2 = val.AddComponent<Canvas>();
val2.renderMode = (RenderMode)0;
val2.sortingOrder = 99;
Object.DontDestroyOnLoad((Object)(object)val);
commandObject = new GameObject("DomainCommand");
commandObject.transform.SetParent(val.transform, false);
commandImage = commandObject.AddComponent<Image>();
RectTransform component = commandObject.GetComponent<RectTransform>();
component.anchorMin = new Vector2(0.5f, 1f);
component.anchorMax = new Vector2(0.5f, 1f);
component.sizeDelta = new Vector2(300f, 100f);
component.anchoredPosition = textBasePosition;
commandObject.SetActive(false);
}
}
public class LitanyDomainNetwork : NetworkBehaviour
{
[CompilerGenerated]
private sealed class <ScheduleCoroutine>d__4 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public float delay;
public LitanyDomainNetwork <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <ScheduleCoroutine>d__4(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(delay);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
<>4__this.StartDomainEventClientRpc();
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
public static LitanyDomainNetwork Instance;
private void Awake()
{
Instance = this;
}
[ClientRpc]
public void StartDomainEventClientRpc()
{
Debug.Log((object)"Litany Domain: получена команда начать событие!");
LitanyDomain litanyDomain = Object.FindObjectOfType<LitanyDomain>();
if ((Object)(object)litanyDomain != (Object)null)
{
litanyDomain.StartEvent();
}
}
public void ScheduleEventForAll(float delay)
{
((MonoBehaviour)this).StartCoroutine(ScheduleCoroutine(delay));
}
[IteratorStateMachine(typeof(<ScheduleCoroutine>d__4))]
private IEnumerator ScheduleCoroutine(float delay)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <ScheduleCoroutine>d__4(0)
{
<>4__this = this,
delay = delay
};
}
}
[HarmonyPatch(typeof(RoundManager), "FinishGeneratingNewLevelClientRpc")]
public class LitanyDomainPatch
{
public static bool eventHappenedThisRound;
private static void Postfix()
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Expected O, but got Unknown
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Expected O, but got Unknown
if (eventHappenedThisRound || (Object)(object)GameObject.Find("LitanyDomainManager") != (Object)null)
{
return;
}
GameObject val = new GameObject("LitanyDomainManager");
val.AddComponent<LitanyDomain>();
Object.DontDestroyOnLoad((Object)(object)val);
if (GameNetworkManager.Instance.isHostingGame)
{
int num = Random.Range(0, 100);
if (num >= Plugin.DomainChance.Value)
{
Debug.Log((object)"Litany Domain: событие не выпало");
return;
}
eventHappenedThisRound = true;
GameObject val2 = new GameObject("LitanyDomainNetworkManager");
LitanyDomainNetwork litanyDomainNetwork = val2.AddComponent<LitanyDomainNetwork>();
Object.DontDestroyOnLoad((Object)(object)val2);
float num2 = Random.Range(Plugin.DomainMinDelay.Value, Plugin.DomainMaxDelay.Value);
litanyDomainNetwork.ScheduleEventForAll(num2);
Debug.Log((object)$"Litany Domain: событие запланировано через {num2} секунд!");
}
}
}
[HarmonyPatch(typeof(RoundManager), "UnloadSceneObjectsEarly")]
public class LitanyDomainCleanupPatch
{
private static void Postfix()
{
LitanyDomainPatch.eventHappenedThisRound = false;
GameObject val = GameObject.Find("LitanyDomainManager");
if ((Object)(object)val != (Object)null)
{
Object.Destroy((Object)(object)val);
}
GameObject val2 = GameObject.Find("LitanyDomainNetworkManager");
if ((Object)(object)val2 != (Object)null)
{
Object.Destroy((Object)(object)val2);
}
}
}
[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), "KillPlayer")]
public class LitanyDeathPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
LitanyBehaviour litanyBehaviour = Object.FindObjectOfType<LitanyBehaviour>();
if ((Object)(object)litanyBehaviour != (Object)null)
{
litanyBehaviour.ForceHide();
}
}
}
}
[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> MinSpawnTime;
public static ConfigEntry<float> MaxSpawnTime;
public static ConfigEntry<float> PrepareDuration;
public static ConfigEntry<float> WarningDuration;
public static ConfigEntry<float> OpenEyeDuration;
public static ConfigEntry<float> BetweenEyesDuration;
public static ConfigEntry<float> CooldownTime;
public static ConfigEntry<float> ShakeSpeed;
public static ConfigEntry<int> DomainChance;
public static ConfigEntry<float> DomainMinDelay;
public static ConfigEntry<float> DomainMaxDelay;
private void Awake()
{
SpawnChance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpawnChance", 35, "Chance of Litany appearing each round (0-100)");
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");
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");
CooldownTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CooldownTime", 60f, "Cooldown time in seconds before Litany can appear again");
ShakeSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "ShakeSpeed", 0.05f, "Speed of Litany shaking (lower = faster)");
DomainChance = ((BaseUnityPlugin)this).Config.Bind<int>("Domain", "DomainChance", 50, "Chance of Litany Domain event (0-100)");
DomainMinDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Domain", "DomainMinDelay", 30f, "Minimum time before event starts");
DomainMaxDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Domain", "DomainMaxDelay", 60f, "Maximum time before event starts");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Litany Entity loaded!");
harmony.PatchAll();
}
}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("SeesayEntity")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SeesayEntity")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e798d498-5a9e-440e-8887-01ec34d10e37")]
[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 SeesayEntity;
[BepInPlugin("com.yourname.seesayentity", "Seesay Entity", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
private readonly Harmony harmony = new Harmony("com.yourname.seesayentity");
public static ConfigEntry<int> SpawnChance;
public static ConfigEntry<float> MinSpawnTime;
public static ConfigEntry<float> MaxSpawnTime;
public static ConfigEntry<float> SeesaySpeed;
public static ConfigEntry<float> CooldownTime;
public static ConfigEntry<bool> EnableTrainSound;
public static ConfigEntry<float> MinFollowTime;
public static ConfigEntry<float> MaxFollowTime;
private void Awake()
{
SpawnChance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpawnChance", 50, "Chance of Seesay appearing each round (0-100)");
MinSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinSpawnTime", 30f, "Minimum time before Seesay appears");
MaxSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxSpawnTime", 90f, "Maximum time before Seesay appears");
SeesaySpeed = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SeesaySpeed", 5f, "Speed of Seesay flying towards player");
CooldownTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CooldownTime", 60f, "Cooldown time in seconds before Seesay can appear again");
MinFollowTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinFollowTime", 3f, "Minimum time before Seesay opens its eye");
MaxFollowTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxFollowTime", 8f, "Maximum time before Seesay opens its eye");
EnableTrainSound = ((BaseUnityPlugin)this).Config.Bind<bool>("Fun", "EnableTrainSound", true, "Don't");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Seesay Entity loaded!");
harmony.PatchAll();
}
}
public class SeesayBehaviour : MonoBehaviour
{
private enum SeesayState
{
Hidden,
Following,
Opening,
Flying,
Screamer
}
private VideoPlayer videoPlayer;
private RawImage videoImage;
private RenderTexture videoTexture;
private bool videoFinished = false;
private SeesayState currentState = SeesayState.Hidden;
private float timer = 0f;
private float nextAppearTime = 0f;
private float followDuration = 5f;
private bool isOnCooldown = false;
private float cooldownTimer = 0f;
private float cooldownTime = 0f;
private AudioSource audioSource;
private AudioClip appearClip;
private AudioClip open1Clip;
private AudioClip open2Clip;
private AudioClip attackClip;
private AudioClip flashedClip;
private AudioSource flyingAudioSource;
private AudioClip flyingClip;
private AudioClip trainClip;
private AudioSource trainAudioSource;
private GameObject seesayObject;
private SpriteRenderer spriteRenderer;
private Camera mainCamera;
private Sprite closedSprite;
private Sprite[] animSprites = (Sprite[])(object)new Sprite[10];
private Sprite flashedSprite;
private float animTimer = 0f;
private int animFrame = 0;
private bool animPaused = false;
private float animPauseTimer = 0f;
private bool isFlashed = false;
private float flashedTimer = 0f;
private float distanceFromCamera = 3f;
private Vector3 frozenWorldPos = Vector3.zero;
private float shakeTimer = 0f;
private float shakeSpeed = 0.05f;
private Vector3 shakeOffset = Vector3.zero;
private string modFolder;
private void Start()
{
string[] directories = Directory.GetDirectories(Paths.PluginPath, "SeesayEntity", SearchOption.AllDirectories);
modFolder = ((directories.Length != 0) ? directories[0] : Path.Combine(Paths.PluginPath, "SeesayEntity"));
LoadSprites();
CreateSeesay3D();
audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
audioSource.loop = false;
audioSource.playOnAwake = false;
audioSource.volume = 10f;
audioSource.spatialBlend = 0f;
flyingClip = LoadAudioClip("Flying.mp3");
flyingAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
flyingAudioSource.loop = true;
flyingAudioSource.playOnAwake = false;
flyingAudioSource.volume = 1f;
flyingAudioSource.spatialBlend = 1f;
flyingAudioSource.maxDistance = 20f;
flyingAudioSource.rolloffMode = (AudioRolloffMode)1;
trainClip = LoadAudioClip("Train.mp3");
trainAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
trainAudioSource.loop = true;
trainAudioSource.playOnAwake = false;
trainAudioSource.volume = 1f;
trainAudioSource.spatialBlend = 1f;
trainAudioSource.maxDistance = 15f;
trainAudioSource.rolloffMode = (AudioRolloffMode)1;
appearClip = LoadAudioClip("Appear.mp3");
open1Clip = LoadAudioClip("Open1.mp3");
open2Clip = LoadAudioClip("Open2.mp3");
attackClip = LoadAudioClip("Attack.mp3");
flashedClip = LoadAudioClip("Flashed.mp3");
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
}
private void CreateSeesay3D()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
//IL_004a: 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_006d: Expected O, but got Unknown
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Expected O, but got Unknown
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Expected O, but got Unknown
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Expected O, but got Unknown
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Expected O, but got Unknown
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
//IL_018b: Unknown result type (might be due to invalid IL or missing references)
//IL_0198: Unknown result type (might be due to invalid IL or missing references)
seesayObject = new GameObject("SeesaySprite");
spriteRenderer = seesayObject.AddComponent<SpriteRenderer>();
((Renderer)spriteRenderer).sortingOrder = 100;
seesayObject.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
seesayObject.SetActive(false);
GameObject val = new GameObject("SeesayCanvas");
Canvas val2 = val.AddComponent<Canvas>();
val2.renderMode = (RenderMode)0;
val2.sortingOrder = 200;
Object.DontDestroyOnLoad((Object)(object)val);
videoTexture = new RenderTexture(1920, 1080, 0);
GameObject val3 = new GameObject("SeesayVideoPlayer");
val3.transform.SetParent(val.transform, false);
videoPlayer = val3.AddComponent<VideoPlayer>();
videoPlayer.playOnAwake = false;
videoPlayer.renderMode = (VideoRenderMode)2;
videoPlayer.targetTexture = videoTexture;
videoPlayer.loopPointReached += new EventHandler(OnVideoFinished);
GameObject val4 = new GameObject("SeesayVideo");
val4.transform.SetParent(val.transform, false);
videoImage = val4.AddComponent<RawImage>();
videoImage.texture = (Texture)(object)videoTexture;
((Graphic)videoImage).color = new Color(1f, 1f, 1f, 0f);
RectTransform component = val4.GetComponent<RectTransform>();
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.sizeDelta = Vector2.zero;
}
private void OnVideoFinished(VideoPlayer vp)
{
videoFinished = true;
}
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)("Seesay 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()
{
closedSprite = LoadSprite("closed.png");
flashedSprite = LoadSprite("flashed.png");
for (int i = 0; i < 10; i++)
{
animSprites[i] = LoadSprite($"anim{i + 1}.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)("Seesay Entity: файл не найден — " + 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()
{
//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_028b: Unknown result type (might be due to invalid IL or missing references)
//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return;
}
if ((Object)(object)mainCamera == (Object)null)
{
Camera[] allCameras = Camera.allCameras;
foreach (Camera val2 in allCameras)
{
if (((Behaviour)val2).isActiveAndEnabled && ((Object)val2).name == "MainCamera")
{
mainCamera = val2;
break;
}
}
}
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 SeesayState.Hidden:
if (timer >= nextAppearTime)
{
Appear(val);
}
break;
case SeesayState.Following:
UpdateFollowingPosition(val);
if (timer >= followDuration)
{
StartOpening();
}
break;
case SeesayState.Opening:
seesayObject.transform.position = frozenWorldPos + shakeOffset;
if ((Object)(object)mainCamera != (Object)null)
{
seesayObject.transform.rotation = ((Component)mainCamera).transform.rotation;
}
AnimateOpening(val);
break;
case SeesayState.Flying:
if (isFlashed)
{
flashedTimer += Time.deltaTime;
if (flashedTimer >= 0.5f)
{
Hide();
}
return;
}
FlyTowardsPlayer(val);
break;
case SeesayState.Screamer:
if (videoFinished)
{
((Graphic)videoImage).color = new Color(1f, 1f, 1f, 0f);
PlayerControllerB val3 = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val3 != (Object)null)
{
val3.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 1, default(Vector3), false);
}
Hide();
}
break;
}
if (currentState == SeesayState.Following || currentState == SeesayState.Opening)
{
UpdateShake();
}
}
}
private void UpdateFollowingPosition(PlayerControllerB player)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: 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_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)mainCamera == (Object)null))
{
Vector3 val = ((Component)mainCamera).transform.position + ((Component)mainCamera).transform.forward * distanceFromCamera;
seesayObject.transform.position = val + shakeOffset;
seesayObject.transform.rotation = ((Component)mainCamera).transform.rotation;
}
}
private void UpdateShake()
{
//IL_0058: 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)
shakeTimer += Time.deltaTime;
if (shakeTimer >= shakeSpeed)
{
shakeTimer = 0f;
shakeOffset = new Vector3(Random.Range(-0.05f, 0.05f), Random.Range(-0.05f, 0.05f), 0f);
}
}
private void StartOpening()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: 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)
currentState = SeesayState.Opening;
timer = 0f;
animFrame = 0;
animTimer = 0f;
animPaused = false;
if ((Object)(object)mainCamera != (Object)null)
{
frozenWorldPos = ((Component)mainCamera).transform.position + ((Component)mainCamera).transform.forward * distanceFromCamera;
}
if ((Object)(object)animSprites[0] != (Object)null)
{
spriteRenderer.sprite = animSprites[0];
}
}
private void AnimateOpening(PlayerControllerB player)
{
if (animPaused)
{
animPauseTimer += Time.deltaTime;
if (animPauseTimer >= 0.2f)
{
animPaused = false;
animPauseTimer = 0f;
if ((Object)(object)open2Clip != (Object)null)
{
audioSource.clip = open2Clip;
audioSource.Play();
}
}
return;
}
animTimer += Time.deltaTime;
if (animTimer >= 0.0667f)
{
animTimer = 0f;
animFrame++;
if (animFrame == 1 && (Object)(object)open1Clip != (Object)null)
{
audioSource.clip = open1Clip;
audioSource.Play();
}
if (animFrame == 3)
{
animPaused = true;
spriteRenderer.sprite = animSprites[3];
}
else if (animFrame >= 10)
{
StartFlying(player);
}
else if ((Object)(object)animSprites[animFrame] != (Object)null)
{
spriteRenderer.sprite = animSprites[animFrame];
}
}
}
private void StartFlying(PlayerControllerB player)
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
currentState = SeesayState.Flying;
timer = 0f;
if ((Object)(object)attackClip != (Object)null)
{
audioSource.clip = attackClip;
audioSource.Play();
}
if ((Object)(object)flyingClip != (Object)null)
{
((Component)flyingAudioSource).transform.position = seesayObject.transform.position;
flyingAudioSource.clip = flyingClip;
flyingAudioSource.Play();
}
}
private void FlyTowardsPlayer(PlayerControllerB player)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: 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_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)mainCamera == (Object)null)
{
return;
}
Vector3 position = ((Component)mainCamera).transform.position;
seesayObject.transform.position = Vector3.MoveTowards(seesayObject.transform.position, position, Plugin.SeesaySpeed.Value * Time.deltaTime);
seesayObject.transform.rotation = ((Component)mainCamera).transform.rotation;
if (IsFlashlightAimingAtSeesay(player))
{
TriggerFlashed();
return;
}
float num = Vector3.Distance(seesayObject.transform.position, position);
if (Plugin.EnableTrainSound.Value && num < 10f)
{
if ((Object)(object)trainClip != (Object)null && !trainAudioSource.isPlaying)
{
((Component)trainAudioSource).transform.position = seesayObject.transform.position;
trainAudioSource.clip = trainClip;
trainAudioSource.Play();
}
}
else if (trainAudioSource.isPlaying)
{
trainAudioSource.Stop();
}
((Component)trainAudioSource).transform.position = seesayObject.transform.position;
if (num < 0.5f)
{
TriggerScreamer();
}
((Component)flyingAudioSource).transform.position = seesayObject.transform.position;
}
private void TriggerScreamer()
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
currentState = SeesayState.Screamer;
videoFinished = false;
seesayObject.SetActive(false);
string text = Path.Combine(modFolder, "Death.mp4");
videoPlayer.url = "file://" + text;
videoPlayer.Play();
((Graphic)videoImage).color = new Color(1f, 1f, 1f, 1f);
flyingAudioSource.Stop();
trainAudioSource.Stop();
}
private void TriggerFlashed()
{
if ((Object)(object)flashedClip != (Object)null)
{
audioSource.clip = flashedClip;
audioSource.Play();
}
isFlashed = true;
flashedTimer = 0f;
if ((Object)(object)flashedSprite != (Object)null)
{
spriteRenderer.sprite = flashedSprite;
}
flyingAudioSource.Stop();
}
private bool CheckFlashlightCondition(PlayerControllerB player)
{
GrabbableObject[] itemSlots = player.ItemSlots;
foreach (GrabbableObject val in itemSlots)
{
if ((Object)(object)val != (Object)null && val is FlashlightItem)
{
return true;
}
}
if ((Object)(object)player.currentlyHeldObjectServer != (Object)null && player.currentlyHeldObjectServer is FlashlightItem)
{
return true;
}
return false;
}
private bool IsFlashlightAimingAtSeesay(PlayerControllerB player)
{
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: 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_0133: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
GrabbableObject[] itemSlots = player.ItemSlots;
foreach (GrabbableObject val in itemSlots)
{
if (!((Object)(object)val != (Object)null))
{
continue;
}
FlashlightItem val2 = (FlashlightItem)(object)((val is FlashlightItem) ? val : null);
if (val2 != null && ((GrabbableObject)val2).isBeingUsed)
{
if ((Object)(object)mainCamera == (Object)null)
{
return false;
}
Vector3 val3 = mainCamera.WorldToViewportPoint(seesayObject.transform.position);
if (val3.z > 0f && val3.x > 0.2f && val3.x < 0.8f && val3.y > 0.2f && val3.y < 0.8f)
{
return true;
}
}
}
if ((Object)(object)player.currentlyHeldObjectServer != (Object)null)
{
GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer;
FlashlightItem val4 = (FlashlightItem)(object)((currentlyHeldObjectServer is FlashlightItem) ? currentlyHeldObjectServer : null);
if (val4 != null && ((GrabbableObject)val4).isBeingUsed)
{
if ((Object)(object)mainCamera == (Object)null)
{
return false;
}
Vector3 val5 = mainCamera.WorldToViewportPoint(seesayObject.transform.position);
if (val5.z > 0f && val5.x > 0.2f && val5.x < 0.8f && val5.y > 0.2f && val5.y < 0.8f)
{
return true;
}
}
}
return false;
}
private void Appear(PlayerControllerB player)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
currentState = SeesayState.Following;
timer = 0f;
seesayObject.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
followDuration = Random.Range(Plugin.MinFollowTime.Value, Plugin.MaxFollowTime.Value);
if ((Object)(object)appearClip != (Object)null)
{
audioSource.clip = appearClip;
audioSource.Play();
}
seesayObject.SetActive(true);
if ((Object)(object)closedSprite != (Object)null)
{
spriteRenderer.sprite = closedSprite;
}
}
private void Hide()
{
currentState = SeesayState.Hidden;
timer = 0f;
seesayObject.SetActive(false);
isFlashed = false;
isOnCooldown = true;
cooldownTimer = 0f;
cooldownTime = Plugin.CooldownTime.Value;
flyingAudioSource.Stop();
trainAudioSource.Stop();
}
public void ForceHide()
{
currentState = SeesayState.Hidden;
timer = 0f;
seesayObject.SetActive(false);
isOnCooldown = false;
}
public void ResetSeesay()
{
currentState = SeesayState.Hidden;
timer = 0f;
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
seesayObject.SetActive(false);
isOnCooldown = false;
cooldownTimer = 0f;
}
private bool IsPlayerInGame()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.isPlayerDead)
{
return false;
}
if (StartOfRound.Instance?.currentLevel?.sceneName == "CompanyBuilding")
{
return false;
}
if ((Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.timesFulfilledQuota < 1)
{
return false;
}
return !val.isInHangarShipRoom && (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded;
}
}
[HarmonyPatch(typeof(PlayerControllerB), "Start")]
public class SeesayPatch
{
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<SeesayBehaviour>() != (Object)null))
{
int num = Random.Range(0, 100);
if (num >= Plugin.SpawnChance.Value)
{
Debug.Log((object)$"Seesay Entity: не появится в этом раунде (бросок {num})");
return;
}
GameObject val = new GameObject("SeesayManager");
val.AddComponent<SeesayBehaviour>();
Object.DontDestroyOnLoad((Object)(object)val);
Debug.Log((object)"Seesay Entity: менеджер создан!");
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
public class SeesayDeathPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
SeesayBehaviour seesayBehaviour = Object.FindObjectOfType<SeesayBehaviour>();
if ((Object)(object)seesayBehaviour != (Object)null)
{
seesayBehaviour.ForceHide();
}
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "SpawnDeadAnimation")]
public class SeesayRespawnPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
SeesayBehaviour seesayBehaviour = Object.FindObjectOfType<SeesayBehaviour>();
if ((Object)(object)seesayBehaviour != (Object)null)
{
seesayBehaviour.ResetSeesay();
}
}
}
}using System;
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 DozerEntity;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("StemEntity")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("StemEntity")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("dea806e2-4ea2-4416-8557-a5243135a1f3")]
[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 StemEntity;
[BepInPlugin("com.yourname.stementity", "Stem Entity", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
private readonly Harmony harmony = new Harmony("com.yourname.stementity");
public static ConfigEntry<int> SpawnChance;
public static ConfigEntry<float> MinSpawnTime;
public static ConfigEntry<float> MaxSpawnTime;
public static ConfigEntry<float> OpenEyeDuration;
public static ConfigEntry<float> CooldownTime;
public static ConfigEntry<float> OpeningPauseTime;
public static ConfigEntry<float> CheckDelay;
private void Awake()
{
SpawnChance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpawnChance", 50, "Chance of Stem appearing each round (0-100)");
MinSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinSpawnTime", 30f, "Minimum time before Stem appears");
MaxSpawnTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxSpawnTime", 90f, "Maximum time before Stem appears");
OpenEyeDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "OpenEyeDuration", 3f, "How long Stem stays with open eye");
CooldownTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CooldownTime", 60f, "Cooldown before Stem can appear again");
OpeningPauseTime = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "OpeningPauseTime", 1f, "Pause duration at opening7");
CheckDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CheckDelay", 0.1f, "Delay before checking sprint condition");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Stem Entity loaded!");
harmony.PatchAll();
}
}
public class StemBehaviour : MonoBehaviour
{
private enum StemState
{
Hidden,
Opening,
OpenEye
}
private List<List<GameObject>> vineChains = new List<List<GameObject>>();
private float[] vineAngles = new float[8];
private float[] vineCurves = new float[8];
private bool deathAnimPlaying = false;
private float deathTimer = 0f;
private float vineGrowTimer = 0f;
private int[] vineSegmentCounts = new int[8];
private GameObject deathCanvas;
private Vector2 stemScreenPos = Vector2.zero;
private StemState currentState = StemState.Hidden;
private float timer = 0f;
private float nextAppearTime = 0f;
private float shakeTimer = 0f;
private float shakeSpeed = 0.05f;
private Vector2 basePosition = new Vector2(-500f, -200f);
private bool isOnCooldown = false;
private float cooldownTimer = 0f;
private float cooldownTime = 0f;
private AudioSource audioSource;
private AudioClip spawnClip;
private AudioClip deathClip;
private float animTimer = 0f;
private int animFrame = 0;
private bool animPaused = false;
private float animPauseTimer = 0f;
private float checkDelayTimer = 0f;
private bool checkStarted = false;
private GameObject rootObject;
private Image eyeImage;
private Sprite[] openingSprites = (Sprite[])(object)new Sprite[11];
private Sprite vineSprite;
private string modFolder;
private void Start()
{
string[] directories = Directory.GetDirectories(Paths.PluginPath, "StemEntity", SearchOption.AllDirectories);
modFolder = ((directories.Length != 0) ? directories[0] : Path.Combine(Paths.PluginPath, "StemEntity"));
LoadSprites();
CreateOverlay();
CreateDeathCanvas();
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
audioSource.loop = false;
audioSource.playOnAwake = false;
audioSource.volume = 1f;
spawnClip = LoadAudioClip("spawn.mp3");
deathClip = LoadAudioClip("death.mp3");
}
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)("Stem 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()
{
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < 11; i++)
{
openingSprites[i] = LoadSprite($"opening{i + 1}.png");
}
Texture2D val = LoadTexture("vine.png");
if ((Object)(object)val != (Object)null)
{
vineSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)1);
}
}
private Texture2D LoadTexture(string filename)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
string path = Path.Combine(modFolder, filename);
if (!File.Exists(path))
{
return null;
}
byte[] array = File.ReadAllBytes(path);
Texture2D val = new Texture2D(2, 2);
ImageConversion.LoadImage(val, array);
return val;
}
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)("Stem Entity: файл не найден — " + 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()
{
//IL_0097: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return;
}
if (currentState == StemState.Opening)
{
Shake();
}
if (deathAnimPlaying)
{
UpdateDeathAnim();
if (deathTimer >= 1f)
{
deathAnimPlaying = false;
deathCanvas.SetActive(false);
PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val2 != (Object)null)
{
val2.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 1, default(Vector3), false);
}
Hide();
}
}
else 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 StemState.Hidden:
if (timer >= nextAppearTime)
{
Appear();
}
break;
case StemState.Opening:
Shake();
if (!IsScreenBusy())
{
AnimateOpening();
}
break;
case StemState.OpenEye:
if (!checkStarted)
{
checkDelayTimer += Time.deltaTime;
if (checkDelayTimer >= Plugin.CheckDelay.Value)
{
checkStarted = true;
}
}
else if (!IsPlayerSprinting(val))
{
TriggerDeath(val);
}
else if (timer >= Plugin.OpenEyeDuration.Value)
{
Hide();
}
break;
}
}
}
private bool IsScreenBusy()
{
GameObject val = GameObject.Find("DozerManager");
if ((Object)(object)val != (Object)null)
{
DozerBehaviour component = val.GetComponent<DozerBehaviour>();
GameObject val2 = GameObject.Find("DozerImage");
if ((Object)(object)val2 != (Object)null && val2.activeSelf)
{
return true;
}
}
GameObject val3 = GameObject.Find("LitanyRoot");
if ((Object)(object)val3 != (Object)null && val3.activeSelf)
{
return true;
}
return false;
}
private void Shake()
{
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
shakeTimer += Time.deltaTime;
if (shakeTimer >= shakeSpeed)
{
shakeTimer = 0f;
float num = Random.Range(-5f, 5f);
float num2 = Random.Range(-5f, 5f);
rootObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(basePosition.x + num, basePosition.y + num2);
}
}
private void CreateDeathCanvas()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
deathCanvas = new GameObject("StemDeathCanvas");
Canvas val = deathCanvas.AddComponent<Canvas>();
val.renderMode = (RenderMode)0;
val.sortingOrder = 150;
Object.DontDestroyOnLoad((Object)(object)deathCanvas);
deathCanvas.SetActive(false);
for (int i = 0; i < 8; i++)
{
vineAngles[i] = (float)i * 45f;
vineChains.Add(new List<GameObject>());
}
}
private GameObject CreateVineSegment(int vineIndex, int segmentIndex)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
//IL_006f: 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_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
float num = vineAngles[vineIndex];
float num2 = vineCurves[vineIndex];
GameObject val = new GameObject($"Vine_{vineIndex}_{segmentIndex}");
val.transform.SetParent(deathCanvas.transform, false);
Image val2 = val.AddComponent<Image>();
val2.sprite = vineSprite;
RectTransform component = val.GetComponent<RectTransform>();
component.anchorMin = new Vector2(0.5f, 0.5f);
component.anchorMax = new Vector2(0.5f, 0.5f);
component.sizeDelta = new Vector2(600f, 600f);
float num3 = (num + num2 * (float)segmentIndex * 0.3f) * ((float)Math.PI / 180f);
float num4 = (float)segmentIndex * 200f;
float num5 = stemScreenPos.x + Mathf.Cos(num3) * num4;
float num6 = stemScreenPos.y + Mathf.Sin(num3) * num4;
component.anchoredPosition = new Vector2(num5, num6);
float num7 = num + num2 * (float)segmentIndex * 0.3f;
((Transform)component).localRotation = Quaternion.Euler(0f, 0f, num7 - 90f);
return val;
}
private void UpdateDeathAnim()
{
deathTimer += Time.deltaTime;
vineGrowTimer += Time.deltaTime;
if (vineGrowTimer >= 0.06f)
{
vineGrowTimer = 0f;
for (int i = 0; i < 8; i++)
{
int segmentIndex = vineSegmentCounts[i];
GameObject item = CreateVineSegment(i, segmentIndex);
vineChains[i].Add(item);
vineSegmentCounts[i]++;
}
}
}
private void AnimateOpening()
{
if (animPaused)
{
animPauseTimer += Time.deltaTime;
if (animPauseTimer >= Plugin.OpeningPauseTime.Value)
{
animPaused = false;
animPauseTimer = 0f;
}
return;
}
animTimer += Time.deltaTime;
if (animTimer >= 0.0667f)
{
animTimer = 0f;
animFrame++;
if (animFrame == 6)
{
animPaused = true;
eyeImage.sprite = openingSprites[6];
}
else if (animFrame >= 10)
{
OpenEye();
}
else if ((Object)(object)openingSprites[animFrame] != (Object)null)
{
eyeImage.sprite = openingSprites[animFrame];
}
}
}
private void Appear()
{
if ((Object)(object)spawnClip != (Object)null)
{
audioSource.clip = spawnClip;
audioSource.Play();
}
currentState = StemState.Opening;
timer = 0f;
animFrame = 0;
animTimer = 0f;
animPaused = false;
rootObject.SetActive(true);
if ((Object)(object)openingSprites[0] != (Object)null)
{
eyeImage.sprite = openingSprites[0];
}
}
private void OpenEye()
{
currentState = StemState.OpenEye;
timer = 0f;
checkDelayTimer = 0f;
checkStarted = false;
if ((Object)(object)openingSprites[10] != (Object)null)
{
eyeImage.sprite = openingSprites[10];
}
}
private void TriggerDeath(PlayerControllerB player)
{
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
deathAnimPlaying = true;
deathTimer = 0f;
vineGrowTimer = 0f;
if ((Object)(object)deathClip != (Object)null)
{
audioSource.clip = deathClip;
audioSource.Play();
}
stemScreenPos = rootObject.GetComponent<RectTransform>().anchoredPosition;
for (int i = 0; i < 8; i++)
{
vineCurves[i] = Random.Range(-15f, 15f);
vineSegmentCounts[i] = 0;
foreach (GameObject item in vineChains[i])
{
Object.Destroy((Object)(object)item);
}
vineChains[i].Clear();
}
deathCanvas.SetActive(true);
}
private void Hide()
{
currentState = StemState.Hidden;
timer = 0f;
rootObject.SetActive(false);
isOnCooldown = true;
cooldownTimer = 0f;
cooldownTime = Plugin.CooldownTime.Value;
}
public void ForceHide()
{
currentState = StemState.Hidden;
timer = 0f;
rootObject.SetActive(false);
isOnCooldown = false;
if (deathAnimPlaying)
{
deathAnimPlaying = false;
deathCanvas.SetActive(false);
}
}
private bool IsPlayerSprinting(PlayerControllerB player)
{
return player.isSprinting;
}
private bool IsPlayerInGame()
{
PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.isPlayerDead)
{
return false;
}
if (StartOfRound.Instance?.currentLevel?.sceneName == "CompanyBuilding")
{
return false;
}
return !val.isInHangarShipRoom && (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded;
}
public void ResetStem()
{
currentState = StemState.Hidden;
timer = 0f;
nextAppearTime = Random.Range(Plugin.MinSpawnTime.Value, Plugin.MaxSpawnTime.Value);
rootObject.SetActive(false);
isOnCooldown = false;
cooldownTimer = 0f;
deathAnimPlaying = false;
deathCanvas.SetActive(false);
}
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_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Expected O, but got Unknown
//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: 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)
GameObject val = new GameObject("StemCanvas");
Canvas val2 = val.AddComponent<Canvas>();
val2.renderMode = (RenderMode)0;
val2.sortingOrder = 100;
Object.DontDestroyOnLoad((Object)(object)val);
rootObject = new GameObject("StemRoot");
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(600f, 600f);
val3.anchoredPosition = new Vector2(-500f, -200f);
GameObject val4 = new GameObject("StemEye");
val4.transform.SetParent(rootObject.transform, false);
eyeImage = val4.AddComponent<Image>();
RectTransform component = val4.GetComponent<RectTransform>();
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.sizeDelta = Vector2.zero;
rootObject.SetActive(false);
}
}
[HarmonyPatch(typeof(PlayerControllerB), "Start")]
public class StemPatch
{
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<StemBehaviour>() != (Object)null))
{
int num = Random.Range(0, 100);
if (num >= Plugin.SpawnChance.Value)
{
Debug.Log((object)$"Stem Entity: не появится в этом раунде (бросок {num})");
return;
}
GameObject val = new GameObject("StemManager");
val.AddComponent<StemBehaviour>();
Object.DontDestroyOnLoad((Object)(object)val);
Debug.Log((object)"Stem Entity: менеджер создан!");
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
public class StemDeathPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
StemBehaviour stemBehaviour = Object.FindObjectOfType<StemBehaviour>();
if ((Object)(object)stemBehaviour != (Object)null)
{
stemBehaviour.ForceHide();
}
}
}
}
[HarmonyPatch(typeof(PlayerControllerB), "SpawnDeadAnimation")]
public class StemRespawnPatch
{
private static void Postfix(PlayerControllerB __instance)
{
if (((NetworkBehaviour)__instance).IsOwner)
{
StemBehaviour stemBehaviour = Object.FindObjectOfType<StemBehaviour>();
if ((Object)(object)stemBehaviour != (Object)null)
{
stemBehaviour.ResetStem();
}
}
}
}