using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Dawn; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations; using UnityEngine.Audio; using UnityEngine.Networking; using UnityEngine.Playables; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using Y4NGZMonsters.Shared; using Y4NGZPoltergeist.AI; using Y4NGZPoltergeist.NetcodePatcher; using Y4NGZPoltergeist.Patches; [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("Y4NGZPoltergeist")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+25ca94fed298fe073d4c739917e3b7f046e875bf")] [assembly: AssemblyProduct("Y4NGZPoltergeist")] [assembly: AssemblyTitle("Y4NGZPoltergeist")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Y4NGZPoltergeist { [BepInPlugin("y4ngz.lethalcompany.poltergeist", "Y4NGZ Poltergeist", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { internal const string BundleFilename = "y4ngzpoltergeist.bundle"; internal const string PrefabName = "Poltergeist"; internal const string AlternatePrefabName = "thc3"; internal const string DisplayName = "Passenger"; internal const string DawnNamespace = "y4ngz_monsters"; internal const string DawnEnemyKey = "passenger"; internal const string ScanNodeSubtitle = ""; internal const string BestiaryText = "Passenger\n\nDanger level: high\n\nAn intangible facility resident that ignores walls entirely, drifting through them to reach a worker it has chosen. Its claws are survivable. What follows them is not: a sufficiently wounded worker can be worn like a coat, and will turn on their own crew for as long as the thing keeps its grip. It does not enjoy being shot at, and leaves when it is.\n\n"; internal static ManualLogSource Log; private static bool _loggedScanNodeLayer; internal static PoltergeistConfig ModConfig; internal static AssetBundle ModAssets; internal static EnemyType PoltergeistEnemyType; internal static readonly string[] BreachEffectNames = new string[3] { "PoltergeistBreach_Dark", "PoltergeistBreach_Portal", "PoltergeistBreach_Authored" }; internal static GameObject[] BreachEffectPrefabs; internal static GameObject PhaseAuraPrefab; internal static AudioClip PossessedLoopClip; internal const string LooseAudioFolderName = "Poltergeist"; private static AudioMixerGroup _diegeticMaster; private static bool _netcodeInitialized; private static readonly Dictionary RuntimeParticleMaterials = new Dictionary(); private void Awake() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; try { InitializeNetcodeRPCs(); ModConfig = new PoltergeistConfig(((BaseUnityPlugin)this).Config); if (!ModConfig.Enabled.Value) { Log.LogInfo((object)"Y4NGZ Poltergeist is disabled in config; skipping registration."); return; } new Harmony("y4ngz.lethalcompany.poltergeist").PatchAll(typeof(Plugin).Assembly); if (!LoadBundle()) { return; } ((MonoBehaviour)this).StartCoroutine(LoadLoosePossessionAudio()); GameObject val = LoadBundlePrefab(); if ((Object)(object)val == (Object)null) { Log.LogError((object)"Prefab 'Poltergeist' not found in y4ngzpoltergeist.bundle."); DumpBundleAssets(); return; } PreparePrefab(val); PoltergeistEnemyType = CreateEnemyType(val); PoltergeistAI component = val.GetComponent(); if ((Object)(object)component != (Object)null) { ((EnemyAI)component).enemyType = PoltergeistEnemyType; } PoltergeistSpawnWeights spawnWeights = new PoltergeistSpawnWeights(() => ModConfig.SpawnWeightMultiplier.Value); DawnLib.RegisterNetworkPrefab(val); DawnLib.DefineEnemy(NamespacedKey.From("y4ngz_monsters", "passenger"), PoltergeistEnemyType, (Action)delegate(EnemyInfoBuilder builder) { ((BaseInfoBuilder)(object)builder.DefineInside((Action)delegate(EnemyLocationBuilder location) { location.SetWeights((Action>)delegate(WeightTableBuilder table) { table.SetGlobalWeight((IWeighted)(object)spawnWeights); }); }).CreateBestiaryNode("Passenger\n\nDanger level: high\n\nAn intangible facility resident that ignores walls entirely, drifting through them to reach a worker it has chosen. Its claws are survivable. What follows them is not: a sufficiently wounded worker can be worn like a coat, and will turn on their own crew for as long as the thing keeps its grip. It does not enjoy being shot at, and leaves when it is.\n\n").CreateNameKeyword("passenger")).AddTags((IEnumerable)(object)new NamespacedKey[5] { Tags.Hostile, Tags.Organic, Tags.Biped, Tags.Killable, Tags.Medium }); }); Log.LogInfo((object)"Y4NGZ Poltergeist v0.1.0 loaded. Registered as 'Passenger'."); } catch (Exception arg) { Log.LogError((object)string.Format("[{0}] Initialization failed: {1}", "Y4NGZ Poltergeist", arg)); } } private static void InitializeNetcodeRPCs() { if (_netcodeInitialized) { return; } _netcodeInitialized = true; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { MethodInfo[] methods; try { methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } foreach (MethodInfo methodInfo in methods) { if (methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false).Length == 0) { continue; } try { methodInfo.Invoke(null, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[NetcodeInit] " + type.Name + "." + methodInfo.Name + " failed: " + ex.Message)); } } } } } private static bool LoadBundle() { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrEmpty(directoryName)) { Log.LogError((object)"Could not resolve plugin assembly directory."); return false; } string text = Path.Combine(directoryName, "y4ngzpoltergeist.bundle"); if (!File.Exists(text)) { Log.LogError((object)("Missing asset bundle: " + text)); return false; } ModAssets = AssetBundle.LoadFromFile(text); if ((Object)(object)ModAssets == (Object)null) { Log.LogError((object)("AssetBundle.LoadFromFile returned null for " + text + ".")); return false; } return true; } private IEnumerator LoadLoosePossessionAudio() { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, "assets", "Poltergeist"); if (!Directory.Exists(text)) { Log.LogWarning((object)("[PoltergeistAudio] Loose audio folder not found: " + text + ". The local possession loop will be silent.")); yield break; } yield return LoadLooseClip(Path.Combine(text, "plt_possession_loop_01.wav"), delegate(AudioClip clip) { PossessedLoopClip = clip; }); Log.LogInfo((object)$"[PoltergeistAudio] Local possession loop loaded: {(Object)(object)PossessedLoopClip != (Object)null}."); } private IEnumerator LoadLooseClip(string path, Action onLoaded) { if (!File.Exists(path)) { Log.LogWarning((object)("[PoltergeistAudio] Audio file not found: " + path)); yield break; } string text = "file:///" + path.Replace('\\', '/'); AudioType val = (AudioType)(string.Equals(Path.GetExtension(path), ".ogg", StringComparison.OrdinalIgnoreCase) ? 14 : 20); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(text, val); try { yield return request.SendWebRequest(); if ((int)request.result != 1) { Log.LogWarning((object)("[PoltergeistAudio] Failed to load " + path + ": " + request.error)); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null) { yield break; } ((Object)content).name = Path.GetFileNameWithoutExtension(path); onLoaded(content); } finally { ((IDisposable)request)?.Dispose(); } } private static GameObject LoadBundlePrefab() { GameObject val = ModAssets.LoadAsset("Poltergeist") ?? ModAssets.LoadAsset("thc3"); if ((Object)(object)val != (Object)null) { return val; } string[] allAssetNames = ModAssets.GetAllAssetNames(); foreach (string text in allAssetNames) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text.Replace('\\', '/')); if (string.Equals(fileNameWithoutExtension, "Poltergeist", StringComparison.OrdinalIgnoreCase) || string.Equals(fileNameWithoutExtension, "thc3", StringComparison.OrdinalIgnoreCase)) { val = ModAssets.LoadAsset(text); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static void LoadPhaseEffectPrefabs() { BreachEffectPrefabs = (GameObject[])(object)new GameObject[BreachEffectNames.Length]; List list = new List(); for (int i = 0; i < BreachEffectNames.Length; i++) { BreachEffectPrefabs[i] = LoadBundleGameObject(BreachEffectNames[i]); if ((Object)(object)BreachEffectPrefabs[i] == (Object)null) { list.Add(BreachEffectNames[i]); } } PhaseAuraPrefab = LoadBundleGameObject("PoltergeistPhaseAura"); if ((Object)(object)PhaseAuraPrefab == (Object)null) { list.Add("PoltergeistPhaseAura"); } if (list.Count > 0) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[PoltergeistVFX] y4ngzpoltergeist.bundle is missing phase effect prefab(s): " + string.Join(", ", list) + ". Phasing keeps its material treatment and its light; those candidates will not spawn.")); } } else { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)$"[PoltergeistVFX] Loaded {BreachEffectPrefabs.Length} breach candidate(s) and the phase aura."); } } } private static GameObject LoadBundleGameObject(string name) { if ((Object)(object)ModAssets == (Object)null) { return null; } GameObject val = ModAssets.LoadAsset(name); if ((Object)(object)val != (Object)null) { return val; } string[] allAssetNames = ModAssets.GetAllAssetNames(); foreach (string text in allAssetNames) { if (string.Equals(Path.GetFileNameWithoutExtension(text.Replace('\\', '/')), name, StringComparison.OrdinalIgnoreCase)) { GameObject val2 = ModAssets.LoadAsset(text); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } internal static void RepairParticleRenderers(GameObject root, bool force = false) { if ((Object)(object)root == (Object)null) { return; } Renderer[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Material[] sharedMaterials = val.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { continue; } bool flag = false; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if (!((Object)(object)val2 == (Object)null) && (force || IsMaterialBroken(val2))) { sharedMaterials[j] = GetOrCreateRuntimeParticleMaterial(val2); flag = true; } } if (flag) { val.sharedMaterials = sharedMaterials; } } } private static Material GetOrCreateRuntimeParticleMaterial(Material source) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_0103: 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_0125: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source != (Object)null && RuntimeParticleMaterials.TryGetValue(source, out var value) && (Object)(object)value != (Object)null) { return value; } Shader val = Shader.Find("HDRP/Unlit") ?? Shader.Find("High Definition Render Pipeline/Unlit") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Transparent"); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogError((object)"[PoltergeistVFX] No usable unlit shader in the game; particle materials cannot be repaired."); } return source; } Material val2 = new Material(val) { name = (((Object)(object)source != (Object)null) ? ((Object)source).name : "Poltergeist") + "_RuntimeParticle" }; Texture val3 = GetTextureIfPresent(source, "_BaseColorMap") ?? GetTextureIfPresent(source, "_MainTex") ?? GetTextureIfPresent(source, "_UnlitColorMap"); if ((Object)(object)val3 != (Object)null) { SetTextureIfPresent(val2, "_UnlitColorMap", val3); SetTextureIfPresent(val2, "_BaseColorMap", val3); SetTextureIfPresent(val2, "_MainTex", val3); } SetMaterialColor(val2, "_UnlitColor", ReadParticleColor(source)); SetMaterialColor(val2, "_BaseColor", ReadParticleColor(source)); SetMaterialColor(val2, "_Color", ReadParticleColor(source)); SetMaterialFloat(val2, "_SurfaceType", 1f); SetMaterialFloat(val2, "_BlendMode", 1f); SetMaterialFloat(val2, "_ZWrite", 0f); SetMaterialFloat(val2, "_TransparentZWrite", 0f); SetMaterialFloat(val2, "_CullMode", 0f); val2.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); val2.EnableKeyword("_BLENDMODE_ADD"); val2.renderQueue = 3000; if ((Object)(object)source != (Object)null) { RuntimeParticleMaterials[source] = val2; } return val2; } private static Color ReadParticleColor(Material material) { //IL_0009: 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_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)material == (Object)null) { return Color.white; } string[] array = new string[4] { "_TintColor", "_BaseColor", "_Color", "_UnlitColor" }; for (int i = 0; i < array.Length; i++) { if (material.HasProperty(array[i])) { return material.GetColor(array[i]); } } return Color.white; } private static void SetTextureIfPresent(Material material, string property, Texture texture) { if ((Object)(object)material != (Object)null && material.HasProperty(property)) { material.SetTexture(property, texture); } } internal static Color ResolvePhaseLightColor() { //IL_0029: 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_003a: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); ((Color)(ref result))..ctor(0.44f, 0.66f, 1f, 1f); string text = "#6FA8FF"; if (string.IsNullOrWhiteSpace(text)) { return result; } Color result2 = default(Color); if (!ColorUtility.TryParseHtmlString(text.Trim(), ref result2)) { return result; } return result2; } internal static Color ResolvePhaseDissolveEdgeColor() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) string text = "#6FA8FF"; if (string.IsNullOrWhiteSpace(text)) { return ResolvePhaseLightColor(); } Color result = default(Color); if (!ColorUtility.TryParseHtmlString(text.Trim(), ref result)) { return ResolvePhaseLightColor(); } return result; } private static void PreparePrefab(GameObject prefab) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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_0182: Unknown result type (might be due to invalid IL or missing references) AssignUniqueNetworkHash(prefab); prefab.SetActive(true); TrySetTag(prefab, "Enemy"); TrySetLayer(prefab, "Enemies"); prefab.transform.localScale = Vector3.one * Mathf.Clamp(1f, 0.5f, 2.5f); PoltergeistAI poltergeistAI = prefab.GetComponent(); if ((Object)(object)poltergeistAI == (Object)null) { poltergeistAI = prefab.AddComponent(); } if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } Animator val = prefab.GetComponent() ?? prefab.GetComponentInChildren(true); if ((Object)(object)val != (Object)null) { val.applyRootMotion = false; val.cullingMode = (AnimatorCullingMode)0; ((EnemyAI)poltergeistAI).creatureAnimator = val; } else { Log.LogWarning((object)"[Poltergeist] Prefab has no Animator."); } NavMeshAgent val2 = prefab.GetComponent(); if ((Object)(object)val2 == (Object)null) { val2 = prefab.AddComponent(); } ConfigureAgent(val2); ((EnemyAI)poltergeistAI).agent = val2; ((EnemyAI)poltergeistAI).creatureSFX = FindOrCreateAudioSource(prefab.transform, "CreatureSFX", 38f); ((EnemyAI)poltergeistAI).creatureVoice = FindOrCreateAudioSource(prefab.transform, "CreatureVoice", 44f); ((EnemyAI)poltergeistAI).eye = FindOrCreateChild(prefab.transform, "EyeTransform", new Vector3(0f, 1.7f, 0.4f)); poltergeistAI.attackOrigin = FindOrCreateChild(prefab.transform, "AttackOrigin", new Vector3(0f, 1.15f, 0.85f)); poltergeistAI.phaseFxAnchor = FindOrCreateChild(prefab.transform, "PhaseFx", new Vector3(0f, 1.15f, 0f)); Light component = ((Component)poltergeistAI.phaseFxAnchor).GetComponent(); if ((Object)(object)component != (Object)null) { component.intensity = 0f; ((Behaviour)component).enabled = false; } poltergeistAI.phaseLight = null; AssignPoltergeistAudio(poltergeistAI); EnsureRenderers(prefab, (EnemyAI)(object)poltergeistAI); RepairBrokenMaterials(prefab); EnsureBehaviourStates(poltergeistAI); EnsureScanNode(prefab); EnsureCollision(prefab, poltergeistAI); } private static void ConfigureAgent(NavMeshAgent agent) { agent.speed = ModConfig.RoamSpeed.Value; agent.angularSpeed = 380f; agent.acceleration = 16f; agent.stoppingDistance = Mathf.Max(0.4f, ModConfig.AttackRange.Value * 0.6f); agent.radius = 0.55f; agent.height = 2.1f; agent.baseOffset = 0f; agent.autoBraking = true; agent.autoRepath = true; agent.updateRotation = false; agent.areaMask = -1; agent.obstacleAvoidanceType = (ObstacleAvoidanceType)4; agent.avoidancePriority = 34; } private static void EnsureRenderers(GameObject prefab, EnemyAI ai) { //IL_0020: 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_003e: 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) SkinnedMeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren(true); MeshRenderer[] componentsInChildren2 = prefab.GetComponentsInChildren(true); ai.skinnedMeshRenderers = componentsInChildren; ai.meshRenderers = componentsInChildren2; Bounds localBounds = default(Bounds); ((Bounds)(ref localBounds))..ctor(Vector3.up * 1.1f, new Vector3(5.5f, 4.5f, 5.5f)); foreach (SkinnedMeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !IsRuntimeHelperRenderer((Renderer)(object)val)) { ActivateRendererHierarchy(prefab.transform, ((Component)val).transform); ((Component)val).gameObject.SetActive(true); ((Renderer)val).enabled = true; ((Renderer)val).forceRenderingOff = false; val.updateWhenOffscreen = true; ((Renderer)val).localBounds = localBounds; ((Renderer)val).shadowCastingMode = (ShadowCastingMode)1; ((Renderer)val).receiveShadows = true; } } foreach (MeshRenderer val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !IsRuntimeHelperRenderer((Renderer)(object)val2)) { ActivateRendererHierarchy(prefab.transform, ((Component)val2).transform); ((Component)val2).gameObject.SetActive(true); ((Renderer)val2).enabled = true; ((Renderer)val2).forceRenderingOff = false; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)1; ((Renderer)val2).receiveShadows = true; } } ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[PoltergeistRenderer] Prepared {componentsInChildren.Length} skinned renderer(s), {componentsInChildren2.Length} mesh renderer(s)."); } } private static void RepairBrokenMaterials(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren(true); int num = 0; foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null || IsRuntimeHelperRenderer(val)) { continue; } Material[] sharedMaterials = val.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { continue; } bool flag = false; for (int j = 0; j < sharedMaterials.Length; j++) { if (IsMaterialBroken(sharedMaterials[j])) { Material val2 = RebuildMaterial(sharedMaterials[j]); if (!((Object)(object)val2 == (Object)null)) { sharedMaterials[j] = val2; flag = true; num++; } } } if (flag) { val.sharedMaterials = sharedMaterials; } } if (num > 0) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[PoltergeistMaterial] Rebuilt {num} material(s) that lost their shader during bundling."); } } else { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[PoltergeistMaterial] All bundled materials kept their shader; no repair needed."); } } } private static bool IsMaterialBroken(Material material) { if ((Object)(object)material == (Object)null) { return false; } if ((Object)(object)material.shader == (Object)null) { return true; } if (((Object)material.shader).name.IndexOf("InternalError", StringComparison.OrdinalIgnoreCase) < 0) { return ((Object)material.shader).name.IndexOf("Hidden/InternalError", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static Material RebuildMaterial(Material source) { //IL_0056: 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_0082: Expected O, but got Unknown //IL_00cc: 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) Shader val = Shader.Find("HDRP/Lit") ?? Shader.Find("High Definition Render Pipeline/Lit") ?? Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard"); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogError((object)"[PoltergeistMaterial] No usable shader found in the game; cannot repair materials."); } return null; } Material val2 = new Material(val) { name = (((Object)(object)source != (Object)null) ? ((Object)source).name : "Poltergeist") + "_Runtime" }; CopyTexture(source, val2, "_BaseColorMap", "_MainTex"); CopyTexture(source, val2, "_NormalMap", "_BumpMap"); CopyTexture(source, val2, "_MaskMap", "_MetallicGlossMap"); CopyTexture(source, val2, "_EmissiveColorMap", "_EmissionMap"); SetMaterialColor(val2, "_BaseColor", Color.white); SetMaterialColor(val2, "_Color", Color.white); SetMaterialFloat(val2, "_SurfaceType", 0f); SetMaterialFloat(val2, "_AlphaCutoffEnable", 1f); SetMaterialFloat(val2, "_AlphaCutoff", 0.5f); SetMaterialFloat(val2, "_Cutoff", 0.5f); val2.EnableKeyword("_ALPHATEST_ON"); SetMaterialFloat(val2, "_DoubleSidedEnable", 1f); SetMaterialFloat(val2, "_CullMode", 0f); SetMaterialFloat(val2, "_CullModeForward", 0f); val2.EnableKeyword("_DOUBLESIDED_ON"); val2.doubleSidedGI = true; if ((Object)(object)val2.GetTexture("_NormalMap") != (Object)null || (Object)(object)val2.GetTexture("_BumpMap") != (Object)null) { val2.EnableKeyword("_NORMALMAP"); } return val2; } private static void CopyTexture(Material source, Material destination, string hdrpProperty, string standardProperty) { if ((Object)(object)source == (Object)null) { return; } Texture val = GetTextureIfPresent(source, hdrpProperty) ?? GetTextureIfPresent(source, standardProperty); if (!((Object)(object)val == (Object)null)) { if (destination.HasProperty(hdrpProperty)) { destination.SetTexture(hdrpProperty, val); } if (destination.HasProperty(standardProperty)) { destination.SetTexture(standardProperty, val); } } } private static Texture GetTextureIfPresent(Material material, string property) { if (!((Object)(object)material != (Object)null) || !material.HasProperty(property)) { return null; } return material.GetTexture(property); } private static void SetMaterialColor(Material material, string property, Color value) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)material != (Object)null && material.HasProperty(property)) { material.SetColor(property, value); } } private static void SetMaterialFloat(Material material, string property, float value) { if ((Object)(object)material != (Object)null && material.HasProperty(property)) { material.SetFloat(property, value); } } private static void ActivateRendererHierarchy(Transform prefabRoot, Transform rendererTransform) { Transform val = rendererTransform; while ((Object)(object)val != (Object)null) { if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } if (!((Object)(object)val == (Object)(object)prefabRoot)) { val = val.parent; continue; } break; } } private static bool IsRuntimeHelperRenderer(Renderer renderer) { Transform val = (((Object)(object)renderer != (Object)null) ? ((Component)renderer).transform : null); while ((Object)(object)val != (Object)null) { string text = ((Object)val).name ?? string.Empty; if (text.IndexOf("ScanNode", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Collision", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("MapDot", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } val = val.parent; } return false; } private static void EnsureBehaviourStates(PoltergeistAI ai) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_001c: 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_002d: Expected O, but got Unknown //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_0040: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //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) //IL_0079: Expected O, but got Unknown //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_008c: Expected O, but got Unknown //IL_008e: 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_009f: Expected O, but got Unknown ((EnemyAI)ai).enemyBehaviourStates = (EnemyBehaviourState[])(object)new EnemyBehaviourState[8] { new EnemyBehaviourState { name = "Roaming" }, new EnemyBehaviourState { name = "Stalking" }, new EnemyBehaviourState { name = "Phasing" }, new EnemyBehaviourState { name = "Attacking" }, new EnemyBehaviourState { name = "PossessCast" }, new EnemyBehaviourState { name = "Possessing" }, new EnemyBehaviourState { name = "Fleeing" }, new EnemyBehaviourState { name = "Dead" } }; ((EnemyAI)ai).currentBehaviourStateIndex = 0; ((EnemyAI)ai).previousBehaviourStateIndex = 0; ((EnemyAI)ai).currentBehaviourState = ((EnemyAI)ai).enemyBehaviourStates[0]; } internal static void EnsureScanNode(GameObject prefab) { //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) ScanNodeProperties val = null; ScanNodeProperties[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (ScanNodeProperties val2 in componentsInChildren) { bool flag = val2.creatureScanID >= 0; bool flag2 = (Object)(object)val != (Object)null && val.creatureScanID >= 0; bool flag3 = string.Equals(((Object)((Component)val2).gameObject).name, "ScanNode", StringComparison.Ordinal); bool flag4 = (Object)(object)val != (Object)null && string.Equals(((Object)((Component)val).gameObject).name, "ScanNode", StringComparison.Ordinal); if ((Object)(object)val == (Object)null || (flag && !flag2) || (flag == flag2 && flag3 && !flag4)) { val = val2; } } if (componentsInChildren.Length > 1) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[Poltergeist] Found {componentsInChildren.Length} scan nodes; retiring every duplicate."); } } for (int j = 0; j < componentsInChildren.Length; j++) { if ((Object)(object)componentsInChildren[j] != (Object)(object)val) { RetireExtraScanNode(componentsInChildren[j], val, prefab); } } Transform val3 = (((Object)(object)val != (Object)null) ? ((Component)val).transform : FindOrCreateChild(prefab.transform, "ScanNode", new Vector3(0f, 1.3f, 0f))); ((Object)((Component)val3).gameObject).name = "ScanNode"; TrySetTag(((Component)val3).gameObject, "DoNotSet"); TrySetLayer(((Component)val3).gameObject, "ScanNode"); EnsureHierarchyActive(val3, prefab.transform); ((Component)val3).gameObject.SetActive(true); if (!_loggedScanNodeLayer) { _loggedScanNodeLayer = true; ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)($"[Poltergeist] Scan node layer resolved to {((Component)val3).gameObject.layer} " + string.Format("(NameToLayer(\"ScanNode\")={0}, fallback 22); ", LayerMask.NameToLayer("ScanNode")) + $"active={((Component)val3).gameObject.activeInHierarchy}.")); } } BoxCollider val4 = ((Component)val3).GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = ((Component)val3).gameObject.AddComponent(); } ((Collider)val4).enabled = true; ((Collider)val4).isTrigger = true; val4.center = Vector3.zero; val4.size = new Vector3(1.5f, 2.1f, 1.2f); if ((Object)(object)val == (Object)null) { val = ((Component)val3).gameObject.AddComponent(); } val.headerText = "Passenger"; val.subText = ""; val.maxRange = 18; val.minRange = 1; val.requiresLineOfSight = true; val.nodeType = 1; } private static void RetireExtraScanNode(ScanNodeProperties extra, ScanNodeProperties preferred, GameObject root) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)extra == (Object)null) { return; } Scene scene; if ((Object)(object)((Component)extra).gameObject == (Object)(object)root || ((Object)(object)preferred != (Object)null && (Object)(object)((Component)extra).gameObject == (Object)(object)((Component)preferred).gameObject)) { scene = root.scene; if (((Scene)(ref scene)).IsValid()) { Object.Destroy((Object)(object)extra); } else { Object.DestroyImmediate((Object)(object)extra, true); } return; } GameObject gameObject = ((Component)extra).gameObject; gameObject.SetActive(false); Collider[] components = gameObject.GetComponents(); for (int i = 0; i < components.Length; i++) { components[i].enabled = false; } scene = root.scene; if (((Scene)(ref scene)).IsValid()) { Object.Destroy((Object)(object)gameObject); } else { Object.DestroyImmediate((Object)(object)gameObject, true); } } private static void EnsureCollision(GameObject prefab, PoltergeistAI ai) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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) CapsuleCollider obj = prefab.GetComponent() ?? prefab.AddComponent(); ((Collider)obj).isTrigger = false; obj.direction = 1; obj.center = new Vector3(0f, 1.05f, 0f); obj.radius = 0.55f; obj.height = 2.1f; Transform val = FindOrCreateChild(prefab.transform, "Collision", new Vector3(0f, 1f, 0f)); TrySetTag(((Component)val).gameObject, "Enemy"); TrySetLayer(((Component)val).gameObject, "Enemies"); BoxCollider obj2 = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); ((Collider)obj2).isTrigger = true; obj2.center = Vector3.zero; obj2.size = new Vector3(1.5f, 2f, 1.4f); Rigidbody obj3 = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); obj3.isKinematic = true; obj3.useGravity = false; obj3.detectCollisions = true; EnemyAICollisionDetect obj4 = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); obj4.mainScript = (EnemyAI)(object)ai; obj4.alwaysAllowHitting = true; obj4.canCollideWithEnemies = true; obj4.onlyCollideWhenGrounded = false; } private static EnemyType CreateEnemyType(GameObject prefab) { EnemyType obj = ScriptableObject.CreateInstance(); obj.enemyName = "Passenger"; obj.enemyPrefab = prefab; obj.PowerLevel = ModConfig.PowerLevel.Value; obj.DiversityPowerLevel = 1; obj.MaxCount = ModConfig.MaxCount.Value; obj.canDie = true; obj.canBeStunned = true; obj.canBeDestroyed = true; obj.stunTimeMultiplier = 1f; obj.stunGameDifficultyMultiplier = 1f; obj.destroyOnDeath = false; obj.doorSpeedMultiplier = 1f; obj.isOutsideEnemy = false; obj.isDaytimeEnemy = false; obj.normalizedTimeInDayToLeave = 1f; obj.probabilityCurve = AnimationCurve.Constant(0f, 1f, 1f); obj.numberSpawnedFalloff = AnimationCurve.Linear(0f, 1f, 1f, 0.35f); obj.useNumberSpawnedFalloff = true; obj.disableAnimatorWhenFar = false; return obj; } private static Light FindOrCreatePhaseLight(Transform anchor) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) Light obj = ((Component)anchor).GetComponent() ?? ((Component)anchor).gameObject.AddComponent(); obj.type = (LightType)2; obj.color = ResolvePhaseLightColor(); obj.intensity = 0f; obj.range = Mathf.Max(1f, (ModConfig != null) ? 7f : 7f); obj.shadows = (LightShadows)0; obj.renderMode = (LightRenderMode)1; ((Behaviour)obj).enabled = false; return obj; } private static AudioSource FindOrCreateAudioSource(Transform parent, string childName, float maxDistance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) Transform val = FindOrCreateChild(parent, childName, Vector3.zero); AudioSource obj = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); ConfigureWorldAudioSource(obj, 3f, maxDistance); return obj; } internal static void ConfigureWorldAudioSource(AudioSource source, float minDistance, float maxDistance) { if (!((Object)(object)source == (Object)null)) { source.playOnAwake = false; source.spatialBlend = 1f; source.rolloffMode = (AudioRolloffMode)1; source.minDistance = Mathf.Max(0.1f, minDistance); source.maxDistance = Mathf.Max(source.minDistance, maxDistance); source.reverbZoneMix = 1f; source.bypassReverbZones = false; AudioMixerGroup val = ResolveDiegeticMaster(); if ((Object)(object)val != (Object)null) { source.outputAudioMixerGroup = val; } OccludeAudio val2 = ((Component)source).GetComponent(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)source).gameObject.AddComponent(); } val2.useReverb = true; } } internal static void ConfigureLocalAudioSource(AudioSource source) { if (!((Object)(object)source == (Object)null)) { source.playOnAwake = false; source.spatialBlend = 0f; source.rolloffMode = (AudioRolloffMode)1; source.reverbZoneMix = 0f; source.bypassReverbZones = true; AudioMixerGroup val = ResolveDiegeticMaster(); if ((Object)(object)val != (Object)null) { source.outputAudioMixerGroup = val; } } } private static AudioMixerGroup ResolveDiegeticMaster() { if ((Object)(object)_diegeticMaster != (Object)null) { return _diegeticMaster; } SoundManager instance = SoundManager.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.diageticMixer == (Object)null) { return null; } AudioMixerGroup[] array = instance.diageticMixer.FindMatchingGroups("Master"); if (array != null && array.Length != 0) { _diegeticMaster = array[0]; } return _diegeticMaster; } private static void AssignPoltergeistAudio(PoltergeistAI ai) { if (!((Object)(object)ai == (Object)null) && !((Object)(object)ModAssets == (Object)null)) { string[] allAssetNames = ModAssets.GetAllAssetNames(); ai.ambientClips = LoadAudioClipSet(allAssetNames, "plt_passive_01", "plt_passive_02"); ai.aggroClips = LoadAudioClipSet(allAssetNames, "plt_detect_01"); ai.attackWhooshClips = LoadAudioClipSet(allAssetNames, "plt_attack_swipe_01"); ai.attackImpactClips = LoadAudioClipSet(allAssetNames, "plt_impact_body_01", "plt_impact_body_02"); ai.footstepClips = LoadAudioClipSet(allAssetNames, "plt_foley_01", "plt_foley_02"); ai.phaseInClips = LoadAudioClipSet(allAssetNames, "plt_phase_01", "plt_phase_02"); ai.phaseOutClips = (AudioClip[])(object)new AudioClip[0]; ai.possessionClips = LoadAudioClipSet(allAssetNames, "plt_possession_01"); AudioClip[] array = LoadAudioClipSet(allAssetNames, "plt_presence_loop_01"); ai.presenceLoopClip = ((array.Length != 0) ? array[0] : null); ai.painClips = (AudioClip[])(object)new AudioClip[0]; ai.fleeClips = (AudioClip[])(object)new AudioClip[0]; ai.deathClips = (AudioClip[])(object)new AudioClip[0]; ai.howlClips = (AudioClip[])(object)new AudioClip[0]; int num = ai.ambientClips.Length + ai.aggroClips.Length + ai.attackWhooshClips.Length + ai.attackImpactClips.Length + ai.footstepClips.Length + ai.phaseInClips.Length + ai.possessionClips.Length + array.Length; ManualLogSource log = Log; if (log != null) { log.LogInfo((object)string.Format("[PoltergeistAudio] Loaded {0}/12 approved world clip(s) from {1}.", num, "y4ngzpoltergeist.bundle")); } } } private static AudioClip[] LoadAudioClipSet(string[] bundleAssets, params string[] clipNames) { List list = new List(clipNames.Length); for (int i = 0; i < clipNames.Length; i++) { AudioClip val = LoadAudioClip(bundleAssets, clipNames[i]); if ((Object)(object)val != (Object)null) { list.Add(val); } } return list.ToArray(); } private static AudioClip LoadAudioClip(string[] bundleAssets, string clipName) { foreach (string text in bundleAssets) { if (string.Equals(Path.GetFileNameWithoutExtension(text), clipName, StringComparison.OrdinalIgnoreCase)) { AudioClip val = ModAssets.LoadAsset(text); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static Transform FindOrCreateChild(Transform parent, string name, Vector3 localPosition) { //IL_001b: 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_0012: Unknown result type (might be due to invalid IL or missing references) Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { val.localPosition = localPosition; return val; } val = new GameObject(name).transform; val.SetParent(parent, false); val.localPosition = localPosition; return val; } private static void AssignUniqueNetworkHash(GameObject prefab) { try { NetworkObject val = prefab.GetComponent(); if ((Object)(object)val == (Object)null) { val = prefab.AddComponent(); } uint num = BitConverter.ToUInt32(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("y4ngz.lethalcompany.poltergeist.Poltergeist")), 0); PropertyInfo property = typeof(NetworkObject).GetProperty("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(val, num); return; } FieldInfo fieldInfo = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(NetworkObject).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (fieldInfo != null) { fieldInfo.SetValue(val, num); } } catch (Exception ex) { Log.LogWarning((object)("Could not assign NetworkObject hash for Poltergeist: " + ex.Message)); } } private static void DumpBundleAssets() { if (!((Object)(object)ModAssets == (Object)null)) { string[] allAssetNames = ModAssets.GetAllAssetNames(); foreach (string text in allAssetNames) { Log.LogError((object)(" - " + text)); } } } private static void TrySetTag(GameObject gameObject, string tag) { try { gameObject.tag = tag; } catch { } } private static void TrySetLayer(GameObject gameObject, string layerName) { if ((Object)(object)gameObject == (Object)null) { return; } int num = LayerMask.NameToLayer(layerName); if (num < 0) { num = ResolveLayerFallback(layerName); } if (num >= 0 && num <= 31) { gameObject.layer = num; return; } ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[Poltergeist] Layer '{layerName}' unresolved; '{((Object)gameObject).name}' stays on layer {gameObject.layer}."); } } private static int ResolveLayerFallback(string layerName) { if (string.Equals(layerName, "ScanNode", StringComparison.OrdinalIgnoreCase)) { return 22; } if (string.Equals(layerName, "Enemies", StringComparison.OrdinalIgnoreCase)) { return 19; } return -1; } private static void EnsureHierarchyActive(Transform child, Transform stopAt) { Transform val = child; while ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(true); if (!((Object)(object)val == (Object)(object)stopAt)) { val = val.parent; continue; } break; } } } internal static class PluginInfo { public const string PLUGIN_GUID = "y4ngz.lethalcompany.poltergeist"; public const string PLUGIN_NAME = "Y4NGZ Poltergeist"; public const string PLUGIN_VERSION = "0.1.0"; } internal sealed class PoltergeistConfig { private const string SectionGeneral = "General"; private const string SectionSpawning = "Spawning"; private const string SectionCoreStats = "Core Stats"; private const string SectionDetection = "Detection"; private const string SectionMovement = "Movement"; private const string SectionCombat = "Combat"; private const string SectionAbilities = "Abilities"; private const string SectionPresentation = "Audio and Presentation"; private const string SectionDiagnostics = "Diagnostics"; public ConfigEntry Enabled { get; } public ConfigEntry SpawnWeightMultiplier { get; } public ConfigEntry MaxCount { get; } public ConfigEntry PowerLevel { get; } public ConfigEntry Health { get; } public ConfigEntry SightRange { get; } public ConfigEntry RoamSpeed { get; } public ConfigEntry StalkSpeed { get; } public ConfigEntry ChaseSpeed { get; } public ConfigEntry FleeSpeed { get; } public ConfigEntry AttackDamage { get; } public ConfigEntry AttackRange { get; } public ConfigEntry AttackCooldown { get; } public ConfigEntry TacticsEnabled { get; } public ConfigEntry SightingRetreatChance { get; } public ConfigEntry WallPhasingEnabled { get; } public ConfigEntry PhaseCooldown { get; } public ConfigEntry PossessionEnabled { get; } public ConfigEntry PossessionHealthThreshold { get; } public ConfigEntry PossessionDurationSeconds { get; } public ConfigEntry PossessionRange { get; } public ConfigEntry PossessionCooldownPerPlayerSeconds { get; } public ConfigEntry PossessedFriendlyFire { get; } public ConfigEntry PossessedAttacksEnabled { get; } public ConfigEntry PossessedAttackCooldownSeconds { get; } public ConfigEntry PossessedAttackDamage { get; } public ConfigEntry StealthPossessionEnabled { get; } public ConfigEntry PhaseVfxEnabled { get; } public ConfigEntry PossessionLoopVolume { get; } public ConfigEntry PossessedVoiceFilterEnabled { get; } public ConfigEntry DebugLogs { get; } public ConfigEntry PhaseDiagnostics { get; } public ConfigEntry PhaseDiagnosticsSeconds { get; } public ConfigEntry PossessionDiagnostics { get; } public ConfigEntry PossessionDebugAllowSolo { get; } public PoltergeistConfig(ConfigFile config) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Expected O, but got Unknown //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Expected O, but got Unknown //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Expected O, but got Unknown //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Expected O, but got Unknown //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Expected O, but got Unknown //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Expected O, but got Unknown //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Expected O, but got Unknown //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Expected O, but got Unknown //IL_0462: Unknown result type (might be due to invalid IL or missing references) //IL_046c: Expected O, but got Unknown //IL_0491: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Expected O, but got Unknown //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Expected O, but got Unknown //IL_058f: Unknown result type (might be due to invalid IL or missing references) //IL_0599: Expected O, but got Unknown bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { PoltergeistConfigMigration.TryMigrate(config, Plugin.Log); ConfigSectionNameMigration.TryMigrate(config, Plugin.Log, "Passenger"); Enabled = config.Bind("General", "Enabled", true, "Enable the Passenger encounter."); SpawnWeightMultiplier = config.Bind("Spawning", "SpawnWeightMultiplier", 1f, new ConfigDescription("Multiplier applied to Passenger's contextual moon/interior spawn policy. 0 disables natural spawns.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); MaxCount = config.Bind("Spawning", "MaxCount", 1, new ConfigDescription("Maximum number of living Passengers at once.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 4), Array.Empty())); PowerLevel = config.Bind("Spawning", "PowerLevel", 2.5f, new ConfigDescription("Indoor enemy power budget cost for each Passenger.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 20f), Array.Empty())); Health = config.Bind("Core Stats", "Health", 5, new ConfigDescription("Hits Passenger can take before dying.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); SightRange = config.Bind("Detection", "SightRange", 45f, new ConfigDescription("Maximum distance at which Passenger tracks line of sight to players.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 150f), Array.Empty())); RoamSpeed = BindSpeed(config, "RoamSpeed", 2.2f, "Movement speed while searching the interior."); StalkSpeed = BindSpeed(config, "StalkSpeed", 3f, "Movement speed while stalking a known target."); ChaseSpeed = BindSpeed(config, "ChaseSpeed", 4.6f, "Movement speed during committed pursuit."); FleeSpeed = BindSpeed(config, "FleeSpeed", 5.4f, "Movement speed while retreating after damage."); AttackDamage = config.Bind("Combat", "AttackDamage", 20, new ConfigDescription("Damage dealt by one claw swing.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); AttackRange = config.Bind("Combat", "AttackRange", 2.2f, new ConfigDescription("Distance required to begin a claw attack.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 8f), Array.Empty())); AttackCooldown = config.Bind("Combat", "AttackCooldown", 1.6f, new ConfigDescription("Seconds between claw attacks.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 30f), Array.Empty())); TacticsEnabled = config.Bind("Combat", "TacticsEnabled", true, "Allow Passenger to flank, stalk, ambush, withdraw, and phase-reposition instead of only approaching directly."); SightingRetreatChance = config.Bind("Combat", "SightingRetreatChance", 0.3f, new ConfigDescription("Chance that acquiring a player opens with a retreat.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); WallPhasingEnabled = config.Bind("Abilities", "WallPhasingEnabled", true, "Allow Passenger to phase through geometry when the navigable route is much longer than the direct route."); PhaseCooldown = config.Bind("Abilities", "PhaseCooldown", 5f, new ConfigDescription("Seconds between wall-phase traversals.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 60f), Array.Empty())); PossessionEnabled = config.Bind("Abilities", "PossessionEnabled", true, "Allow Passenger to possess eligible workers."); PossessionHealthThreshold = config.Bind("Abilities", "PossessionHealthThreshold", 75, new ConfigDescription("A wounded worker below this health can be possessed while Passenger is visible.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); PossessionDurationSeconds = config.Bind("Abilities", "PossessionDurationSeconds", 20f, new ConfigDescription("Base possession duration in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 120f), Array.Empty())); PossessionRange = config.Bind("Abilities", "PossessionRange", 3f, new ConfigDescription("Maximum distance for Passenger to begin possession.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 12f), Array.Empty())); PossessionCooldownPerPlayerSeconds = config.Bind("Abilities", "PossessionCooldownPerPlayerSeconds", 90f, new ConfigDescription("Seconds before the same worker can be possessed again.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 600f), Array.Empty())); PossessedFriendlyFire = config.Bind("Abilities", "PossessedFriendlyFire", true, "Allow a possessed worker to attack teammates."); PossessedAttacksEnabled = config.Bind("Abilities", "PossessedAttacksEnabled", true, "Allow Passenger to force possessed workers to perform claw attacks."); PossessedAttackCooldownSeconds = config.Bind("Abilities", "PossessedAttackCooldownSeconds", 1.6f, new ConfigDescription("Seconds between forced attacks by a possessed worker.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 30f), Array.Empty())); PossessedAttackDamage = config.Bind("Abilities", "PossessedAttackDamage", 10, new ConfigDescription("Damage dealt when a possessed worker hits a teammate.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); StealthPossessionEnabled = config.Bind("Abilities", "StealthPossessionEnabled", true, "Allow Passenger to attempt possession before the worker is wounded when it remains unseen."); PhaseVfxEnabled = config.Bind("Audio and Presentation", "PhaseVfxEnabled", true, "Fade Passenger to neutral transparency during wall-phase traversals."); PossessionLoopVolume = config.Bind("Audio and Presentation", "PossessionLoopVolume", 0.7f, new ConfigDescription("Volume of the local possession loop.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); PossessedVoiceFilterEnabled = config.Bind("Audio and Presentation", "PossessedVoiceFilterEnabled", true, "Apply Passenger's authored voice effect to a possessed worker."); DebugLogs = config.Bind("Diagnostics", "DebugLogs", false, "Write verbose Passenger AI state logs."); PhaseDiagnostics = config.Bind("Diagnostics", "PhaseDiagnostics", true, "Write periodic wall-phase planning diagnostics."); PhaseDiagnosticsSeconds = config.Bind("Diagnostics", "PhaseDiagnosticsSeconds", 20f, new ConfigDescription("Seconds between phase diagnostic summaries.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 300f), Array.Empty())); PossessionDiagnostics = config.Bind("Diagnostics", "PossessionDiagnostics", true, "Write possession eligibility and lifecycle diagnostics."); PossessionDebugAllowSolo = config.Bind("Diagnostics", "PossessionDebugAllowSolo", false, "Permit possession with no other living teammate for local diagnostics."); } finally { config.SaveOnConfigSet = saveOnConfigSet; if (saveOnConfigSet) { config.Save(); } } } private static ConfigEntry BindSpeed(ConfigFile config, string key, float defaultValue, string description) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown return config.Bind("Movement", key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 20f), Array.Empty())); } } internal static class PoltergeistConfigMigration { private static readonly ConfigDefinition LegacySpawnWeight = new ConfigDefinition("10 - Spawning", "SpawnWeight"); private static readonly Dictionary RemovedDefaults = new Dictionary { { new ConfigDefinition("10 - Spawning", "LevelMask"), "-1" }, { new ConfigDefinition("20 - Core Stats", "ModelScale"), "1.0" }, { new ConfigDefinition("30 - Detection", "SightConeWidth"), "75" }, { new ConfigDefinition("30 - Detection", "ProximityAwarenessRange"), "4" }, { new ConfigDefinition("30 - Detection", "LoseTargetSeconds"), "6" }, { new ConfigDefinition("30 - Detection", "LoseTargetDistance"), "55" }, { new ConfigDefinition("30 - Detection", "RoamSearchWidth"), "50" }, { new ConfigDefinition("30 - Detection", "RoamSearchPrecision"), "6" }, { new ConfigDefinition("40 - Movement", "TurnSpeed"), "380" }, { new ConfigDefinition("40 - Movement", "Acceleration"), "16" }, { new ConfigDefinition("50 - Combat", "AttackDamageDelay"), "0.42" }, { new ConfigDefinition("50 - Combat", "FleeSeconds"), "9" }, { new ConfigDefinition("50 - Combat", "FleeBackpedalSeconds"), "1.2" }, { new ConfigDefinition("50 - Combat", "TacticCommitSeconds"), "3" }, { new ConfigDefinition("50 - Combat", "TacticStalkHoldDistance"), "11" }, { new ConfigDefinition("50 - Combat", "TacticAmbushRange"), "6.5" }, { new ConfigDefinition("50 - Combat", "TacticPlayerGazeAngle"), "50" }, { new ConfigDefinition("50 - Combat", "TacticWeightDirect"), "16" }, { new ConfigDefinition("50 - Combat", "TacticWeightFlank"), "22" }, { new ConfigDefinition("50 - Combat", "TacticWeightStalk"), "16" }, { new ConfigDefinition("50 - Combat", "TacticWeightAmbush"), "20" }, { new ConfigDefinition("50 - Combat", "TacticWeightWithdraw"), "16" }, { new ConfigDefinition("50 - Combat", "TacticWeightPhaseReposition"), "22" }, { new ConfigDefinition("60 - Abilities", "PhasePathRatioThreshold"), "1.7" }, { new ConfigDefinition("60 - Abilities", "PhaseMinDistance"), "4" }, { new ConfigDefinition("60 - Abilities", "PhaseMaxDistance"), "18" }, { new ConfigDefinition("60 - Abilities", "PhaseDriftSpeed"), "3.2" }, { new ConfigDefinition("60 - Abilities", "PhaseRepositionPathRatio"), "1.2" }, { new ConfigDefinition("60 - Abilities", "PhaseImpulseChance"), "0.35" }, { new ConfigDefinition("60 - Abilities", "PhaseWhileRoaming"), "true" }, { new ConfigDefinition("60 - Abilities", "PhaseRoamPathRatio"), "1.5" }, { new ConfigDefinition("60 - Abilities", "PossessionCastSeconds"), "1.6" }, { new ConfigDefinition("60 - Abilities", "PossessionBreakHealthFloor"), "20" }, { new ConfigDefinition("60 - Abilities", "PuppetWalkSpeed"), "3.5" }, { new ConfigDefinition("60 - Abilities", "PuppetSprintDistance"), "6" }, { new ConfigDefinition("60 - Abilities", "PuppetSprintMultiplier"), "2.25" }, { new ConfigDefinition("60 - Abilities", "PossessionExtensionSeconds"), "10" }, { new ConfigDefinition("60 - Abilities", "PossessionExtensionRange"), "2.5" }, { new ConfigDefinition("60 - Abilities", "PuppetTurretAvoidRadius"), "5" }, { new ConfigDefinition("60 - Abilities", "PuppetMineAvoidRadius"), "2.5" }, { new ConfigDefinition("60 - Abilities", "PossessedAttackRange"), "2.2" }, { new ConfigDefinition("60 - Abilities", "PossessionShipSafeRadius"), "20" }, { new ConfigDefinition("60 - Abilities", "StealthSneakAbortDistance"), "4" }, { new ConfigDefinition("60 - Abilities", "StealthSneakCooldownSeconds"), "12" }, { new ConfigDefinition("70 - Audio and Presentation", "MatchAnimationToMovement"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "AuthoredWalkSpeed"), "1.9" }, { new ConfigDefinition("70 - Audio and Presentation", "DeathDissipateDelay"), "0.9" }, { new ConfigDefinition("70 - Audio and Presentation", "DeathDissipateSeconds"), "2" }, { new ConfigDefinition("70 - Audio and Presentation", "AuthoredRunSpeed"), "4.2" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseVfxVariant"), "0" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseBreachAlignAxis"), "0" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseBreachScale"), "1" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseForceHdrpParticleShaders"), "false" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseBreachLingerSeconds"), "2.5" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseLightIntensity"), "14" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseLightRange"), "7" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseLightColor"), "#6FA8FF" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseDissolveEnabled"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseDissolveOffsetAxis"), "0" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseDissolveDepth"), "0.65" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseDissolveEdgeWidth"), "0.5" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseDissolveEdgeIntensity"), "40" }, { new ConfigDefinition("70 - Audio and Presentation", "PhaseDissolveEdgeColor"), "#6FA8FF" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionMergeVfxEnabled"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionMergeSeconds"), "0.7" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionEmergeSeconds"), "0.9" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionMergeMinScale"), "0.12" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionRelocateOnRelease"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionVisorGlowIntensity"), "10" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionVisorGlowRange"), "3" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoicePitch"), "0.68" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceDistortionV2"), "0.35" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceEchoDelayV2"), "110" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceEchoDecayV2"), "0.45" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceEchoWetMixV2"), "0.45" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceDemonLayerMix"), "0.8" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceRingModHzV2"), "48" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceRingModMixV2"), "0.5" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceNoiseGateMix"), "0.12" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceBitcrushMixV2"), "0.25" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedVoiceDriveV2"), "1.6" }, { new ConfigDefinition("70 - Audio and Presentation", "SpectralArmsEnabled"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "SpectralArmsScale"), "0.8" }, { new ConfigDefinition("70 - Audio and Presentation", "SpectralArmsHeightOffset"), "0.08" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedHandsOutEnabled"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedHandsOutWeight"), "1" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionBlurIntensity"), "0.75" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessionAudioLowPassCutoff"), "1000" }, { new ConfigDefinition("70 - Audio and Presentation", "UpdateBoundsWhenOffscreen"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "AlwaysAnimate"), "true" }, { new ConfigDefinition("70 - Audio and Presentation", "PossessedHitVolume"), "0.9" } }; internal static bool TryMigrate(ConfigFile config, ManualLogSource log) { string configFilePath = config.ConfigFilePath; if (!File.Exists(configFilePath)) { return false; } Dictionary dictionary = null; string text = null; try { dictionary = ReadValues(configFilePath); if (!ContainsLegacyConfig(dictionary)) { return false; } text = GetBackupPath(configFilePath); File.Copy(configFilePath, text, overwrite: false); File.WriteAllText(configFilePath, BuildMigratedConfig(dictionary), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); config.Reload(); } catch (Exception ex) { string text2 = "The original config was not modified."; if (!string.IsNullOrEmpty(text) && File.Exists(text)) { try { File.Copy(text, configFilePath, overwrite: true); config.Reload(); text2 = "The original config was restored from its backup."; } catch (Exception ex2) { text2 = "Automatic restore also failed: " + ex2.GetBaseException().Message + ". Recover manually from " + text + "."; } } if (log != null) { log.LogWarning((object)("[Poltergeist] Could not migrate legacy config: " + ex.GetBaseException().Message + " " + text2)); } return false; } List list = FindCustomizedDroppedKeys(dictionary); if (log != null) { log.LogInfo((object)("[Poltergeist] Migrated legacy config to the compact DawnLib layout. Backup: " + text)); } if (list.Count > 0 && log != null) { log.LogWarning((object)("[Poltergeist] These customized legacy settings are now authored source tuning and were not carried forward: " + string.Join(", ", list))); } return true; } private static bool ContainsLegacyConfig(Dictionary values) { if (values.ContainsKey(LegacySpawnWeight)) { return true; } foreach (ConfigDefinition key in RemovedDefaults.Keys) { if (values.ContainsKey(key)) { return true; } } return false; } private static string BuildMigratedConfig(Dictionary values) { string text = GetValue(values, "10 - Spawning", "SpawnWeightMultiplier", null); if (string.IsNullOrWhiteSpace(text)) { text = ((!float.TryParse(GetValue(values, LegacySpawnWeight, "14"), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) ? "1" : Math.Max(0f, Math.Min(5f, result / 14f)).ToString("0.###", CultureInfo.InvariantCulture)); } StringBuilder stringBuilder = new StringBuilder(); AppendSection(stringBuilder, "00 - General", ("Enabled", GetValue(values, "00 - General", "Enabled", "true"))); AppendSection(stringBuilder, "10 - Spawning", ("SpawnWeightMultiplier", text), ("MaxCount", GetValue(values, "10 - Spawning", "MaxCount", "1")), ("PowerLevel", GetValue(values, "10 - Spawning", "PowerLevel", "2.5"))); AppendSection(stringBuilder, "20 - Core Stats", ("Health", GetValue(values, "20 - Core Stats", "Health", "5"))); AppendSection(stringBuilder, "30 - Detection", ("SightRange", GetValue(values, "30 - Detection", "SightRange", "45"))); AppendSection(stringBuilder, "40 - Movement", ("RoamSpeed", GetValue(values, "40 - Movement", "RoamSpeed", "2.2")), ("StalkSpeed", GetValue(values, "40 - Movement", "StalkSpeed", "3")), ("ChaseSpeed", GetValue(values, "40 - Movement", "ChaseSpeed", "4.6")), ("FleeSpeed", GetValue(values, "40 - Movement", "FleeSpeed", "5.4"))); AppendSection(stringBuilder, "50 - Combat", ("AttackDamage", GetValue(values, "50 - Combat", "AttackDamage", "20")), ("AttackRange", GetValue(values, "50 - Combat", "AttackRange", "2.2")), ("AttackCooldown", GetValue(values, "50 - Combat", "AttackCooldown", "1.6")), ("TacticsEnabled", GetValue(values, "50 - Combat", "TacticsEnabled", "true")), ("SightingRetreatChance", GetValue(values, "50 - Combat", "SightingRetreatChance", "0.3"))); AppendSection(stringBuilder, "60 - Abilities", ("WallPhasingEnabled", GetValue(values, "60 - Abilities", "WallPhasingEnabled", "true")), ("PhaseCooldown", GetValue(values, "60 - Abilities", "PhaseCooldown", "5")), ("PossessionEnabled", GetValue(values, "60 - Abilities", "PossessionEnabled", "true")), ("PossessionHealthThreshold", GetValue(values, "60 - Abilities", "PossessionHealthThreshold", "75")), ("PossessionDurationSeconds", GetValue(values, "60 - Abilities", "PossessionDurationSeconds", "20")), ("PossessionRange", GetValue(values, "60 - Abilities", "PossessionRange", "3")), ("PossessionCooldownPerPlayerSeconds", GetValue(values, "60 - Abilities", "PossessionCooldownPerPlayerSeconds", "90")), ("PossessedFriendlyFire", GetValue(values, "60 - Abilities", "PossessedFriendlyFire", "true")), ("PossessedAttacksEnabled", GetValue(values, "60 - Abilities", "PossessedAttacksEnabled", "true")), ("PossessedAttackCooldownSeconds", GetValue(values, "60 - Abilities", "PossessedAttackCooldownSeconds", "1.6")), ("PossessedAttackDamage", GetValue(values, "60 - Abilities", "PossessedAttackDamage", "10")), ("StealthPossessionEnabled", GetValue(values, "60 - Abilities", "StealthPossessionEnabled", "true"))); AppendSection(stringBuilder, "70 - Audio and Presentation", ("PhaseVfxEnabled", GetValue(values, "70 - Audio and Presentation", "PhaseVfxEnabled", "true")), ("PossessionLoopVolume", GetValue(values, "70 - Audio and Presentation", "PossessionLoopVolume", "0.7")), ("PossessedVoiceFilterEnabled", GetValue(values, "70 - Audio and Presentation", "PossessedVoiceFilterEnabled", "true"))); AppendSection(stringBuilder, "99 - Diagnostics", ("DebugLogs", GetValue(values, "99 - Diagnostics", "DebugLogs", "false")), ("PhaseDiagnostics", GetValue(values, "99 - Diagnostics", "PhaseDiagnostics", "true")), ("PhaseDiagnosticsSeconds", GetValue(values, "99 - Diagnostics", "PhaseDiagnosticsSeconds", "20")), ("PossessionDiagnostics", GetValue(values, "99 - Diagnostics", "PossessionDiagnostics", "true")), ("PossessionDebugAllowSolo", GetValue(values, "99 - Diagnostics", "PossessionDebugAllowSolo", "false"))); return stringBuilder.ToString(); } private static void AppendSection(StringBuilder output, string section, params (string Key, string Value)[] entries) { output.Append('[').Append(section).AppendLine("]"); for (int i = 0; i < entries.Length; i++) { output.Append(entries[i].Key).Append(" = ").AppendLine(entries[i].Value); } output.AppendLine(); } private static string GetValue(Dictionary values, string section, string key, string fallback) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown return GetValue(values, new ConfigDefinition(section, key), fallback); } private static string GetValue(Dictionary values, ConfigDefinition definition, string fallback) { if (!values.TryGetValue(definition, out var value)) { return fallback; } return value; } private static Dictionary ReadValues(string path) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown Dictionary dictionary = new Dictionary(); string text = string.Empty; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal)) { continue; } if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal)) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string value = text2.Substring(num + 1).Trim(); dictionary[new ConfigDefinition(text, text3)] = value; } } return dictionary; } private static List FindCustomizedDroppedKeys(Dictionary values) { List list = new List(); foreach (KeyValuePair removedDefault in RemovedDefaults) { if (values.TryGetValue(removedDefault.Key, out var value) && !ValuesEquivalent(value, removedDefault.Value)) { list.Add(removedDefault.Key.Section + "." + removedDefault.Key.Key); } } list.Sort(StringComparer.OrdinalIgnoreCase); return list; } private static bool ValuesEquivalent(string left, string right) { if (string.Equals(left.Trim(), right.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } if (float.TryParse(left, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(right, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return Math.Abs(result - result2) < 0.0001f; } return false; } private static string GetBackupPath(string configPath) { string text = configPath + ".pre-dawn.bak"; if (!File.Exists(text)) { return text; } for (int i = 2; i < 1000; i++) { string text2 = configPath + $".pre-dawn.{i}.bak"; if (!File.Exists(text2)) { return text2; } } throw new IOException("Could not allocate a Poltergeist config backup filename."); } } internal sealed class PoltergeistSpawnWeights : IWeighted, IContextualWeighted { internal const int DefaultWeight = 3; internal const int MaximumWeight = 100; private readonly Func _multiplier; internal PoltergeistSpawnWeights(Func multiplier) { _multiplier = multiplier ?? throw new ArgumentNullException("multiplier"); } public int GetWeight() { return Scale(3, 1f); } public int GetWeight(SpawnWeightContext context) { int moonWeight = GetMoonWeight(((SpawnWeightContext)(ref context)).Moon); if (moonWeight <= 0) { return 0; } return Scale(moonWeight, GetDungeonMultiplier(((SpawnWeightContext)(ref context)).Dungeon)); } internal static int GetMoonWeight(DawnMoonInfo moon) { if (moon == null) { return 3; } if (((DawnBaseInfo)(object)moon).HasTag(Tags.Company) || ((DawnBaseInfo)(object)moon).HasTag(Tags.Unimplemented) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Test)) { return 0; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Experimentation)) { return 1; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Assurance) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Vow)) { return 3; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Offense) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.March) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Embrion)) { return 5; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Adamance)) { return 7; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Rend) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Dine)) { return 9; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Titan) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Artifice) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Liquidation)) { return 11; } if (((DawnBaseInfo)(object)moon).HasTag(Tags.Paid)) { return 8; } ((DawnBaseInfo)(object)moon).HasTag(Tags.Free); return 3; } internal static float GetDungeonMultiplier(DawnDungeonInfo dungeon) { if (dungeon == null) { return 1f; } if (KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.MansionFlow) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Gothic) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Grand) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Ornate) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Lavish) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Eerie)) { return 1.25f; } if (KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.FacilityFlow) || KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.FacilityFlowThreeExits) || KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.FacilityFlowExtraLarge) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Industrial) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Mechanical) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Factory) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Bunker)) { return 1f; } if (KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.MineshaftFlow) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Cavern) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Quarry) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Flooded)) { return 0.5f; } return 0.75f; } private int Scale(int moonWeight, float dungeonMultiplier) { float num = Mathf.Clamp(_multiplier(), 0f, 5f); return Mathf.Clamp(Mathf.RoundToInt((float)moonWeight * dungeonMultiplier * num), 0, 100); } private static bool KeyEquals(NamespacedKey left, NamespacedKey right) where T : INamespaced { if (left != null && right != null) { return ((object)left).Equals((object?)right); } return false; } } internal static class PoltergeistTuning { internal const float ModelScale = 1f; internal const float SightConeWidth = 75f; internal const float ProximityAwarenessRange = 4f; internal const float LoseTargetSeconds = 6f; internal const float LoseTargetDistance = 55f; internal const float RoamSearchWidth = 50f; internal const float RoamSearchPrecision = 6f; internal const float TurnSpeed = 380f; internal const float Acceleration = 16f; internal const float AttackAnimationSpeed = 1.4f; internal const float AttackContactWindupSeconds = 0.08f; internal const float AttackContactWindowSeconds = 0.72f; internal const float AttackHandRadius = 0.18f; internal const float FleeSeconds = 9f; internal const float FleeBackpedalSeconds = 1.2f; internal const float TacticCommitSeconds = 3f; internal const float TacticStalkHoldDistance = 11f; internal const float TacticAmbushRange = 6.5f; internal const float TacticPlayerGazeAngle = 50f; internal const int TacticWeightDirect = 16; internal const int TacticWeightFlank = 22; internal const int TacticWeightStalk = 16; internal const int TacticWeightAmbush = 20; internal const int TacticWeightWithdraw = 16; internal const int TacticWeightPhaseReposition = 22; internal const float PhasePathRatioThreshold = 1.7f; internal const float PhaseMinDistance = 4f; internal const float PhaseMaxDistance = 18f; internal const float PhaseDriftSpeed = 3.2f; internal const float PhaseBodyClearanceMargin = 0.15f; internal const float PhaseLandingSearchStep = 0.35f; internal const float PhaseLandingSearchDistance = 6f; internal const float PhaseRepositionPathRatio = 1.2f; internal const float PhaseImpulseChance = 0.35f; internal const bool PhaseWhileRoaming = true; internal const float PhaseRoamPathRatio = 1.5f; internal const float PossessionCastSeconds = 1.6f; internal const int PossessionBreakHealthFloor = 20; internal const float PuppetWalkSpeed = 3.5f; internal const float PuppetSprintDistance = 6f; internal const float PuppetSprintMultiplier = 2.25f; internal const float PossessionExtensionSeconds = 10f; internal const float PossessionExtensionRange = 2.5f; internal const float PuppetTurretAvoidRadius = 5f; internal const float PuppetMineAvoidRadius = 2.5f; internal const float PossessedAttackRange = 2.2f; internal const float PossessionShipSafeRadius = 20f; internal const float StealthSneakAbortDistance = 4f; internal const float StealthSneakCooldownSeconds = 12f; internal const bool MatchAnimationToMovement = true; internal const float AuthoredWalkSpeed = 1.9f; internal const float AuthoredRunSpeed = 4.2f; internal const float DeathDissipateDelay = 0.9f; internal const float DeathDissipateSeconds = 2f; internal const float PresenceLoopVolume = 0.45f; internal const float PresenceLoopFadePerSecond = 1.5f; internal const float PresenceLoopMaxDistance = 12f; internal const float FoleyVolume = 0.5f; internal const float FoleyMaxDistance = 10f; internal const float FoleyWalkSpacing = 1.25f; internal const float FoleyRunSpacing = 1.65f; internal const float SwipeVolume = 0.65f; internal const float SwipeMaxDistance = 18f; internal const float PhaseAudioVolume = 0.75f; internal const float PhaseAudioMaxDistance = 30f; internal const int PhaseVfxVariant = 0; internal const int PhaseBreachAlignAxis = 0; internal const float PhaseBreachScale = 1f; internal const bool PhaseForceHdrpParticleShaders = false; internal const float PhaseBreachLingerSeconds = 2.5f; internal const float PhaseLightIntensity = 14f; internal const float PhaseLightRange = 7f; internal const string PhaseLightColor = "#6FA8FF"; internal const bool PhaseDissolveEnabled = true; internal const int PhaseDissolveOffsetAxis = 0; internal const float PhaseDissolveDepth = 0.65f; internal const float PhaseDissolveEdgeWidth = 0.5f; internal const float PhaseDissolveEdgeIntensity = 40f; internal const string PhaseDissolveEdgeColor = "#6FA8FF"; internal const bool PossessionMergeVfxEnabled = true; internal const float PossessionMergeSeconds = 0.7f; internal const float PossessionEmergeSeconds = 0.9f; internal const float PossessionMergeMinScale = 0.12f; internal const bool PossessionRelocateOnRelease = true; internal const float PossessionVisorGlowIntensity = 10f; internal const float PossessionVisorGlowRange = 3f; internal const float PossessedVoicePitch = 0.68f; internal const float PossessedVoiceDistortion = 0.35f; internal const float PossessedVoiceEchoDelay = 110f; internal const float PossessedVoiceEchoDecay = 0.45f; internal const float PossessedVoiceEchoWetMix = 0.45f; internal const float PossessedVoiceDemonLayerMix = 0.8f; internal const float PossessedVoiceRingModHz = 48f; internal const float PossessedVoiceRingModMix = 0.5f; internal const float PossessedVoiceNoiseGateMix = 0.12f; internal const float PossessedVoiceBitcrushMix = 0.25f; internal const float PossessedVoiceDrive = 1.6f; internal const bool SpectralArmsEnabled = true; internal const float SpectralArmsScale = 0.8f; internal const float SpectralArmsHeightOffset = 0.08f; internal const bool PossessedHandsOutEnabled = true; internal const float PossessedHandsOutWeight = 1f; internal const bool UpdateBoundsWhenOffscreen = true; internal const bool AlwaysAnimate = true; } } namespace Y4NGZPoltergeist.Patches { [HarmonyPatch] internal static class PossessedVoicePatches { [HarmonyPatch(typeof(PlayerControllerB), "Update")] [HarmonyPostfix] private static void UpdatePostfix(PlayerControllerB __instance) { if (PoltergeistPossessedVoice.AnyActive && !((Object)(object)__instance == (Object)null)) { PoltergeistPossessedVoice.ReassertPitchFor(__instance); } } [HarmonyPatch(typeof(StartOfRound), "UpdatePlayerVoiceEffects")] [HarmonyPostfix] private static void UpdatePlayerVoiceEffectsPostfix() { if (PoltergeistPossessedVoice.AnyActive) { PoltergeistPossessedVoice.ReassertAll(); } } } [HarmonyPatch] internal static class PossessionInputPatches { private static bool LocalPlayerIsPossessed(PlayerControllerB player) { if ((Object)(object)player == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)player != (Object)(object)GameNetworkManager.Instance.localPlayerController) { return false; } PoltergeistPossessionLock component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null) { return component.Holds; } return false; } [HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")] [HarmonyPrefix] private static bool JumpPerformedPrefix(PlayerControllerB __instance) { return !LocalPlayerIsPossessed(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "Crouch_performed")] [HarmonyPrefix] private static bool CrouchPerformedPrefix(PlayerControllerB __instance) { return !LocalPlayerIsPossessed(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "ClickHoldInteraction")] [HarmonyPrefix] private static bool ClickHoldInteractionPrefix(PlayerControllerB __instance) { if (!LocalPlayerIsPossessed(__instance)) { return true; } __instance.isHoldingInteract = false; return false; } [HarmonyPatch(typeof(PlayerControllerB), "Interact_performed")] [HarmonyPrefix] private static bool InteractPerformedPrefix(PlayerControllerB __instance) { return !LocalPlayerIsPossessed(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "PerformEmote")] [HarmonyPrefix] private static bool PerformEmotePrefix(PlayerControllerB __instance) { return !LocalPlayerIsPossessed(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "Discard_performed")] [HarmonyPrefix] private static bool DiscardPerformedPrefix(PlayerControllerB __instance) { return !LocalPlayerIsPossessed(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "Update")] [HarmonyPostfix] private static void UpdatePostfix(PlayerControllerB __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)__instance != (Object)(object)GameNetworkManager.Instance.localPlayerController)) { PoltergeistPossessionLock component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null && component.Holds) { component.DrivePuppetMovement(); } } } [HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")] [HarmonyPostfix] private static void LateUpdatePostfix(PlayerControllerB __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)__instance != (Object)(object)GameNetworkManager.Instance.localPlayerController)) { PoltergeistPossessionLock component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null && component.Holds) { component.DrivePuppetLook(); } } } [HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")] [HarmonyPriority(800)] [HarmonyPrefix] private static bool ActivateItemPerformedPrefix(PlayerControllerB __instance) { return !LocalPlayerIsPossessed(__instance); } } internal static class PossessionInteractionBridge { private const string PackId = "y4ngz.poltergeist.possessed-claws"; private const string FirstPersonManifestFile = "y4ngz-upgrades-fists-firstperson.manifest.json"; private const string ThirdPersonManifestFile = "y4ngz-upgrades-fists-thirdperson.manifest.json"; internal const string FirstPersonInteractionId = "y4ngz.native-fists.firstperson"; internal const string ThirdPersonInteractionId = "y4ngz.native-fists.thirdperson"; internal const string EnterTrigger = "Y4NGZ_Fists_Enter"; internal const string PunchLeftTrigger = "PunchLeft"; internal const string PunchRightTrigger = "PunchRight"; private static bool _resolved; private static MethodInfo _tryStopPlayerInteractions; private static object _interruptedReason; private static Type _packType; private static Type _definitionType; private static Type _requestType; private static Type _presentationKindType; private static MethodInfo _tryRegisterPack; private static MethodInfo _tryStartInteraction; private static MethodInfo _tryStopInteraction; private static MethodInfo _isInteractionActive; private static MethodInfo _tryFireTrigger; private static MethodInfo _tryBeginExit; private static bool _packRegistered; private static bool _packUnavailableLogged; internal static void TryStopInteractions(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return; } try { if (!_resolved) { Resolve(); } if (_tryStopPlayerInteractions != null) { _tryStopPlayerInteractions.Invoke(null, new object[2] { player, _interruptedReason }); } } catch (Exception ex) { PoltergeistAI.LogPossession("Interaction-API arbitration failed: " + ex.Message); } } private static void Resolve() { _resolved = true; Type type = Type.GetType("Y4NGZInteractions.InteractionAnimationApi.LCInteractionAnimationAPI, Y4NGZInteractions"); Type type2 = Type.GetType("Y4NGZInteractions.InteractionAnimationApi.InteractionAnimationStopReason, Y4NGZInteractions"); if (!(type == null) && !(type2 == null)) { _interruptedReason = Enum.Parse(type2, "Interrupted"); _tryStopPlayerInteractions = type.GetMethod("TryStopPlayerInteractions", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(PlayerControllerB), type2 }, null); _packType = Type.GetType("Y4NGZInteractions.InteractionAnimationApi.InteractionAnimationPackDefinition, Y4NGZInteractions"); _definitionType = Type.GetType("Y4NGZInteractions.InteractionAnimationApi.InteractionAnimationDefinition, Y4NGZInteractions"); _requestType = Type.GetType("Y4NGZInteractions.InteractionAnimationApi.InteractionAnimationRequest, Y4NGZInteractions"); _presentationKindType = Type.GetType("Y4NGZInteractions.InteractionAnimationApi.InteractionAnimationPresentationKind, Y4NGZInteractions"); _tryRegisterPack = type.GetMethod("TryRegisterInteractionPack", BindingFlags.Static | BindingFlags.Public); _tryStartInteraction = type.GetMethod("TryStartInteraction", BindingFlags.Static | BindingFlags.Public); _tryStopInteraction = type.GetMethod("TryStopInteraction", BindingFlags.Static | BindingFlags.Public); _isInteractionActive = type.GetMethod("IsInteractionActive", BindingFlags.Static | BindingFlags.Public); _tryFireTrigger = type.GetMethod("TryFireInteractionTrigger", BindingFlags.Static | BindingFlags.Public); _tryBeginExit = type.GetMethod("TryBeginInteractionExit", BindingFlags.Static | BindingFlags.Public); } } internal static bool EnsureFistsPackRegistered() { if (_packRegistered) { return true; } try { if (!_resolved) { Resolve(); } if (_tryRegisterPack == null || _packType == null || _definitionType == null) { return LogPackUnavailable("Y4NGZInteractions' animation API is not present"); } if (!TryFindFistsAssetRoot(out var root)) { return LogPackUnavailable("Y4NGZUpgrades' fists manifests were not found under BepInEx/plugins"); } object obj = Activator.CreateInstance(_packType); SetProperty(obj, "PackId", "y4ngz.poltergeist.possessed-claws"); SetProperty(obj, "DisplayName", "Y4NGZ Poltergeist Possessed Claws"); SetProperty(obj, "Version", "0.1.0"); SetProperty(obj, "Author", "Y4NGZ"); SetProperty(obj, "AssetRootPath", root); Array array = Array.CreateInstance(_definitionType, 2); array.SetValue(BuildDefinition("y4ngz.native-fists.firstperson", "Possessed Claws (First Person)", "DedicatedLocalViewmodel", File.ReadAllText(Path.Combine(root, "y4ngz-upgrades-fists-firstperson.manifest.json"))), 0); array.SetValue(BuildDefinition("y4ngz.native-fists.thirdperson", "Possessed Claws (Third Person)", "BodyWorld", File.ReadAllText(Path.Combine(root, "y4ngz-upgrades-fists-thirdperson.manifest.json"))), 1); SetProperty(obj, "Interactions", array); object[] array2 = new object[2] { obj, null }; bool num = (bool)_tryRegisterPack.Invoke(null, array2); string text = (array2[1] as string) ?? string.Empty; if (!num && !string.Equals(text, "pack_already_registered", StringComparison.OrdinalIgnoreCase)) { return LogPackUnavailable("pack registration refused (" + text + ")"); } _packRegistered = true; PoltergeistAI.LogPossession("Possessed-attack pack registered against '" + root + "'."); return true; } catch (Exception ex) { return LogPackUnavailable(ex.Message); } } internal static bool TryStartFistsPresentation(PlayerControllerB player, bool firstPerson, out object handle) { handle = null; if ((Object)(object)player == (Object)null || !EnsureFistsPackRegistered() || _requestType == null || _tryStartInteraction == null) { return false; } try { object obj = Activator.CreateInstance(_requestType); SetProperty(obj, "Player", player); SetProperty(obj, "PackId", "y4ngz.poltergeist.possessed-claws"); SetProperty(obj, "InteractionId", firstPerson ? "y4ngz.native-fists.firstperson" : "y4ngz.native-fists.thirdperson"); SetProperty(obj, "OwnerModId", "y4ngz.lethalcompany.poltergeist"); object[] array = new object[3] { obj, null, null }; if (!(bool)_tryStartInteraction.Invoke(null, array)) { PoltergeistAI.LogPossession("Possessed-attack " + (firstPerson ? "FP" : "TP") + " presentation refused: " + (array[2] as string) + "."); return false; } handle = array[1]; return true; } catch (Exception ex) { PoltergeistAI.LogPossession("Possessed-attack presentation failed: " + ex.Message); return false; } } internal static bool IsPresentationActive(object handle) { if (handle != null && _isInteractionActive != null) { return Safe(() => (bool)_isInteractionActive.Invoke(null, new object[1] { handle })); } return false; } internal static bool FirePresentationTrigger(object handle, string trigger) { if (IsPresentationActive(handle) && _tryFireTrigger != null) { return Safe(() => (bool)_tryFireTrigger.Invoke(null, new object[2] { handle, trigger })); } return false; } internal static void EndPresentation(object handle, bool immediate) { if (IsPresentationActive(handle) && (immediate || !(_tryBeginExit != null) || !Safe(() => (bool)_tryBeginExit.Invoke(null, new object[2] { handle, null }))) && _tryStopInteraction != null && _interruptedReason != null) { Safe(() => (bool)_tryStopInteraction.Invoke(null, new object[2] { handle, _interruptedReason })); } } private static bool TryFindFistsAssetRoot(out string root) { root = null; string pluginPath = Paths.PluginPath; if (string.IsNullOrEmpty(pluginPath) || !Directory.Exists(pluginPath)) { return false; } string[] files = Directory.GetFiles(pluginPath, "y4ngz-upgrades-fists-firstperson.manifest.json", SearchOption.AllDirectories); for (int i = 0; i < files.Length; i++) { string directoryName = Path.GetDirectoryName(files[i]); if (directoryName != null && File.Exists(Path.Combine(directoryName, "y4ngz-upgrades-fists-thirdperson.manifest.json"))) { root = directoryName; return true; } } return false; } private static object BuildDefinition(string interactionId, string displayName, string kind, string manifestJson) { object? obj = Activator.CreateInstance(_definitionType); SetProperty(obj, "InteractionId", interactionId); SetProperty(obj, "DisplayName", displayName); SetProperty(obj, "ExpectedDurationSeconds", 0f); SetProperty(obj, "PresentationKind", Enum.Parse(_presentationKindType, kind)); SetProperty(obj, "ManifestJson", manifestJson); return obj; } private static void SetProperty(object target, string name, object value) { target.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public)?.SetValue(target, value); } private static bool Safe(Func call) { try { return call(); } catch (Exception ex) { PoltergeistAI.LogPossession("Interaction-API call failed: " + ex.Message); return false; } } private static bool LogPackUnavailable(string why) { if (!_packUnavailableLogged) { _packUnavailableLogged = true; PoltergeistAI.LogPossession("Possessed attacks disabled: " + why + "."); } return false; } } } namespace Y4NGZPoltergeist.AI { public class PoltergeistAI : EnemyAI { private enum PhaseReject { Throttled, Disabled, Busy, Cooldown, NoAnchor, NoAgent, NoTarget, OutOfBand, NoNavPoint, DestinationTooFar, TravelTooShort, NoWall, NoClearLanding, PathNotWorse, Started, Count } private enum PhaseSource { Opportunistic, Reposition, Roam, Flee } private struct CachedAnimatorParam { public int Hash; public AnimatorControllerParameterType Type; } private enum PossessionEndReason { Expired, PoltergeistDamaged, VictimHealthFloor, VictimLost, PoltergeistDied } private enum Tactic { Direct, Flank, Stalk, Ambush, Withdraw, PhaseReposition, SneakPossess } private struct PhasePlan { public Vector3 Destination; public Vector3 ResumeDestination; public Vector3 EntryPoint; public Vector3 EntryNormal; public Vector3 ExitPoint; public Vector3 ExitNormal; public float Duration; } public const int StateRoaming = 0; public const int StateStalking = 1; public const int StatePhasing = 2; public const int StateAttacking = 3; public const int StatePossessCast = 4; public const int StatePossessing = 5; public const int StateFleeing = 6; public const int StateDead = 7; [Header("Audio")] public AudioClip[] ambientClips; public AudioClip[] aggroClips; public AudioClip[] attackWhooshClips; public AudioClip[] attackImpactClips; public AudioClip[] painClips; public AudioClip[] fleeClips; public AudioClip[] deathClips; public AudioClip[] footstepClips; public AudioClip[] phaseInClips; public AudioClip[] phaseOutClips; public AudioClip[] howlClips; public AudioClip[] possessionClips; public AudioClip presenceLoopClip; [Header("Rig")] public Transform attackOrigin; public Transform phaseFxAnchor; public Light phaseLight; private const float AmbientMinSeconds = 9f; private const float AmbientMaxSeconds = 18f; private const float MaxPlausibleSpeed = 25f; private const float SpeedSmoothing = 12f; private const string PhaseMaterialName = "Poltergeist_Phase"; private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor"); private static readonly int LegacyColorId = Shader.PropertyToID("_Color"); private const float PhaseChestHeight = 1.15f; private const float PhaseEmergeStandoff = 2.6f; private const float WorldSfxMaxDistance = 30f; private const float PhaseVisualFadeSeconds = 0.25f; private const float PhaseEvaluateInterval = 1f; private float _attackCooldownTimer; private float _loseTargetTimer; private float _fleeTimer; private float _ambientTimer; private bool _detectPlayed; private float _animatorSpeed = 1f; private float _observedSpeed; private Vector3 _lastSpeedSamplePosition; private AudioSource _presenceLoopSource; private AudioSource _foleySource; private AudioSource _swipeSource; private AudioSource _phaseSource; private float _foleyTravel; private int _nextFoleyIndex; private float _backpedalTimer; private int _lastObservedState = -1; private Material _dissipateMaterial; private int _nextAttackIndex; private int _nextHitIndex; private Coroutine _attackRoutine; private Transform _leftAttackHand; private Transform _rightAttackHand; private bool _loggedMissingAttackHands; private readonly RaycastHit[] _lineOfSightHits = (RaycastHit[])(object)new RaycastHit[24]; private readonly RaycastHit[] _phaseSurfaceHits = (RaycastHit[])(object)new RaycastHit[48]; private readonly Collider[] _phaseClearanceHits = (Collider[])(object)new Collider[48]; private Vector3 _fleeAnchor; private Vector3 _fleeThreat; private bool _searchConfigured; private float _phaseCooldownTimer; private float _phaseEvaluateTimer; private bool _phaseActive; private Vector3 _phaseEnd; private Vector3 _prePhasePosition; private bool _moveTowardsDestinationBeforePhase; private Coroutine _phaseRoutine; private NavMeshPath _phasePath; private readonly int[] _phaseRejectCounts = new int[15]; private float _phaseDiagnosticsTimer; private float _phaseDiagBestRatio; private float _phaseDiagBestRatioThreshold; private float _phaseDiagBandMin = float.MaxValue; private float _phaseDiagBandMax; private bool _phaseDiagPending; private const PhaseReject FirstReal = PhaseReject.NoTarget; private SkinnedMeshRenderer[] _bodyRenderers; private Material[] _bodyMaterials; private Material _phaseMaterialInstance; private bool _phaseVisualApplied; private float _phaseVisualBlend; private float _phaseVisualTargetAlpha = 0.35f; private bool _phasePresentationActive; private readonly Dictionary _animatorParams = new Dictionary(StringComparer.Ordinal); private bool _animatorParamsCached; public const ulong NoPossessedPlayer = ulong.MaxValue; private readonly NetworkVariable _possessedClientId = new NetworkVariable(ulong.MaxValue, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private readonly NetworkVariable _possessionEndTime = new NetworkVariable(0.0, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private readonly NetworkVariable _puppetWaypoint = new NetworkVariable(Vector3.zero, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private readonly NetworkVariable _puppetLookPoint = new NetworkVariable(Vector3.zero, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private const byte GaitHalted = 0; private const byte GaitWalk = 1; private const byte GaitSprint = 2; private readonly NetworkVariable _puppetGait = new NetworkVariable((byte)0, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private readonly NetworkVariable _possessedAttackSwings = new NetworkVariable(0, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private const int PossessedHitTargetBits = 8; private const int PossessedHitPayloadBits = 16; private const ulong PossessedHitTargetMask = 255uL; private const ulong PossessedHitVariantMask = 255uL; private readonly NetworkVariable _possessedAttackHits = new NetworkVariable(0uL, (NetworkVariableReadPermission)0, (NetworkVariableWritePermission)0); private ulong _possessedAttackHitCount; private float _possessCastTimer; private bool _possessCastStealth; private PlayerControllerB _possessionVictim; private readonly Dictionary _repossessionAllowedAt = new Dictionary(); private float _nextPossessGateLogAt; private bool _possessionExtensionGranted; private float _nextPossessedAttackAt; private Turret[] _puppetTurrets; private Landmine[] _puppetMines; private NavMeshPath _puppetPath; private Vector3 _puppetProgressAnchor; private float _puppetProgressCheckedAt; private int _puppetStuckRewrites; private bool _mergedRenderersHidden; private bool _mergedCollidersDisabled; private Collider[] _mergedColliders; private bool _mergeVfxActive; private bool _mergeVfxEmerging; private float _mergeVfxTimer; private float _mergeVfxDuration; private PlayerControllerB _mergeVfxVictim; private Transform _visualRoot; private Vector3 _visualRootRestLocalPosition; private Vector3 _visualRootRestLocalScale; private bool _visualRootResolved; private const float MergeVisorForward = 0.15f; private float _emergeHoldTimer; private bool _emergeFleePending; private Vector3 _emergeFleeThreat; private static readonly Tactic[] AllTactics = new Tactic[6] { Tactic.Direct, Tactic.Flank, Tactic.Stalk, Tactic.Ambush, Tactic.Withdraw, Tactic.PhaseReposition }; private Tactic _tactic; private float _tacticTimer; private Vector3 _tacticAnchor; private bool _tacticAnchorValid; private readonly Tactic[] _tacticCandidates = new Tactic[6]; private readonly int[] _tacticWeights = new int[6]; private const float SneakCommitSeconds = 3f; private const float SneakMaxSeconds = 15f; private const float SneakBehindDistance = 2.5f; private const float SneakFacingDot = 0.25f; private float _sneakElapsed; private readonly Dictionary _sneakBlockedUntil = new Dictionary(); private static int _geometryMask; private const float PuppetStopDistance = 1.4f; private const float PuppetWaypointMinDistance = 1.8f; private const float PuppetWaypointRewriteThreshold = 0.4f; private const float PuppetProgressEpsilon = 0.15f; private const float PuppetProgressWindowSeconds = 1f; private const int PuppetMaxStuckRewrites = 2; private const float PuppetSprintHysteresis = 1.5f; private const float PuppetLookTargetRange = 6f; private const float PuppetLookEyeHeight = 2.2f; private const float PuppetLookRewriteThreshold = 0.05f; private const float PossessedAttackContactSeconds = 0.3f; private const float PossessionReconcileIntervalSeconds = 0.5f; private float _possessionReconcileTimer; private static string _lastVerbosePossessionLine; private NavMeshPath PhasePath { get { //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_0012: Expected O, but got Unknown //IL_0017: Expected O, but got Unknown NavMeshPath obj = _phasePath; if (obj == null) { NavMeshPath val = new NavMeshPath(); NavMeshPath val2 = val; _phasePath = val; obj = val2; } return obj; } } private NavMeshPath PuppetPath { get { //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_0012: Expected O, but got Unknown //IL_0017: Expected O, but got Unknown NavMeshPath obj = _puppetPath; if (obj == null) { NavMeshPath val = new NavMeshPath(); NavMeshPath val2 = val; _puppetPath = val; obj = val2; } return obj; } } internal Vector3 PuppetWaypoint => _puppetWaypoint.Value; internal bool PuppetWalking => _puppetGait.Value != 0; internal bool PuppetSprinting => _puppetGait.Value == 2; internal Vector3 PuppetLookPoint => _puppetLookPoint.Value; internal ulong PossessedPlayerClientId => _possessedClientId.Value; internal float PossessionSecondsRemaining { get { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) try { if (_possessedClientId.Value == ulong.MaxValue) { return 0f; } double value = _possessionEndTime.Value; NetworkTime serverTime = ((NetworkBehaviour)this).NetworkManager.ServerTime; return (float)(value - ((NetworkTime)(ref serverTime)).Time); } catch { return -999f; } } } private static PoltergeistConfig Cfg => Plugin.ModConfig; private bool AgentReady { get { if ((Object)(object)base.agent != (Object)null && ((Behaviour)base.agent).enabled) { return base.agent.isOnNavMesh; } return false; } } private static int GeometryMask { get { if (_geometryMask != 0) { return _geometryMask; } int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : 0); if (num == 0) { int num2 = LayerMask.NameToLayer("Room"); int num3 = LayerMask.NameToLayer("Colliders"); int num4 = LayerMask.NameToLayer("Default"); if (num2 >= 0) { num |= 1 << num2; } if (num3 >= 0) { num |= 1 << num3; } if (num4 >= 0) { num |= 1 << num4; } } if (num == 0) { num = -1; } LogPhase($"Breach geometry mask resolved to {num} (includes Default where the interior does)."); _geometryMask = num; return _geometryMask; } } private bool HasRetreatState { get { int hash; return TryGetParameter("isRetreating", (AnimatorControllerParameterType)4, out hash); } } private bool IsBackpedaling { get { if (base.currentBehaviourStateIndex == 6 && _backpedalTimer > 0f) { return HasRetreatState; } return false; } } private static bool IsPhaseEvaluation(PhaseReject reason) { return reason >= PhaseReject.NoTarget; } public override void OnNetworkSpawn() { ((NetworkBehaviour)this).OnNetworkSpawn(); NetworkVariable possessedClientId = _possessedClientId; possessedClientId.OnValueChanged = (OnValueChangedDelegate)(object)Delegate.Combine((Delegate?)(object)possessedClientId.OnValueChanged, (Delegate?)(object)new OnValueChangedDelegate(OnPossessedClientIdChanged)); NetworkVariable possessedAttackSwings = _possessedAttackSwings; possessedAttackSwings.OnValueChanged = (OnValueChangedDelegate)(object)Delegate.Combine((Delegate?)(object)possessedAttackSwings.OnValueChanged, (Delegate?)(object)new OnValueChangedDelegate(OnPossessedAttackSwingsChanged)); NetworkVariable possessedAttackHits = _possessedAttackHits; possessedAttackHits.OnValueChanged = (OnValueChangedDelegate)(object)Delegate.Combine((Delegate?)(object)possessedAttackHits.OnValueChanged, (Delegate?)(object)new OnValueChangedDelegate(OnPossessedAttackHitsChanged)); ApplyPossessedClientIdLocal(ulong.MaxValue, _possessedClientId.Value, animate: false); } public override void OnNetworkDespawn() { NetworkVariable possessedClientId = _possessedClientId; possessedClientId.OnValueChanged = (OnValueChangedDelegate)(object)Delegate.Remove((Delegate?)(object)possessedClientId.OnValueChanged, (Delegate?)(object)new OnValueChangedDelegate(OnPossessedClientIdChanged)); NetworkVariable possessedAttackSwings = _possessedAttackSwings; possessedAttackSwings.OnValueChanged = (OnValueChangedDelegate)(object)Delegate.Remove((Delegate?)(object)possessedAttackSwings.OnValueChanged, (Delegate?)(object)new OnValueChangedDelegate(OnPossessedAttackSwingsChanged)); NetworkVariable possessedAttackHits = _possessedAttackHits; possessedAttackHits.OnValueChanged = (OnValueChangedDelegate)(object)Delegate.Remove((Delegate?)(object)possessedAttackHits.OnValueChanged, (Delegate?)(object)new OnValueChangedDelegate(OnPossessedAttackHitsChanged)); ReleasePossessionLockLocal(_possessedClientId.Value); StopPresentationAudio(); ((NetworkBehaviour)this).OnNetworkDespawn(); } public override void Start() { //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) ((EnemyAI)this).Start(); try { Plugin.EnsureScanNode(((Component)this).gameObject); base.enemyHP = ((Cfg != null) ? Cfg.Health.Value : 5); _ambientTimer = Random.Range(9f, 18f); _lastSpeedSamplePosition = ((Component)this).transform.position; ResolveAttackHands(); Plugin.ConfigureWorldAudioSource(base.creatureSFX, 3f, 38f); Plugin.ConfigureWorldAudioSource(base.creatureVoice, 3f, 44f); EnsurePresentationAudioSources(); ApplyAgentSpeed((Cfg != null) ? Cfg.RoamSpeed.Value : 2.2f); if (((NetworkBehaviour)this).IsOwner) { ((EnemyAI)this).SwitchToBehaviourState(0); } } catch (Exception arg) { LogError($"Start: {arg}"); } } public override void Update() { ((EnemyAI)this).Update(); if (base.isEnemyDead) { return; } try { float deltaTime = Time.deltaTime; if (_attackCooldownTimer > 0f) { _attackCooldownTimer -= deltaTime; } if (_phaseCooldownTimer > 0f) { _phaseCooldownTimer -= deltaTime; } if (_phaseEvaluateTimer > 0f) { _phaseEvaluateTimer -= deltaTime; } UpdateObservedSpeed(deltaTime); TickPresenceLoop(deltaTime); TickMovementFoley(deltaTime); TickBackpedalBeat(deltaTime); if (((NetworkBehaviour)this).IsOwner) { TickAmbientAudio(deltaTime); } TickLocomotionAnimation(); TickAnimatorPlaybackSpeed(deltaTime); TickPhaseVisual(deltaTime); TickPhaseDiagnostics(deltaTime); TickFacing(deltaTime); TickEmergeHold(deltaTime); TickPossessionReconcile(deltaTime); } catch (Exception arg) { LogError($"Update: {arg}"); } } private void LateUpdate() { if (!_mergeVfxActive) { return; } try { TickMergeVfx(Time.deltaTime); } catch (Exception arg) { LogError($"LateUpdate: {arg}"); AbortMergeVfx(); } } public override void DoAIInterval() { ((EnemyAI)this).DoAIInterval(); if (base.isEnemyDead || !((NetworkBehaviour)this).IsOwner || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayersDead) { return; } try { switch (base.currentBehaviourStateIndex) { case 0: TickRoaming(); break; case 1: TickStalking(); break; case 3: TickAttacking(); break; case 4: TickPossessCast(); break; case 5: TickPossessing(); break; case 6: TickFleeing(); break; case 2: break; } } catch (Exception arg) { LogError($"DoAIInterval: {arg}"); } } private void TickRoaming() { //IL_008b: 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) ApplyAgentSpeed(Cfg.RoamSpeed.Value); StartFacilitySearchIfNeeded(); PlayerControllerB val = FindVisiblePlayer(); if ((Object)(object)val == (Object)null) { TryStartRoamPhase(); return; } base.targetPlayer = val; _loseTargetTimer = 0f; _tacticTimer = 0f; StopFacilitySearch(); PlayDetectOnce(); if (Cfg.TacticsEnabled.Value && Random.value < Cfg.SightingRetreatChance.Value) { CommitTactic(Tactic.Withdraw, Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position)); } ((EnemyAI)this).SwitchToBehaviourState(1); } private void TickStalking() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!TargetIsValid()) { ReturnToRoaming(); return; } float num = Vector3.Distance(((Component)this).transform.position, ((Component)base.targetPlayer).transform.position); if (num > 55f) { ReturnToRoaming(); return; } bool observed = TargetIsWatchingMe(); TickTacticCommitment(num, observed); if (HasLineOfSightTo(base.targetPlayer)) { _loseTargetTimer = 0f; } else if (!TacticHidesOnPurpose(_tactic)) { _loseTargetTimer += base.AIIntervalTime; if (_loseTargetTimer >= 6f) { ReturnToRoaming(); return; } } if ((_tactic != Tactic.Direct || !TryStartPhase()) && !DriveTactic(num) && !TryBeginPossessCast(num) && num <= Cfg.AttackRange.Value && _attackCooldownTimer <= 0f) { ((EnemyAI)this).SwitchToBehaviourState(3); } } private void TickAttacking() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!TargetIsValid()) { ReturnToRoaming(); return; } float num = Vector3.Distance(((Component)this).transform.position, ((Component)base.targetPlayer).transform.position); if (_attackRoutine == null && num > Cfg.AttackRange.Value) { ((EnemyAI)this).SwitchToBehaviourState(1); return; } StopAgentPath(); if (!TryBeginPossessCast(num) && _attackCooldownTimer <= 0f && _attackRoutine == null) { _attackCooldownTimer = Cfg.AttackCooldown.Value; _attackRoutine = ((MonoBehaviour)this).StartCoroutine(AttackRoutine()); } } private void TickFleeing() { //IL_0095: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) _fleeTimer -= base.AIIntervalTime; if (_fleeTimer <= 0f) { ReturnToRoaming(); return; } ApplyAgentSpeed(Cfg.FleeSpeed.Value); if (!TryStartFleePhase() && AgentReady) { if (!base.agent.pathPending && base.agent.remainingDistance <= base.agent.stoppingDistance + 0.75f) { _fleeAnchor = PickFleeDestination(_fleeThreat); } ((EnemyAI)this).SetDestinationToPosition(_fleeAnchor, false); } } private void ReturnToRoaming() { base.targetPlayer = null; _loseTargetTimer = 0f; ApplyAgentSpeed(Cfg.RoamSpeed.Value); ((EnemyAI)this).SwitchToBehaviourState(0); StartFacilitySearchIfNeeded(); } private static bool TacticHidesOnPurpose(Tactic tactic) { if (tactic != Tactic.Stalk && tactic != Tactic.Withdraw && tactic != Tactic.PhaseReposition) { return tactic == Tactic.SneakPossess; } return true; } private bool TargetIsWatchingMe() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)base.targetPlayer == (Object)null || Cfg == null) { return false; } return base.targetPlayer.HasLineOfSightToPosition(((Component)this).transform.position + Vector3.up * 1.15f, 50f, Mathf.CeilToInt(Cfg.SightRange.Value), -1f, -1); } private void TickTacticCommitment(float distance, bool observed) { if (Cfg == null || !Cfg.TacticsEnabled.Value) { _tactic = Tactic.Direct; return; } _tacticTimer -= base.AIIntervalTime; if (_tactic == Tactic.SneakPossess) { _sneakElapsed += base.AIIntervalTime; } if (_tacticTimer > 0f && TacticIsLegal(_tactic, distance, observed)) { return; } if (_tactic == Tactic.SneakPossess) { if (TacticIsLegal(Tactic.SneakPossess, distance, observed)) { _tacticTimer = 3f; } else { AbortSneak(distance, observed); } } else if (!TryCommitSneakPossess(distance, observed)) { if (_tactic == Tactic.Stalk && !observed) { CommitTactic(Tactic.Direct, distance); } else { SelectTactic(distance, observed); } } } private bool TacticIsLegal(Tactic tactic, float distance, bool observed) { switch (tactic) { case Tactic.Direct: return true; case Tactic.Flank: return distance > Cfg.AttackRange.Value * 2.5f; case Tactic.Stalk: if (observed) { return distance > 7.7f; } return false; case Tactic.Ambush: if (distance <= 6.5f) { return !observed; } return false; case Tactic.Withdraw: return distance > Cfg.AttackRange.Value * 1.5f; case Tactic.PhaseReposition: if (observed && Cfg.WallPhasingEnabled.Value && _phaseCooldownTimer <= 0f && !_phaseActive) { return distance >= 4f; } return false; case Tactic.SneakPossess: if (!observed && !TargetIsCrowdingMe(distance) && _sneakElapsed < 15f) { return StealthPossessionAvailable(); } return false; default: return false; } } private int TacticWeight(Tactic tactic) { return tactic switch { Tactic.Direct => 16, Tactic.Flank => 22, Tactic.Stalk => 16, Tactic.Ambush => 20, Tactic.Withdraw => 16, Tactic.PhaseReposition => 22, _ => 0, }; } private static float TacticCommitScale(Tactic tactic) { return tactic switch { Tactic.Ambush => 0.6f, Tactic.Flank => 1.6f, Tactic.Stalk => 1.8f, Tactic.Withdraw => 2f, _ => 1f, }; } private void SelectTactic(float distance, bool observed) { int num = 0; int num2 = 0; for (int i = 0; i < AllTactics.Length; i++) { Tactic tactic = AllTactics[i]; if (!TacticIsLegal(tactic, distance, observed)) { continue; } int num3 = Mathf.Max(0, TacticWeight(tactic)); if (num3 > 0) { if (tactic == _tactic) { num3 = Mathf.Max(1, num3 / 3); } _tacticCandidates[num] = tactic; _tacticWeights[num] = num3; num++; num2 += num3; } } Tactic tactic2 = Tactic.Direct; if (num > 0 && num2 > 0) { int num4 = Random.Range(0, num2); for (int j = 0; j < num; j++) { num4 -= _tacticWeights[j]; if (num4 < 0) { tactic2 = _tacticCandidates[j]; break; } } } CommitTactic(tactic2, distance); } private void CommitTactic(Tactic tactic, float distance) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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) _tactic = tactic; _tacticTimer = Mathf.Max(0.5f, 3f * TacticCommitScale(tactic)); _tacticAnchorValid = false; switch (tactic) { case Tactic.Flank: _tacticAnchorValid = TryResolveOffAxisAnchor(55f, 110f, Mathf.Max(4f, distance * 0.8f), out _tacticAnchor); break; case Tactic.Stalk: _tacticAnchorValid = TryResolveOffAxisAnchor(30f, 120f, 11f, out _tacticAnchor); break; case Tactic.Withdraw: _tacticAnchor = PickFleeDestination(((Object)(object)base.targetPlayer != (Object)null) ? ((Component)base.targetPlayer).transform.position : ((Component)this).transform.position); _tacticAnchorValid = true; break; case Tactic.SneakPossess: _tacticAnchorValid = TryResolveBehindTargetAnchor(out _tacticAnchor); _sneakElapsed = 0f; _tacticTimer = Mathf.Max(3f, _tacticTimer); break; case Tactic.PhaseReposition: _tacticAnchorValid = TryResolveOffAxisAnchor(110f, 200f, Mathf.Clamp(distance, 6f, 18f), out _tacticAnchor); if (!_tacticAnchorValid) { _tactic = Tactic.Flank; } break; } LogDebug($"Tactic -> {_tactic} at {distance:F1}m, observed anchor {_tacticAnchorValid}, holding {_tacticTimer:F1}s."); } private bool TryResolveOffAxisAnchor(float minDegrees, float maxDegrees, float radius, out Vector3 anchor) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_002c: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_009c: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_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_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) anchor = Vector3.zero; if ((Object)(object)base.targetPlayer == (Object)null) { return false; } Vector3 position = ((Component)base.targetPlayer).transform.position; Vector3 val = ((Component)this).transform.position - position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); NavMeshHit val2 = default(NavMeshHit); for (int i = 0; i < 8; i++) { float num = Random.Range(minDegrees, maxDegrees); if (Random.value < 0.5f) { num = 0f - num; } float num2 = radius * Mathf.Lerp(1f, 0.4f, (float)i / 7f); if (NavMesh.SamplePosition(position + Quaternion.Euler(0f, num, 0f) * val * num2, ref val2, 5f, -1)) { anchor = ((NavMeshHit)(ref val2)).position; return true; } } return false; } private bool DriveTactic(float distance) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) switch (_tactic) { case Tactic.Flank: DriveFlank(distance); return false; case Tactic.Stalk: DriveStalk(distance); return false; case Tactic.Ambush: ApplyAgentSpeed(Cfg.ChaseSpeed.Value); MoveTo(((Component)base.targetPlayer).transform.position); return false; case Tactic.Withdraw: DriveWithdraw(); return false; case Tactic.SneakPossess: DriveSneakPossess(distance); return false; case Tactic.PhaseReposition: return DrivePhaseReposition(); default: DriveDirect(distance); return false; } } private void DriveDirect(float distance) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) ApplyAgentSpeed((distance > Cfg.AttackRange.Value * 3f) ? Cfg.ChaseSpeed.Value : Cfg.StalkSpeed.Value); MoveTo(((Component)base.targetPlayer).transform.position); } private void DriveFlank(float distance) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!_tacticAnchorValid || ArrivedAt(_tacticAnchor, 2.5f)) { DriveDirect(distance); return; } ApplyAgentSpeed(Cfg.ChaseSpeed.Value); MoveTo(_tacticAnchor); } private void DriveStalk(float distance) { //IL_0050: 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) ApplyAgentSpeed(Cfg.StalkSpeed.Value); float num = 11f; if (distance < num * 0.8f && _tacticAnchorValid) { MoveTo(_tacticAnchor); } else if (distance > num * 1.4f) { MoveTo(((Component)base.targetPlayer).transform.position); } else { StopAgentPath(); } } private void DriveWithdraw() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_0055: Unknown result type (might be due to invalid IL or missing references) ApplyAgentSpeed(Cfg.FleeSpeed.Value); if (!_tacticAnchorValid || ArrivedAt(_tacticAnchor, 2f)) { _tacticAnchor = PickFleeDestination(((Component)base.targetPlayer).transform.position); _tacticAnchorValid = true; } MoveTo(_tacticAnchor); } private bool StealthPossessionAvailable() { if (Cfg == null || !Cfg.StealthPossessionEnabled.Value || !Cfg.PossessionEnabled.Value) { return false; } if ((Object)(object)base.targetPlayer == (Object)null) { return false; } if (_possessedClientId.Value != ulong.MaxValue) { return false; } if (_phaseActive || _phaseRoutine != null) { return false; } return PossessionGateRefusal(base.targetPlayer) == null; } private bool TargetIsCrowdingMe(float distance) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)base.targetPlayer == (Object)null) { return false; } if (distance > 4f) { return false; } Transform val = (((Object)(object)base.targetPlayer.gameplayCamera != (Object)null) ? ((Component)base.targetPlayer.gameplayCamera).transform : ((Component)base.targetPlayer).transform); Vector3 val2 = ((Component)this).transform.position - ((Component)base.targetPlayer).transform.position; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { return true; } Vector3 forward = val.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { return false; } return Vector3.Dot(((Vector3)(ref forward)).normalized, ((Vector3)(ref val2)).normalized) >= 0.25f; } private bool TryCommitSneakPossess(float distance, bool observed) { if (observed || (Object)(object)base.targetPlayer == (Object)null) { return false; } if (!StealthPossessionAvailable()) { return false; } if (TargetIsCrowdingMe(distance)) { return false; } if (distance <= Cfg.PossessionRange.Value) { return false; } if (_sneakBlockedUntil.TryGetValue(base.targetPlayer.playerClientId, out var value) && Time.time < value) { return false; } CommitTactic(Tactic.SneakPossess, distance); LogPossession($"Sneak started on {base.targetPlayer.playerUsername} at {distance:F1}m " + $"(health {base.targetPlayer.health}, unseen)."); return true; } private void AbortSneak(float distance, bool observed) { string arg = (observed ? "the target looked at it" : ((_sneakElapsed >= 15f) ? $"it ran past {15f:F0}s" : (TargetIsCrowdingMe(distance) ? $"the target closed to {distance:F1}m facing it" : "possession stopped being available"))); if ((Object)(object)base.targetPlayer != (Object)null) { _sneakBlockedUntil[base.targetPlayer.playerClientId] = Time.time + Mathf.Max(0f, 12f); LogPossession($"Sneak aborted on {base.targetPlayer.playerUsername} after {_sneakElapsed:F1}s: {arg}. " + "Reverting to normal attacking."); } _sneakElapsed = 0f; CommitTactic(Tactic.Direct, distance); } private bool TryResolveBehindTargetAnchor(out Vector3 anchor) { //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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_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_00aa: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) anchor = Vector3.zero; if ((Object)(object)base.targetPlayer == (Object)null) { return false; } Vector3 forward = (((Object)(object)base.targetPlayer.gameplayCamera != (Object)null) ? ((Component)base.targetPlayer.gameplayCamera).transform : ((Component)base.targetPlayer).transform).forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { forward = ((Component)base.targetPlayer).transform.forward; } forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { return false; } ((Vector3)(ref forward)).Normalize(); Vector3 position = ((Component)base.targetPlayer).transform.position; NavMeshHit val2 = default(NavMeshHit); for (int i = 0; i < 3; i++) { Vector3 val = Quaternion.Euler(0f, i switch { 1 => 35f, 0 => 0f, _ => -35f, }, 0f) * -forward; if (NavMesh.SamplePosition(position + val * 2.5f, ref val2, 3f, -1)) { anchor = ((NavMeshHit)(ref val2)).position; return true; } } return false; } private void DriveSneakPossess(float distance) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) //IL_006a: Unknown result type (might be due to invalid IL or missing references) ApplyAgentSpeed((distance > Cfg.PossessionRange.Value * 2.5f) ? Cfg.ChaseSpeed.Value : Cfg.StalkSpeed.Value); if (TryResolveBehindTargetAnchor(out var anchor)) { _tacticAnchor = anchor; _tacticAnchorValid = true; } MoveTo(_tacticAnchorValid ? _tacticAnchor : ((Component)base.targetPlayer).transform.position); } private bool DrivePhaseReposition() { //IL_006b: 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_0099: Unknown result type (might be due to invalid IL or missing references) if (_phaseActive || _phaseRoutine != null) { return RejectPhase(PhaseReject.Busy); } if (_phaseCooldownTimer > 0f) { return RejectPhase(PhaseReject.Cooldown); } if (!_tacticAnchorValid) { return RejectPhase(PhaseReject.NoAnchor); } if (_phaseEvaluateTimer > 0f) { MoveTo(_tacticAnchor); return RejectPhase(PhaseReject.Throttled); } _phaseEvaluateTimer = 1f; if (TryPlanPhaseTo(_tacticAnchor, 1.2f, 0.35f, out var plan, out var reason)) { BeginPhase(plan, PhaseSource.Reposition); return true; } RejectPhase(reason); MoveTo(_tacticAnchor); return false; } private void MoveTo(Vector3 destination) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (AgentReady) { ((EnemyAI)this).SetDestinationToPosition(destination, false); } } private bool ArrivedAt(Vector3 destination, float tolerance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) Vector3 val = destination - ((Component)this).transform.position; val.y = 0f; return ((Vector3)(ref val)).sqrMagnitude <= tolerance * tolerance; } private bool TryStartPhase() { if (_phaseActive || _phaseRoutine != null) { return RejectPhase(PhaseReject.Busy); } if (Cfg == null || !Cfg.WallPhasingEnabled.Value) { return RejectPhase(PhaseReject.Disabled); } if (_phaseCooldownTimer > 0f) { return RejectPhase(PhaseReject.Cooldown); } if (_phaseEvaluateTimer > 0f) { return RejectPhase(PhaseReject.Throttled); } _phaseEvaluateTimer = 1f; if (!TryPlanPhase(out var plan, out var reason)) { return RejectPhase(reason); } BeginPhase(plan, PhaseSource.Opportunistic); return true; } private bool TryStartRoamPhase() { //IL_0098: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) if (_phaseActive || _phaseRoutine != null) { return RejectPhase(PhaseReject.Busy); } if (Cfg == null || !Cfg.WallPhasingEnabled.Value) { return RejectPhase(PhaseReject.Disabled); } if (_phaseCooldownTimer > 0f) { return RejectPhase(PhaseReject.Cooldown); } if (!AgentReady) { return RejectPhase(PhaseReject.NoAgent); } if (!base.agent.hasPath) { return RejectPhase(PhaseReject.NoAnchor); } if (_phaseEvaluateTimer > 0f) { return RejectPhase(PhaseReject.Throttled); } _phaseEvaluateTimer = 1f; Vector3 destination = base.agent.destination; Vector3 val = destination - ((Component)this).transform.position; val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 4f) { RecordPhaseBand(magnitude); return RejectPhase(PhaseReject.OutOfBand); } Vector3 desired = ((magnitude > 18f) ? (((Component)this).transform.position + val / magnitude * 18f) : destination); if (!TryPlanPhaseTo(desired, 1.5f, 0f, out var plan, out var reason)) { return RejectPhase(reason); } BeginPhase(plan, PhaseSource.Roam); return true; } private bool TryStartFleePhase() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) if (_phaseActive || _phaseRoutine != null) { return RejectPhase(PhaseReject.Busy); } if (Cfg == null || !Cfg.WallPhasingEnabled.Value) { return RejectPhase(PhaseReject.Disabled); } if (_phaseCooldownTimer > 0f) { return RejectPhase(PhaseReject.Cooldown); } if (!AgentReady) { return RejectPhase(PhaseReject.NoAgent); } if (_phaseEvaluateTimer > 0f) { return RejectPhase(PhaseReject.Throttled); } Vector3 val = _fleeAnchor - ((Component)this).transform.position; val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 4f) { RecordPhaseBand(magnitude); return RejectPhase(PhaseReject.OutOfBand); } _phaseEvaluateTimer = 1f; Vector3 desired = ((magnitude > 18f) ? (((Component)this).transform.position + val / magnitude * 18f) : _fleeAnchor); if (!TryPlanPhaseTo(desired, 0f, 0f, out var plan, out var reason)) { return RejectPhase(reason); } BeginPhase(plan, PhaseSource.Flee); return true; } private bool TryPlanPhase(out PhasePlan plan, out PhaseReject reason) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) plan = default(PhasePlan); if (!AgentReady) { reason = PhaseReject.NoAgent; return false; } if ((Object)(object)base.targetPlayer == (Object)null) { reason = PhaseReject.NoTarget; return false; } Vector3 val = ((Component)base.targetPlayer).transform.position - ((Component)this).transform.position; val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 4f || magnitude > 18f) { RecordPhaseBand(magnitude); reason = PhaseReject.OutOfBand; return false; } Vector3 desired = ((Component)base.targetPlayer).transform.position - val / magnitude * 2.6f; return TryPlanPhaseTo(desired, 1.7f, 0.35f, out plan, out reason); } private bool TryPlanPhaseTo(Vector3 desired, float ratio, float impulseChance, out PhasePlan plan, out PhaseReject reason) { //IL_001b: 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) //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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_007a: 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_0085: 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_0090: 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_00b0: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: 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_0120: 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_0128: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0165: 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_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0182: 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_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) plan = default(PhasePlan); reason = PhaseReject.Started; if (!AgentReady) { reason = PhaseReject.NoAgent; return false; } NavMeshHit val = default(NavMeshHit); if (!NavMesh.SamplePosition(desired, ref val, 4f, base.agent.areaMask)) { reason = PhaseReject.NoNavPoint; return false; } Vector3 position = ((Component)this).transform.position; Vector3 position2 = ((NavMeshHit)(ref val)).position; if (Vector3.Distance(position, position2) > 18f) { reason = PhaseReject.DestinationTooFar; return false; } Vector3 val2 = position + Vector3.up * 1.15f; Vector3 val3 = position2 + Vector3.up * 1.15f - val2; float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude < 1f) { reason = PhaseReject.TravelTooShort; return false; } Vector3 val4 = val3 / magnitude; if (!TryFindPhaseSurface(val2, val4, magnitude, out var surface)) { reason = PhaseReject.NoWall; return false; } if (!PathIsWorseThan(position2, ratio, out var observed) && (!(impulseChance > 0f) || !(Random.value < impulseChance))) { RecordPhaseRatio(observed, ratio); reason = PhaseReject.PathNotWorse; return false; } if (!TryResolvePhaseLanding(surface, val4, position, magnitude, out var landing, out var exit)) { reason = PhaseReject.NoClearLanding; return false; } plan.Destination = landing; plan.ResumeDestination = position2; plan.EntryPoint = ((RaycastHit)(ref surface)).point; plan.EntryNormal = ((RaycastHit)(ref surface)).normal; plan.ExitPoint = (((Object)(object)((RaycastHit)(ref exit)).collider != (Object)null) ? ((RaycastHit)(ref exit)).point : ((RaycastHit)(ref surface)).point); plan.ExitNormal = (((Object)(object)((RaycastHit)(ref exit)).collider != (Object)null) ? ((RaycastHit)(ref exit)).normal : (-((RaycastHit)(ref surface)).normal)); plan.Duration = Mathf.Clamp(Vector3.Distance(position, landing) / Mathf.Max(0.5f, 3.2f), 0.35f, 12f); return true; } private bool TryFindPhaseSurface(Vector3 origin, Vector3 direction, float distance, out RaycastHit surface) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0038: 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) surface = default(RaycastHit); int num = Physics.RaycastNonAlloc(new Ray(origin, direction), _phaseSurfaceHits, distance, GeometryMask, (QueryTriggerInteraction)1); float num2 = float.MaxValue; for (int i = 0; i < num; i++) { RaycastHit val = _phaseSurfaceHits[i]; if (IsPhaseGeometryCollider(((RaycastHit)(ref val)).collider) && !(((RaycastHit)(ref val)).distance >= num2)) { num2 = ((RaycastHit)(ref val)).distance; surface = val; } } return (Object)(object)((RaycastHit)(ref surface)).collider != (Object)null; } private bool IsPhaseGeometryCollider(Collider collider) { if ((Object)(object)collider == (Object)null || collider.isTrigger) { return false; } Transform transform = ((Component)collider).transform; if ((Object)(object)transform == (Object)null || transform.IsChildOf(((Component)this).transform)) { return false; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { return false; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { return false; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { return false; } Rigidbody attachedRigidbody = collider.attachedRigidbody; if (!((Object)(object)attachedRigidbody == (Object)null)) { return attachedRigidbody.isKinematic; } return true; } private bool TryResolvePhaseLanding(RaycastHit entry, Vector3 travelDirection, Vector3 phaseStart, float plannedTravelDistance, out Vector3 landing, out RaycastHit exit) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00cf: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0119: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) landing = Vector3.zero; exit = default(RaycastHit); GetPhaseBodyDimensions(out var radius, out var _); float num = radius + 0.15f; float num2 = Mathf.Max(0.1f, 0.35f); float num3 = Vector3.Dot(((RaycastHit)(ref entry)).point - (phaseStart + Vector3.up * 1.15f), travelDirection); float num4 = Mathf.Max(0f, plannedTravelDistance - num3); float num5 = Mathf.Min(6f, num4 + num * 0.5f); if (num5 < num) { return false; } Vector3 val = ((RaycastHit)(ref entry)).point - Vector3.up * 1.15f; int num6 = Mathf.CeilToInt((num5 - num) / num2); NavMeshHit val2 = default(NavMeshHit); for (int i = 0; i <= num6; i++) { float num7 = Mathf.Min(num5, num + (float)i * num2); if (NavMesh.SamplePosition(val + travelDirection * num7, ref val2, 0.8f, base.agent.areaMask)) { Vector3 val3 = ((NavMeshHit)(ref val2)).position + Vector3.up * 1.15f; if (!(Vector3.Dot(val3 - ((RaycastHit)(ref entry)).point, travelDirection) < num) && !(Vector3.Dot(((NavMeshHit)(ref val2)).position - phaseStart, travelDirection) <= 0f) && HasPhaseBodyClearance(((NavMeshHit)(ref val2)).position)) { landing = ((NavMeshHit)(ref val2)).position; float distance = Vector3.Distance(val3, ((RaycastHit)(ref entry)).point) + 0.5f; TryFindPhaseSurface(val3, -travelDirection, distance, out exit); return true; } } } return false; } private bool HasPhaseBodyClearance(Vector3 rootPosition) { //IL_0022: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_003b: 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_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_005c: 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) GetPhaseBodyDimensions(out var radius, out var height); float num = Mathf.Max(0.1f, radius - 0.08f); Vector3 up = ((Component)this).transform.up; Vector3 val = rootPosition + up * (radius + 0.08f); Vector3 val2 = rootPosition + up * Mathf.Max(radius + 0.08f, height - radius - 0.08f); int num2 = Physics.OverlapCapsuleNonAlloc(val, val2, num, _phaseClearanceHits, GeometryMask, (QueryTriggerInteraction)1); for (int i = 0; i < num2; i++) { Collider collider = _phaseClearanceHits[i]; _phaseClearanceHits[i] = null; if (IsPhaseGeometryCollider(collider)) { return false; } } return true; } private void GetPhaseBodyDimensions(out float radius, out float height) { //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_000c: 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_0028: Unknown result type (might be due to invalid IL or missing references) Vector3 lossyScale = ((Component)this).transform.lossyScale; float num = Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z)); float num2 = Mathf.Abs(lossyScale.y); radius = Mathf.Max(0.1f, (((Object)(object)base.agent != (Object)null) ? base.agent.radius : 0.55f) * num); height = Mathf.Max(radius * 2f, (((Object)(object)base.agent != (Object)null) ? base.agent.height : 2.1f) * num2); } private bool PathIsWorseThan(Vector3 destination, float ratio, out float observed) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0033: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) observed = float.PositiveInfinity; Vector3 position = ((Component)this).transform.position; float num = Vector3.Distance(position, destination); if (num < 0.01f) { observed = 0f; return false; } NavMeshPath phasePath = PhasePath; if (!NavMesh.CalculatePath(position, destination, base.agent.areaMask, phasePath)) { return true; } if ((int)phasePath.status != 0) { return true; } Vector3[] corners = phasePath.corners; float num2 = 0f; for (int i = 1; i < corners.Length; i++) { num2 += Vector3.Distance(corners[i - 1], corners[i]); } observed = num2 / num; return observed > ratio; } private void BeginPhase(PhasePlan plan, PhaseSource source) { //IL_0033: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_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_00c6: 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) if (_phaseRoutine == null) { _phaseRejectCounts[14]++; _phaseDiagPending = true; LogPhase($"Phase started ({source}): clear wall in {Vector3.Distance(((Component)this).transform.position, plan.Destination):F1}m " + $"over {plan.Duration:F2}s, then resume toward a point " + $"{Vector3.Distance(plan.Destination, plan.ResumeDestination):F1}m away; " + $"entry normal {plan.EntryNormal:F2}, exit normal {plan.ExitNormal:F2}."); _phaseActive = true; _phaseEnd = plan.Destination; _prePhasePosition = ((Component)this).transform.position; _phaseRoutine = ((MonoBehaviour)this).StartCoroutine(PhaseRoutine(plan, source)); } } private IEnumerator PhaseRoutine(PhasePlan plan, PhaseSource source) { StopFacilitySearch(); if (_attackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_attackRoutine); _attackRoutine = null; } ((EnemyAI)this).SwitchToBehaviourState(2); _moveTowardsDestinationBeforePhase = base.moveTowardsDestination; base.moveTowardsDestination = false; StopAgentPath(); if ((Object)(object)base.agent != (Object)null) { ((Behaviour)base.agent).enabled = false; } AnnouncePhaseAudio(); Vector3 start = _prePhasePosition; Vector3 end = plan.Destination; float duration = plan.Duration; float elapsed = 0f; while (elapsed < duration && !base.isEnemyDead) { elapsed += Time.deltaTime; float num = Mathf.Clamp01(elapsed / duration); ((Component)this).transform.position = Vector3.Lerp(start, end, num); yield return null; } _phaseRoutine = null; FinishPhase(end, plan.ResumeDestination, source); } private void FinishPhase(Vector3 destination, Vector3 resumeDestination, PhaseSource source) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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) _phaseActive = false; if (Cfg != null) { _phaseCooldownTimer = Cfg.PhaseCooldown.Value; } RestoreAgentAfterPhase(destination); if (!base.isEnemyDead) { LogPhase($"Phase ended ({source}) at the first body-cleared landing; resuming navigation " + $"{Vector3.Distance(destination, resumeDestination):F1}m toward the committed destination."); if (source == PhaseSource.Flee) { ((EnemyAI)this).SwitchToBehaviourState(6); MoveTo(_fleeAnchor); } else if (TargetIsValid()) { ((EnemyAI)this).SwitchToBehaviourState(1); MoveTo(resumeDestination); } else { ReturnToRoaming(); MoveTo(resumeDestination); } } } private void RestoreAgentAfterPhase(Vector3 destination) { //IL_0021: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_00ba: Unknown result type (might be due to invalid IL or missing references) base.moveTowardsDestination = _moveTowardsDestinationBeforePhase; if ((Object)(object)base.agent == (Object)null) { return; } ((Component)this).transform.position = destination; ((Behaviour)base.agent).enabled = true; if (base.agent.Warp(destination) && base.agent.isOnNavMesh) { return; } LogError("Phase warp did not land on the NavMesh; returning to the pre-phase position."); ((Component)this).transform.position = _prePhasePosition; if (!base.agent.Warp(_prePhasePosition) || !base.agent.isOnNavMesh) { NavMeshHit val = default(NavMeshHit); if (NavMesh.SamplePosition(_prePhasePosition, ref val, 8f, -1)) { ((Component)this).transform.position = ((NavMeshHit)(ref val)).position; base.agent.Warp(((NavMeshHit)(ref val)).position); } if (!base.agent.isOnNavMesh) { LogError("Poltergeist could not be returned to the NavMesh after a phase."); } } } private void AbortPhase() { //IL_0059: 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_0096: Unknown result type (might be due to invalid IL or missing references) if (_phaseRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_phaseRoutine); _phaseRoutine = null; } if (!_phaseActive) { return; } _phaseActive = false; base.moveTowardsDestination = _moveTowardsDestinationBeforePhase; if (!((Object)(object)base.agent == (Object)null) && !((Behaviour)base.agent).enabled) { NavMeshHit val = default(NavMeshHit); bool num = NavMesh.SamplePosition(((Component)this).transform.position, ref val, 8f, -1); if (num) { ((Component)this).transform.position = ((NavMeshHit)(ref val)).position; } ((Behaviour)base.agent).enabled = true; if (num) { base.agent.Warp(((NavMeshHit)(ref val)).position); } } } private void TickPhaseVisual(float dt) { bool flag = base.currentBehaviourStateIndex == 2; if (!flag && (Object)(object)_phaseSource != (Object)null && _phaseSource.isPlaying) { _phaseSource.Stop(); } if (_mergeVfxActive) { return; } bool flag2 = flag && (Cfg == null || Cfg.PhaseVfxEnabled.Value); if (flag2 && !_phasePresentationActive) { BeginPhasePresentation(); } if (_phasePresentationActive) { _phaseVisualBlend = Mathf.MoveTowards(_phaseVisualBlend, flag2 ? 1f : 0f, dt / 0.25f); if ((Object)(object)_phaseMaterialInstance != (Object)null) { SetMaterialAlpha(_phaseMaterialInstance, Mathf.Lerp(1f, _phaseVisualTargetAlpha, _phaseVisualBlend)); } if (!flag2 && _phaseVisualBlend <= 0f) { EndPhasePresentation(); } } } private void BeginPhasePresentation() { _phasePresentationActive = true; ApplyPhaseVisual(intangible: true); } private void EndPhasePresentation() { _phasePresentationActive = false; ApplyPhaseVisual(intangible: false); } private void ApplyPhaseVisual(bool intangible) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown CacheBodyRenderers(); if (_bodyRenderers == null || _bodyRenderers.Length == 0) { return; } if (intangible) { Material val = ResolvePhaseBodyMaterial(); if ((Object)(object)val == (Object)null) { LogError("Phase visual could not resolve Poltergeist_Phase; the creature stays opaque while phasing."); return; } if ((Object)(object)_phaseMaterialInstance == (Object)null) { _phaseMaterialInstance = new Material(val); _phaseVisualTargetAlpha = GetMaterialAlpha(val); SetMaterialAlpha(_phaseMaterialInstance, 1f); } for (int i = 0; i < _bodyRenderers.Length; i++) { if ((Object)(object)_bodyRenderers[i] != (Object)null) { ((Renderer)_bodyRenderers[i]).sharedMaterial = _phaseMaterialInstance; } } _phaseVisualApplied = true; } else { if (!_phaseVisualApplied) { return; } for (int j = 0; j < _bodyRenderers.Length; j++) { if ((Object)(object)_bodyRenderers[j] != (Object)null && (Object)(object)_bodyMaterials[j] != (Object)null) { ((Renderer)_bodyRenderers[j]).sharedMaterial = _bodyMaterials[j]; } } _phaseVisualApplied = false; } } private void CacheBodyRenderers() { if (_bodyRenderers == null) { _bodyRenderers = ((Component)this).GetComponentsInChildren(true); _bodyMaterials = (Material[])(object)new Material[_bodyRenderers.Length]; for (int i = 0; i < _bodyRenderers.Length; i++) { _bodyMaterials[i] = (((Object)(object)_bodyRenderers[i] != (Object)null) ? ((Renderer)_bodyRenderers[i]).sharedMaterial : null); } } } private void AnnouncePhaseAudio() { if (((NetworkBehaviour)this).IsServer) { int audioVariant = ((phaseInClips != null && phaseInClips.Length != 0) ? Random.Range(0, phaseInClips.Length) : (-1)); PlayPhaseAudioClientRpc(audioVariant); } } [ClientRpc] public void PlayPhaseAudioClientRpc(int audioVariant) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: 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_00ce: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(3207978809u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, audioVariant); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 3207978809u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; if (!base.isEnemyDead && !((Object)(object)_phaseSource == (Object)null) && phaseInClips != null && audioVariant >= 0 && audioVariant < phaseInClips.Length && !((Object)(object)phaseInClips[audioVariant] == (Object)null)) { _phaseSource.Stop(); _phaseSource.PlayOneShot(phaseInClips[audioVariant], 0.75f); } } } private IEnumerator AttackRoutine() { int nextAttackIndex = _nextAttackIndex; _nextAttackIndex = (_nextAttackIndex + 1) % 3; PlayAttackAnimationClientRpc(nextAttackIndex); ResolveAttackHands(); if ((Object)(object)_leftAttackHand == (Object)null && (Object)(object)_rightAttackHand == (Object)null) { if (!_loggedMissingAttackHands) { _loggedMissingAttackHands = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[PoltergeistCombat] Rig has no hand.L or hand.R transform; refusing distance-only fallback damage."); } } _attackRoutine = null; yield break; } Vector3 previousLeft = (((Object)(object)_leftAttackHand != (Object)null) ? _leftAttackHand.position : Vector3.zero); Vector3 previousRight = (((Object)(object)_rightAttackHand != (Object)null) ? _rightAttackHand.position : Vector3.zero); yield return (object)new WaitForSeconds(0.08f); float remaining = 0.72f; while (remaining > 0f && !base.isEnemyDead && base.currentBehaviourStateIndex == 3) { yield return null; remaining -= Time.deltaTime; Vector3 val = (((Object)(object)_leftAttackHand != (Object)null) ? _leftAttackHand.position : Vector3.zero); Vector3 val2 = (((Object)(object)_rightAttackHand != (Object)null) ? _rightAttackHand.position : Vector3.zero); if (TryFindAttackContact(previousLeft, val, (Object)(object)_leftAttackHand != (Object)null, previousRight, val2, (Object)(object)_rightAttackHand != (Object)null, out var victim, out var contactPoint)) { int impactClipIndex = ((attackImpactClips != null && attackImpactClips.Length != 0) ? Random.Range(0, attackImpactClips.Length) : (-1)); ConfirmClawHitClientRpc(victim.actualClientId, Cfg.AttackDamage.Value, impactClipIndex, contactPoint); break; } previousLeft = val; previousRight = val2; } _attackRoutine = null; } private void ResolveAttackHands() { if ((Object)(object)_leftAttackHand != (Object)null && (Object)(object)_rightAttackHand != (Object)null) { return; } Transform[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { if ((Object)(object)_leftAttackHand == (Object)null && string.Equals(((Object)val).name, "hand.L", StringComparison.Ordinal)) { _leftAttackHand = val; } else if ((Object)(object)_rightAttackHand == (Object)null && string.Equals(((Object)val).name, "hand.R", StringComparison.Ordinal)) { _rightAttackHand = val; } if ((Object)(object)_leftAttackHand != (Object)null && (Object)(object)_rightAttackHand != (Object)null) { break; } } } } private bool TryFindAttackContact(Vector3 leftStart, Vector3 leftEnd, bool hasLeft, Vector3 rightStart, Vector3 rightEnd, bool hasRight, out PlayerControllerB victim, out Vector3 contactPoint) { //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_008c: 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_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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00b3: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_0106: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: 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_0133: 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_0137: Unknown result type (might be due to invalid IL or missing references) victim = null; contactPoint = Vector3.zero; if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return false; } float num = float.MaxValue; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val == (Object)null || val.isPlayerDead || !val.isPlayerControlled || !((EnemyAI)this).PlayerIsTargetable(val, false, false, true)) { continue; } GetPlayerCollisionCapsule(val, out var bottom, out var top, out var radius); float num2 = radius + 0.18f; float num3 = num2 * num2; bool flag = false; Vector3 val2 = Vector3.zero; Vector3 val3 = Vector3.zero; float num4 = float.MaxValue; Vector3 val4; if (hasLeft) { ClosestPointsOnSegments(leftStart, leftEnd, bottom, top, out var firstPoint, out var secondPoint); val4 = firstPoint - secondPoint; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude <= num3 && !IsWorldPathBlocked(leftStart, firstPoint, val)) { flag = true; num4 = sqrMagnitude; val2 = firstPoint; val3 = secondPoint; } } if (hasRight) { ClosestPointsOnSegments(rightStart, rightEnd, bottom, top, out var firstPoint2, out var secondPoint2); val4 = firstPoint2 - secondPoint2; float sqrMagnitude2 = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude2 <= num3 && sqrMagnitude2 < num4 && !IsWorldPathBlocked(rightStart, firstPoint2, val)) { flag = true; num4 = sqrMagnitude2; val2 = firstPoint2; val3 = secondPoint2; } } if (flag && !(num4 >= num) && !IsWorldPathBlocked(val2, val3, val)) { num = num4; victim = val; contactPoint = Vector3.Lerp(val2, val3, 0.5f); } } return (Object)(object)victim != (Object)null; } private static void GetPlayerCollisionCapsule(PlayerControllerB player, out Vector3 bottom, out Vector3 top, out float radius) { //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_008f: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00aa: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_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_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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_0123: 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_012d: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0141: 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_014b: Unknown result type (might be due to invalid IL or missing references) CharacterController thisController = player.thisController; if ((Object)(object)thisController != (Object)null) { Transform transform = ((Component)thisController).transform; Vector3 lossyScale = transform.lossyScale; radius = Mathf.Max(0.1f, thisController.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z))); float num = Mathf.Max(radius, thisController.height * Mathf.Abs(lossyScale.y) * 0.5f); Vector3 val = transform.TransformPoint(thisController.center); Vector3 val2 = transform.up * Mathf.Max(0f, num - radius); bottom = val - val2; top = val + val2; return; } Collider playerCollider = player.playerCollider; if ((Object)(object)playerCollider != (Object)null) { Bounds bounds = playerCollider.bounds; radius = Mathf.Max(0.1f, Mathf.Min(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.z)); float num2 = Mathf.Max(0f, ((Bounds)(ref bounds)).extents.y - radius); bottom = ((Bounds)(ref bounds)).center - Vector3.up * num2; top = ((Bounds)(ref bounds)).center + Vector3.up * num2; } else { radius = 0.4f; bottom = ((Component)player).transform.position + Vector3.up * radius; top = ((Component)player).transform.position + Vector3.up * 2.1f; } } private static void ClosestPointsOnSegments(Vector3 firstStart, Vector3 firstEnd, Vector3 secondStart, Vector3 secondEnd, out Vector3 firstPoint, out Vector3 secondPoint) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) //IL_0012: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0029: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) Vector3 val = firstEnd - firstStart; Vector3 val2 = secondEnd - secondStart; Vector3 val3 = firstStart - secondStart; float num = Vector3.Dot(val, val); float num2 = Vector3.Dot(val2, val2); float num3 = Vector3.Dot(val2, val3); float num4; float num5; if (num <= 1E-06f && num2 <= 1E-06f) { num4 = 0f; num5 = 0f; } else if (num <= 1E-06f) { num4 = 0f; num5 = Mathf.Clamp01(num3 / num2); } else { float num6 = Vector3.Dot(val, val3); if (num2 <= 1E-06f) { num5 = 0f; num4 = Mathf.Clamp01((0f - num6) / num); } else { float num7 = Vector3.Dot(val, val2); float num8 = num * num2 - num7 * num7; num4 = ((num8 > 1E-06f) ? Mathf.Clamp01((num7 * num3 - num6 * num2) / num8) : 0f); num5 = (num7 * num4 + num3) / num2; if (num5 < 0f) { num5 = 0f; num4 = Mathf.Clamp01((0f - num6) / num); } else if (num5 > 1f) { num5 = 1f; num4 = Mathf.Clamp01((num7 - num6) / num); } } } firstPoint = firstStart + val * num4; secondPoint = secondStart + val2 * num5; } private bool IsWorldPathBlocked(Vector3 from, Vector3 to, PlayerControllerB ignoredPlayer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0028: 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) Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.03f || (Object)(object)StartOfRound.Instance == (Object)null) { return false; } int num = Physics.RaycastNonAlloc(new Ray(from, val / magnitude), _lineOfSightHits, magnitude, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider collider = ((RaycastHit)(ref _lineOfSightHits[i])).collider; if (!((Object)(object)collider == (Object)null) && !((Component)collider).transform.IsChildOf(((Component)this).transform) && (!((Object)(object)ignoredPlayer != (Object)null) || !((Component)collider).transform.IsChildOf(((Component)ignoredPlayer).transform))) { return true; } } return false; } private void PlayWorldClip(AudioClip[] clips, int clipIndex, Vector3 position, string objectName) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown if (clips != null && clipIndex >= 0 && clipIndex < clips.Length && !((Object)(object)clips[clipIndex] == (Object)null)) { AudioClip val = clips[clipIndex]; GameObject val2 = new GameObject(objectName); val2.transform.position = position; AudioSource obj = val2.AddComponent(); obj.clip = val; Plugin.ConfigureWorldAudioSource(obj, 2f, 30f); obj.Play(); Object.Destroy((Object)val2, val.length + 0.25f); } } [ClientRpc] public void ConfirmClawHitClientRpc(ulong targetClientId, int damage, int impactClipIndex, Vector3 contactPoint) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0141: 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) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(460116105u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, targetClientId); BytePacker.WriteValueBitPacked(val, damage); BytePacker.WriteValueBitPacked(val, impactClipIndex); ((FastBufferWriter)(ref val)).WriteValueSafe(ref contactPoint); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 460116105u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; PlayerControllerB val3 = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val3 != (Object)null && !val3.isPlayerDead && (val3.actualClientId == targetClientId || val3.playerClientId == targetClientId)) { val3.DamagePlayer(damage, true, true, (CauseOfDeath)0, 0, false, default(Vector3)); } PlayWorldClip(attackImpactClips, impactClipIndex, contactPoint, "PoltergeistClawImpact"); } } [ClientRpc] public void PlayAttackAnimationClientRpc(int attackIndex) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: 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_00ce: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(829262534u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, attackIndex); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 829262534u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; if (!base.isEnemyDead) { SetAnimatorInt("attackIndex", Mathf.Clamp(attackIndex, 0, 2)); SetAnimatorTrigger("doAttack"); PlayRandomClip(_swipeSource, attackWhooshClips, 0.65f); } } } private bool TryBeginPossessCast(float distance) { if (Cfg == null || !Cfg.PossessionEnabled.Value) { return false; } if (_possessedClientId.Value != ulong.MaxValue) { return false; } if (_phaseActive || _phaseRoutine != null) { return false; } if (!TargetIsValid()) { return false; } if (distance > Cfg.PossessionRange.Value) { return false; } bool flag = base.targetPlayer.health < Cfg.PossessionHealthThreshold.Value; bool flag2 = Cfg.StealthPossessionEnabled.Value && !TargetIsWatchingMe(); if (!flag && !flag2) { return false; } string text = PossessionGateRefusal(base.targetPlayer); if (text != null) { if (Time.time >= _nextPossessGateLogAt) { _nextPossessGateLogAt = Time.time + 10f; LogPossession("Cast refused on " + base.targetPlayer.playerUsername + ": " + text + "."); } return false; } _possessCastStealth = !flag; if (_possessCastStealth) { LogPossession("Stealth cast opening on " + base.targetPlayer.playerUsername + " at " + $"{distance:F1}m (health {base.targetPlayer.health}, no line of sight to the creature)."); } BeginPossessCast(); return true; } private string PossessionGateRefusal(PlayerControllerB target) { if (_repossessionAllowedAt.TryGetValue(target.playerClientId, out var value) && Time.time < value) { return $"re-possession cooldown ({value - Time.time:F0}s left)"; } if (TargetInShipSafety(target)) { return "target is inside the ship safety radius"; } if (!HasValidPossessionTeammate(target)) { if (!Cfg.PossessionDebugAllowSolo.Value) { return "no valid teammate to puppet the victim toward"; } return null; } return null; } private bool TargetInShipSafety(PlayerControllerB target) { //IL_004c: 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) float num = 20f; if (num <= 0f) { return false; } if (target.isInHangarShipRoom || target.isInElevator) { return true; } Transform val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.elevatorTransform : null); if ((Object)(object)val != (Object)null) { return Vector3.Distance(((Component)target).transform.position, val.position) <= num; } return false; } private static bool HasValidPossessionTeammate(PlayerControllerB victim) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return false; } for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)victim && val.isPlayerControlled && !val.isPlayerDead) { return true; } } return false; } private void BeginPossessCast() { if (_attackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_attackRoutine); _attackRoutine = null; } StopFacilitySearch(); StopAgentPath(); _possessCastTimer = Mathf.Max(0.2f, 1.6f); ((EnemyAI)this).SwitchToBehaviourState(4); PlayHowlClientRpc(); LogPossession("Possession cast started on " + base.targetPlayer.playerUsername + " " + $"({_possessCastTimer:F1}s telegraph)."); } private void TickPossessCast() { //IL_001a: 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) if (!TargetIsValid()) { CancelPossessCast("the target became invalid"); return; } if (Vector3.Distance(((Component)this).transform.position, ((Component)base.targetPlayer).transform.position) > Cfg.PossessionRange.Value * 2f) { CancelPossessCast("the target got away"); return; } StopAgentPath(); _possessCastTimer -= base.AIIntervalTime; if (!(_possessCastTimer > 0f)) { if ((!_possessCastStealth && base.targetPlayer.health >= Cfg.PossessionHealthThreshold.Value) || PossessionGateRefusal(base.targetPlayer) != null) { CancelPossessCast("a gate closed during the telegraph"); } else { BeginPossession(base.targetPlayer); } } } private void CancelPossessCast(string why) { LogPossession("Possession cast cancelled: " + why + "."); if (TargetIsValid()) { ((EnemyAI)this).SwitchToBehaviourState(1); } else { ReturnToRoaming(); } } private void BeginPossession(PlayerControllerB victim) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(1f, Cfg.PossessionDurationSeconds.Value); _possessionVictim = victim; _possessionExtensionGranted = false; _nextPossessedAttackAt = 0f; _puppetTurrets = Object.FindObjectsByType((FindObjectsSortMode)0); _puppetMines = Object.FindObjectsByType((FindObjectsSortMode)0); _puppetGait.Value = 0; _puppetLookPoint.Value = Vector3.zero; _puppetProgressAnchor = ((Component)victim).transform.position; _puppetProgressCheckedAt = Time.time; _puppetStuckRewrites = 0; _possessedClientId.Value = victim.playerClientId; NetworkVariable possessionEndTime = _possessionEndTime; NetworkTime serverTime = ((NetworkBehaviour)this).NetworkManager.ServerTime; possessionEndTime.Value = ((NetworkTime)(ref serverTime)).Time + (double)num; ((EnemyAI)this).SwitchToBehaviourState(5); int clipIndex = ((possessionClips != null && possessionClips.Length != 0) ? Random.Range(0, possessionClips.Length) : (-1)); PlayPossessionOnsetClientRpc(clipIndex, ((Component)victim).transform.position + Vector3.up * 1.4f); LogPossession($"Possessed {victim.playerUsername} for {num:F0}s."); } private void TickPossessing() { if (!(_emergeHoldTimer > 0f)) { PlayerControllerB possessionVictim = _possessionVictim; if ((Object)(object)possessionVictim == (Object)null || possessionVictim.isPlayerDead || !possessionVictim.isPlayerControlled) { ReleasePossession(PossessionEndReason.VictimLost); return; } if (possessionVictim.health < 20) { ReleasePossession(PossessionEndReason.VictimHealthFloor); return; } if (PossessionSecondsRemaining <= 0f) { ReleasePossession(PossessionEndReason.Expired); return; } base.targetPlayer = possessionVictim; StopAgentPath(); TickPuppetServer(possessionVictim); } } private void TickPuppetServer(PlayerControllerB victim) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0141: 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_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: 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_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) float distance; PlayerControllerB val = FindNearestPuppetTeammate(victim, out distance); UpdatePuppetLookPoint(victim, val, distance); if (!_possessionExtensionGranted && (Object)(object)val != (Object)null && distance <= 2.5f) { _possessionExtensionGranted = true; NetworkVariable possessionEndTime = _possessionEndTime; possessionEndTime.Value += (double)Mathf.Max(0f, 10f); LogPossession($"Possession extended +{10f:F0}s: " + $"reached {val.playerUsername} at {distance:F1}m."); } TryPossessedAttack(victim, val, distance); if ((Object)(object)val != (Object)null && distance <= 1.4f) { SetPuppetGait(0, "arrived at teammate"); return; } bool flag = TickPuppetProgress(victim); if (flag && _puppetStuckRewrites > 2) { SetPuppetGait(0, "no progress after repeated waypoint rewrites"); return; } bool flag2 = (Object)(object)val == (Object)null; Vector3 val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).transform.position; } else { val2 = RoundManager.FindMainEntrancePosition(false, !victim.isInsideFactory); if (val2 == Vector3.zero) { SetPuppetGait(0, "no main entrance to walk toward"); return; } } LogPossessionVerbose("Puppet target: " + (flag2 ? "main entrance" : val.playerUsername) + " " + $"at {Vector3.Distance(((Component)victim).transform.position, val2):F1}m."); if (!TryResolvePuppetWaypoint(((Component)victim).transform.position, val2, flag2, flag, out var waypoint)) { SetPuppetGait(0, "no clear waypoint this interval"); return; } if (flag || _puppetGait.Value == 0 || Vector3.Distance(_puppetWaypoint.Value, waypoint) > 0.4f) { LogPossessionVerbose("Puppet waypoint " + (flag ? "forced " : string.Empty) + "rewrite: " + $"{_puppetWaypoint.Value} -> {waypoint}."); _puppetWaypoint.Value = waypoint; } SetPuppetGait((byte)((!ResolvePuppetSprint(victim, val2)) ? 1 : 2), null); } private void UpdatePuppetLookPoint(PlayerControllerB victim, PlayerControllerB nearestTeammate, float teammateDistance) { //IL_003b: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)victim == (Object)null) { return; } float y = (((Object)(object)victim.gameplayCamera != (Object)null) ? ((Component)victim.gameplayCamera).transform.position.y : (((Component)victim).transform.position.y + 2.2f)); Vector3 val; if ((Object)(object)nearestTeammate != (Object)null && teammateDistance <= 6f) { val = (((Object)(object)nearestTeammate.gameplayCamera != (Object)null) ? ((Component)nearestTeammate.gameplayCamera).transform.position : (((Component)nearestTeammate).transform.position + Vector3.up * 2.2f)); } else { if (_puppetGait.Value == 0 || !(_puppetWaypoint.Value != Vector3.zero)) { return; } val = _puppetWaypoint.Value; val.y = y; } if (!(_puppetLookPoint.Value != Vector3.zero) || !(Vector3.Distance(_puppetLookPoint.Value, val) <= 0.05f)) { _puppetLookPoint.Value = val; } } private bool ResolvePuppetSprint(PlayerControllerB victim, Vector3 destination) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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) float num = ((Cfg != null) ? 6f : 6f); if (num <= 0f) { return true; } Vector3 val = destination - ((Component)victim).transform.position; val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; if (_puppetGait.Value == 2) { return magnitude > Mathf.Max(0f, num - 1.5f); } return magnitude > num; } private bool TickPuppetProgress(PlayerControllerB victim) { //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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_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) //IL_0073: 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) Vector3 position = ((Component)victim).transform.position; if (_puppetGait.Value == 0) { _puppetProgressAnchor = position; _puppetProgressCheckedAt = Time.time; _puppetStuckRewrites = 0; return false; } if (Time.time - _puppetProgressCheckedAt < 1f) { return _puppetStuckRewrites > 0; } Vector3 val = position - _puppetProgressAnchor; val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; _puppetProgressAnchor = position; _puppetProgressCheckedAt = Time.time; if (magnitude >= 0.15f) { _puppetStuckRewrites = 0; return false; } _puppetStuckRewrites++; LogPossessionVerbose($"Puppet made no progress ({magnitude:F2}m in {1f:F0}s); " + $"forced rewrite {_puppetStuckRewrites}/{2}."); return true; } private void TryPossessedAttack(PlayerControllerB victim, PlayerControllerB teammate, float distance) { if (Cfg != null && Cfg.PossessedAttacksEnabled.Value && !((Object)(object)victim == (Object)null) && !((Object)(object)teammate == (Object)null) && !(distance > 2.2f) && !(Time.time < _nextPossessedAttackAt) && PossessionInteractionBridge.EnsureFistsPackRegistered()) { _nextPossessedAttackAt = Time.time + Mathf.Max(0.2f, Cfg.PossessedAttackCooldownSeconds.Value); NetworkVariable possessedAttackSwings = _possessedAttackSwings; int value = possessedAttackSwings.Value; possessedAttackSwings.Value = value + 1; ((MonoBehaviour)this).StartCoroutine(ResolvePossessedAttack(victim, teammate, _possessedAttackSwings.Value)); } } private IEnumerator ResolvePossessedAttack(PlayerControllerB victim, PlayerControllerB teammate, int swing) { yield return (object)new WaitForSeconds(0.3f); if (Cfg != null && Cfg.PossessedFriendlyFire.Value && !((Object)(object)victim == (Object)null) && !((Object)(object)teammate == (Object)null) && _possessedClientId.Value == victim.playerClientId && !((Object)(object)teammate == (Object)(object)victim) && teammate.isPlayerControlled && !teammate.isPlayerDead && !Object.op_Implicit((Object)(object)teammate.inAnimationWithEnemy)) { Vector3 val = (((Object)(object)victim.gameplayCamera != (Object)null) ? ((Component)victim.gameplayCamera).transform.position : ((Component)victim).transform.position); Vector3 val2 = ((Component)teammate).transform.position + Vector3.up; if (!(Vector3.Distance(((Component)victim).transform.position, ((Component)teammate).transform.position) > 2.7f) && (!((Object)(object)StartOfRound.Instance != (Object)null) || !Physics.Linecast(val, val2, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))) { int num = Mathf.Max(0, Cfg.PossessedAttackDamage.Value); Vector3 val3 = val2 - val; teammate.DamagePlayerFromOtherClientClientRpc(num, ((Vector3)(ref val3)).normalized, (int)victim.playerClientId, Mathf.Max(0, teammate.health - num)); _possessedAttackHitCount++; int num2 = ((attackImpactClips != null && attackImpactClips.Length != 0) ? Random.Range(0, attackImpactClips.Length) : 0); _possessedAttackHits.Value = (_possessedAttackHitCount << 16) | (ulong)((long)num2 << 8) | (teammate.playerClientId & 0xFF); LogPossession($"Possessed swing {swing} landed on {teammate.playerUsername} for {num}."); } } } private void OnPossessedAttackSwingsChanged(int previous, int next) { if (next > previous) { PoltergeistPossessedAttacks.Swing(ResolvePlayerByClientId(_possessedClientId.Value), (next & 1) == 1); } } private void OnPossessedAttackHitsChanged(ulong previous, ulong next) { if (next >> 16 > previous >> 16) { int impactVariant = (int)((next >> 8) & 0xFF); PlayPossessedHitAudio(ResolvePlayerByClientId(next & 0xFF), impactVariant); } } private void PlayPossessedHitAudio(PlayerControllerB struck, int impactVariant) { //IL_0055: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) if (attackImpactClips != null && attackImpactClips.Length != 0 && !((Object)(object)struck == (Object)null)) { Vector3 position = (((Object)(object)struck.gameplayCamera != (Object)null) ? ((Component)struck.gameplayCamera).transform.position : (((Component)struck).transform.position + Vector3.up * 1.4f)); PlayWorldClip(attackImpactClips, impactVariant, position, "PoltergeistPossessedImpact"); } } private void SetPuppetGait(byte gait, string why) { if (_puppetGait.Value != gait) { if (gait == 0 && why != null) { LogPossessionVerbose("Puppet halted: " + why + "."); } else if (gait != 0 && _puppetGait.Value != 0) { LogPossessionVerbose("Puppet gait: " + ((gait == 2) ? "sprint" : "walk") + "."); } _puppetGait.Value = gait; } } private PlayerControllerB FindNearestPuppetTeammate(PlayerControllerB victim, out float distance) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) distance = float.MaxValue; PlayerControllerB result = null; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return null; } for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)victim) && val.isPlayerControlled && !val.isPlayerDead) { float num = Vector3.Distance(((Component)victim).transform.position, ((Component)val).transform.position); if (num < distance) { distance = num; result = val; } } } return result; } private bool TryResolvePuppetWaypoint(Vector3 from, Vector3 destination, bool allowPartial, bool forceDetour, out Vector3 waypoint) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Invalid comparison between Unknown and I4 //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Invalid comparison between Unknown and I4 //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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_010d: 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_00bc: 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_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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_013e: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: 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_021a: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_023b: 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_01e2: 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_01ff: Unknown result type (might be due to invalid IL or missing references) waypoint = default(Vector3); NavMeshHit val = default(NavMeshHit); NavMeshHit val2 = default(NavMeshHit); if (!NavMesh.SamplePosition(from, ref val, 4f, -1) || !NavMesh.SamplePosition(destination, ref val2, 6f, -1)) { LogPossessionVerbose("Puppet path: victim or destination is off the NavMesh."); return false; } NavMeshPath puppetPath = PuppetPath; bool flag = NavMesh.CalculatePath(((NavMeshHit)(ref val)).position, ((NavMeshHit)(ref val2)).position, -1, puppetPath); LogPossessionVerbose($"Puppet path: status {puppetPath.status}, {puppetPath.corners.Length} corners, calculated {flag}."); if (!flag || puppetPath.corners.Length < 2) { return false; } if ((int)puppetPath.status == 2) { return false; } if ((int)puppetPath.status == 1 && !allowPartial) { return false; } Vector3 val3 = puppetPath.corners[1]; NavMeshHit val4 = default(NavMeshHit); for (int i = 2; i < puppetPath.corners.Length; i++) { if (!(Vector3.Distance(from, val3) < 1.8f)) { break; } if (NavMesh.Raycast(((NavMeshHit)(ref val)).position, puppetPath.corners[i], ref val4, -1)) { break; } val3 = puppetPath.corners[i]; } if (!forceDetour && PuppetSegmentIsClear(from, val3)) { LogPossessionVerbose($"Puppet waypoint chosen: {val3} ({Vector3.Distance(from, val3):F1}m)."); waypoint = val3; return true; } Vector3 val5 = val3 - from; val5.y = 0f; if (((Vector3)(ref val5)).sqrMagnitude < 0.01f) { return false; } Vector3 val6 = Vector3.Cross(Vector3.up, ((Vector3)(ref val5)).normalized); NavMeshHit val7 = default(NavMeshHit); for (int j = -1; j <= 1; j += 2) { if (NavMesh.SamplePosition(val3 + val6 * (3f * (float)j), ref val7, 3f, -1) && PuppetSegmentIsClear(from, ((NavMeshHit)(ref val7)).position) && !NavMesh.Raycast(((NavMeshHit)(ref val)).position, ((NavMeshHit)(ref val7)).position, ref val4, -1)) { LogPossessionVerbose($"Puppet waypoint detour {j:+0;-0}: {((NavMeshHit)(ref val7)).position}."); waypoint = ((NavMeshHit)(ref val7)).position; return true; } } if (forceDetour && PuppetSegmentIsClear(from, val3)) { LogPossessionVerbose($"Puppet waypoint fallback after failed detours: {val3}."); waypoint = val3; return true; } return false; } private bool PuppetSegmentIsClear(Vector3 from, Vector3 to) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0083: 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_008c: Unknown result type (might be due to invalid IL or missing references) if (_puppetTurrets != null) { float radius = 5f; for (int i = 0; i < _puppetTurrets.Length; i++) { Turret val = _puppetTurrets[i]; if ((Object)(object)val != (Object)null && val.turretActive && SegmentWithinRadius(from, to, ((Component)val).transform.position, radius)) { return false; } } } if (_puppetMines != null) { float radius2 = 2.5f; for (int j = 0; j < _puppetMines.Length; j++) { Landmine val2 = _puppetMines[j]; if ((Object)(object)val2 != (Object)null && !val2.hasExploded && SegmentWithinRadius(from, to, ((Component)val2).transform.position, radius2)) { return false; } } } return true; } private static bool SegmentWithinRadius(Vector3 from, Vector3 to, Vector3 point, float radius) { //IL_0000: 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_000c: 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_0052: 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_0063: 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_0070: 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_0081: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0030: 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_009e: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_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) if (Mathf.Abs(point.y - Mathf.Min(from.y, to.y)) > 4f && Mathf.Abs(point.y - Mathf.Max(from.y, to.y)) > 4f) { return false; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(from.x, from.z); Vector2 val2 = new Vector2(to.x, to.z); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(point.x, point.z); Vector2 val4 = val2 - val; float num = ((((Vector2)(ref val4)).sqrMagnitude > 0.0001f) ? Mathf.Clamp01(Vector2.Dot(val3 - val, val4) / ((Vector2)(ref val4)).sqrMagnitude) : 0f); return Vector2.Distance(val3, val + val4 * num) <= radius; } private void ReleasePossession(PossessionEndReason reason) { //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) if (!((NetworkBehaviour)this).IsServer || _possessedClientId.Value == ulong.MaxValue) { return; } PlayerControllerB possessionVictim = _possessionVictim; _repossessionAllowedAt[_possessedClientId.Value] = Time.time + Mathf.Max(0f, (Cfg != null) ? Cfg.PossessionCooldownPerPlayerSeconds.Value : 90f); LogPossession($"Possession released ({reason}) with " + $"{Mathf.Max(0f, PossessionSecondsRemaining):F0}s remaining."); _possessionVictim = null; if ((Object)(object)possessionVictim != (Object)null && !base.isEnemyDead && reason != PossessionEndReason.PoltergeistDied) { _ = Cfg; RelocateToVictim(possessionVictim); } try { _puppetGait.Value = 0; _possessedClientId.Value = ulong.MaxValue; _possessionEndTime.Value = 0.0; } catch (Exception ex) { LogError("ReleasePossession: " + ex.Message); } if (!base.isEnemyDead && reason != PossessionEndReason.PoltergeistDied && reason != PossessionEndReason.PoltergeistDamaged) { Vector3 val = (((Object)(object)possessionVictim != (Object)null) ? ((Component)possessionVictim).transform.position : ((Component)this).transform.position); float num = ((Cfg != null) ? Mathf.Max(0f, 0.9f) : 0f); if (num <= 0f) { BeginFlee(val); return; } _emergeHoldTimer = num; _emergeFleeThreat = val; _emergeFleePending = true; } } private void RelocateToVictim(PlayerControllerB victim) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) NavMeshHit val = default(NavMeshHit); if (!NavMesh.SamplePosition(((Component)victim).transform.position, ref val, 4f, -1)) { LogPossession("Release relocation found no NavMesh near the victim; the body emerges where it was parked."); return; } ((Component)this).transform.position = ((NavMeshHit)(ref val)).position; base.serverPosition = ((NavMeshHit)(ref val)).position; if ((Object)(object)base.agent != (Object)null && ((Behaviour)base.agent).enabled) { base.agent.Warp(((NavMeshHit)(ref val)).position); } } private void TickEmergeHold(float dt) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (_emergeHoldTimer <= 0f) { return; } _emergeHoldTimer -= dt; if (_emergeHoldTimer > 0f) { return; } _emergeHoldTimer = 0f; if (_emergeFleePending) { _emergeFleePending = false; if (((NetworkBehaviour)this).IsOwner && !base.isEnemyDead) { BeginFlee(_emergeFleeThreat); } } } [ClientRpc] public void PlayHowlClientRpc() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_007c: 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) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(4051472000u, val2, (RpcDelivery)0); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 4051472000u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; if (!base.isEnemyDead) { SetAnimatorTrigger("doHowl"); } } } [ClientRpc] public void PlayPossessionOnsetClientRpc(int clipIndex, Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: 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_00db: 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) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager != null && networkManager.IsListening) { if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(2105154708u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, clipIndex); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 2105154708u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; PlayWorldClip(possessionClips, clipIndex, position, "PoltergeistPossession"); } } } private void OnPossessedClientIdChanged(ulong previousId, ulong nextId) { ApplyPossessedClientIdLocal(previousId, nextId, animate: true); } private void TickPossessionReconcile(float dt) { _possessionReconcileTimer -= dt; if (_possessionReconcileTimer > 0f) { return; } _possessionReconcileTimer = 0.5f; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return; } ulong value = _possessedClientId.Value; for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if ((Object)(object)val == (Object)null || (ulong)i == value) { continue; } PoltergeistPossessionLock component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null) && component.Holds && !((Object)(object)component.Poltergeist != (Object)(object)this)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PoltergeistAI] Possession reconcile: releasing a stale lock on " + val.playerUsername + "; the replicated victim is " + ((value == ulong.MaxValue) ? "nobody" : value.ToString()) + ".")); } ReleasePossessionLockLocal((ulong)i); } } if (value == ulong.MaxValue) { return; } PlayerControllerB val2 = ResolvePlayerByClientId(value); if ((Object)(object)val2 == (Object)null || val2.isPlayerDead || !val2.isPlayerControlled || PossessionSecondsRemaining <= 0f) { return; } PoltergeistPossessionLock component2 = ((Component)val2).GetComponent(); if ((!((Object)(object)component2 != (Object)null) || !component2.Holds || !((Object)(object)component2.Poltergeist == (Object)(object)this)) && (!((Object)(object)component2 != (Object)null) || !component2.Holds || !((Object)(object)component2.Poltergeist != (Object)null))) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[PoltergeistAI] Possession reconcile: " + val2.playerUsername + " is the replicated victim but carries no live lock - re-running the attach (a value-changed callback was missed or threw).")); } ApplyPossessedClientIdLocal(ulong.MaxValue, value, animate: false); } } private void ApplyPossessedClientIdLocal(ulong previousId, ulong nextId, bool animate) { if (previousId != ulong.MaxValue && previousId != nextId) { ReleasePossessionLockLocal(previousId); } AbortMergeVfx(); if (nextId == ulong.MaxValue) { PlayerControllerB victim = ResolvePlayerByClientId(previousId); if (!animate || previousId == ulong.MaxValue || base.isEnemyDead || !BeginMergeVfx(victim, emerging: true)) { SetMergedRenderers(hidden: false); SetMergedColliders(disabled: false); } return; } SetMergedColliders(!base.isEnemyDead); PlayerControllerB val = ResolvePlayerByClientId(nextId); if ((Object)(object)val == (Object)null) { SetMergedRenderers(!base.isEnemyDead); return; } List list = new List(5); if (TryAttachPossessionPart(val, "lock", delegate(PlayerControllerB p) { PoltergeistPossessionLock poltergeistPossessionLock = ((Component)p).GetComponent(); if ((Object)(object)poltergeistPossessionLock == (Object)null) { poltergeistPossessionLock = ((Component)p).gameObject.AddComponent(); } poltergeistPossessionLock.Configure(p, this); })) { list.Add("lock"); } if (TryAttachPossessionPart(val, "arms", delegate(PlayerControllerB p) { PoltergeistSpectralArms.Attach(p, this); })) { list.Add("arms"); } if (TryAttachPossessionPart(val, "attacks", delegate(PlayerControllerB p) { PoltergeistPossessedAttacks.Attach(p); })) { list.Add("attacks"); } if (TryAttachPossessionPart(val, "voice", delegate(PlayerControllerB p) { PoltergeistPossessedVoice.Attach(p); })) { list.Add("voice"); } if (TryAttachPossessionPart(val, "pose", delegate(PlayerControllerB p) { PoltergeistPossessedPose.Attach(p); })) { list.Add("pose"); } bool flag = false; try { flag = animate && BeginMergeVfx(val, emerging: false); } catch (Exception arg) { LogError($"Possession attach failed (merge vfx) on {val.playerUsername}: {arg}"); } if (!flag) { if (TryAttachPossessionPart(val, "glow", delegate(PlayerControllerB p) { SetMergedRenderers(!base.isEnemyDead); PoltergeistPossessionGlow.Attach(p); })) { list.Add("glow"); } } else { list.Add("merge"); } LogPossession("Possession attached on " + val.playerUsername + " " + string.Format("(local player: {0}): {1}.", IsLocalPossessionVictim(val), string.Join(", ", list.ToArray()))); } private static bool IsLocalPossessionVictim(PlayerControllerB victim) { if ((Object)(object)victim != (Object)null && (Object)(object)GameNetworkManager.Instance != (Object)null) { return (Object)(object)victim == (Object)(object)GameNetworkManager.Instance.localPlayerController; } return false; } private bool TryAttachPossessionPart(PlayerControllerB victim, string part, Action attach) { try { attach(victim); return true; } catch (Exception arg) { LogError($"Possession attach failed ({part}) on {victim.playerUsername}: {arg}"); return false; } } private void ReleasePossessionLockLocal(ulong playerClientId) { PlayerControllerB val = ResolvePlayerByClientId(playerClientId); if (!((Object)(object)val == (Object)null)) { PoltergeistPossessionLock component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.Poltergeist == (Object)(object)this) { component.ReleaseNow(); } PoltergeistSpectralArms.Detach(val); PoltergeistPossessedAttacks.Detach(val); PoltergeistPossessedPose.Detach(val); PoltergeistPossessionGlow.Detach(val); PoltergeistPossessedVoice.Detach(val); } } private static PlayerControllerB ResolvePlayerByClientId(ulong playerClientId) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return null; } if (playerClientId >= (ulong)instance.allPlayerScripts.Length) { return null; } return instance.allPlayerScripts[playerClientId]; } private void SetMergedRenderers(bool hidden) { if (_mergedRenderersHidden == hidden) { return; } _mergedRenderersHidden = hidden; CacheBodyRenderers(); if (_bodyRenderers == null) { return; } for (int i = 0; i < _bodyRenderers.Length; i++) { if ((Object)(object)_bodyRenderers[i] != (Object)null) { ((Renderer)_bodyRenderers[i]).enabled = !hidden; } } } private void SetMergedColliders(bool disabled) { if (_mergedCollidersDisabled == disabled) { return; } _mergedCollidersDisabled = disabled; if (disabled) { Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); int num = 0; for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null && componentsInChildren[i].enabled) { componentsInChildren[num++] = componentsInChildren[i]; } } _mergedColliders = (Collider[])(object)new Collider[num]; Array.Copy(componentsInChildren, _mergedColliders, num); for (int j = 0; j < _mergedColliders.Length; j++) { _mergedColliders[j].enabled = false; } } else { if (_mergedColliders == null) { return; } for (int k = 0; k < _mergedColliders.Length; k++) { if ((Object)(object)_mergedColliders[k] != (Object)null) { _mergedColliders[k].enabled = true; } } _mergedColliders = null; } } private bool BeginMergeVfx(PlayerControllerB victim, bool emerging) { if (Cfg == null || (Object)(object)victim == (Object)null || base.isEnemyDead) { return false; } if (!TryResolveVisualRoot()) { return false; } float num = (emerging ? 0.9f : 0.7f); if (num <= 0.05f) { return false; } _mergeVfxVictim = victim; _mergeVfxEmerging = emerging; _mergeVfxDuration = num; _mergeVfxTimer = 0f; _mergeVfxActive = true; SetMergedRenderers(hidden: false); BeginPhasePresentation(); TickMergeVfx(0f); return true; } private void TickMergeVfx(float dt) { //IL_0090: 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_009a: 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_00b3: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_visualRoot == (Object)null || (Object)(object)_mergeVfxVictim == (Object)null || base.isEnemyDead) { AbortMergeVfx(); return; } _mergeVfxTimer += dt; float num = Mathf.Clamp01(_mergeVfxTimer / _mergeVfxDuration); float num2 = (_mergeVfxEmerging ? ((1f - num) * (1f - num)) : (num * num)); float num3 = Mathf.Lerp(1f, Mathf.Clamp(0.12f, 0.01f, 1f), num2); Vector3 val = ((Component)this).transform.TransformPoint(_visualRootRestLocalPosition); Vector3 val2 = ResolveVisorPoint(_mergeVfxVictim) - Vector3.up * (1.15f * num3); _visualRoot.position = Vector3.Lerp(val, val2, num2); _visualRoot.localScale = _visualRootRestLocalScale * num3; _phaseVisualBlend = num2; if ((Object)(object)_phaseMaterialInstance != (Object)null) { SetMaterialAlpha(_phaseMaterialInstance, Mathf.Lerp(1f, 0f, num2)); } if (num >= 1f) { FinishMergeVfx(); } } private void FinishMergeVfx() { PlayerControllerB mergeVfxVictim = _mergeVfxVictim; bool mergeVfxEmerging = _mergeVfxEmerging; RestoreVisualRoot(); _mergeVfxActive = false; _mergeVfxVictim = null; _phaseVisualBlend = 0f; EndPhasePresentation(); if (mergeVfxEmerging) { SetMergedRenderers(hidden: false); SetMergedColliders(disabled: false); } else { SetMergedRenderers(hidden: true); PoltergeistPossessionGlow.Attach(mergeVfxVictim); } } private void AbortMergeVfx() { if (_mergeVfxActive) { RestoreVisualRoot(); _mergeVfxActive = false; _mergeVfxVictim = null; _phaseVisualBlend = 0f; EndPhasePresentation(); } } private Vector3 ResolveVisorPoint(PlayerControllerB victim) { //IL_003c: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_002b: 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) Camera gameplayCamera = victim.gameplayCamera; if ((Object)(object)gameplayCamera != (Object)null) { return ((Component)gameplayCamera).transform.position + ((Component)gameplayCamera).transform.forward * 0.15f; } return ((Component)victim).transform.position + Vector3.up * 2.2f; } private bool TryResolveVisualRoot() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (_visualRootResolved) { return (Object)(object)_visualRoot != (Object)null; } _visualRootResolved = true; CacheBodyRenderers(); if (_bodyRenderers != null) { for (int i = 0; i < _bodyRenderers.Length; i++) { if (!((Object)(object)_visualRoot == (Object)null)) { break; } SkinnedMeshRenderer val = _bodyRenderers[i]; if (!((Object)(object)val == (Object)null)) { Transform val2 = (((Object)(object)val.rootBone != (Object)null) ? val.rootBone : ((Component)val).transform); while ((Object)(object)val2 != (Object)null && (Object)(object)val2.parent != (Object)(object)((Component)this).transform) { val2 = val2.parent; } if ((Object)(object)val2 != (Object)null) { _visualRoot = val2; } } } } if ((Object)(object)_visualRoot == (Object)null) { LogPossession("Merge VFX could not resolve the armature under the enemy root; falling back to the plain hide."); return false; } _visualRootRestLocalPosition = _visualRoot.localPosition; _visualRootRestLocalScale = _visualRoot.localScale; return true; } private void RestoreVisualRoot() { //IL_0016: 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) if (!((Object)(object)_visualRoot == (Object)null)) { _visualRoot.localPosition = _visualRootRestLocalPosition; _visualRoot.localScale = _visualRootRestLocalScale; } } public override void HitEnemy(int force = 1, PlayerControllerB playerWhoHit = null, bool playHitSFX = false, int hitID = -1) { //IL_00a9: 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) try { ((EnemyAI)this).HitEnemy(force, playerWhoHit, playHitSFX, hitID); if (!base.isEnemyDead && base.currentBehaviourStateIndex != 2 && base.currentBehaviourStateIndex != 5 && ((NetworkBehaviour)this).IsServer) { base.enemyHP -= Mathf.Max(1, force); int nextHitIndex = _nextHitIndex; _nextHitIndex = (_nextHitIndex + 1) % 2; PlayHitReactionClientRpc(nextHitIndex); if (base.enemyHP <= 0) { ((EnemyAI)this).KillEnemyOnOwnerClient(false); return; } ReleasePossession(PossessionEndReason.PoltergeistDamaged); BeginFlee(((Object)(object)playerWhoHit != (Object)null) ? ((Component)playerWhoHit).transform.position : ((Component)this).transform.position); } } catch (Exception arg) { LogError($"HitEnemy: {arg}"); } } private void BeginFlee(Vector3 threatPosition) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) StopFacilitySearch(); if (_attackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_attackRoutine); _attackRoutine = null; } base.targetPlayer = null; _fleeTimer = 9f; _fleeThreat = threatPosition; _fleeAnchor = PickFleeDestination(_fleeThreat); ApplyAgentSpeed(Cfg.FleeSpeed.Value); PlayRandomClip(base.creatureVoice, fleeClips); ((EnemyAI)this).SwitchToBehaviourState(6); } private Vector3 PickFleeDestination(Vector3 threat) { //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_000c: 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) //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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_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_0062: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)this).transform.position - threat; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Random.insideUnitSphere; } val.y = 0f; val = ((Vector3)(ref val)).normalized; NavMeshHit val3 = default(NavMeshHit); for (int i = 0; i < 6; i++) { Vector3 val2 = Quaternion.Euler(0f, Random.Range(-55f, 55f), 0f) * val; if (NavMesh.SamplePosition(((Component)this).transform.position + val2 * Random.Range(12f, 22f), ref val3, 6f, -1)) { return ((NavMeshHit)(ref val3)).position; } } return ((Component)this).transform.position; } [ClientRpc] public void PlayHitReactionClientRpc(int hitIndex) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: 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_00ce: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(761061376u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, hitIndex); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 761061376u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; if (!base.isEnemyDead) { SetAnimatorInt("hitIndex", Mathf.Clamp(hitIndex, 0, 1)); SetAnimatorTrigger("doHit"); PlayRandomClip(base.creatureVoice, painClips); } } } public override void KillEnemy(bool destroy = false) { if (destroy) { ((EnemyAI)this).KillEnemy(true); return; } try { ((EnemyAI)this).KillEnemy(false); base.isEnemyDead = true; ReleasePossession(PossessionEndReason.PoltergeistDied); AbortMergeVfx(); SetMergedRenderers(hidden: false); SetMergedColliders(disabled: false); _emergeHoldTimer = 0f; _emergeFleePending = false; AbortPhase(); _phaseVisualBlend = 0f; EndPhasePresentation(); StopPresentationAudio(); StopFacilitySearch(); StopAgentPath(); _backpedalTimer = 0f; if (_attackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_attackRoutine); _attackRoutine = null; } SetAnimatorBool("isMoving", value: false); SetAnimatorBool("isRunning", value: false); SetAnimatorBool("isRetreating", value: false); SetAnimatorBool("isPhasing", value: false); SetAnimatorBool("isPossessing", value: false); SetAnimatorInt("deathIndex", Random.Range(0, 2)); SetAnimatorTrigger("doDeath"); PlayRandomClip(base.creatureVoice, deathClips); if ((Object)(object)base.creatureAnimator != (Object)null) { base.creatureAnimator.speed = 1f; } ((MonoBehaviour)this).StartCoroutine(DissipateRoutine()); } catch (Exception arg) { LogError($"KillEnemy: {arg}"); } } private IEnumerator DissipateRoutine() { _ = Cfg; float num = ((Cfg != null) ? 0.9f : 0.9f); if (num > 0f) { yield return (object)new WaitForSeconds(num); } SkinnedMeshRenderer[] renderers = ((Component)this).GetComponentsInChildren(true); Material val = LoadPhaseMaterial(); if ((Object)(object)val != (Object)null && renderers.Length != 0) { _dissipateMaterial = new Material(val); for (int i = 0; i < renderers.Length; i++) { if ((Object)(object)renderers[i] != (Object)null) { ((Renderer)renderers[i]).sharedMaterial = _dissipateMaterial; } } } else { LogError("Dissipate could not resolve Poltergeist_Phase or any renderer; skipping the fade and despawning."); } Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { if ((Object)(object)componentsInChildren[j] != (Object)null && !componentsInChildren[j].isTrigger) { componentsInChildren[j].enabled = false; } } float duration = ((Cfg != null) ? 2f : 2f); if ((Object)(object)_dissipateMaterial != (Object)null) { float startAlpha = GetMaterialAlpha(_dissipateMaterial); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; SetMaterialAlpha(_dissipateMaterial, Mathf.Lerp(startAlpha, 0f, elapsed / duration)); yield return null; } SetMaterialAlpha(_dissipateMaterial, 0f); for (int k = 0; k < renderers.Length; k++) { if ((Object)(object)renderers[k] != (Object)null) { ((Renderer)renderers[k]).enabled = false; } } } else { yield return (object)new WaitForSeconds(duration); } if (((NetworkBehaviour)this).IsServer) { ((EnemyAI)this).KillEnemy(true); } } private Material ResolvePhaseBodyMaterial() { return LoadPhaseMaterial(); } internal static Material LoadPhaseMaterial() { return LoadBundledMaterial("Poltergeist_Phase"); } private static Material LoadBundledMaterial(string materialName) { if ((Object)(object)Plugin.ModAssets == (Object)null) { return null; } Material val = Plugin.ModAssets.LoadAsset(materialName); if ((Object)(object)val != (Object)null) { return val; } Material[] array = Plugin.ModAssets.LoadAllAssets(); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && string.Equals(((Object)array[i]).name, materialName, StringComparison.OrdinalIgnoreCase)) { return array[i]; } } for (int j = 0; j < array.Length; j++) { if ((Object)(object)array[j] != (Object)null && ((Object)array[j]).name.IndexOf(materialName, StringComparison.OrdinalIgnoreCase) >= 0) { return array[j]; } } return null; } private static float GetMaterialAlpha(Material material) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(BaseColorId)) { return material.GetColor(BaseColorId).a; } if (!material.HasProperty(LegacyColorId)) { return 1f; } return material.GetColor(LegacyColorId).a; } private static void SetMaterialAlpha(Material material, float alpha) { //IL_0013: 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_0027: 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_0054: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(BaseColorId)) { Color color = material.GetColor(BaseColorId); color.a = alpha; material.SetColor(BaseColorId, color); } if (material.HasProperty(LegacyColorId)) { Color color2 = material.GetColor(LegacyColorId); color2.a = alpha; material.SetColor(LegacyColorId, color2); } } public override void OnDestroy() { ((EnemyAI)this).OnDestroy(); if ((Object)(object)_dissipateMaterial != (Object)null) { Object.Destroy((Object)(object)_dissipateMaterial); _dissipateMaterial = null; } if ((Object)(object)_phaseMaterialInstance != (Object)null) { Object.Destroy((Object)(object)_phaseMaterialInstance); _phaseMaterialInstance = null; } } private PlayerControllerB FindVisiblePlayer() { //IL_0047: 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) if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return null; } PlayerControllerB val = null; float num = float.MaxValue; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (CanSeePlayer(val2)) { float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)val2).transform.position); bool flag = num2 < num - 0.01f; bool flag2 = Mathf.Abs(num2 - num) <= 0.01f && ((Object)(object)val == (Object)null || val2.playerClientId < val.playerClientId); if (flag || flag2) { val = val2; num = num2; } } } return val; } private bool HasLineOfSightTo(PlayerControllerB player) { return CanSeePlayer(player); } private bool CanSeePlayer(PlayerControllerB player) { //IL_005c: 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_004a: 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_0061: 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_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_0085: 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_00a1: 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_00a4: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || player.isPlayerDead || !player.isPlayerControlled || !((EnemyAI)this).PlayerIsTargetable(player, false, false, true)) { return false; } Vector3 val = (((Object)(object)base.eye != (Object)null) ? base.eye.position : (((Component)this).transform.position + Vector3.up * 1.5f)); Vector3 val2 = (((Object)(object)player.gameplayCamera != (Object)null) ? ((Component)player.gameplayCamera).transform.position : (((Component)player).transform.position + Vector3.up * 1.4f)); Vector3 val3 = val2 - val; float magnitude = ((Vector3)(ref val3)).magnitude; float num = ((Cfg != null) ? Cfg.SightRange.Value : 30f); if (magnitude > num) { return false; } if (magnitude > 4f && Vector3.Angle(((Object)(object)base.eye != (Object)null) ? base.eye.forward : ((Component)this).transform.forward, val3) > 75f) { return false; } return !IsWorldPathBlocked(val, val2, player); } private bool TargetIsValid() { if ((Object)(object)base.targetPlayer != (Object)null && !base.targetPlayer.isPlayerDead && base.targetPlayer.isPlayerControlled) { return ((EnemyAI)this).PlayerIsTargetable(base.targetPlayer, false, false, true); } return false; } private void StartFacilitySearchIfNeeded() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)this).IsOwner && AgentReady) { if (base.currentSearch == null) { base.currentSearch = new AISearchRoutine(); } if (!_searchConfigured) { base.currentSearch.loopSearch = true; base.currentSearch.randomized = true; base.currentSearch.searchWidth = ((Cfg != null) ? 50f : 50f); base.currentSearch.searchPrecision = ((Cfg != null) ? 6f : 6f); _searchConfigured = true; } if (!base.currentSearch.inProgress) { ((EnemyAI)this).StartSearch(((Component)this).transform.position, base.currentSearch); } } } private void StopFacilitySearch() { if (base.currentSearch != null && base.currentSearch.inProgress) { ((EnemyAI)this).StopSearch(base.currentSearch, false); } } private void StopAgentPath() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (AgentReady) { base.agent.velocity = Vector3.zero; base.agent.ResetPath(); } } private void ApplyAgentSpeed(float speed) { if (!((Object)(object)base.agent == (Object)null)) { base.agent.speed = speed; base.agent.angularSpeed = ((Cfg != null) ? 380f : 380f); base.agent.acceleration = ((Cfg != null) ? 16f : 16f); } } private void TickFacing(float dt) { //IL_0012: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)this).IsOwner && !(dt <= 0f)) { Vector3 val = ResolveFacingDirection(); val.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f)) { float num = ((Cfg != null) ? 380f : 380f); ((Component)this).transform.rotation = Quaternion.RotateTowards(((Component)this).transform.rotation, Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up), num * dt); } } } private Vector3 ResolveFacingDirection() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0084: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_0098: 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_00a8: Unknown result type (might be due to invalid IL or missing references) if (base.currentBehaviourStateIndex == 3 && (Object)(object)base.targetPlayer != (Object)null) { return ((Component)base.targetPlayer).transform.position - ((Component)this).transform.position; } if (base.currentBehaviourStateIndex == 4 && (Object)(object)base.targetPlayer != (Object)null) { return ((Component)base.targetPlayer).transform.position - ((Component)this).transform.position; } if (IsBackpedaling) { return _fleeThreat - ((Component)this).transform.position; } if (_phaseActive) { return _phaseEnd - ((Component)this).transform.position; } return CurrentTravelDirection(); } private Vector3 CurrentTravelDirection() { //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_0008: 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_0041: 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_0062: 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) if (!AgentReady) { return Vector3.zero; } Vector3 velocity = base.agent.velocity; velocity.y = 0f; if (((Vector3)(ref velocity)).sqrMagnitude > 0.04f) { return velocity; } Vector3 desiredVelocity = base.agent.desiredVelocity; desiredVelocity.y = 0f; if (!(((Vector3)(ref desiredVelocity)).sqrMagnitude > 0.04f)) { return Vector3.zero; } return desiredVelocity; } private void UpdateObservedSpeed(float dt) { //IL_000f: 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_0015: 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_001c: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!(dt <= 0f)) { Vector3 position = ((Component)this).transform.position; Vector3 val = position - _lastSpeedSamplePosition; _lastSpeedSamplePosition = position; val.y = 0f; float num = ((Vector3)(ref val)).magnitude / dt; if (num > 25f) { num = 0f; } _observedSpeed = Mathf.Lerp(_observedSpeed, num, Mathf.Clamp01(dt * 12f)); } } private void TickBackpedalBeat(float dt) { if (base.currentBehaviourStateIndex != _lastObservedState) { _lastObservedState = base.currentBehaviourStateIndex; _backpedalTimer = ((base.currentBehaviourStateIndex == 6 && Cfg != null) ? 1.2f : 0f); } else if (_backpedalTimer > 0f) { _backpedalTimer -= dt; } } private void TickLocomotionAnimation() { if (!((Object)(object)base.creatureAnimator == (Object)null)) { bool flag = base.currentBehaviourStateIndex == 2; bool flag2 = base.currentBehaviourStateIndex == 5; SetAnimatorBool("isPhasing", flag); SetAnimatorBool("isPossessing", value: false); if (flag || flag2) { SetAnimatorBool("isRetreating", value: false); SetAnimatorBool("isMoving", value: false); SetAnimatorBool("isRunning", value: false); return; } SetAnimatorBool("isRetreating", IsBackpedaling); float observedSpeed = _observedSpeed; bool flag3 = observedSpeed > 0.15f; bool value = base.currentBehaviourStateIndex == 6 || (flag3 && observedSpeed > Cfg.StalkSpeed.Value * 0.9f); SetAnimatorBool("isMoving", flag3); SetAnimatorBool("isRunning", value); } } private void TickAnimatorPlaybackSpeed(float dt) { if ((Object)(object)base.creatureAnimator == (Object)null || Cfg == null) { return; } if (base.currentBehaviourStateIndex == 3 || base.currentBehaviourStateIndex == 4) { _animatorSpeed = 1f; base.creatureAnimator.speed = 1f; return; } if (base.currentBehaviourStateIndex == 2 || base.currentBehaviourStateIndex == 5) { _animatorSpeed = Mathf.MoveTowards(_animatorSpeed, 1f, dt * 4f); base.creatureAnimator.speed = _animatorSpeed; return; } float observedSpeed = _observedSpeed; if (observedSpeed <= 0.15f) { _animatorSpeed = Mathf.MoveTowards(_animatorSpeed, 1f, dt * 4f); base.creatureAnimator.speed = _animatorSpeed; return; } float num = ((observedSpeed > Cfg.StalkSpeed.Value * 0.9f) ? 4.2f : 1.9f); if (!(num <= 0.01f)) { float num2 = Mathf.Clamp(observedSpeed / num, 0.7f, 1.9f); _animatorSpeed = Mathf.MoveTowards(_animatorSpeed, num2, dt * 4f); base.creatureAnimator.speed = _animatorSpeed; } } private void SetAnimatorBool(string name, bool value) { if (TryGetParameter(name, (AnimatorControllerParameterType)4, out var hash)) { base.creatureAnimator.SetBool(hash, value); } } private void SetAnimatorInt(string name, int value) { if (TryGetParameter(name, (AnimatorControllerParameterType)3, out var hash)) { base.creatureAnimator.SetInteger(hash, value); } } private void SetAnimatorTrigger(string name) { if (TryGetParameter(name, (AnimatorControllerParameterType)9, out var hash)) { base.creatureAnimator.SetTrigger(hash); } } private bool TryGetParameter(string name, AnimatorControllerParameterType type, out int hash) { //IL_0022: 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) hash = 0; if (!_animatorParamsCached) { CacheAnimatorParameters(); } if (!_animatorParams.TryGetValue(name, out var value) || value.Type != type) { return false; } hash = value.Hash; return true; } private void CacheAnimatorParameters() { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) _animatorParamsCached = true; _animatorParams.Clear(); if (!((Object)(object)base.creatureAnimator == (Object)null) && !((Object)(object)base.creatureAnimator.runtimeAnimatorController == (Object)null)) { AnimatorControllerParameter[] parameters = base.creatureAnimator.parameters; foreach (AnimatorControllerParameter val in parameters) { _animatorParams[val.name] = new CachedAnimatorParam { Hash = val.nameHash, Type = val.type }; } LogDebug($"Cached {_animatorParams.Count} animator parameter(s)."); } } private void EnsurePresentationAudioSources() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0033: 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_006b: Unknown result type (might be due to invalid IL or missing references) Vector3 localPosition = Vector3.up * 1.15f; _presenceLoopSource = FindOrCreatePresentationAudioSource("PassengerPresenceLoop", localPosition, 1.5f, 12f); _foleySource = FindOrCreatePresentationAudioSource("PassengerMovementFoley", localPosition, 1.25f, 10f); _swipeSource = FindOrCreatePresentationAudioSource("PassengerSwipe", localPosition, 1.5f, 18f); _phaseSource = FindOrCreatePresentationAudioSource("PassengerPhase", localPosition, 2f, 30f); if ((Object)(object)_presenceLoopSource != (Object)null && (Object)(object)presenceLoopClip != (Object)null) { _presenceLoopSource.clip = presenceLoopClip; _presenceLoopSource.loop = true; _presenceLoopSource.volume = 0f; _presenceLoopSource.Play(); } _nextFoleyIndex = ((footstepClips != null && footstepClips.Length > 1) ? Random.Range(0, footstepClips.Length) : 0); } private AudioSource FindOrCreatePresentationAudioSource(string childName, Vector3 localPosition, float minDistance, float maxDistance) { //IL_0030: 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) Transform val = ((Component)this).transform.Find(childName); if ((Object)(object)val == (Object)null) { val = new GameObject(childName).transform; val.SetParent(((Component)this).transform, false); } val.localPosition = localPosition; AudioSource obj = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); Plugin.ConfigureWorldAudioSource(obj, minDistance, maxDistance); obj.loop = false; obj.volume = 1f; obj.dopplerLevel = 0f; return obj; } private void TickPresenceLoop(float dt) { if (!((Object)(object)_presenceLoopSource == (Object)null) && !((Object)(object)_presenceLoopSource.clip == (Object)null)) { if (!_presenceLoopSource.isPlaying) { _presenceLoopSource.Play(); } float num = ((!_mergeVfxActive && base.currentBehaviourStateIndex != 2 && base.currentBehaviourStateIndex != 5) ? 0.45f : 0f); _presenceLoopSource.volume = Mathf.MoveTowards(_presenceLoopSource.volume, num, dt * 1.5f); } } private void TickMovementFoley(float dt) { if ((Object)(object)_foleySource == (Object)null || footstepClips == null || footstepClips.Length == 0) { return; } if (_mergeVfxActive || !(_observedSpeed > 0.3f) || (base.currentBehaviourStateIndex != 0 && base.currentBehaviourStateIndex != 1 && base.currentBehaviourStateIndex != 6)) { _foleyTravel = 0f; return; } float num = ((base.currentBehaviourStateIndex == 6 || _observedSpeed > 2.565f) ? 1.65f : 1.25f); _foleyTravel += _observedSpeed * dt; if (!(_foleyTravel < num)) { _foleyTravel %= num; int num2 = Mathf.Clamp(_nextFoleyIndex, 0, footstepClips.Length - 1); _nextFoleyIndex = (_nextFoleyIndex + 1) % footstepClips.Length; AudioClip val = footstepClips[num2]; if (!((Object)(object)val == (Object)null)) { _foleySource.pitch = Random.Range(0.96f, 1.04f); _foleySource.PlayOneShot(val, 0.5f); } } } private void StopPresentationAudio() { if ((Object)(object)_presenceLoopSource != (Object)null) { _presenceLoopSource.Stop(); } if ((Object)(object)_foleySource != (Object)null) { _foleySource.Stop(); } if ((Object)(object)_swipeSource != (Object)null) { _swipeSource.Stop(); } if ((Object)(object)_phaseSource != (Object)null) { _phaseSource.Stop(); } } private void PlayDetectOnce() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) if (!_detectPlayed && ((NetworkBehaviour)this).IsServer) { _detectPlayed = true; int clipIndex = ((aggroClips != null && aggroClips.Length != 0) ? Random.Range(0, aggroClips.Length) : (-1)); PlayDetectClientRpc(clipIndex, ((Component)this).transform.position + Vector3.up * 1.15f); _ambientTimer = Random.Range(9f, 18f); } } [ClientRpc] public void PlayDetectClientRpc(int clipIndex, Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: 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_00db: 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) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager != null && networkManager.IsListening) { if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(2198796180u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, clipIndex); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 2198796180u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; PlayWorldClip(aggroClips, clipIndex, position, "PoltergeistDetect"); } } } private void TickAmbientAudio(float dt) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //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) if (((NetworkBehaviour)this).IsServer && ambientClips != null && ambientClips.Length != 0 && base.currentBehaviourStateIndex != 5) { _ambientTimer -= dt; if (!(_ambientTimer > 0f)) { _ambientTimer = Random.Range(9f, 18f); int clipIndex = Random.Range(0, ambientClips.Length); PlayPassiveClientRpc(clipIndex, ((Component)this).transform.position + Vector3.up * 1.15f); } } } [ClientRpc] public void PlayPassiveClientRpc(int clipIndex, Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_005f: 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) //IL_0071: 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_00db: 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) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager != null && networkManager.IsListening) { if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(675187663u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, clipIndex); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 675187663u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; PlayWorldClip(ambientClips, clipIndex, position, "PoltergeistPassive"); } } } private static void PlayRandomClip(AudioSource source, AudioClip[] clips) { PlayRandomClip(source, clips, 1f); } private static void PlayRandomClip(AudioSource source, AudioClip[] clips, float volumeScale) { if (!((Object)(object)source == (Object)null) && clips != null && clips.Length != 0) { AudioClip val = clips[Random.Range(0, clips.Length)]; if ((Object)(object)val != (Object)null) { source.PlayOneShot(val, Mathf.Clamp01(volumeScale)); } } } private bool RejectPhase(PhaseReject reason) { _phaseRejectCounts[(int)reason]++; if (IsPhaseEvaluation(reason)) { _phaseDiagPending = true; } return false; } private void RecordPhaseRatio(float observed, float threshold) { if (!float.IsInfinity(observed) && !float.IsNaN(observed) && !(observed <= _phaseDiagBestRatio)) { _phaseDiagBestRatio = observed; _phaseDiagBestRatioThreshold = threshold; } } private void RecordPhaseBand(float distance) { _phaseDiagBandMin = Mathf.Min(_phaseDiagBandMin, distance); _phaseDiagBandMax = Mathf.Max(_phaseDiagBandMax, distance); } private void TickPhaseDiagnostics(float dt) { if (Cfg == null || !Cfg.PhaseDiagnostics.Value) { return; } _phaseDiagnosticsTimer -= dt; if (!(_phaseDiagnosticsTimer > 0f)) { _phaseDiagnosticsTimer = Mathf.Max(2f, Cfg.PhaseDiagnosticsSeconds.Value); if (_phaseDiagPending) { LogPhase(BuildPhaseDiagnosticsSummary()); ResetPhaseDiagnostics(); } } } private string BuildPhaseDiagnosticsSummary() { int num = 0; for (int i = 6; i < 15; i++) { num += _phaseRejectCounts[i]; } StringBuilder stringBuilder = new StringBuilder(192); stringBuilder.Append("Phase diagnostics: ").Append(num).Append(" evaluation(s), ") .Append(_phaseRejectCounts[14]) .Append(" started."); AppendPhaseCounts(stringBuilder, " Rejected: ", 6, 15); AppendPhaseCounts(stringBuilder, " Never evaluated: ", 0, 6); if (_phaseRejectCounts[13] > 0) { stringBuilder.Append(" Best path ratio ").Append(_phaseDiagBestRatio.ToString("F2")).Append(" against a ") .Append(_phaseDiagBestRatioThreshold.ToString("F2")) .Append(" threshold."); } if (_phaseRejectCounts[7] > 0) { stringBuilder.Append(" Out-of-band distances ").Append(_phaseDiagBandMin.ToString("F1")).Append('-') .Append(_phaseDiagBandMax.ToString("F1")) .Append("m against a ") .Append(4f.ToString("F1")) .Append('-') .Append(18f.ToString("F1")) .Append("m band."); } return stringBuilder.ToString(); } private void AppendPhaseCounts(StringBuilder summary, string label, int from, int to) { bool flag = false; for (int i = from; i < to; i++) { if (i != 14 && _phaseRejectCounts[i] != 0) { summary.Append(flag ? ", " : label).Append(PhaseRejectLabel((PhaseReject)i)).Append(' ') .Append(_phaseRejectCounts[i]); flag = true; } } if (flag) { summary.Append('.'); } } private void ResetPhaseDiagnostics() { Array.Clear(_phaseRejectCounts, 0, _phaseRejectCounts.Length); _phaseDiagBestRatio = 0f; _phaseDiagBestRatioThreshold = 0f; _phaseDiagBandMin = float.MaxValue; _phaseDiagBandMax = 0f; _phaseDiagPending = false; } private static string PhaseRejectLabel(PhaseReject reason) { return reason switch { PhaseReject.Throttled => "throttled", PhaseReject.Disabled => "disabled", PhaseReject.Busy => "already-phasing", PhaseReject.Cooldown => "cooldown", PhaseReject.NoAnchor => "no-anchor", PhaseReject.NoAgent => "agent-off-mesh", PhaseReject.NoTarget => "no-target", PhaseReject.OutOfBand => "out-of-band", PhaseReject.NoNavPoint => "destination-off-mesh", PhaseReject.DestinationTooFar => "destination-too-far", PhaseReject.TravelTooShort => "travel-too-short", PhaseReject.NoWall => "no-wall-on-line", PhaseReject.NoClearLanding => "no-body-cleared-landing", PhaseReject.PathNotWorse => "path-not-worse", _ => reason.ToString(), }; } private static void LogError(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("[PoltergeistAI] " + message)); } } internal static void LogDebug(string message) { if (Plugin.ModConfig != null && Plugin.ModConfig.DebugLogs.Value) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PoltergeistAI] " + message)); } } } internal static void LogPhase(string message) { if (Plugin.ModConfig != null && Plugin.ModConfig.PhaseDiagnostics.Value) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PoltergeistAI] " + message)); } } } internal static void LogPossessionVerbose(string message) { if (Plugin.ModConfig != null && Plugin.ModConfig.PossessionDiagnostics.Value && !string.Equals(message, _lastVerbosePossessionLine, StringComparison.Ordinal)) { _lastVerbosePossessionLine = message; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PoltergeistAI] " + message)); } } } internal static void LogPossession(string message) { if (Plugin.ModConfig != null && Plugin.ModConfig.PossessionDiagnostics.Value) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PoltergeistAI] " + message)); } } } protected override void __initializeVariables() { if (_possessedClientId == null) { throw new Exception("PoltergeistAI._possessedClientId cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_possessedClientId).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_possessedClientId, "_possessedClientId"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_possessedClientId); if (_possessionEndTime == null) { throw new Exception("PoltergeistAI._possessionEndTime cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_possessionEndTime).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_possessionEndTime, "_possessionEndTime"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_possessionEndTime); if (_puppetWaypoint == null) { throw new Exception("PoltergeistAI._puppetWaypoint cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_puppetWaypoint).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_puppetWaypoint, "_puppetWaypoint"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_puppetWaypoint); if (_puppetLookPoint == null) { throw new Exception("PoltergeistAI._puppetLookPoint cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_puppetLookPoint).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_puppetLookPoint, "_puppetLookPoint"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_puppetLookPoint); if (_puppetGait == null) { throw new Exception("PoltergeistAI._puppetGait cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_puppetGait).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_puppetGait, "_puppetGait"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_puppetGait); if (_possessedAttackSwings == null) { throw new Exception("PoltergeistAI._possessedAttackSwings cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_possessedAttackSwings).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_possessedAttackSwings, "_possessedAttackSwings"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_possessedAttackSwings); if (_possessedAttackHits == null) { throw new Exception("PoltergeistAI._possessedAttackHits cannot be null. All NetworkVariableBase instances must be initialized."); } ((NetworkVariableBase)_possessedAttackHits).Initialize((NetworkBehaviour)(object)this); ((NetworkBehaviour)this).__nameNetworkVariable((NetworkVariableBase)(object)_possessedAttackHits, "_possessedAttackHits"); ((NetworkBehaviour)this).NetworkVariableFields.Add((NetworkVariableBase)(object)_possessedAttackHits); ((EnemyAI)this).__initializeVariables(); } protected override void __initializeRpcs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown ((NetworkBehaviour)this).__registerRpc(3207978809u, new RpcReceiveHandler(__rpc_handler_3207978809), "PlayPhaseAudioClientRpc"); ((NetworkBehaviour)this).__registerRpc(460116105u, new RpcReceiveHandler(__rpc_handler_460116105), "ConfirmClawHitClientRpc"); ((NetworkBehaviour)this).__registerRpc(829262534u, new RpcReceiveHandler(__rpc_handler_829262534), "PlayAttackAnimationClientRpc"); ((NetworkBehaviour)this).__registerRpc(4051472000u, new RpcReceiveHandler(__rpc_handler_4051472000), "PlayHowlClientRpc"); ((NetworkBehaviour)this).__registerRpc(2105154708u, new RpcReceiveHandler(__rpc_handler_2105154708), "PlayPossessionOnsetClientRpc"); ((NetworkBehaviour)this).__registerRpc(761061376u, new RpcReceiveHandler(__rpc_handler_761061376), "PlayHitReactionClientRpc"); ((NetworkBehaviour)this).__registerRpc(2198796180u, new RpcReceiveHandler(__rpc_handler_2198796180), "PlayDetectClientRpc"); ((NetworkBehaviour)this).__registerRpc(675187663u, new RpcReceiveHandler(__rpc_handler_675187663), "PlayPassiveClientRpc"); ((EnemyAI)this).__initializeRpcs(); } private static void __rpc_handler_3207978809(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0050: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { int audioVariant = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref audioVariant); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayPhaseAudioClientRpc(audioVariant); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_460116105(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //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_003d: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { ulong targetClientId = default(ulong); ByteUnpacker.ReadValueBitPacked(reader, ref targetClientId); int damage = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref damage); int impactClipIndex = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref impactClipIndex); Vector3 contactPoint = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref contactPoint); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).ConfirmClawHitClientRpc(targetClientId, damage, impactClipIndex, contactPoint); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_829262534(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0050: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { int attackIndex = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref attackIndex); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayAttackAnimationClientRpc(attackIndex); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_4051472000(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0029: 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) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayHowlClientRpc(); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_2105154708(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { int clipIndex = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref clipIndex); Vector3 position = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref position); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayPossessionOnsetClientRpc(clipIndex, position); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_761061376(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0050: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { int hitIndex = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref hitIndex); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayHitReactionClientRpc(hitIndex); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_2198796180(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { int clipIndex = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref clipIndex); Vector3 position = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref position); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayDetectClientRpc(clipIndex, position); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_675187663(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { int clipIndex = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref clipIndex); Vector3 position = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref position); target.__rpc_exec_stage = (__RpcExecStage)1; ((PoltergeistAI)(object)target).PlayPassiveClientRpc(clipIndex, position); target.__rpc_exec_stage = (__RpcExecStage)0; } } protected internal override string __getTypeName() { return "PoltergeistAI"; } } internal sealed class PoltergeistPhaseEffect : MonoBehaviour { private Light _light; private float _peakIntensity; private float _lifetime = 1f; private float _elapsed; internal static void Attach(GameObject effect, Light light, float peakIntensity, float lifetime) { if (!((Object)(object)effect == (Object)null)) { PoltergeistPhaseEffect poltergeistPhaseEffect = effect.GetComponent() ?? effect.AddComponent(); poltergeistPhaseEffect._light = light; poltergeistPhaseEffect._peakIntensity = Mathf.Max(0f, peakIntensity); poltergeistPhaseEffect._lifetime = Mathf.Max(0.05f, lifetime); poltergeistPhaseEffect._elapsed = 0f; if ((Object)(object)light != (Object)null) { light.intensity = poltergeistPhaseEffect._peakIntensity; ((Behaviour)light).enabled = poltergeistPhaseEffect._peakIntensity > 0f; } } } private void Update() { _elapsed += Time.deltaTime; if ((Object)(object)_light != (Object)null) { float num = Mathf.Clamp01(_elapsed / _lifetime); _light.intensity = _peakIntensity * (1f - num) * (1f - num); ((Behaviour)_light).enabled = _light.intensity > 0.01f; } if (_elapsed >= _lifetime) { Object.Destroy((Object)(object)((Component)this).gameObject); } } } public sealed class PoltergeistPossessedAttacks : MonoBehaviour { private const float EquipLeadSeconds = 0.1f; private const float HoldAfterSwingSeconds = 2f; private PlayerControllerB _victim; private object _handle; private float _releaseAt; private float _pendingPunchAt; private string _pendingPunchTrigger; private static PoltergeistConfig Cfg => Plugin.ModConfig; internal static void Attach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null) && Cfg != null && Cfg.PossessedAttacksEnabled.Value && (Object)(object)((Component)victim).GetComponent() == (Object)null) { ((Component)victim).gameObject.AddComponent()._victim = victim; } } internal static void Detach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null)) { PoltergeistPossessedAttacks component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } } internal static void Swing(PlayerControllerB victim, bool leftHand) { if (!((Object)(object)victim == (Object)null) && Cfg != null && Cfg.PossessedAttacksEnabled.Value) { Attach(victim); PoltergeistPossessedAttacks component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { component.PlaySwing(leftHand); } } } internal static bool SessionActive(PlayerControllerB victim) { if ((Object)(object)victim == (Object)null) { return false; } PoltergeistPossessedAttacks component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { return component._handle != null; } return false; } private void PlaySwing(bool leftHand) { if ((Object)(object)_victim == (Object)null) { return; } float num = 0f; if (!PossessionInteractionBridge.IsPresentationActive(_handle)) { _handle = null; bool firstPerson = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)_victim == (Object)(object)GameNetworkManager.Instance.localPlayerController; if (!PossessionInteractionBridge.TryStartFistsPresentation(_victim, firstPerson, out _handle)) { return; } PossessionInteractionBridge.FirePresentationTrigger(_handle, "Y4NGZ_Fists_Enter"); num = 0.1f; } _pendingPunchTrigger = (leftHand ? "PunchLeft" : "PunchRight"); _pendingPunchAt = Time.time + num; _releaseAt = Time.time + num + 2f; } private void Update() { if (_pendingPunchTrigger != null && Time.time >= _pendingPunchAt) { PossessionInteractionBridge.FirePresentationTrigger(_handle, _pendingPunchTrigger); _pendingPunchTrigger = null; } if (_handle != null && _pendingPunchTrigger == null && Time.time >= _releaseAt) { EndSession(immediate: false); } } private void OnDestroy() { EndSession(immediate: true); } private void EndSession(bool immediate) { if (_handle != null) { PossessionInteractionBridge.EndPresentation(_handle, immediate); _handle = null; _pendingPunchTrigger = null; } } } public sealed class PoltergeistPossessedPose : MonoBehaviour { private const string HandsOutClipName = "animWalkArmsOut"; private const string UpperBodyRootBone = "spine.003"; private const string UpperBodyRootFallbackBone = "spine.002"; private static AnimationClip _clip; private static bool _clipResolved; private PlayerControllerB _victim; private Animator _animator; private RuntimeAnimatorController _controller; private AvatarMask _mask; private PlayableGraph _graph; private bool _graphBuilt; private AnimatorControllerPlayable _controllerPlayable; private AnimationClipPlayable _clipPlayable; private AnimationLayerMixerPlayable _mixer; private static PoltergeistConfig Cfg => Plugin.ModConfig; internal static void Attach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null) && Cfg != null && !((Object)(object)((Component)victim).GetComponent() != (Object)null)) { ((Component)victim).gameObject.AddComponent().Configure(victim); } } internal static void Detach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null)) { PoltergeistPossessedPose component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { component.TearDown(); } } } private void Configure(PlayerControllerB victim) { _victim = victim; _animator = victim.playerBodyAnimator; if ((Object)(object)_animator == (Object)null || (Object)(object)_animator.runtimeAnimatorController == (Object)null || (Object)(object)ResolveClip() == (Object)null) { TearDown(); return; } _controller = _animator.runtimeAnimatorController; _mask = BuildUpperBodyMask(_animator); if ((Object)(object)_mask == (Object)null) { TearDown(); } } private void LateUpdate() { //IL_0082: 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_00e0: Unknown result type (might be due to invalid IL or missing references) PoltergeistPossessionLock poltergeistPossessionLock = (((Object)(object)_victim != (Object)null) ? ((Component)_victim).GetComponent() : null); if ((Object)(object)poltergeistPossessionLock == (Object)null || !poltergeistPossessionLock.Holds || _victim.isPlayerDead || (Object)(object)_animator == (Object)null) { TearDown(); return; } if (PoltergeistPossessedAttacks.SessionActive(_victim)) { DestroyGraph(); return; } if (!_graphBuilt && !TryBuildGraph()) { TearDown(); return; } MirrorParameters(); PlayableExtensions.SetInputWeight(_mixer, 1, Mathf.Clamp01((Cfg != null) ? 1f : 1f)); AnimationClip val = ResolveClip(); if ((Object)(object)val != (Object)null && !((Motion)val).isLooping && val.length > 0.01f) { double time = PlayableExtensions.GetTime(_clipPlayable); if (time >= (double)val.length) { PlayableExtensions.SetTime(_clipPlayable, time % (double)val.length); } } } private bool TryBuildGraph() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) AnimationClip val = ResolveClip(); if ((Object)(object)val == (Object)null || (Object)(object)_animator == (Object)null || (Object)(object)_animator.runtimeAnimatorController == (Object)null) { return false; } try { _controller = _animator.runtimeAnimatorController; _graph = PlayableGraph.Create($"PoltergeistPossessedPose_{_victim.playerClientId}"); ((PlayableGraph)(ref _graph)).SetTimeUpdateMode((DirectorUpdateMode)1); _controllerPlayable = AnimatorControllerPlayable.Create(_graph, _controller); _clipPlayable = AnimationClipPlayable.Create(_graph, val); ((AnimationClipPlayable)(ref _clipPlayable)).SetApplyFootIK(false); _mixer = AnimationLayerMixerPlayable.Create(_graph, 2); ((PlayableGraph)(ref _graph)).Connect(_controllerPlayable, 0, _mixer, 0); ((PlayableGraph)(ref _graph)).Connect(_clipPlayable, 0, _mixer, 1); PlayableExtensions.SetInputWeight(_mixer, 0, 1f); PlayableExtensions.SetInputWeight(_mixer, 1, 0f); ((AnimationLayerMixerPlayable)(ref _mixer)).SetLayerAdditive(1u, false); ((AnimationLayerMixerPlayable)(ref _mixer)).SetLayerMaskFromAvatarMask(1u, _mask); PlayableOutputExtensions.SetSourcePlayable(AnimationPlayableOutput.Create(_graph, "PoltergeistPossessedPose", _animator), _mixer); ((PlayableGraph)(ref _graph)).Play(); _graphBuilt = true; return true; } catch (Exception ex) { PoltergeistAI.LogPossession("Possessed hands-out pose graph failed to build: " + ex.Message); DestroyGraph(); return false; } } private void MirrorParameters() { //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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected I4, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 AnimatorControllerParameter[] parameters = _animator.parameters; foreach (AnimatorControllerParameter val in parameters) { AnimatorControllerParameterType type = val.type; switch (type - 1) { case 3: ((AnimatorControllerPlayable)(ref _controllerPlayable)).SetBool(val.nameHash, _animator.GetBool(val.nameHash)); continue; case 0: ((AnimatorControllerPlayable)(ref _controllerPlayable)).SetFloat(val.nameHash, _animator.GetFloat(val.nameHash)); continue; case 2: ((AnimatorControllerPlayable)(ref _controllerPlayable)).SetInteger(val.nameHash, _animator.GetInteger(val.nameHash)); continue; case 1: continue; } if ((int)type == 9 && _animator.GetBool(val.nameHash)) { _animator.ResetTrigger(val.nameHash); ((AnimatorControllerPlayable)(ref _controllerPlayable)).SetTrigger(val.nameHash); } } } private static AvatarMask BuildUpperBodyMask(Animator animator) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown Transform transform = ((Component)animator).transform; Transform val = FindDeep(transform, "spine.003") ?? FindDeep(transform, "spine.002"); if ((Object)(object)val == (Object)null) { PoltergeistAI.LogPossession("Possessed hands-out pose: no 'spine.003' under the body animator; skipping the pose."); return null; } Transform[] componentsInChildren = ((Component)transform).GetComponentsInChildren(true); AvatarMask val2 = new AvatarMask { name = "Poltergeist_HandsOut_UpperBody" }; val2.transformCount = componentsInChildren.Length; for (int i = 0; i < componentsInChildren.Length; i++) { val2.SetTransformPath(i, RelativePath(transform, componentsInChildren[i])); val2.SetTransformActive(i, IsDescendantOrSelf(componentsInChildren[i], val)); } return val2; } private static bool IsDescendantOrSelf(Transform candidate, Transform ancestor) { Transform val = candidate; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)ancestor) { return true; } val = val.parent; } return false; } private static string RelativePath(Transform root, Transform target) { if ((Object)(object)target == (Object)(object)root) { return string.Empty; } string text = ((Object)target).name; Transform parent = target.parent; while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)root) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private static AnimationClip ResolveClip() { if (_clipResolved) { return _clip; } _clipResolved = true; try { MaskedPlayerEnemy[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { if (!((Object)(object)_clip == (Object)null)) { break; } if ((Object)(object)array[i] == (Object)null) { continue; } Animator val = (((Object)(object)((EnemyAI)array[i]).creatureAnimator != (Object)null) ? ((EnemyAI)array[i]).creatureAnimator : ((Component)array[i]).GetComponentInChildren(true)); RuntimeAnimatorController val2 = (((Object)(object)val != (Object)null) ? val.runtimeAnimatorController : null); if ((Object)(object)val2 == (Object)null) { continue; } AnimationClip[] animationClips = val2.animationClips; AnimationClip val3 = null; for (int j = 0; j < animationClips.Length; j++) { if (!((Object)(object)animationClips[j] == (Object)null)) { if (string.Equals(((Object)animationClips[j]).name, "animWalkArmsOut", StringComparison.Ordinal)) { _clip = animationClips[j]; break; } if ((Object)(object)val3 == (Object)null && ((Object)animationClips[j]).name.IndexOf("ArmsOut", StringComparison.OrdinalIgnoreCase) >= 0 && ((Object)animationClips[j]).name.IndexOf("Walk", StringComparison.OrdinalIgnoreCase) >= 0 && ((Object)animationClips[j]).name.IndexOf("Sideways", StringComparison.OrdinalIgnoreCase) < 0) { val3 = animationClips[j]; } } } if ((Object)(object)_clip == (Object)null) { _clip = val3; } } } catch (Exception ex) { PoltergeistAI.LogPossession("Possessed hands-out clip lookup failed: " + ex.Message); } if ((Object)(object)_clip == (Object)null) { PoltergeistAI.LogPossession("Possessed hands-out pose: no 'animWalkArmsOut' on any MaskedPlayerEnemy animator; the possessed body keeps its normal walk."); } return _clip; } private void DestroyGraph() { if (_graphBuilt) { _graphBuilt = false; if (((PlayableGraph)(ref _graph)).IsValid()) { ((PlayableGraph)(ref _graph)).Stop(); ((PlayableGraph)(ref _graph)).Destroy(); } if ((Object)(object)_animator != (Object)null && (Object)(object)_animator.runtimeAnimatorController != (Object)null) { _animator.runtimeAnimatorController = _animator.runtimeAnimatorController; } } } private void TearDown() { DestroyGraph(); if ((Object)(object)_mask != (Object)null) { Object.Destroy((Object)(object)_mask); _mask = null; } Object.Destroy((Object)(object)this); } private void OnDestroy() { DestroyGraph(); if ((Object)(object)_mask != (Object)null) { Object.Destroy((Object)(object)_mask); } } private static Transform FindDeep(Transform root, string childName) { if ((Object)(object)root == (Object)null) { return null; } if (string.Equals(((Object)root).name, childName, StringComparison.Ordinal)) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = FindDeep(root.GetChild(i), childName); if ((Object)(object)val != (Object)null) { return val; } } return null; } } public sealed class PoltergeistPossessedVoice : MonoBehaviour { private const float SelfCheckInterval = 0.25f; private const float MissingSourceWarnSeconds = 2f; private static readonly List Active = new List(); private PlayerControllerB _victim; private AudioSource _source; private AudioDistortionFilter _distortion; private AudioEchoFilter _echo; private PossessedVoiceDsp _dsp; private bool _addedDistortion; private bool _addedEcho; private bool _addedDsp; private bool _savedDistortionEnabled; private float _savedDistortionLevel; private bool _savedEchoEnabled; private float _savedEchoDelay; private float _savedEchoDecay; private float _savedEchoWetMix; private float _savedEchoDryMix; private bool _pitchApplied; private float _pitchWritten = 1f; private float _selfCheckTimer; private float _sourcelessSeconds; private bool _sourcelessWarned; private bool _engagedLogged; private bool _restoreLogged; private const float PitchProbeDelay = 1f; private bool _pitchProbePending; private float _pitchProbeTimer; private static PoltergeistConfig Cfg => Plugin.ModConfig; internal static bool AnyActive => Active.Count > 0; internal static void Attach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null)) { if (Cfg == null) { Plugin.Log.LogError((object)"[PossessedVoice] Attach called before the config existed; the possessed voice will not engage."); } else if (!Cfg.PossessedVoiceFilterEnabled.Value) { Plugin.Log.LogWarning((object)"[PossessedVoice] PossessedVoiceFilterEnabled is false - the possessed voice is OFF by configuration, not by failure. Set it true to hear the possession."); } else if (!((Object)(object)((Component)victim).GetComponent() != (Object)null)) { ((Component)victim).gameObject.AddComponent().Configure(victim); } } } internal static void Detach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null)) { PoltergeistPossessedVoice component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { component.TearDown(); } } } internal static void ReassertPitchFor(PlayerControllerB player) { for (int i = 0; i < Active.Count; i++) { PoltergeistPossessedVoice poltergeistPossessedVoice = Active[i]; if ((Object)(object)poltergeistPossessedVoice != (Object)null && (Object)(object)poltergeistPossessedVoice._victim == (Object)(object)player) { poltergeistPossessedVoice.ApplyPitch(); break; } } } internal static void ReassertAll() { for (int num = Active.Count - 1; num >= 0; num--) { PoltergeistPossessedVoice poltergeistPossessedVoice = Active[num]; if (!((Object)(object)poltergeistPossessedVoice == (Object)null)) { poltergeistPossessedVoice.ApplyFilters(); poltergeistPossessedVoice.ApplyPitch(); } } } private void Configure(PlayerControllerB victim) { _victim = victim; Active.Add(this); ApplyFilters(); ApplyPitch(); } private void ApplyFilters() { if ((Object)(object)_victim == (Object)null || Cfg == null) { return; } try { AudioSource currentVoiceChatAudioSource = _victim.currentVoiceChatAudioSource; if ((Object)(object)currentVoiceChatAudioSource != (Object)(object)_source) { RestoreFilters(); _source = currentVoiceChatAudioSource; if ((Object)(object)_source != (Object)null) { CaptureAndAddFilters(); } } else if ((Object)(object)_source != (Object)null && (Object)(object)_dsp == (Object)null) { ReattachDsp(); } if (!((Object)(object)_source == (Object)null)) { bool flag = _victim.isPlayerDead || _victim.speakingToWalkieTalkie; if ((Object)(object)_distortion != (Object)null) { ((Behaviour)_distortion).enabled = !flag; _distortion.distortionLevel = Mathf.Clamp01(0.35f); } if ((Object)(object)_echo != (Object)null) { ((Behaviour)_echo).enabled = !flag; _echo.delay = Mathf.Clamp(110f, 10f, 5000f); _echo.decayRatio = Mathf.Clamp(0.45f, 0f, 1f); _echo.wetMix = Mathf.Clamp01(0.45f); _echo.dryMix = 1f; } if ((Object)(object)_dsp != (Object)null) { _dsp.DemonLayerMix = Mathf.Clamp(0.8f, 0f, 2f); _dsp.RingFrequency = Mathf.Clamp(48f, 10f, 400f); _dsp.RingMix = Mathf.Clamp01(0.5f); _dsp.NoiseGateMix = Mathf.Clamp01(0.12f); _dsp.BitcrushMix = Mathf.Clamp01(0.25f); _dsp.Drive = Mathf.Clamp(1.6f, 1f, 8f); ((Behaviour)_dsp).enabled = !flag && (_dsp.DemonLayerMix > 0f || _dsp.RingMix > 0f || _dsp.NoiseGateMix > 0f || _dsp.BitcrushMix > 0f || _dsp.Drive > 1f); } LogEngagedOnce(); } } catch (Exception arg) { PoltergeistAI.LogPossession($"Possessed voice failed to apply: {arg}"); TearDown(); } } private void CaptureAndAddFilters() { _distortion = ((Component)_source).GetComponent(); _addedDistortion = (Object)(object)_distortion == (Object)null; if (_addedDistortion) { _distortion = ((Component)_source).gameObject.AddComponent(); } else { _savedDistortionEnabled = ((Behaviour)_distortion).enabled; _savedDistortionLevel = _distortion.distortionLevel; } _echo = ((Component)_source).GetComponent(); _addedEcho = (Object)(object)_echo == (Object)null; if (_addedEcho) { _echo = ((Component)_source).gameObject.AddComponent(); } else { _savedEchoEnabled = ((Behaviour)_echo).enabled; _savedEchoDelay = _echo.delay; _savedEchoDecay = _echo.decayRatio; _savedEchoWetMix = _echo.wetMix; _savedEchoDryMix = _echo.dryMix; } ReattachDsp(); } private void ReattachDsp() { if (!((Object)(object)_source == (Object)null)) { _dsp = ((Component)_source).GetComponent(); if ((Object)(object)_dsp == (Object)null) { _dsp = ((Component)_source).gameObject.AddComponent(); } _addedDsp = true; } } private void RestoreFilters() { if ((Object)(object)_distortion != (Object)null) { if (_addedDistortion) { Object.Destroy((Object)(object)_distortion); } else { _distortion.distortionLevel = _savedDistortionLevel; ((Behaviour)_distortion).enabled = _savedDistortionEnabled; } } if ((Object)(object)_echo != (Object)null) { if (_addedEcho) { Object.Destroy((Object)(object)_echo); } else { _echo.delay = _savedEchoDelay; _echo.decayRatio = _savedEchoDecay; _echo.wetMix = _savedEchoWetMix; _echo.dryMix = _savedEchoDryMix; ((Behaviour)_echo).enabled = _savedEchoEnabled; } } if ((Object)(object)_dsp != (Object)null && _addedDsp) { Object.Destroy((Object)(object)_dsp); } _distortion = null; _echo = null; _dsp = null; _addedDistortion = false; _addedEcho = false; _addedDsp = false; _source = null; _engagedLogged = false; } private void ApplyPitch() { SoundManager instance = SoundManager.Instance; if ((Object)(object)_victim == (Object)null || (Object)(object)instance == (Object)null || Cfg == null) { return; } if (_victim.isPlayerDead || _victim.speakingToWalkieTalkie) { RestorePitch(); return; } float[] playerVoicePitchTargets = instance.playerVoicePitchTargets; if (playerVoicePitchTargets != null && _victim.playerClientId < (ulong)playerVoicePitchTargets.Length) { _pitchWritten = Mathf.Clamp(0.68f, 0.25f, 3f); playerVoicePitchTargets[_victim.playerClientId] = _pitchWritten; _pitchApplied = true; } } private void RestorePitch() { if (!_pitchApplied) { return; } _pitchApplied = false; SoundManager instance = SoundManager.Instance; if (!((Object)(object)_victim == (Object)null) && !((Object)(object)instance == (Object)null)) { float[] playerVoicePitchTargets = instance.playerVoicePitchTargets; if (playerVoicePitchTargets != null && _victim.playerClientId < (ulong)playerVoicePitchTargets.Length) { playerVoicePitchTargets[_victim.playerClientId] = 1f; } } } private void LogEngagedOnce() { if (!_engagedLogged && !((Object)(object)_source == (Object)null) && _pitchApplied) { _engagedLogged = true; _pitchProbePending = true; _pitchProbeTimer = 1f; bool flag = (Object)(object)_victim != (Object)null && (Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.localPlayerController == (Object)(object)_victim; string text = (_addedDistortion ? "distortion " : "distortion(existing) ") + (_addedEcho ? "echo " : "echo(existing) ") + (_addedDsp ? "dsp" : "dsp(none)"); Plugin.Log.LogInfo((object)($"[PossessedVoice] Engaged on {SafeName()} (isLocalPlayer={flag}" + (flag ? ", local player - inaudible to self by design" : string.Empty) + "), source resolved=yes (" + ((Object)((Component)_source).gameObject).name + "), added: " + text + ", " + $"pitch target={_pitchWritten:0.###}.")); } } private void ProbePitchOnce() { _pitchProbePending = false; SoundManager instance = SoundManager.Instance; if ((Object)(object)_victim == (Object)null || (Object)(object)instance == (Object)null) { return; } float[] playerVoicePitchTargets = instance.playerVoicePitchTargets; if (playerVoicePitchTargets == null || _victim.playerClientId >= (ulong)playerVoicePitchTargets.Length) { Plugin.Log.LogWarning((object)("[PossessedVoice] Pitch readback for " + SafeName() + " found no usable playerVoicePitchTargets slot - relying on the DSP demon layer for the deep voice.")); return; } float num = playerVoicePitchTargets[_victim.playerClientId]; if (Mathf.Abs(num - _pitchWritten) <= 0.02f) { Plugin.Log.LogInfo((object)($"[PossessedVoice] Pitch readback for {SafeName()}: wrote {_pitchWritten:0.###}, " + $"read {num:0.###} after {1f:0.#}s - the vanilla voice mixer pitch is holding.")); } else { Plugin.Log.LogWarning((object)($"[PossessedVoice] Pitch readback for {SafeName()}: wrote {_pitchWritten:0.###} but read " + $"{num:0.###} after {1f:0.#}s - mixer pitch appears overridden by another " + "mod (MoreCompany/Mirage), relying on DSP demon layer.")); } } private string SafeName() { if ((Object)(object)_victim == (Object)null) { return ""; } string text = _victim.playerUsername; if (string.IsNullOrEmpty(text)) { text = ((Object)((Component)_victim).gameObject).name; } return $"{text}#{_victim.playerClientId}"; } private void LateUpdate() { PoltergeistPossessionLock poltergeistPossessionLock = (((Object)(object)_victim != (Object)null) ? ((Component)_victim).GetComponent() : null); if ((Object)(object)poltergeistPossessionLock == (Object)null || !poltergeistPossessionLock.Holds || (Object)(object)_victim == (Object)null || _victim.isPlayerDead) { TearDown(); return; } _selfCheckTimer -= Time.deltaTime; if (_selfCheckTimer <= 0f) { _selfCheckTimer = 0.25f; if ((Object)(object)_victim.currentVoiceChatAudioSource != (Object)(object)_source || (Object)(object)_source == (Object)null || (Object)(object)_dsp == (Object)null) { ApplyFilters(); } ApplyPitch(); } if (_pitchProbePending) { _pitchProbeTimer -= Time.deltaTime; if (_pitchProbeTimer <= 0f) { ProbePitchOnce(); } } if ((Object)(object)_source == (Object)null) { _sourcelessSeconds += Time.deltaTime; if (!_sourcelessWarned && _sourcelessSeconds >= 2f) { _sourcelessWarned = true; Plugin.Log.LogWarning((object)("[PossessedVoice] Attached to " + SafeName() + " but currentVoiceChatAudioSource has been null " + $"for {2f:0.#}s - the DSP layer is not in the chain. Pitch " + "modulation is unaffected. This is normal only if the player has never transmitted.")); } } else { _sourcelessSeconds = 0f; _sourcelessWarned = false; } } private void TearDown() { Restore(); Active.Remove(this); Object.Destroy((Object)(object)this); } private void OnDestroy() { Restore(); Active.Remove(this); } private void Restore() { bool num = _pitchApplied || (Object)(object)_source != (Object)null; RestorePitch(); RestoreFilters(); if (num && !_restoreLogged) { _restoreLogged = true; Plugin.Log.LogInfo((object)("[PossessedVoice] Restored " + SafeName() + ": filters removed, pitch target returned to 1.")); } } } public sealed class PoltergeistPossessionGlow : MonoBehaviour { private PlayerControllerB _victim; private GameObject _host; private Light _light; private static PoltergeistConfig Cfg => Plugin.ModConfig; internal static void Attach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null) && Cfg != null && !((Object)(object)((Component)victim).GetComponent() != (Object)null)) { ((Component)victim).gameObject.AddComponent().Configure(victim); } } internal static void Detach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null)) { PoltergeistPossessionGlow component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { component.TearDown(); } } } private void Configure(PlayerControllerB victim) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0065: 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) _victim = victim; try { Transform val = (((Object)(object)victim.gameplayCamera != (Object)null) ? ((Component)victim.gameplayCamera).transform : ((Component)victim).transform); _host = new GameObject("PoltergeistPossessionGlow"); _host.transform.SetParent(val, false); _host.transform.localPosition = new Vector3(0f, 0f, 0.28f); float num = Mathf.Max(0f, 10f); if (num > 0f) { _light = _host.AddComponent(); _light.type = (LightType)2; _light.color = Plugin.ResolvePhaseLightColor(); _light.intensity = num; _light.range = Mathf.Max(0.5f, 3f); _light.shadows = (LightShadows)0; _light.renderMode = (LightRenderMode)1; } } catch (Exception arg) { PoltergeistAI.LogPossession($"Possession glow failed to attach: {arg}"); TearDown(); } } private void LateUpdate() { PoltergeistPossessionLock poltergeistPossessionLock = (((Object)(object)_victim != (Object)null) ? ((Component)_victim).GetComponent() : null); if ((Object)(object)poltergeistPossessionLock == (Object)null || !poltergeistPossessionLock.Holds || _victim.isPlayerDead) { TearDown(); } } private void TearDown() { if ((Object)(object)_host != (Object)null) { Object.Destroy((Object)(object)_host); } _host = null; _light = null; Object.Destroy((Object)(object)this); } private void OnDestroy() { if ((Object)(object)_host != (Object)null) { Object.Destroy((Object)(object)_host); } } } public sealed class PoltergeistPossessionLock : MonoBehaviour { private const float PuppetLookTurnSpeed = 240f; private const float PuppetLookMaxPitch = 60f; private const float PuppetSidewaysAngle = 60f; private const float PuppetBackwardsAngle = 120f; private const float PossessionFearLevel = 0.9f; private const float RotationSyncInterval = 0.05f; private const float RotationSyncThreshold = 1.5f; private static readonly FieldRef CameraUpRef = ResolvePlayerFloatField("cameraUp", "the puppet's head pitch will not replicate to other clients"); private static readonly FieldRef LookIntervalRef = ResolvePlayerFloatField("updatePlayerLookInterval", "the puppet's rotation will not replicate to other clients"); private static readonly FieldRef OldCameraUpRef = ResolvePlayerFloatField("oldCameraUp", "the puppet's rotation will not replicate to other clients"); private static readonly FieldRef PreviousYRotRef = ResolvePlayerFloatField("previousYRot", "the puppet's rotation will not replicate to other clients"); private static readonly Action UpdatePlayerRotationRpc = ResolveRotationRpc(); private PlayerControllerB _player; private PoltergeistAI _poltergeist; private bool _configured; private bool _controlsApplied; private bool _savedDisableMoveInput; private bool _savedDisableInteract; private bool _savedDisableLookInput; private AudioSource _possessionLoop; internal PoltergeistAI Poltergeist => _poltergeist; internal bool Holds => _configured; private static FieldRef ResolvePlayerFloatField(string name, string cost) { try { return AccessTools.FieldRefAccess(name); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PoltergeistPossessionLock] PlayerControllerB." + name + " could not be resolved (" + ex.Message + "); " + cost + ".")); } return null; } } private static Action ResolveRotationRpc() { try { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerControllerB), "UpdatePlayerRotationServerRpc", new Type[2] { typeof(short), typeof(short) }, (Type[])null); if (methodInfo == null) { throw new MissingMethodException("PlayerControllerB", "UpdatePlayerRotationServerRpc"); } return (Action)Delegate.CreateDelegate(typeof(Action), methodInfo); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[PoltergeistPossessionLock] PlayerControllerB.UpdatePlayerRotationServerRpc could not be resolved (" + ex.Message + "); the puppet's rotation will not replicate to other clients.")); } return null; } } internal void Configure(PlayerControllerB player, PoltergeistAI poltergeist) { if (_configured) { RestorePlayerFlags(); } _player = player; _poltergeist = poltergeist; if ((Object)(object)_player == (Object)null || (Object)(object)_poltergeist == (Object)null) { ((Behaviour)this).enabled = false; return; } if (IsLocalPlayer()) { ApplyLocalPossessionControls(); } _configured = true; ((Behaviour)this).enabled = true; } private void LateUpdate() { if (!_configured || (Object)(object)_player == (Object)null || _player.isPlayerDead || !_player.isPlayerControlled || !PossessionStillHolds()) { ReleaseNow(); } else if (IsLocalPlayer()) { _player.disableMoveInput = true; _player.disableInteract = true; _player.disableLookInput = true; StartPossessionLoopIfReady(); SustainPossessionFear(); } } internal void DrivePuppetMovement() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if (!_configured || (Object)(object)_player == (Object)null || (Object)(object)_poltergeist == (Object)null || !IsLocalPlayer() || _player.isPlayerDead || _player.isClimbingLadder || (Object)(object)_player.thisController == (Object)null) { return; } bool flag = _poltergeist.PuppetWalking; Vector3 val = Vector3.zero; if (flag) { val = _poltergeist.PuppetWaypoint - ((Component)_player).transform.position; val.y = 0f; if (((Vector3)(ref val)).magnitude < 0.6f) { flag = false; } } bool flag2 = flag && _poltergeist.PuppetSprinting; if (flag) { float num = ((Plugin.ModConfig != null) ? 3.5f : 3.5f); if (flag2) { num *= ((Plugin.ModConfig != null) ? 2.25f : 2.25f); } _player.thisController.Move(((Vector3)(ref val)).normalized * num * Time.deltaTime); } _player.playerBodyAnimator.SetBool("Walking", flag); _player.playerBodyAnimator.SetBool("Sprinting", flag2); if (flag) { float num2 = Mathf.Abs(Vector3.SignedAngle((((Object)(object)_player.thisPlayerBody != (Object)null) ? _player.thisPlayerBody : ((Component)_player).transform).forward, ((Vector3)(ref val)).normalized, Vector3.up)); _player.playerBodyAnimator.SetBool("Sideways", num2 > 60f && num2 < 120f); _player.playerBodyAnimator.SetFloat("animationSpeed", (num2 >= 120f) ? (-1f) : 1f); } } internal void DrivePuppetLook() { //IL_004e: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010f: 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_011d: 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_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) if (!_configured || (Object)(object)_player == (Object)null || (Object)(object)_poltergeist == (Object)null || !IsLocalPlayer() || _player.isPlayerDead || _player.inSpecialInteractAnimation) { return; } Vector3 puppetLookPoint = _poltergeist.PuppetLookPoint; if (puppetLookPoint == Vector3.zero) { return; } Transform val = (((Object)(object)_player.thisPlayerBody != (Object)null) ? _player.thisPlayerBody : ((Component)_player).transform); Vector3 val2 = puppetLookPoint - val.position; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude > 0.001f) { Quaternion val3 = Quaternion.LookRotation(((Vector3)(ref val2)).normalized, Vector3.up); val.rotation = Quaternion.RotateTowards(val.rotation, val3, 240f * Time.deltaTime); } Camera gameplayCamera = _player.gameplayCamera; if ((Object)(object)gameplayCamera == (Object)null) { return; } Vector3 val4 = puppetLookPoint - ((Component)gameplayCamera).transform.position; Vector2 val5 = new Vector2(val4.x, val4.z); float magnitude = ((Vector2)(ref val5)).magnitude; if (!(magnitude <= 0.001f) || !(Mathf.Abs(val4.y) <= 0.001f)) { float num = Mathf.Clamp((0f - Mathf.Atan2(val4.y, magnitude)) * 57.29578f, -60f, 60f); Vector3 localEulerAngles = ((Component)gameplayCamera).transform.localEulerAngles; float num2 = Mathf.MoveTowards((localEulerAngles.x > 180f) ? (localEulerAngles.x - 360f) : localEulerAngles.x, num, 240f * Time.deltaTime); ((Component)gameplayCamera).transform.localEulerAngles = new Vector3(num2, localEulerAngles.y, localEulerAngles.z); if (CameraUpRef != null) { CameraUpRef.Invoke(_player) = num2; } ReplicatePuppetRotation(num2); } } private void ReplicatePuppetRotation(float cameraPitch) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (UpdatePlayerRotationRpc == null || LookIntervalRef == null || OldCameraUpRef == null || PreviousYRotRef == null || !((NetworkBehaviour)_player).IsOwner) { return; } StartOfRound playersManager = _player.playersManager; if ((Object)(object)playersManager == (Object)null || playersManager.newGameIsLoading || _player.isTestingPlayer || (((NetworkBehaviour)_player).IsServer && playersManager.connectedPlayersAmount < 1)) { return; } ref float reference = ref LookIntervalRef.Invoke(_player); if (!(reference <= 0.05f)) { reference = 0f; Transform obj = (((Object)(object)_player.thisPlayerBody != (Object)null) ? _player.thisPlayerBody : ((Component)_player).transform); float y = obj.eulerAngles.y; float y2 = obj.localEulerAngles.y; if (!(Mathf.Abs(OldCameraUpRef.Invoke(_player) + PreviousYRotRef.Invoke(_player) - (cameraPitch + y)) <= 1.5f)) { UpdatePlayerRotationRpc(_player, (short)cameraPitch, (short)y2); OldCameraUpRef.Invoke(_player) = cameraPitch; PreviousYRotRef.Invoke(_player) = y2; } } } private bool PossessionStillHolds() { if ((Object)(object)_poltergeist == (Object)null || ((EnemyAI)_poltergeist).isEnemyDead) { return false; } if (_poltergeist.PossessedPlayerClientId != _player.playerClientId) { return false; } return _poltergeist.PossessionSecondsRemaining > -2f; } internal void ReleaseNow() { if (_configured) { RestorePlayerFlags(); } _configured = false; Object.Destroy((Object)(object)this); } private void OnDestroy() { if (_configured) { RestorePlayerFlags(); _configured = false; } StopPossessionLoop(); } private void ApplyLocalPossessionControls() { _savedDisableMoveInput = _player.disableMoveInput; _savedDisableInteract = _player.disableInteract; _savedDisableLookInput = _player.disableLookInput; _controlsApplied = true; PoltergeistAI.LogPossession("Local possession controls engaged on " + _player.playerUsername + " (move, interact and look all locked)."); _player.CancelSpecialTriggerAnimations(); if (_player.performingEmote) { _player.StopPerformingEmoteServerRpc(); } if (_player.isCrouching) { _player.Crouch(false); } if ((Object)(object)_player.currentlyHeldObjectServer != (Object)null) { _player.DropAllHeldItemsAndSyncNonexact(); } PossessionInteractionBridge.TryStopInteractions(_player); StartPossessionLoopIfReady(); SustainPossessionFear(); } private void RestorePlayerFlags() { if (!((Object)(object)_player == (Object)null) && _controlsApplied) { StopPossessionLoop(); _player.disableMoveInput = _savedDisableMoveInput; _player.disableInteract = _savedDisableInteract; _player.disableLookInput = _savedDisableLookInput; _controlsApplied = false; PoltergeistAI.LogPossession("Local possession controls released on " + _player.playerUsername + "."); } } private void StartPossessionLoopIfReady() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown if (!((Object)(object)_possessionLoop != (Object)null) && !((Object)(object)_player == (Object)null) && Plugin.ModConfig != null && !((Object)(object)Plugin.PossessedLoopClip == (Object)null)) { float num = Mathf.Clamp01(Plugin.ModConfig.PossessionLoopVolume.Value); if (!(num <= 0f)) { Transform val = (((Object)(object)_player.gameplayCamera != (Object)null) ? ((Component)_player.gameplayCamera).transform : ((Component)_player).transform); GameObject val2 = new GameObject("PoltergeistPossessionLoop"); val2.transform.SetParent(val, false); _possessionLoop = val2.AddComponent(); _possessionLoop.clip = Plugin.PossessedLoopClip; _possessionLoop.loop = true; _possessionLoop.volume = num; Plugin.ConfigureLocalAudioSource(_possessionLoop); _possessionLoop.Play(); } } } private void StopPossessionLoop() { if (!((Object)(object)_possessionLoop == (Object)null)) { _possessionLoop.Stop(); Object.Destroy((Object)(object)((Component)_possessionLoop).gameObject); _possessionLoop = null; } } private void SustainPossessionFear() { try { _player.JumpToFearLevel(0.9f, false); } catch { } } private bool IsLocalPlayer() { if ((Object)(object)_player != (Object)null && (Object)(object)GameNetworkManager.Instance != (Object)null) { return (Object)(object)_player == (Object)(object)GameNetworkManager.Instance.localPlayerController; } return false; } } public sealed class PoltergeistSpectralArms : MonoBehaviour { private static readonly (string victim, string claw)[] BonePairs = new(string, string)[4] { ("arm.L_lower", "arm_bot.L"), ("arm.R_lower", "arm_bot.R"), ("hand.L", "hand.L"), ("hand.R", "hand.R") }; private const string ClawRendererName = "Hand_LP.133"; private static GameObject _template; private static bool _templateFailed; private PlayerControllerB _victim; private GameObject _rig; private SkinnedMeshRenderer _renderer; private Material _material; private Transform[] _victimBones; private Transform[] _clawBones; private Quaternion[] _rotationOffsets; private static PoltergeistConfig Cfg => Plugin.ModConfig; internal static void Attach(PlayerControllerB victim, PoltergeistAI poltergeist) { if (!((Object)(object)victim == (Object)null) && !((Object)(object)poltergeist == (Object)null) && Cfg != null && !((Object)(object)((Component)victim).GetComponent() != (Object)null)) { ((Component)victim).gameObject.AddComponent().Configure(victim); } } internal static void Detach(PlayerControllerB victim) { if (!((Object)(object)victim == (Object)null)) { PoltergeistSpectralArms component = ((Component)victim).GetComponent(); if ((Object)(object)component != (Object)null) { component.TearDown(); } } } private void Configure(PlayerControllerB victim) { _victim = victim; try { if (!TryBuildRig()) { TearDown(); } else { RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering; } } catch (Exception arg) { PoltergeistAI.LogPossession($"Spectral arms failed to attach: {arg}"); TearDown(); } } private void LateUpdate() { //IL_008c: 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_009c: 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_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) PoltergeistPossessionLock poltergeistPossessionLock = (((Object)(object)_victim != (Object)null) ? ((Component)_victim).GetComponent() : null); if ((Object)(object)poltergeistPossessionLock == (Object)null || !poltergeistPossessionLock.Holds || _victim.isPlayerDead) { TearDown(); } else { if (_victimBones == null) { return; } float num = ((Cfg != null) ? 0.08f : 0.08f); for (int i = 0; i < _victimBones.Length; i++) { Transform val = _victimBones[i]; Transform val2 = _clawBones[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { val2.position = val.position + Vector3.up * num; val2.rotation = val.rotation * _rotationOffsets[i]; } } } } private bool TryBuildRig() { //IL_008c: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) GameObject val = ResolveTemplate(); if ((Object)(object)val == (Object)null) { return false; } Transform val2 = ((Component)_victim).transform.Find("ScavengerModel/metarig"); if ((Object)(object)val2 == (Object)null) { PoltergeistAI.LogPossession("Spectral arms: victim has no ScavengerModel/metarig; skipping."); return false; } _rig = Object.Instantiate(val, ((Component)_victim).transform); ((Object)_rig).name = "PoltergeistSpectralArms"; float num = ((Cfg != null) ? Mathf.Max(0.05f, 0.8f) : 0.8f); _rig.transform.localScale = Vector3.one * num; _rig.SetActive(true); _renderer = _rig.GetComponentInChildren(true); if ((Object)(object)_renderer == (Object)null) { return false; } Material val3 = PoltergeistAI.LoadPhaseMaterial(); if ((Object)(object)val3 != (Object)null) { _material = new Material(val3) { name = "Poltergeist_SpectralArms_Runtime" }; Color val4 = Plugin.ResolvePhaseLightColor(); if (_material.HasProperty("_BaseColor")) { val4.a = _material.GetColor("_BaseColor").a; _material.SetColor("_BaseColor", val4); } ((Renderer)_renderer).sharedMaterial = _material; } int num2 = BonePairs.Length; _victimBones = (Transform[])(object)new Transform[num2]; _clawBones = (Transform[])(object)new Transform[num2]; _rotationOffsets = (Quaternion[])(object)new Quaternion[num2]; for (int i = 0; i < num2; i++) { _victimBones[i] = FindDeep(val2, BonePairs[i].victim); _clawBones[i] = FindDeep(_rig.transform, BonePairs[i].claw); if ((Object)(object)_victimBones[i] == (Object)null || (Object)(object)_clawBones[i] == (Object)null) { PoltergeistAI.LogPossession("Spectral arms: bone pair " + BonePairs[i].victim + " -> " + BonePairs[i].claw + " did not resolve; skipping."); return false; } _rotationOffsets[i] = Quaternion.Inverse(_victimBones[i].rotation) * _clawBones[i].rotation; } return true; } private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Invalid comparison between Unknown and I4 if (!((Object)(object)_renderer == (Object)null) && !((Object)(object)_victim == (Object)null)) { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)_victim == (Object)(object)GameNetworkManager.Instance.localPlayerController; bool flag2 = (Object)(object)_victim.thisPlayerModel != (Object)null && (int)((Renderer)_victim.thisPlayerModel).shadowCastingMode == 3; ((Renderer)_renderer).forceRenderingOff = flag && flag2; } } private void TearDown() { RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering; if ((Object)(object)_rig != (Object)null) { Object.Destroy((Object)(object)_rig); _rig = null; } if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); _material = null; } _victimBones = null; _clawBones = null; Object.Destroy((Object)(object)this); } private void OnDestroy() { RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering; if ((Object)(object)_rig != (Object)null) { Object.Destroy((Object)(object)_rig); } if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } } private static GameObject ResolveTemplate() { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_0214: 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) if ((Object)(object)_template != (Object)null || _templateFailed) { return _template; } try { GameObject val = (((Object)(object)Plugin.PoltergeistEnemyType != (Object)null) ? Plugin.PoltergeistEnemyType.enemyPrefab : null); if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("no registered Poltergeist prefab"); } Transform val2 = FindDeep(val.transform, "Hand_LP.133"); SkinnedMeshRenderer val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent() : null); if ((Object)(object)val3 == (Object)null || (Object)(object)val3.sharedMesh == (Object)null) { throw new InvalidOperationException("prefab has no skinned renderer 'Hand_LP.133'"); } Transform val4 = val3.rootBone; if ((Object)(object)val4 == (Object)null) { throw new InvalidOperationException("'Hand_LP.133' has no root bone"); } while ((Object)(object)val4.parent != (Object)null && (Object)(object)val4.parent != (Object)(object)val.transform) { val4 = val4.parent; } GameObject val5 = new GameObject("PoltergeistSpectralArmsTemplate"); val5.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val5); GameObject val6 = Object.Instantiate(((Component)val4).gameObject, val5.transform); ((Object)val6).name = ((Object)val4).name; Component[] componentsInChildren = val6.GetComponentsInChildren(true); foreach (Component val7 in componentsInChildren) { if ((Object)(object)val7 != (Object)null && !(val7 is Transform)) { Object.DestroyImmediate((Object)(object)val7); } } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); Transform[] componentsInChildren2 = val6.GetComponentsInChildren(true); foreach (Transform val8 in componentsInChildren2) { dictionary[((Object)val8).name] = val8; } Transform[] bones = val3.bones; Transform[] array = (Transform[])(object)new Transform[bones.Length]; for (int j = 0; j < bones.Length; j++) { if ((Object)(object)bones[j] == (Object)null || !dictionary.TryGetValue(((Object)bones[j]).name, out array[j])) { throw new InvalidOperationException("bone '" + (((Object)(object)bones[j] != (Object)null) ? ((Object)bones[j]).name : "") + "' not found in armature clone"); } } GameObject val9 = new GameObject("SpectralClaws"); val9.transform.SetParent(val5.transform, false); SkinnedMeshRenderer obj = val9.AddComponent(); obj.sharedMesh = val3.sharedMesh; obj.bones = array; obj.rootBone = (dictionary.TryGetValue(((Object)val3.rootBone).name, out var value) ? value : ((array.Length != 0) ? array[0] : null)); ((Renderer)obj).sharedMaterial = ((Renderer)val3).sharedMaterial; obj.updateWhenOffscreen = true; ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj).receiveShadows = false; _template = val5; } catch (Exception ex) { _templateFailed = true; PoltergeistAI.LogPossession("Spectral arms template build failed (" + ex.Message + "); arms disabled this session."); } return _template; } private static Transform FindDeep(Transform root, string childName) { if ((Object)(object)root == (Object)null) { return null; } if (string.Equals(((Object)root).name, childName, StringComparison.Ordinal)) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = FindDeep(root.GetChild(i), childName); if ((Object)(object)val != (Object)null) { return val; } } return null; } } public sealed class PossessedVoiceDsp : MonoBehaviour { public float DemonLayerMix = 0.8f; public float RingFrequency = 48f; public float RingMix = 0.5f; public float NoiseGateMix = 0.12f; public float BitcrushMix = 0.25f; public float Drive = 1.6f; public float CrushRateHz = 16000f; public float CrushBits = 8.5f; public float HumMix = 0.03f; private const float RingDriftHz = 4f; private const float RingDriftPeriod = 3f; private const float GrainMs = 90f; private const float GrainBufferMs = 200f; private const float GrainReadSpeed = 0.5f; private const float GrainMinDelay = 2f; private const int WindowSize = 1024; private const float EnvAttackSeconds = 0.005f; private const float EnvReleaseSeconds = 0.15f; private const float EnvGateGain = 4f; private const float HumHz = 50f; private const double TwoPi = Math.PI * 2.0; private double _sampleRate = 48000.0; private double _invSampleRate = 2.0833333333333333E-05; private double _ringPhase; private double _lfoPhase; private double _lfoInc; private double _humPhase; private double _humInc; private float _hold0; private float _hold1; private double _holdAccumulator; private float[] _grainBuffer; private float[] _window; private int _bufferLength; private int _writePos; private float _grainLength = 1f; private float _halfGrain; private float _invGrainLength = 1f; private float _grainPhase; private float _envelope; private float _envAttackCoef = 1f; private float _envReleaseCoef = 1f; private uint _noiseState = 2654435769u; private bool _ready; private void Awake() { int outputSampleRate = AudioSettings.outputSampleRate; _sampleRate = ((outputSampleRate > 0) ? ((double)outputSampleRate) : 48000.0); _invSampleRate = 1.0 / _sampleRate; _lfoInc = Math.PI * 2.0 / (3.0 * _sampleRate); _humInc = Math.PI * 100.0 * _invSampleRate; _ringPhase = 0.0; _lfoPhase = 0.0; _humPhase = 0.0; _holdAccumulator = 1.0; _grainLength = (float)(0.09 * _sampleRate); if (_grainLength < 64f) { _grainLength = 64f; } _halfGrain = _grainLength * 0.5f; _invGrainLength = 1f / _grainLength; _grainPhase = 0f; int num = Mathf.CeilToInt(2f + 0.5f * _grainLength) + 4; _bufferLength = Mathf.CeilToInt((float)(0.2 * _sampleRate)); if (_bufferLength < num) { _bufferLength = num; } _grainBuffer = new float[_bufferLength]; _writePos = 0; _window = new float[1024]; for (int i = 0; i < 1024; i++) { _window[i] = Mathf.Sin(MathF.PI * (float)i / 1023f); } _envelope = 0f; _envAttackCoef = OnePoleCoef(0.005f); _envReleaseCoef = OnePoleCoef(0.15f); _ready = true; } private float OnePoleCoef(float seconds) { double num = (double)seconds * _sampleRate; if (num < 1.0) { return 1f; } return 1f - Mathf.Exp((float)(-1.0 / num)); } private void OnAudioFilterRead(float[] data, int channels) { if (!_ready || data == null || data.Length == 0) { return; } int num = ((channels < 1) ? 1 : channels); float num2 = DemonLayerMix; if (num2 < 0f) { num2 = 0f; } else if (num2 > 2f) { num2 = 2f; } float num3 = RingMix; if (num3 < 0f) { num3 = 0f; } else if (num3 > 1f) { num3 = 1f; } float num4 = NoiseGateMix; if (num4 < 0f) { num4 = 0f; } else if (num4 > 1f) { num4 = 1f; } float num5 = HumMix; if (num5 < 0f) { num5 = 0f; } else if (num5 > 0.5f) { num5 = 0.5f; } float num6 = BitcrushMix; if (num6 < 0f) { num6 = 0f; } else if (num6 > 1f) { num6 = 1f; } float num7 = Drive; if (num7 < 1f) { num7 = 1f; } else if (num7 > 8f) { num7 = 8f; } float num8 = 1f / SoftClip(num7); float num9 = 1f / (1f + 0.5f * num2); float num10 = RingFrequency; if (num10 < 10f) { num10 = 10f; } else if (num10 > 400f) { num10 = 400f; } float num11 = CrushRateHz; if (num11 < 1000f) { num11 = 1000f; } double num12 = (double)num11 * _invSampleRate; if (num12 > 1.0) { num12 = 1.0; } float num13 = CrushBits; if (num13 < 2f) { num13 = 2f; } else if (num13 > 16f) { num13 = 16f; } float num14 = Mathf.Pow(2f, num13) * 0.5f; float num15 = 1f / num14; float grainLength = _grainLength; float halfGrain = _halfGrain; float invGrainLength = _invGrainLength; int bufferLength = _bufferLength; float num16 = 1f / (float)num; int num17 = data.Length / num; for (int i = 0; i < num17; i++) { int num18 = i * num; float num19 = 0f; for (int j = 0; j < num; j++) { num19 += data[num18 + j]; } num19 *= num16; _grainBuffer[_writePos] = num19; float grainPhase = _grainPhase; float num20 = _grainPhase + halfGrain; if (num20 >= grainLength) { num20 -= grainLength; } float num21 = ReadInterpolated(2f + 0.5f * grainPhase, bufferLength) * WindowAt(grainPhase * invGrainLength); float num22 = ReadInterpolated(2f + 0.5f * num20, bufferLength) * WindowAt(num20 * invGrainLength); float num23 = (num21 + num22) * num2; _grainPhase += 1f; if (_grainPhase >= grainLength) { _grainPhase -= grainLength; } _writePos++; if (_writePos >= bufferLength) { _writePos = 0; } float num24 = ((num19 < 0f) ? (0f - num19) : num19); float num25 = ((num24 > _envelope) ? _envAttackCoef : _envReleaseCoef); _envelope += (num24 - _envelope) * num25; float num26 = _envelope * 4f; if (num26 > 1f) { num26 = 1f; } float num27 = Mathf.Sin((float)_lfoPhase) * 4f; _ringPhase += Math.PI * 2.0 * (double)(num10 + num27) * _invSampleRate; if (_ringPhase > Math.PI * 2.0) { _ringPhase -= Math.PI * 2.0; } _lfoPhase += _lfoInc; if (_lfoPhase > Math.PI * 2.0) { _lfoPhase -= Math.PI * 2.0; } _humPhase += _humInc; if (_humPhase > Math.PI * 2.0) { _humPhase -= Math.PI * 2.0; } float num28 = Mathf.Sin((float)_ringPhase); _noiseState ^= _noiseState << 13; _noiseState ^= _noiseState >> 17; _noiseState ^= _noiseState << 5; float num29 = ((float)(_noiseState & 0xFFFFFF) * 1.1920929E-07f - 1f) * num4 * num26 + Mathf.Sin((float)_humPhase) * num5; _holdAccumulator += num12; bool flag = _holdAccumulator >= 1.0; if (flag) { _holdAccumulator -= 1.0; } for (int k = 0; k < num; k++) { int num30 = num18 + k; float num31 = (data[num30] + num23) * num9; num31 += (num31 * num28 - num31) * num3; num31 += num29; if (flag) { float num32 = Mathf.Round(num31 * num14) * num15; if ((k & 1) == 0) { _hold0 = num32; } else { _hold1 = num32; } } float num33 = (((k & 1) == 0) ? _hold0 : _hold1); num31 += (num33 - num31) * num6; num31 = SoftClip(num31 * num7) * num8; if (num31 > 1f) { num31 = 1f; } else if (num31 < -1f) { num31 = -1f; } data[num30] = num31; } } } private float ReadInterpolated(float delay, int bufferLength) { float num = (float)_writePos - delay; if (num < 0f) { num += (float)bufferLength; } int num2 = (int)num; if (num2 < 0) { num2 = 0; } else if (num2 >= bufferLength) { num2 -= bufferLength; } int num3 = num2 + 1; if (num3 >= bufferLength) { num3 = 0; } float num4 = num - (float)(int)num; float num5 = _grainBuffer[num2]; return num5 + (_grainBuffer[num3] - num5) * num4; } private float WindowAt(float t) { if (t <= 0f) { return 0f; } if (t >= 1f) { return 0f; } int num = (int)(t * 1023f); if (num < 0) { num = 0; } else if (num >= 1024) { num = 1023; } return _window[num]; } private static float SoftClip(float x) { if (x < -3f) { return -1f; } if (x > 3f) { return 1f; } float num = x * x; return x * (27f + num) / (27f + 9f * num); } } } namespace Y4NGZMonsters.Shared { internal static class ConfigSectionNameMigration { private static readonly IReadOnlyDictionary SectionNames = new Dictionary(StringComparer.Ordinal) { { "00 - General", "General" }, { "10 - Spawning", "Spawning" }, { "20 - Core Stats", "Core Stats" }, { "20 - Video", "Video" }, { "30 - Detection", "Detection" }, { "30 - Encounter Pacing", "Encounter Pacing" }, { "30 - Encounter Rules", "Encounter Rules" }, { "40 - Movement", "Movement" }, { "50 - Combat", "Combat" }, { "60 - Abilities", "Abilities" }, { "60 - Presentation", "Presentation" }, { "60 - Audio", "Audio" }, { "70 - Audio and Presentation", "Audio and Presentation" }, { "99 - Diagnostics", "Diagnostics" } }; private static readonly Regex NumberedSectionHeader = new Regex("^(?[ \\t]*)\\[(?
00 - General|10 - Spawning|20 - Core Stats|20 - Video|30 - Detection|30 - Encounter Pacing|30 - Encounter Rules|40 - Movement|50 - Combat|60 - Abilities|60 - Presentation|60 - Audio|70 - Audio and Presentation|99 - Diagnostics)\\](?[ \\t]*)(?\\r?)$", RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant); internal static bool TryMigrate(ConfigFile config, ManualLogSource log, string creatureName) { string configFilePath = config.ConfigFilePath; if (!File.Exists(configFilePath)) { return false; } string text = null; try { int replacementCount; string contents = RenameSectionHeaders(File.ReadAllText(configFilePath), out replacementCount); if (replacementCount == 0) { return false; } text = GetBackupPath(configFilePath); File.Copy(configFilePath, text, overwrite: false); File.WriteAllText(configFilePath, contents, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); config.Reload(); if (log != null) { log.LogInfo((object)($"[{creatureName}] Removed numeric prefixes from {replacementCount} config section header(s). " + "The previous config is backed up at '" + text + "'.")); } return true; } catch (Exception ex) { string text2 = "The original config was not modified."; if (!string.IsNullOrEmpty(text) && File.Exists(text)) { try { File.Copy(text, configFilePath, overwrite: true); config.Reload(); text2 = "The original config was restored from its backup."; } catch (Exception ex2) { text2 = "Automatic restore also failed (" + ex2.Message + "). Restore '" + text + "' manually before editing the config."; } } if (log != null) { log.LogWarning((object)("[" + creatureName + "] Could not remove numeric config section prefixes: " + ex.Message + " " + text2)); } return false; } } internal static string RenameSectionHeaders(string text, out int replacementCount) { int count = 0; string result = NumberedSectionHeader.Replace(text, delegate(Match match) { string value = match.Groups["section"].Value; if (!SectionNames.TryGetValue(value, out var value2)) { return match.Value; } count++; return match.Groups["leading"].Value + "[" + value2 + "]" + match.Groups["trailing"].Value + match.Groups["carriage"].Value; }); replacementCount = count; return result; } private static string GetBackupPath(string configPath) { string text = configPath + ".pre-section-names.bak"; int num = 1; while (File.Exists(text)) { text = configPath + $".pre-section-names.{num}.bak"; num++; } return text; } } } namespace __GEN { internal class NetworkVariableSerializationHelper { [RuntimeInitializeOnLoadMethod] internal static void InitializeSerialization() { NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedByMemcpy(); NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedIEquatable(); NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedByMemcpy(); NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedIEquatable(); NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedByMemcpy(); NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedIEquatable(); } } } namespace Y4NGZPoltergeist.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }