Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PaperPals v1.0.0
plugins/PaperPals.dll
Decompiled 2 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalLib.Modules; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PaperPals")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5cba7b5268d5f8e5ee36d0074b0fe356dd32378b")] [assembly: AssemblyProduct("PaperPals")] [assembly: AssemblyTitle("PaperPals")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PaperPals { internal static class Net { private sealed class Pending { public int Count; public int Got; public int Rate; public float Yaw; public float Volume; public float Started; public byte[] Pcm; } [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnVoice; public static HandleNamedMessageDelegate <1>__OnTeleport; } private const string VoiceMsg = "PaperPals.Voice"; private const string TeleportMsg = "PaperPals.Teleport"; private const int ChunkSize = 16384; private const int MaxPcm = 2000000; private static CustomMessagingManager registeredOn; private static int nextClipId; private static readonly Dictionary<(ulong, int), Pending> pending = new Dictionary<(ulong, int), Pending>(); public static void EnsureRegistered() { //IL_0034: 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_003f: Expected O, but got Unknown //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) //IL_0065: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if (val != null && val != registeredOn) { object obj = <>O.<0>__OnVoice; if (obj == null) { HandleNamedMessageDelegate val2 = OnVoice; <>O.<0>__OnVoice = val2; obj = (object)val2; } val.RegisterNamedMessageHandler("PaperPals.Voice", (HandleNamedMessageDelegate)obj); object obj2 = <>O.<1>__OnTeleport; if (obj2 == null) { HandleNamedMessageDelegate val3 = OnTeleport; <>O.<1>__OnTeleport = val3; obj2 = (object)val3; } val.RegisterNamedMessageHandler("PaperPals.Teleport", (HandleNamedMessageDelegate)obj2); registeredOn = val; pending.Clear(); } } public static IEnumerator SendVoice(ulong npcId, float yaw, float volume, VoiceClip clip) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { yield break; } EnsureRegistered(); int clipId = ++nextClipId; int total = clip.Pcm.Length; int count = (total + 16384 - 1) / 16384; FastBufferWriter val = default(FastBufferWriter); for (int i = 0; i < count; i++) { singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || singleton.CustomMessagingManager == null) { break; } int num = i * 16384; int num2 = Math.Min(16384, total - num); ((FastBufferWriter)(ref val))..ctor(num2 + 64, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref npcId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clipId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref i, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref yaw, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref volume, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref clip.Rate, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref total, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteBytesSafe(clip.Pcm, num2, num); singleton.CustomMessagingManager.SendNamedMessageToAll("PaperPals.Voice", val, (NetworkDelivery)4); } finally { ((FastBufferWriter)(ref val)).Dispose(); } if (i + 1 < count) { yield return (object)new WaitForSecondsRealtime(0.05f); } } } private static void OnVoice(ulong sender, FastBufferReader r) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0049: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || sender != 0L) { return; } ulong num = default(ulong); ((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); int item = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref item, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num2, default(ForPrimitives)); int num3 = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num3, default(ForPrimitives)); float yaw = default(float); ((FastBufferReader)(ref r)).ReadValueSafe<float>(ref yaw, default(ForPrimitives)); float volume = default(float); ((FastBufferReader)(ref r)).ReadValueSafe<float>(ref volume, default(ForPrimitives)); int num4 = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num4, default(ForPrimitives)); int num5 = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num5, default(ForPrimitives)); int num6 = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num6, default(ForPrimitives)); if (num5 <= 0 || num5 > 2000000 || num4 < 4000 || num4 > 96000 || num3 <= 0 || num2 < 0 || num2 >= num3 || num6 < 0 || num2 * 16384 + num6 > num5) { return; } (ulong, int) key = (num, item); if (!pending.TryGetValue(key, out var value)) { if (pending.Count > 32) { DropStale(); } value = new Pending { Count = num3, Rate = num4, Yaw = yaw, Volume = volume, Pcm = new byte[num5], Started = Time.realtimeSinceStartup }; pending[key] = value; } byte[] src = new byte[num6]; ((FastBufferReader)(ref r)).ReadBytesSafe(ref src, num6, 0); Buffer.BlockCopy(src, 0, value.Pcm, num2 * 16384, num6); if (++value.Got >= value.Count) { pending.Remove(key); PaperPalAI paperPalAI = default(PaperPalAI); if (singleton.SpawnManager.SpawnedObjects.TryGetValue(num, out var value2) && (Object)(object)value2 != (Object)null && ((Component)value2).TryGetComponent<PaperPalAI>(ref paperPalAI)) { paperPalAI.PlayVoice(value.Yaw, value.Volume, value.Rate, value.Pcm); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Voice message: " + ex.Message)); } } private static void DropStale() { float realtimeSinceStartup = Time.realtimeSinceStartup; foreach (var item in new List<(ulong, int)>(pending.Keys)) { if (realtimeSinceStartup - pending[item].Started > 15f) { pending.Remove(item); } } } public static void SendTeleport(ulong npcId, Vector3 pos, bool outside) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0060: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { return; } EnsureRegistered(); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref npcId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref pos); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref outside, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("PaperPals.Teleport", val, (NetworkDelivery)3); } finally { ((FastBufferWriter)(ref val)).Dispose(); } } private static void OnTeleport(ulong sender, FastBufferReader r) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && sender == 0L) { ulong key = default(ulong); ((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref key, default(ForPrimitives)); Vector3 pos = default(Vector3); ((FastBufferReader)(ref r)).ReadValueSafe(ref pos); bool outside = default(bool); ((FastBufferReader)(ref r)).ReadValueSafe<bool>(ref outside, default(ForPrimitives)); PaperPalAI paperPalAI = default(PaperPalAI); if (singleton.SpawnManager.SpawnedObjects.TryGetValue(key, out var value) && (Object)(object)value != (Object)null && ((Component)value).TryGetComponent<PaperPalAI>(ref paperPalAI)) { paperPalAI.ApplyTeleport(pos, outside); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Teleport message: " + ex.Message)); } } } [Serializable] public class NpcJson { public int format = 1; public string id = ""; public string displayName = ""; public string scanSubtext = ""; public VoiceJson voice = new VoiceJson(); public SpriteJson sprite = new SpriteJson(); public PosesJson poses = new PosesJson(); public FearJson fear = new FearJson(); public StatsJson stats = new StatsJson(); public FeaturesJson features = new FeaturesJson(); } [Serializable] public class VoiceJson { public string targetPlayer = ""; public string categories = "frase,afirmacion,risa,llamada,seguir,aviso,nave,botin,ayuda,nombre,respuesta"; public string panicCategories = "grito,ayuda,aviso"; public string fearCategories = "ayuda,aviso,grito"; public string hurtCategories = "grito,ayuda"; } [Serializable] public class SpriteJson { public float eyeLevel = 0.912f; public float heightCm = 180f; } [Serializable] public class PosesJson { public float crouchChance; } [Serializable] public class FearJson { public bool enabled = true; public float radius = 10f; public float calmDelay = 3f; public float runDistance = 4f; } [Serializable] public class StatsJson { public int hp = 6; public float walkSpeed = 2.6f; public int rarity = 25; } [Serializable] public class FeaturesJson { public bool meltdownPanic = true; public bool deathScream = true; } internal sealed class NpcDefinition { public NpcJson J; public string JsonPath; public string Dir; public SpriteSet Sprites; public VoiceLibrary Voice; public EnemyType Type; public int Index; public ConfigEntry<bool> Enabled; public ConfigEntry<string> TargetPlayerCfg; public ConfigEntry<string> ScanNameCfg; public ConfigEntry<string> FearEnabledCfg; public ConfigEntry<int> RarityCfg; public ConfigEntry<int> HpCfg; public ConfigEntry<float> WalkSpeedCfg; public ConfigEntry<float> HeightCmCfg; public ConfigEntry<float> FearRadiusCfg; public ConfigEntry<float> FearRunCfg; public string Id => J.id; public string DisplayName { get { if (string.IsNullOrWhiteSpace(ScanNameCfg?.Value)) { if (string.IsNullOrWhiteSpace(J.displayName)) { return J.id; } return J.displayName; } return ScanNameCfg.Value.Trim(); } } public string TargetPlayer { get { if (string.IsNullOrWhiteSpace(TargetPlayerCfg?.Value)) { return (J.voice.targetPlayer ?? "").Trim(); } return TargetPlayerCfg.Value.Trim(); } } public int Rarity { get { if (RarityCfg == null || RarityCfg.Value < 0) { return Math.Max(0, J.stats.rarity); } return RarityCfg.Value; } } public int Hp { get { if (HpCfg == null || HpCfg.Value <= 0) { return Math.Max(1, J.stats.hp); } return HpCfg.Value; } } public float WalkSpeed { get { if (WalkSpeedCfg == null || !(WalkSpeedCfg.Value > 0f)) { return Mathf.Max(0.5f, J.stats.walkSpeed); } return WalkSpeedCfg.Value; } } public float HeightCm => ClampHeight((HeightCmCfg != null && HeightCmCfg.Value >= 0f) ? HeightCmCfg.Value : J.sprite.heightCm, warn: false); public float HeightScale => HeightCm / 180f; public float EyeLevel { get { if (!(J.sprite.eyeLevel > 0.05f) || !(J.sprite.eyeLevel <= 1f)) { return 0.912f; } return J.sprite.eyeLevel; } } public bool FearEnabled { get { if (FearEnabledCfg == null || !bool.TryParse(FearEnabledCfg.Value?.Trim(), out var result)) { return J.fear.enabled; } return result; } } public float FearRadius { get { if (FearRadiusCfg == null || !(FearRadiusCfg.Value >= 0f)) { return Mathf.Max(0f, J.fear.radius); } return FearRadiusCfg.Value; } } public float FearRunDistance { get { if (FearRunCfg == null || !(FearRunCfg.Value >= 0f)) { return Mathf.Max(0f, J.fear.runDistance); } return FearRunCfg.Value; } } public float CalmDelay => Mathf.Max(0.5f, J.fear.calmDelay); public float CrouchChance => Mathf.Clamp01(J.poses.crouchChance); public static float ClampHeight(float cm, bool warn, string who = null) { if (float.IsNaN(cm) || float.IsInfinity(cm)) { cm = 180f; } float num = Mathf.Clamp(cm, 1f, 200f); if (warn && !Mathf.Approximately(num, cm)) { Plugin.Log.LogWarning((object)$"{who}: heightCm {cm} clamped to {num} (allowed 1-200)."); } return num; } public static NpcDefinition Load(string path) { string text; try { text = File.ReadAllText(path); } catch (Exception ex) { Plugin.Log.LogError((object)("Cannot read " + path + ": " + ex.Message)); return null; } NpcJson npcJson = new NpcJson(); try { JsonUtility.FromJsonOverwrite(text, (object)npcJson); } catch (Exception ex2) { Plugin.Log.LogError((object)("Skipping " + path + ": invalid JSON (" + ex2.Message + ").")); return null; } if (npcJson.format != 1) { Plugin.Log.LogError((object)$"Skipping {path}: unknown npc.json format {npcJson.format} (this PaperPals reads format 1). Update PaperPals."); return null; } if (string.IsNullOrEmpty(npcJson.id) || !Regex.IsMatch(npcJson.id, "^[a-z0-9_]+$")) { Plugin.Log.LogError((object)("Skipping " + path + ": \"id\" must be lowercase letters, digits or _ (got \"" + npcJson.id + "\").")); return null; } NpcJson npcJson2 = npcJson; if (npcJson2.voice == null) { npcJson2.voice = new VoiceJson(); } npcJson2 = npcJson; if (npcJson2.sprite == null) { npcJson2.sprite = new SpriteJson(); } npcJson2 = npcJson; if (npcJson2.poses == null) { npcJson2.poses = new PosesJson(); } npcJson2 = npcJson; if (npcJson2.fear == null) { npcJson2.fear = new FearJson(); } npcJson2 = npcJson; if (npcJson2.stats == null) { npcJson2.stats = new StatsJson(); } npcJson2 = npcJson; if (npcJson2.features == null) { npcJson2.features = new FeaturesJson(); } if (!Regex.IsMatch(text, "\"heightCm\"\\s*:\\s*-?[0-9]")) { Plugin.Log.LogWarning((object)(npcJson.id + ": sprite.heightCm missing or not a number, using 180.")); npcJson.sprite.heightCm = 180f; } npcJson.sprite.heightCm = ClampHeight(npcJson.sprite.heightCm, warn: true, npcJson.id); if (npcJson.sprite.eyeLevel <= 0.05f || npcJson.sprite.eyeLevel > 1f) { Plugin.Log.LogWarning((object)$"{npcJson.id}: sprite.eyeLevel {npcJson.sprite.eyeLevel} is not between 0.05 and 1, using 0.912."); npcJson.sprite.eyeLevel = 0.912f; } return new NpcDefinition { J = npcJson, JsonPath = path, Dir = Path.GetDirectoryName(path) }; } public void BindConfig(ConfigFile cfg) { string id = J.id; Enabled = cfg.Bind<bool>(id, "Enabled", true, "Spawn " + J.displayName + " at all. Needs a restart."); TargetPlayerCfg = cfg.Bind<string>(id, "TargetPlayer", "", "Whose MirageVoces clips he speaks (Steam name, SteamID64 or 'yo' for the host). Empty = use npc.json (\"" + J.voice.targetPlayer + "\"). Host only."); ScanNameCfg = cfg.Bind<string>(id, "ScanName", "", "Scanner name. Empty = use npc.json (\"" + J.displayName + "\")."); RarityCfg = cfg.Bind<int>(id, "Rarity", -1, $"Indoor spawn weight. -1 = use npc.json ({J.stats.rarity}). Needs a restart."); HpCfg = cfg.Bind<int>(id, "HP", -1, $"Hits he survives. -1 = use npc.json ({J.stats.hp})."); WalkSpeedCfg = cfg.Bind<float>(id, "WalkSpeed", -1f, $"Walking speed (m/s). -1 = use npc.json ({J.stats.walkSpeed})."); HeightCmCfg = cfg.Bind<float>(id, "HeightCm", -1f, $"How tall he looks, 1-200 (180 = eyes level with a player). -1 = use npc.json ({J.sprite.heightCm}). The picture updates live; hitbox and scan node after a restart. Every player should use the same value."); FearEnabledCfg = cfg.Bind<string>(id, "FearEnabled", "", "true/false. Empty = use npc.json (" + J.fear.enabled.ToString().ToLowerInvariant() + "). Host only."); FearRadiusCfg = cfg.Bind<float>(id, "FearRadius", -1f, $"Metres at which a visible monster scares him. -1 = use npc.json ({J.fear.radius}). Host only."); FearRunCfg = cfg.Bind<float>(id, "FearRunDistance", -1f, $"Closer than this he runs. -1 = use npc.json ({J.fear.runDistance}). Host only."); } } public class PaperPalAI : EnemyAI { public int DefIndex; internal const int Normal = 0; internal const int Panic = 1; internal const int FearSneak = 2; internal const int FearHold = 3; internal const int FearRun = 4; internal const int IdleCrouch = 5; internal const int InShip = 6; private Transform board; private MeshRenderer rend; private AudioSource voice; private AudioSource[] allSources; private Vector3 lastPos; private Vector3 facing = Vector3.forward; private float speed; private float stepTimer; private float mouthCloseAt; private float talkFaceUntil; private float talkYaw; private float sourcesRefreshAt; private float hitCrouchUntil; private int step; private bool walking; private bool mouthOpen; private Material shown; private readonly float[] rmsBuf = new float[256]; private float nextTalkAt; private float busyUntil; private float pauseUntil; private float wanderDeadline; private float lastMovedAt; private float nextAloneAt; private float cooldownUntil; private float hitPauseUntil; private bool wandering; internal static bool PanicActive; private bool panicking; private bool arrivedShip; private bool shipTargetSet; private float panicMoveAt; private float nextShoutAt; private float doorStartedAt; private float nextHurtShoutAt; private float shipStartedAt; private float shipLastMovedAt; private Vector3 shipTarget; private Vector3 insideDoor; private Vector3 outsideDoor; private int shipNudges; private bool inFear; private bool hasSpot; private bool fearRunning; private float lastThreatAt; private float spotPickedAt; private Vector3 threatPos; private Vector3 spot; private float graceUntil; private bool everOutside; internal bool SelfKilling; private float lastStateAt = -10f; private float nextDebugAt; private bool riding; private Vector3 rideOffset; private int hiddenReports; private static float playerEyeHeight; private static float loggedEye; private const float DefaultPlayerEye = 2.35f; internal NpcDefinition Def { get { if (DefIndex < 0 || DefIndex >= Plugin.Defs.Count) { return null; } return Plugin.Defs[DefIndex]; } } internal static EnemyBehaviourState[] MakeStates() { return ((IEnumerable<string>)new string[7] { "Normal", "Panic", "FearSneak", "FearHold", "FearRun", "IdleCrouch", "InShip" }).Select((Func<string, EnemyBehaviourState>)((string n) => new EnemyBehaviourState { name = n })).ToArray(); } public override void Awake() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown ((EnemyAI)this).Awake(); if (base.currentSearch == null) { base.currentSearch = new AISearchRoutine(); } } public override void Start() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) if (base.enemyBehaviourStates == null || base.enemyBehaviourStates.Length < 7) { base.enemyBehaviourStates = MakeStates(); } ((EnemyAI)this).Start(); NpcDefinition def = Def; board = ((Component)this).transform.Find("Billboard"); rend = (((Object)(object)board != (Object)null) ? ((Component)board).GetComponent<MeshRenderer>() : null); Transform obj = ((Component)this).transform.Find("Voice"); voice = ((obj != null) ? ((Component)obj).GetComponent<AudioSource>() : null); if ((Object)(object)voice != (Object)null) { voice.maxDistance = Plugin.HearingDistance.Value; } allSources = ((Component)this).GetComponentsInChildren<AudioSource>(true); lastPos = ((Component)this).transform.position; facing = Flat(((Component)this).transform.forward, Vector3.forward); Net.EnsureRegistered(); if (def != null) { if ((Object)(object)base.agent != (Object)null) { base.agent.speed = def.WalkSpeed; } base.enemyHP = def.Hp; } graceUntil = Time.time + Mathf.Max(0f, Plugin.GraceSeconds.Value); if (((NetworkBehaviour)this).IsServer) { nextTalkAt = Time.time + Random.Range(6f, 12f); pauseUntil = Time.time + Random.Range(0.5f, 2f); } if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)$"[Debug] {def?.Id} start: pos={((Component)this).transform.position} server={((NetworkBehaviour)this).IsServer} onNavMesh={(Object)(object)base.agent != (Object)null && base.agent.isOnNavMesh} outside={base.isOutside}"); } } private void SetState(int s, bool force = false) { if (base.currentBehaviourStateIndex != s && (force || !(Time.time - lastStateAt < 0.6f))) { lastStateAt = Time.time; ((EnemyAI)this).SwitchToBehaviourState(s); } } public override void DoAIInterval() { //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Invalid comparison between Unknown and I4 //IL_017f: 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) ((EnemyAI)this).DoAIInterval(); NpcDefinition def = Def; if (!((NetworkBehaviour)this).IsServer || base.isEnemyDead || (Object)(object)base.agent == (Object)null || def == null) { return; } float time = Time.time; DebugTick(def, time); if (PanicActive && !panicking && Plugin.PanicEnabled.Value && def.J.features.meltdownPanic) { BeginPanic(time); } if (panicking) { if (!arrivedShip) { SetState(1, force: true); } PanicTick(time); } else { if (!((Behaviour)base.agent).enabled || !base.agent.isOnNavMesh || time < hitPauseUntil || FearTick(def, time) || time < busyUntil) { return; } if (time >= nextTalkAt) { PlayerControllerB val = FindListener(); if ((Object)(object)val != (Object)null && TrySpeakTo(val)) { return; } nextTalkAt = time + 2f; } if (Plugin.AloneMin.Value > 0f) { if (nextAloneAt <= 0f) { ScheduleAlone(time); } else if (time >= nextAloneAt && time >= cooldownUntil) { ScheduleAlone(time); if (TrySpeakTo(null)) { return; } } } if (time < pauseUntil) { return; } if (wandering) { bool flag = !base.agent.pathPending && (int)base.agent.pathStatus == 2; Vector3 velocity = base.agent.velocity; if (((Vector3)(ref velocity)).sqrMagnitude > 0.04f) { lastMovedAt = time; } else if (time - lastMovedAt > 3f) { flag = true; } if ((!base.agent.pathPending && base.agent.remainingDistance <= base.agent.stoppingDistance + 0.4f) || flag || !(time < wanderDeadline)) { wandering = false; StandStill(); pauseUntil = time + Random.Range(2f, 7f); SetState((def.CrouchChance > 0f && Random.value < def.CrouchChance) ? 5 : 0); } } else { SetState(0); PickWanderTarget(); } } } private void DebugTick(NpcDefinition def, float now) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) if (Plugin.DebugLog.Value && !(now < nextDebugAt)) { nextDebugAt = now + 10f; ManualLogSource log = Plugin.Log; string[] obj = new string[6] { $"[Debug] {def.Id} pos={((Component)this).transform.position} state={base.currentBehaviourStateIndex} outside={base.isOutside} hp={base.enemyHP} ", null, null, null, null, null }; object[] obj2 = new object[4] { panicking, inFear, base.agent.isOnNavMesh, null }; Vector3 velocity = base.agent.velocity; obj2[3] = ((Vector3)(ref velocity)).magnitude; obj[1] = string.Format("panic={0} fear={1} onNavMesh={2} vel={3:0.0} ", obj2); obj[2] = $"sprite: enabled={(Object)(object)rend != (Object)null && ((Renderer)rend).enabled} active={(Object)(object)board != (Object)null && ((Component)board).gameObject.activeInHierarchy} "; obj[3] = $"layer={(((Object)(object)board != (Object)null) ? ((Component)board).gameObject.layer : (-1))} seen={(Object)(object)rend != (Object)null && ((Renderer)rend).isVisible} "; obj[4] = string.Format("h={0:0.00} mat={1} ", ((Object)(object)board != (Object)null) ? board.localScale.y : 0f, ((Object)(object)shown != (Object)null) ? ((Object)shown).name : "none"); obj[5] = $"you={(((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) ? Vector3.Distance(((Component)GameNetworkManager.Instance.localPlayerController).transform.position, ((Component)this).transform.position) : (-1f)):0} m"; log.LogInfo((object)string.Concat(obj)); } } private bool MoveTo(Vector3 target, bool checkForPath = false) { //IL_0001: 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_004d: 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_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) if (!((EnemyAI)this).SetDestinationToPosition(target, checkForPath)) { return false; } base.moveTowardsDestination = true; if (((Behaviour)base.agent).enabled && base.agent.isOnNavMesh && !base.agent.pathPending) { if (base.agent.hasPath) { Vector3 val = base.agent.destination - base.destination; if (!(((Vector3)(ref val)).sqrMagnitude > 1f)) { goto IL_007e; } } base.agent.SetDestination(base.destination); } goto IL_007e; IL_007e: return true; } private void StandStill() { base.moveTowardsDestination = false; base.movingTowardsTargetPlayer = false; if (((Behaviour)base.agent).enabled && base.agent.isOnNavMesh) { base.agent.ResetPath(); } } private void PickWanderTarget() { //IL_0068: 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_0095: Unknown result type (might be due to invalid IL or missing references) base.agent.speed = Def.WalkSpeed; if (base.allAINodes == null || base.allAINodes.Length == 0) { pauseUntil = Time.time + 5f; return; } for (int i = 0; i < 8; i++) { GameObject val = base.allAINodes[Random.Range(0, base.allAINodes.Length)]; if (!((Object)(object)val == (Object)null)) { float num = Vector3.Distance(val.transform.position, ((Component)this).transform.position); if (!(num < 6f) && !(num > 40f) && MoveTo(val.transform.position, checkForPath: true)) { base.moveTowardsDestination = true; wandering = true; lastMovedAt = Time.time + 1f; wanderDeadline = Time.time + num / Mathf.Max(0.5f, base.agent.speed) * 2f + 5f; return; } } } pauseUntil = Time.time + 3f; } private PlayerControllerB FindListener() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00af: 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) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB result = null; float num = Plugin.TalkRange.Value; int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead && val.isInsideFactory != base.isOutside) { Vector3 val2 = (((Object)(object)val.gameplayCamera != (Object)null) ? ((Component)val.gameplayCamera).transform.position : (((Component)val).transform.position + Vector3.up * 1.5f)); float num2 = Vector3.Distance(base.eye.position, val2); if (!(num2 > num) && !Physics.Linecast(base.eye.position, val2, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { result = val; num = num2; } } } return result; } private void ScheduleAlone(float now) { nextAloneAt = now + Random.Range(Plugin.AloneMin.Value, Mathf.Max(Plugin.AloneMin.Value, Plugin.AloneMax.Value)); } private bool TrySpeakTo(PlayerControllerB p) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) VoiceClip voiceClip = Def.Voice.PickNormal(); if (voiceClip == null) { nextTalkAt = Time.time + 60f; return false; } Quaternion val = Quaternion.LookRotation(((Object)(object)p != (Object)null) ? Flat(((Component)p).transform.position - ((Component)this).transform.position, facing) : facing); float y = ((Quaternion)(ref val)).eulerAngles.y; StandStill(); wandering = false; busyUntil = Time.time + voiceClip.Seconds + (float)voiceClip.Pcm.Length / 16384f * 0.05f + 1.5f; pauseUntil = busyUntil + Random.Range(1f, 3f); nextTalkAt = busyUntil + Random.Range(Plugin.CooldownMin.Value, Mathf.Max(Plugin.CooldownMin.Value, Plugin.CooldownMax.Value)); cooldownUntil = nextTalkAt; if ((Object)(object)p != (Object)null) { ScheduleAlone(Time.time); } Plugin.Log.LogInfo((object)string.Format("{0}: saying {1} ({2:0.0} s) to {3}.", Def.Id, Path.GetFileName(voiceClip.File), voiceClip.Seconds, ((Object)(object)p != (Object)null) ? p.playerUsername : "nobody")); ((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, y, 1f, voiceClip)); return true; } private bool FearTick(NpcDefinition def, float now) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) if (!def.FearEnabled || def.FearRadius <= 0f) { if (inFear) { EndFear(); } return false; } float dist; EnemyAI val = FindThreat(def, out dist); if ((Object)(object)val != (Object)null) { lastThreatAt = now; threatPos = ((Component)val).transform.position; if (!inFear) { StartFear(val); } } if (!inFear) { return false; } if ((Object)(object)val == (Object)null) { if (now - lastThreatAt > def.CalmDelay) { EndFear(); return false; } if (!hasSpot || Arrived(spot)) { HoldFacing(); } FearShout(def, now, 0.6f); return true; } fearRunning = dist < def.FearRunDistance || (fearRunning && dist < def.FearRunDistance + 1f); if (fearRunning) { SetState(4); base.agent.speed = Mathf.Max(1f, Plugin.PanicSpeed.Value); if ((!hasSpot && now - spotPickedAt > 1f) || (hasSpot && Arrived(spot)) || now - spotPickedAt > 3f) { PickSpot(val, 6f); } FearShout(def, now, 1f); return true; } if (hasSpot && Arrived(spot) && !CanSee(val, base.eye.position)) { HoldFacing(); } else { SetState(2); base.agent.speed = Mathf.Max(0.3f, Plugin.SneakSpeed.Value); if ((!hasSpot && now - spotPickedAt > 1f) || (hasSpot && Arrived(spot)) || now - spotPickedAt > 6f) { PickSpot(val, 2f); } } FearShout(def, now, 0.6f); return true; } private void StartFear(EnemyAI threat) { //IL_0075: 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) inFear = true; hasSpot = false; fearRunning = false; wandering = false; StandStill(); busyUntil = 0f; nextShoutAt = Time.time + Random.Range(0.2f, 0.8f); Plugin.Log.LogInfo((object)$"{Def.Id}: scared of {threat.enemyType?.enemyName} at {Vector3.Distance(((Component)this).transform.position, ((Component)threat).transform.position):0.0} m."); } private void EndFear() { inFear = false; hasSpot = false; fearRunning = false; base.agent.speed = Def.WalkSpeed; StandStill(); SetState(0, force: true); pauseUntil = Time.time + Random.Range(1f, 3f); Plugin.Log.LogInfo((object)(Def.Id + ": calmed down.")); } private void HoldFacing() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) SetState(3); StandStill(); Vector3 val = Flat(threatPos - ((Component)this).transform.position, -((Component)this).transform.forward); ((Component)this).transform.rotation = Quaternion.LookRotation(val, Vector3.up); } private bool Arrived(Vector3 p) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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) return Vector3.Distance(Flat3(((Component)this).transform.position), Flat3(p)) < 1.2f; } private static Vector3 Flat3(Vector3 v) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) v.y = 0f; return v; } private EnemyAI FindThreat(NpcDefinition def, out float dist) { //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0179: 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_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0196: 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_01a3: Unknown result type (might be due to invalid IL or missing references) dist = float.MaxValue; EnemyAI result = null; RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null) { return null; } string[] source = (from x in (Plugin.FearIgnore.Value ?? "").Split(',') select x.Trim() into x where x.Length > 0 select x).ToArray(); int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault; foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if ((Object)(object)spawnedEnemy == (Object)null || (Object)(object)spawnedEnemy == (Object)(object)this || spawnedEnemy.isEnemyDead || spawnedEnemy is PaperPalAI || spawnedEnemy.isOutside != base.isOutside) { continue; } string n = (((Object)(object)spawnedEnemy.enemyType != (Object)null) ? spawnedEnemy.enemyType.enemyName : ""); if (source.Any((string x) => string.Equals(x, n, StringComparison.OrdinalIgnoreCase))) { continue; } float num = Vector3.Distance(((Component)this).transform.position, ((Component)spawnedEnemy).transform.position); if (!(num > def.FearRadius) && !(num >= dist)) { Vector3 val = (((Object)(object)spawnedEnemy.eye != (Object)null) ? spawnedEnemy.eye.position : (((Component)spawnedEnemy).transform.position + Vector3.up)); if (!Physics.Linecast(base.eye.position, val, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { result = spawnedEnemy; dist = num; } } } return result; } private bool CanSee(EnemyAI threat, Vector3 point) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) return !Physics.Linecast(((Object)(object)threat.eye != (Object)null) ? threat.eye.position : (((Component)threat).transform.position + Vector3.up), point, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1); } private void PickSpot(EnemyAI threat, float minGain) { //IL_0046: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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_016b: 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) spotPickedAt = Time.time; if (base.allAINodes == null || base.allAINodes.Length == 0) { hasSpot = false; return; } float curD = Vector3.Distance(((Component)this).transform.position, ((Component)threat).transform.position); List<Vector3> source = (from n in base.allAINodes where (Object)(object)n != (Object)null select n.transform.position into p where Vector3.Distance(p, ((Component)this).transform.position) < 30f && Vector3.Distance(p, ((Component)threat).transform.position) > curD + minGain orderby Vector3.Distance(p, ((Component)this).transform.position) select p).Take(14).ToList(); bool[] array = new bool[2] { true, false }; for (int num = 0; num < array.Length; num++) { IEnumerable<Vector3> enumerable2; if (!array[num]) { IEnumerable<Vector3> enumerable = source.OrderByDescending((Vector3 c) => Vector3.Distance(c, ((Component)threat).transform.position)); enumerable2 = enumerable; } else { enumerable2 = source.Where((Vector3 c) => !CanSee(threat, c + Vector3.up)); } foreach (Vector3 item in enumerable2) { if (MoveTo(item, checkForPath: true)) { spot = item; hasSpot = true; return; } } } hasSpot = false; StandStill(); } private void FearShout(NpcDefinition def, float now, float volume) { if (!(now < nextShoutAt)) { VoiceClip voiceClip = def.Voice.PickFear(); if (voiceClip == null) { nextShoutAt = now + 6f; return; } nextShoutAt = now + voiceClip.Seconds + Random.Range(Plugin.ShoutPauseMin.Value, Mathf.Max(Plugin.ShoutPauseMin.Value, Plugin.ShoutPauseMax.Value)); ((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, volume, voiceClip)); } } private void BeginPanic(float now) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) panicking = true; inFear = false; wandering = false; arrivedShip = false; shipTargetSet = false; busyUntil = 0f; panicMoveAt = now + Random.Range(0.3f, 1.5f); nextShoutAt = panicMoveAt; doorStartedAt = now; insideDoor = RoundManager.FindMainEntrancePosition(true, false); outsideDoor = RoundManager.FindMainEntrancePosition(true, true); Plugin.Log.LogInfo((object)$"{Def.Id}: panics (meltdown), outside={base.isOutside}."); } private void PanicTick(float now) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_047f: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0430: Unknown result type (might be due to invalid IL or missing references) if (now < panicMoveAt) { return; } base.agent.speed = Mathf.Max(1f, Plugin.PanicSpeed.Value); base.agent.acceleration = 14f; if (now >= nextShoutAt) { Shout(arrivedShip ? 3f : 1f); } if (arrivedShip) { return; } bool flag = ((Behaviour)base.agent).enabled && base.agent.isOnNavMesh; if (!base.isOutside) { if (insideDoor == Vector3.zero || outsideDoor == Vector3.zero) { arrivedShip = true; StandStill(); return; } bool flag2 = Vector3.Distance(((Component)this).transform.position, insideDoor) < 1.6f; bool flag3 = now - doorStartedAt > Mathf.Max(10f, Plugin.DoorTimeout.Value); if (flag2 || flag3) { if (flag3 && !flag2) { Plugin.Log.LogInfo((object)(Def.Id + ": could not reach the main entrance in time; using it anyway.")); } Net.SendTeleport(((NetworkBehaviour)this).NetworkObjectId, outsideDoor, outside: true); doorStartedAt = now; } else if (flag) { MoveTo(insideDoor); base.moveTowardsDestination = true; } return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.shipIsLeaving || instance.inShipPhase) { StandStill(); return; } if (!shipTargetSet) { shipTargetSet = true; shipNudges = 0; shipStartedAt = (shipLastMovedAt = now); shipTarget = ((instance.insideShipPositions != null && instance.insideShipPositions.Length != 0) ? instance.insideShipPositions[Random.Range(0, instance.insideShipPositions.Length)].position : (((Object)(object)instance.middleOfShipNode != (Object)null) ? instance.middleOfShipNode.position : instance.shipLandingPosition.position)); if (flag && !MoveTo(shipTarget, checkForPath: true) && (Object)(object)instance.middleOfShipNode != (Object)null) { shipTarget = instance.middleOfShipNode.position; } } if (flag) { Vector3 velocity = base.agent.velocity; if (((Vector3)(ref velocity)).sqrMagnitude > 0.25f) { shipLastMovedAt = now; } } if (now - shipLastMovedAt > 4f && shipNudges < 3 && now - shipStartedAt <= 45f) { shipNudges++; Vector3 val = Flat(shipTarget - ((Component)this).transform.position, ((Component)this).transform.forward); Vector3 val2 = ((Component)this).transform.position + val * (4f + 2f * (float)shipNudges); Plugin.Log.LogInfo((object)$"{Def.Id}: stuck on the way to the ship (path {base.agent.pathStatus}); nudging toward it (try {shipNudges})."); NavMeshHit val3 = default(NavMeshHit); if (NavMesh.SamplePosition(val2, ref val3, 5f, base.agent.areaMask)) { base.agent.Warp(((NavMeshHit)(ref val3)).position); base.serverPosition = ((NavMeshHit)(ref val3)).position; } shipLastMovedAt = now; MoveTo(shipTarget); base.moveTowardsDestination = true; return; } if (now - shipStartedAt > 45f || now - shipLastMovedAt > 4f) { arrivedShip = true; StandStill(); Plugin.Log.LogInfo((object)$"{Def.Id}: could not reach the ship; staying at {((Component)this).transform.position}."); return; } if (!flag) { return; } if (!(Vector3.Distance(((Component)this).transform.position, shipTarget) < 1.6f)) { if ((Object)(object)instance.shipInnerRoomBounds != (Object)null) { Bounds bounds = instance.shipInnerRoomBounds.bounds; if (((Bounds)(ref bounds)).Contains(((Component)this).transform.position) && Vector3.Distance(((Component)this).transform.position, shipTarget) < 4f) { goto IL_0441; } } MoveTo(shipTarget); base.moveTowardsDestination = true; return; } goto IL_0441; IL_0441: arrivedShip = true; base.isInsidePlayerShip = true; StandStill(); SetState(6, force: true); Plugin.Log.LogInfo((object)(Def.Id + ": reached the ship.")); } private void Shout(float intervalScale) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; VoiceClip voiceClip = Def.Voice.PickPanic(); if (voiceClip == null) { nextShoutAt = time + 5f; return; } nextShoutAt = time + voiceClip.Seconds + Random.Range(Plugin.ShoutPauseMin.Value, Mathf.Max(Plugin.ShoutPauseMin.Value, Plugin.ShoutPauseMax.Value)) * intervalScale; ((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, 1f, voiceClip)); if (base.isOutside && (Object)(object)RoundManager.Instance != (Object)null) { RoundManager.Instance.PlayAudibleNoise(((Component)this).transform.position, 25f, 0.9f, 0, base.isInsidePlayerShip && StartOfRound.Instance.hangarDoorsClosed, 7531); } } internal void ApplyTeleport(Vector3 pos, bool outside) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00a3: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (base.isEnemyDead) { return; } Vector3 val = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.GetNavMeshPosition(pos, default(NavMeshHit), 5f, -1) : pos); if (((NetworkBehaviour)this).IsOwner && (Object)(object)base.agent != (Object)null) { ((Behaviour)base.agent).enabled = false; ((Component)this).transform.position = val; ((Behaviour)base.agent).enabled = true; if (!base.agent.isOnNavMesh) { base.agent.Warp(val); } base.agent.ResetPath(); } else { ((Component)this).transform.position = val; } base.serverPosition = val; lastPos = val; ((EnemyAI)this).SetEnemyOutside(outside); if (outside && !everOutside) { everOutside = true; graceUntil = Time.time + Mathf.Max(0f, Plugin.GraceSeconds.Value); } EntranceTeleport val2 = RoundManager.FindMainEntranceScript(outside); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.entrancePointAudio != (Object)null && val2.doorAudios != null && val2.doorAudios.Length != 0) { val2.entrancePointAudio.PlayOneShot(val2.doorAudios[0]); } if (((NetworkBehaviour)this).IsServer) { shipTargetSet = false; doorStartedAt = Time.time; } } internal bool MonsterDamageAllowed(out string why) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (!base.isOutside) { why = "inside"; return false; } if (Time.time < graceUntil) { why = "grace"; return false; } Vector3 val = (((Object)(object)base.eye != (Object)null) ? base.eye.position : (((Component)this).transform.position + Vector3.up)); int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (!((Object)(object)val2 == (Object)null) && val2.isPlayerControlled && !val2.isPlayerDead) { Vector3 val3 = (((Object)(object)val2.gameplayCamera != (Object)null) ? ((Component)val2.gameplayCamera).transform.position : (((Component)val2).transform.position + Vector3.up * 1.5f)); float num = Vector3.Distance(val3, val); if (num <= Plugin.WitnessCloseRange.Value) { why = "close witness"; return true; } if (num <= Plugin.WitnessRange.Value && !Physics.Linecast(val3, val, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { why = "witness in sight"; return true; } } } why = "no witness"; return false; } public override void HitEnemy(int force = 1, PlayerControllerB playerWhoHit = null, bool playHitSFX = false, int hitID = -1) { if (base.isEnemyDead) { return; } if ((Object)(object)playerWhoHit == (Object)null && !MonsterDamageAllowed(out var why)) { if (Plugin.DebugLog.Value && ((NetworkBehaviour)this).IsServer) { Plugin.Log.LogInfo((object)("[Debug] " + Def?.Id + ": monster hit ignored (" + why + ").")); } return; } base.enemyHP -= force; hitCrouchUntil = Time.time + 1.5f; if (((NetworkBehaviour)this).IsServer) { if (!panicking && base.currentBehaviourStateIndex != 4) { hitPauseUntil = Time.time + 1.5f; StandStill(); } if (base.enemyHP > 0 && Time.time >= nextHurtShoutAt) { nextHurtShoutAt = Time.time + 0.3f; VoiceClip voiceClip = Def?.Voice.PickHurt(); if (voiceClip != null) { ((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, 1f, voiceClip)); } } } if (base.enemyHP > 0 || !((NetworkBehaviour)this).IsOwner) { return; } SelfKilling = true; try { ((EnemyAI)this).KillEnemyOnOwnerClient(false); } finally { SelfKilling = false; } } public override void KillEnemy(bool destroy = false) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)this).KillEnemy(destroy); if (!base.isEnemyDead) { return; } if ((Object)(object)voice != (Object)null) { voice.Stop(); } panicking = false; inFear = false; Plugin.Log.LogInfo((object)$"{Def?.Id}: died at {((Component)this).transform.position}."); if (((NetworkBehaviour)this).IsServer && Def != null && Def.J.features.deathScream) { VoiceClip voiceClip = Def.Voice.Pick("grito"); if (voiceClip != null) { ((MonoBehaviour)this).StartCoroutine(Net.SendVoice(((NetworkBehaviour)this).NetworkObjectId, float.NaN, 1f, voiceClip)); } } } public override void OnCollideWithPlayer(Collider other) { } internal void PlayVoice(float yaw, float volume, int rate, byte[] pcm) { if (!((Object)(object)voice == (Object)null)) { int num = pcm.Length / 2; float[] array = new float[num]; for (int i = 0; i < num; i++) { array[i] = (float)(short)(pcm[2 * i] | (pcm[2 * i + 1] << 8)) / 32768f; } AudioClip val = AudioClip.Create("PaperPalLine", num, 1, rate, false); val.SetData(array, 0); if ((Object)(object)voice.clip != (Object)null) { voice.Stop(); Object.Destroy((Object)(object)voice.clip); } voice.clip = val; voice.volume = Mathf.Clamp01(Plugin.Volume.Value * Mathf.Clamp01((volume <= 0f) ? 1f : volume)); voice.Play(); if (!float.IsNaN(yaw)) { talkYaw = yaw; talkFaceUntil = Time.time + val.length + 0.5f; } } } private void LateUpdate() { //IL_0034: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_009d: 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_00b6: 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_044e: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Unknown result type (might be due to invalid IL or missing references) //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04be: Unknown result type (might be due to invalid IL or missing references) //IL_04c3: Unknown result type (might be due to invalid IL or missing references) NpcDefinition def = Def; if ((Object)(object)board == (Object)null || (Object)(object)rend == (Object)null || def == null) { return; } float num = SpriteHeight(def); if (!Mathf.Approximately(board.localScale.y, num)) { board.localScale = new Vector3(num, num, num); } EnsureVisible(); RideShip(); if (base.isEnemyDead) { LieDown(def, num); return; } float num2 = Mathf.Max(Time.deltaTime, 0.0001f); int currentBehaviourStateIndex = base.currentBehaviourStateIndex; Vector3 position = ((Component)this).transform.position; Vector3 val = position - lastPos; val.y = 0f; lastPos = position; float num3 = ((Vector3)(ref val)).magnitude / num2; if (num3 > 20f) { num3 = 0f; } speed = Mathf.Lerp(speed, num3, 1f - Mathf.Exp(-10f * num2)); Vector3 val2 = facing; if (currentBehaviourStateIndex == 3 && speed < 0.4f) { val2 = Flat(((Component)this).transform.forward, facing); } else if (Time.time < talkFaceUntil) { val2 = Quaternion.Euler(0f, talkYaw, 0f) * Vector3.forward; } else if (num3 > 0.3f) { val2 = ((Vector3)(ref val)).normalized; } facing = Vector3.RotateTowards(facing, val2, 8f * num2, 0f); bool flag = speed > 0.3f; if (flag) { if (!walking) { step = 0; stepTimer = 0f; } stepTimer += num2 * Mathf.Clamp(speed / Mathf.Max(0.5f, def.WalkSpeed), 0.4f, 2.5f); float num4 = Mathf.Clamp(Plugin.StepTime.Value, 0.05f, 2f); if (stepTimer >= num4) { stepTimer -= num4; step ^= 1; } } walking = flag; SpriteSet sprites = def.Sprites; Pose pose = ((!walking) ? (((Time.time < hitCrouchUntil || currentBehaviourStateIndex == 3 || currentBehaviourStateIndex == 2 || currentBehaviourStateIndex == 5) && sprites.HasCrouch) ? Pose.Crouch : Pose.Idle) : ((currentBehaviourStateIndex == 2) ? ((!sprites.HasSneak) ? (sprites.HasCrouch ? Pose.Crouch : ((step == 0) ? Pose.StepA : Pose.StepB)) : ((step == 0) ? Pose.CrouchA : Pose.CrouchB)) : (((currentBehaviourStateIndex != 1 && currentBehaviourStateIndex != 4) || !sprites.HasRun) ? ((step == 0) ? Pose.StepA : Pose.StepB) : ((step == 0) ? Pose.RunA : Pose.RunB)))); if (Time.time > sourcesRefreshAt) { allSources = ((Component)this).GetComponentsInChildren<AudioSource>(true); sourcesRefreshAt = Time.time + 2f; } float num5 = 0f; AudioSource[] array = allSources; foreach (AudioSource val3 in array) { if (!((Object)(object)val3 == (Object)null) && val3.isPlaying && !val3.mute) { val3.GetOutputData(rmsBuf, 0); float num6 = 0f; for (int j = 0; j < rmsBuf.Length; j++) { num6 += rmsBuf[j] * rmsBuf[j]; } num5 = Mathf.Max(num5, Mathf.Sqrt(num6 / (float)rmsBuf.Length) / Mathf.Max(0.05f, val3.volume)); } } if (num5 > Plugin.MouthThreshold.Value) { mouthOpen = true; mouthCloseAt = Time.time + 0.08f; } else if (Time.time > mouthCloseAt) { mouthOpen = false; } Camera val4 = (((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.activeCamera != (Object)null) ? StartOfRound.Instance.activeCamera : Camera.main); if (!((Object)(object)val4 == (Object)null)) { Vector3 val5 = Flat(((Component)val4).transform.position - position, -facing); float num7 = Vector3.SignedAngle(facing, val5, Vector3.up); int view = ((!(Mathf.Abs(num7) <= 45f)) ? ((Mathf.Abs(num7) >= 135f) ? 1 : ((num7 > 0f) ? 3 : 2)) : 0); board.rotation = Quaternion.LookRotation(-val5, Vector3.up); Show(sprites.Get(view, pose, mouthOpen ? 1 : 0)); } } private void RideShip() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.elevatorTransform : null); if ((Object)(object)val == (Object)null || (base.currentBehaviourStateIndex != 6 && !riding)) { return; } if (!riding) { riding = true; rideOffset = val.InverseTransformPoint(((Component)this).transform.position); if ((Object)(object)base.agent != (Object)null) { base.agent.updatePosition = false; } } Vector3 val2 = val.TransformPoint(rideOffset); ((Component)this).transform.position = val2; base.serverPosition = val2; lastPos = val2; if ((Object)(object)base.agent != (Object)null && ((Behaviour)base.agent).enabled && base.agent.isOnNavMesh) { base.agent.nextPosition = val2; } } private void EnsureVisible() { bool flag = false; int layer = ((Component)board).gameObject.layer; if (layer != 19) { ((Component)board).gameObject.layer = 19; flag = true; } if (!((Renderer)rend).enabled) { ((Renderer)rend).enabled = true; flag = true; } if (!((Component)board).gameObject.activeSelf) { ((Component)board).gameObject.SetActive(true); flag = true; } if (flag && hiddenReports < 5) { hiddenReports++; Plugin.Log.LogInfo((object)($"{Def?.Id}: sprite had been hidden (layer {layer}, outside={base.isOutside}, " + $"you inside={GameNetworkManager.Instance?.localPlayerController?.isInsideFactory}); shown again.")); } } private void Show(Material m) { if ((Object)(object)m != (Object)null && (Object)(object)m != (Object)(object)shown) { ((Renderer)rend).sharedMaterial = m; shown = m; } } private void LieDown(NpcDefinition def, float h) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_006f: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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) Vector3 val = Flat(facing, Vector3.forward); board.rotation = Quaternion.LookRotation(val, Vector3.up) * Quaternion.Euler(90f, 0f, 0f); board.position = ((Component)this).transform.position + Vector3.up * Mathf.Clamp(Plugin.LieOffset.Value, 0f, 1f) - val * (h * 0.5f); Show(def.Sprites.Get(1, Pose.Idle, 0)); } private static float SpriteHeight(NpcDefinition def) { return Mathf.Clamp((Plugin.MatchPlayerEyes.Value ? MeasurePlayerEye() : 2.35f) / def.EyeLevel * def.HeightScale, 0.02f, 8f); } private static float MeasurePlayerEye() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_00c8: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null && !val.isPlayerDead && !val.isCrouching && !val.isClimbingLadder && !val.inSpecialInteractAnimation && (Object)(object)val.thisController != (Object)null && val.thisController.isGrounded) { Vector3 velocity = val.thisController.velocity; if (((Vector3)(ref velocity)).sqrMagnitude < 0.01f && !val.isInHangarShipRoom) { float num = ((Component)val.gameplayCamera).transform.position.y - ((Component)val).transform.position.y; if (num > 0.8f && num < 5f) { playerEyeHeight = ((playerEyeHeight <= 0f) ? num : Mathf.Lerp(playerEyeHeight, num, 0.02f)); if (Mathf.Abs(playerEyeHeight - loggedEye) > 0.02f) { loggedEye = playerEyeHeight; Plugin.Log.LogInfo((object)$"Player eye height {playerEyeHeight:0.00}."); } } } } if (!(playerEyeHeight > 0f)) { return 2.35f; } return playerEyeHeight; } private static Vector3 Flat(Vector3 v, Vector3 fallback) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) v.y = 0f; if (((Vector3)(ref v)).sqrMagnitude < 1E-06f) { fallback.y = 0f; if (!(((Vector3)(ref fallback)).sqrMagnitude < 1E-06f)) { return ((Vector3)(ref fallback)).normalized; } return Vector3.forward; } return ((Vector3)(ref v)).normalized; } public override void OnDestroy() { if ((Object)(object)voice != (Object)null && (Object)(object)voice.clip != (Object)null) { Object.Destroy((Object)(object)voice.clip); } ((EnemyAI)this).OnDestroy(); } } [HarmonyPatch] internal static class Patches { private static float panicTestAt = -1f; private static float nextSweep; private static bool IsHost { get { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsServer; } return false; } } [HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")] [HarmonyPostfix] private static void OnLanded() { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0114: 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_011b: Unknown result type (might be due to invalid IL or missing references) PaperPalAI.PanicActive = false; panicTestAt = ((Plugin.PanicAfterLanding.Value > 0f && IsHost) ? (Time.time + Plugin.PanicAfterLanding.Value) : (-1f)); try { if (!Plugin.GuaranteedSpawn.Value || !IsHost || Plugin.Defs.Count == 0) { return; } RoundManager instance = RoundManager.Instance; GameObject[] array = GameObject.FindGameObjectsWithTag("AINode"); if ((Object)(object)instance == (Object)null || array == null || array.Length == 0) { Plugin.Log.LogInfo((object)"No interior on this moon; no guaranteed PaperPals."); return; } int num = Mathf.Max(1, Plugin.GuaranteedSpawnCount.Value); List<NpcDefinition> list = Plugin.Defs.OrderBy((NpcDefinition _) => Random.value).ToList(); for (int num2 = 0; num2 < num; num2++) { NpcDefinition npcDefinition = list[num2 % list.Count]; Vector3 val = array[Random.Range(0, array.Length)].transform.position; if (num2 == 0 && Plugin.SpawnNearEntrance.Value) { val = NearMainEntrance(array, val); } val = instance.GetNavMeshPosition(val, default(NavMeshHit), 5f, -1); instance.SpawnEnemyGameObject(val, Random.Range(0f, 360f), -1, npcDefinition.Type); Plugin.Log.LogInfo((object)$"Guaranteed spawn: {npcDefinition.Id} at {val}."); } } catch (Exception ex) { Plugin.Log.LogError((object)("Guaranteed spawn failed: " + ex)); } } private static Vector3 NearMainEntrance(GameObject[] nodes, Vector3 fallback) { //IL_0033: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) EntranceTeleport val = ((IEnumerable<EntranceTeleport>)Object.FindObjectsOfType<EntranceTeleport>()).FirstOrDefault((Func<EntranceTeleport, bool>)((EntranceTeleport t) => !t.isEntranceToBuilding && t.entranceId == 0)); if ((Object)(object)val == (Object)null) { return fallback; } Vector3 val2 = (((Object)(object)val.entrancePoint != (Object)null) ? val.entrancePoint.position : ((Component)val).transform.position); Vector3 result = fallback; float num = float.MaxValue; for (int num2 = 0; num2 < nodes.Length; num2++) { Vector3 position = nodes[num2].transform.position; float num3 = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(val2.x, val2.z)); if (!(num3 < 2f)) { float num4 = num3 + Mathf.Abs(position.y - val2.y) * 6f; if (num4 < num) { num = num4; result = position; } } } return result; } [HarmonyPatch(typeof(StartOfRound), "Update")] [HarmonyPostfix] private static void PanicTestTimer() { if (!(panicTestAt < 0f) && !(Time.time < panicTestAt)) { panicTestAt = -1f; if (IsHost && !PaperPalAI.PanicActive && Plugin.Defs.Count != 0) { PaperPalAI.PanicActive = true; Plugin.Log.LogInfo((object)"[Test] PanicAfterLanding: PaperPals panic (no apparatus pulled)."); } } } [HarmonyPatch(typeof(LungProp), "EquipItem")] [HarmonyPrefix] private static void ApparatusPulled(LungProp __instance) { try { if (IsHost && __instance.isLungDocked && !PaperPalAI.PanicActive && Plugin.Defs.Count != 0) { PaperPalAI.PanicActive = true; Plugin.Log.LogInfo((object)"Apparatus pulled: PaperPals panic."); } } catch (Exception ex) { Plugin.Log.LogError((object)("Apparatus hook failed: " + ex)); } } [HarmonyPatch(typeof(SandWormAI), "OnCollideWithEnemy")] [HarmonyPrefix] private static bool WormWitness(EnemyAI enemyScript) { if (!(enemyScript is PaperPalAI paperPalAI) || paperPalAI.MonsterDamageAllowed(out var why)) { return true; } if (Plugin.DebugLog.Value && IsHost) { Plugin.Log.LogInfo((object)("[Debug] " + paperPalAI.Def?.Id + ": sand worm ignored (" + why + ").")); } return false; } [HarmonyPatch(typeof(EnemyAI), "KillEnemyOnOwnerClient")] [HarmonyPrefix] private static bool KillGuard(EnemyAI __instance) { if (!(__instance is PaperPalAI { SelfKilling: false, isEnemyDead: false } paperPalAI)) { return true; } if (paperPalAI.MonsterDamageAllowed(out var why)) { return true; } if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)("[Debug] " + paperPalAI.Def?.Id + ": instant kill ignored (" + why + ").")); } return false; } [HarmonyPatch(typeof(RoundManager), "SpawnEnemyGameObject")] [HarmonyPrefix] private static bool ReplaceSpawn(RoundManager __instance, ref int enemyNumber, ref EnemyType enemyType, ref NetworkObjectReference __result) { //IL_014e: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.OnlyPaperPals.Value || !((NetworkBehaviour)__instance).IsServer || Plugin.Defs.Count == 0) { return true; } EnemyType original = enemyType; if ((Object)(object)original != (Object)null && Plugin.Defs.Any((NpcDefinition d) => (Object)(object)d.Type == (Object)(object)original)) { return true; } if ((Object)(object)original == (Object)null) { if (enemyNumber == -2 || enemyNumber == -3) { original = null; } else if (enemyNumber >= 0 && enemyNumber < __instance.currentLevel.Enemies.Count) { original = __instance.currentLevel.Enemies[enemyNumber].enemyType; } else { original = Plugin.Defs[0].Type; } } if ((Object)(object)original != (Object)null && Plugin.Defs.Any((NpcDefinition d) => (Object)(object)d.Type == (Object)(object)original)) { return true; } if ((Object)(object)original == (Object)null || original.isOutsideEnemy || original.isDaytimeEnemy) { Plugin.Log.LogInfo((object)("[OnlyPaperPals] cancelled outside spawn (" + (original?.enemyName ?? "outside/daytime") + ").")); __result = default(NetworkObjectReference); return false; } NpcDefinition npcDefinition = Plugin.Defs[Random.Range(0, Plugin.Defs.Count)]; Plugin.Log.LogInfo((object)("[OnlyPaperPals] " + original.enemyName + " -> " + npcDefinition.Id)); enemyType = npcDefinition.Type; return true; } [HarmonyPatch(typeof(RoundManager), "SpawnEnemiesOutside")] [HarmonyPrefix] private static bool NoOutside() { return !Plugin.OnlyPaperPals.Value; } [HarmonyPatch(typeof(RoundManager), "SpawnDaytimeEnemiesOutside")] [HarmonyPrefix] private static bool NoDaytime() { return !Plugin.OnlyPaperPals.Value; } [HarmonyPatch(typeof(RoundManager), "Update")] [HarmonyPostfix] private static void Sweep(RoundManager __instance) { if (!Plugin.OnlyPaperPals.Value || Time.time < nextSweep || !((NetworkBehaviour)__instance).IsServer) { return; } nextSweep = Time.time + 2f; try { EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>(); foreach (EnemyAI val in array) { if (!((Object)(object)val == (Object)null) && !(val is PaperPalAI)) { NetworkObject networkObject = ((NetworkBehaviour)val).NetworkObject; if (!((Object)(object)networkObject == (Object)null) && networkObject.IsSpawned) { Plugin.Log.LogInfo((object)("[OnlyPaperPals] despawned " + (val.enemyType?.enemyName ?? ((Object)val).name) + ".")); __instance.SpawnedEnemies.Remove(val); networkObject.Despawn(true); } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[OnlyPaperPals] sweep: " + ex.Message)); } } } [BepInPlugin("luver_inn.paperpals", "PaperPals", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "luver_inn.paperpals"; public const string Version = "1.0.0"; internal static ManualLogSource Log; internal static string PluginDir; internal static readonly List<NpcDefinition> Defs = new List<NpcDefinition>(); internal static ConfigEntry<string> MirageFolder; internal static ConfigEntry<string> FearIgnore; internal static ConfigEntry<bool> OnlyPaperPals; internal static ConfigEntry<bool> GuaranteedSpawn; internal static ConfigEntry<bool> SpawnNearEntrance; internal static ConfigEntry<bool> DebugLog; internal static ConfigEntry<bool> MatchPlayerEyes; internal static ConfigEntry<bool> PanicEnabled; internal static ConfigEntry<int> GuaranteedSpawnCount; internal static ConfigEntry<float> Volume; internal static ConfigEntry<float> HearingDistance; internal static ConfigEntry<float> MouthThreshold; internal static ConfigEntry<float> StepTime; internal static ConfigEntry<float> CooldownMin; internal static ConfigEntry<float> CooldownMax; internal static ConfigEntry<float> AloneMin; internal static ConfigEntry<float> AloneMax; internal static ConfigEntry<float> TalkRange; internal static ConfigEntry<float> PanicSpeed; internal static ConfigEntry<float> PanicAfterLanding; internal static ConfigEntry<float> ShoutPauseMin; internal static ConfigEntry<float> ShoutPauseMax; internal static ConfigEntry<float> DoorTimeout; internal static ConfigEntry<float> LieOffset; internal static ConfigEntry<float> SneakSpeed; internal static ConfigEntry<float> WitnessRange; internal static ConfigEntry<float> WitnessCloseRange; internal static ConfigEntry<float> GraceSeconds; private void Awake() { //IL_0100: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); BindGlobalConfig(); foreach (NpcDefinition item in FindDefinitions()) { item.BindConfig(((BaseUnityPlugin)this).Config); if (!item.Enabled.Value) { Log.LogInfo((object)(item.Id + ": disabled in config.")); continue; } item.Sprites = SpriteSet.Load(item); if (item.Sprites.Ok) { item.Voice = new VoiceLibrary(item); item.Index = Defs.Count; Defs.Add(item); try { BuildAndRegister(item); } catch (Exception arg) { Log.LogError((object)$"{item.Id}: registration failed: {arg}"); Defs.Remove(item); } } } new Harmony("luver_inn.paperpals").PatchAll(typeof(Patches)); Log.LogInfo((object)(string.Format("PaperPals {0} loaded: {1} PaperPal(s) [{2}]", "1.0.0", Defs.Count, string.Join(", ", Defs.Select((NpcDefinition d) => d.Id))) + ((OnlyPaperPals.Value || GuaranteedSpawn.Value) ? " ** TEST HELPERS ON **" : ""))); } private void BindGlobalConfig() { MirageFolder = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "MirageFolder", "", "Mirage data folder. Empty = <game folder>/Mirage. Host only."); Volume = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "Volume", 1f, "0 to 1."); HearingDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "HearingDistance", 28f, "Metres at which their voices fade out."); CooldownMin = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "CooldownMin", 20f, "Minimum seconds between lines to a player. Host only."); CooldownMax = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "CooldownMax", 60f, "Maximum seconds between lines to a player. Host only."); AloneMin = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "AloneMin", 90f, "Lines said to nobody: minimum seconds between them. 0 = never. Host only."); AloneMax = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "AloneMax", 240f, "Lines said to nobody: maximum seconds between them. Host only."); TalkRange = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "TalkRange", 12f, "They talk to a player in view within this many metres. Host only."); MouthThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Sprite", "MouthThreshold", 0.02f, "Audio loudness (RMS) above which the mouth opens."); StepTime = ((BaseUnityPlugin)this).Config.Bind<float>("Sprite", "StepTime", 0.32f, "Seconds each walking frame is shown at normal speed. Live."); MatchPlayerEyes = ((BaseUnityPlugin)this).Config.Bind<bool>("Sprite", "MatchPlayerEyeHeight", true, "Size every pal from a standing player's eye height (a 180 cm pal has his eyes level with yours)."); PanicEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Meltdown", "Enabled", true, "Pulling the apparatus makes every pal panic and run to the ship. Host only."); PanicSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "RunSpeed", 6.5f, "Running speed (meltdown and fleeing)."); ShoutPauseMin = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "ShoutPauseMin", 1.5f, "Seconds between panic/fear lines (min)."); ShoutPauseMax = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "ShoutPauseMax", 4f, "Seconds between panic/fear lines (max)."); DoorTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("Meltdown", "DoorTimeout", 40f, "If a pal can't reach the main entrance in time, he goes through it anyway."); LieOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Health", "LieOffset", 0.12f, "Dead pals lie this far above the floor so clutter doesn't poke through."); SneakSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Fear", "SneakSpeed", 1.4f, "Crouched sneaking speed (m/s)."); FearIgnore = ((BaseUnityPlugin)this).Config.Bind<string>("Fear", "FearIgnore", "Manticoil,Docile Locust Bees", "Monsters (enemyName, comma-separated) that never scare pals. Other PaperPals never do."); WitnessRange = ((BaseUnityPlugin)this).Config.Bind<float>("Witness", "WitnessRange", 30f, "Outside, monsters may hurt a pal only while a player within this range can see him..."); WitnessCloseRange = ((BaseUnityPlugin)this).Config.Bind<float>("Witness", "WitnessCloseRange", 15f, "...or a player is within this range (no sight needed)."); GraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Witness", "GraceSeconds", 10f, "Full immunity from monsters after spawning and after first stepping outside."); OnlyPaperPals = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "OnlyPaperPals", false, "TEST ONLY (host): every inside monster spawn becomes a random pal; no other monsters."); GuaranteedSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "GuaranteedSpawn", false, "TEST ONLY (host): spawn pals on every landing."); GuaranteedSpawnCount = ((BaseUnityPlugin)this).Config.Bind<int>("Testing", "GuaranteedSpawnCount", 1, "How many (cycles through the installed pals)."); SpawnNearEntrance = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "SpawnNearEntrance", false, "TEST ONLY: the first guaranteed pal spawns by the main entrance."); PanicAfterLanding = ((BaseUnityPlugin)this).Config.Bind<float>("Testing", "PanicAfterLanding", 0f, "TEST ONLY (host): start the meltdown panic this many seconds after landing, without pulling the apparatus. 0 = off."); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "DebugLog", false, "Log state every 10 s and every ignored monster hit (host)."); } private static IEnumerable<NpcDefinition> FindDefinitions() { List<string> list = new List<string>(); try { list = (from f in Directory.GetFiles(Paths.PluginPath, "npc.json", SearchOption.AllDirectories) where Path.GetFileName(f) == "npc.json" where !InTemplateFolder(f) select f).OrderBy<string, string>((string f) => f, StringComparer.Ordinal).ToList(); } catch (Exception ex) { Log.LogError((object)("Scanning for npc.json failed: " + ex.Message)); } Log.LogInfo((object)$"{list.Count} npc.json file(s) found."); Dictionary<string, NpcDefinition> dictionary = new Dictionary<string, NpcDefinition>(); foreach (string item in list) { NpcDefinition npcDefinition = NpcDefinition.Load(item); if (npcDefinition != null) { if (dictionary.TryGetValue(npcDefinition.Id, out var value)) { Log.LogError((object)("Duplicate PaperPal id '" + npcDefinition.Id + "' in " + item + "; keeping " + value.JsonPath + ".")); } else { dictionary[npcDefinition.Id] = npcDefinition; } } } return dictionary.Values.OrderBy<NpcDefinition, string>((NpcDefinition d) => d.Id, StringComparer.Ordinal); } private static bool InTemplateFolder(string file) { if (!file.StartsWith(PluginDir, StringComparison.OrdinalIgnoreCase)) { return false; } bool num = file.Substring(PluginDir.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Any((string p) => p == "Template"); if (num) { Log.LogInfo((object)("Skipping template file " + file + ".")); } return num; } private static void BuildAndRegister(NpcDefinition def) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0261: 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_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03f9: Expected O, but got Unknown //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Expected O, but got Unknown //IL_043a: Unknown result type (might be due to invalid IL or missing references) float heightScale = def.HeightScale; GameObject val = NetworkPrefabs.CreateNetworkPrefab("PaperPal_" + def.Id); NavMeshAgent obj = val.AddComponent<NavMeshAgent>(); obj.speed = def.WalkSpeed; obj.angularSpeed = 360f; obj.acceleration = 8f; obj.stoppingDistance = 0.3f; obj.radius = Mathf.Max(0.1f, 0.35f * heightScale); obj.height = Mathf.Max(0.3f, 1.8f * heightScale); obj.obstacleAvoidanceType = (ObstacleAvoidanceType)1; obj.avoidancePriority = 99; Transform transform = new GameObject("Eye").transform; transform.SetParent(val.transform, false); transform.localPosition = new Vector3(0f, 1.6f * heightScale, 0f); GameObject val2 = new GameObject("Billboard"); val2.transform.SetParent(val.transform, false); val2.AddComponent<MeshFilter>().sharedMesh = def.Sprites.Quad;