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 SpawnChance; public static ConfigEntry MinSpawnTime; public static ConfigEntry MaxSpawnTime; public static ConfigEntry OpenEyeDuration; public static ConfigEntry CooldownTime; public static ConfigEntry OpeningPauseTime; public static ConfigEntry CheckDelay; private void Awake() { SpawnChance = ((BaseUnityPlugin)this).Config.Bind("General", "SpawnChance", 50, "Chance of Stem appearing each round (0-100)"); MinSpawnTime = ((BaseUnityPlugin)this).Config.Bind("Timing", "MinSpawnTime", 30f, "Minimum time before Stem appears"); MaxSpawnTime = ((BaseUnityPlugin)this).Config.Bind("Timing", "MaxSpawnTime", 90f, "Maximum time before Stem appears"); OpenEyeDuration = ((BaseUnityPlugin)this).Config.Bind("Timing", "OpenEyeDuration", 3f, "How long Stem stays with open eye"); CooldownTime = ((BaseUnityPlugin)this).Config.Bind("Timing", "CooldownTime", 60f, "Cooldown before Stem can appear again"); OpeningPauseTime = ((BaseUnityPlugin)this).Config.Bind("Timing", "OpeningPauseTime", 1f, "Pause duration at opening7"); CheckDelay = ((BaseUnityPlugin)this).Config.Bind("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> vineChains = new List>(); 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.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(); 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().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(); 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()); } } 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(); val2.sprite = vineSprite; RectTransform component = val.GetComponent(); 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().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(); 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(); 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(); RectTransform component = val4.GetComponent(); 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() != (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(); 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(); 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(); if ((Object)(object)stemBehaviour != (Object)null) { stemBehaviour.ResetStem(); } } } }