Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PoltergeistPLUS v1.0.0
PoltergeistPlus.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Api; using Microsoft.CodeAnalysis; using Poltergeist; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PoltergeistPlus { [DefaultExecutionOrder(1000)] public class AbilityController : MonoBehaviour { private class Ability { public AbilityType Type; public string Label; public bool UsesInteractKey; public string KeyLabel => UsesInteractKey ? PoltergeistBridge.InteractLabel() : PlusInputs.Label(PlusInputs.Instance.SecondaryInteraction); public Ability(AbilityType type, string label, bool usesInteractKey) { Type = type; Label = label; UsesInteractKey = usesInteractKey; } } private class Target { public Ability[] Abilities; public ulong NetId; public Component Anchor; public Turret Turret; public Landmine Mine; } private enum Kind { None, Turret, Mine, Player } private struct Info { public Kind Kind; public Component Comp; } public static AbilityController Instance; private const float Range = 5f; private const int PoltergeistMask = 525120; private const float ScanInterval = 0.07f; private static readonly Ability[] EnemyAbilities = new Ability[1] { new Ability(AbilityType.Stun, "Stun enemy", usesInteractKey: false) }; private static readonly Ability[] TurretAbilities = new Ability[2] { new Ability(AbilityType.TurretOff, "Pester Turret", usesInteractKey: true), new Ability(AbilityType.TurretBerserk, "Anger Turret", usesInteractKey: false) }; private static readonly Ability[] MineAbilities = new Ability[2] { new Ability(AbilityType.MineOff, "Pester Landmine", usesInteractKey: true), new Ability(AbilityType.MineDetonate, "Detonate landmine", usesInteractKey: false) }; private static readonly Ability[] PlayerAbilities = new Ability[2] { new Ability(AbilityType.PlayerHeal, "Heal Player", usesInteractKey: true), new Ability(AbilityType.PlayerStamina, "Energize Player", usesInteractKey: false) }; private static readonly Dictionary<Collider, Info> ClassCache = new Dictionary<Collider, Info>(128); private static readonly Dictionary<Collider, EnemyAI> EnemyCache = new Dictionary<Collider, EnemyAI>(64); private static readonly RaycastHit[] Buffer = (RaycastHit[])(object)new RaycastHit[24]; private static readonly Comparer<RaycastHit> ByDistance = Comparer<RaycastHit>.Create((RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); private readonly Target _scanTarget = new Target(); private readonly Target _pressTarget = new Target(); private float _nextScan; private float _lastRaycast; private Vector3 _scanPos; private Vector3 _scanForward; private Target _cachedTarget; private string _ours; private string _lastExisting; private string _lastOurs; private string _lastCombined; private Ability[] _sigAbilities; private readonly string[] _sigReason = new string[2]; private readonly int[] _sigCost = new int[2]; private readonly bool[] _sigAfford = new bool[2]; private readonly string[] _tmpReason = new string[2]; private readonly int[] _tmpCost = new int[2]; private readonly bool[] _tmpAfford = new bool[2]; private readonly List<string> _lines = new List<string>(4); private static int _readyFrame = -1; private static bool _readyResult; private static SpectatorCamController _readyCam; private static PlayerControllerB _readyPlayer; private static Target Fill(Target t, Ability[] abilities, ulong netId, Component anchor, Turret turret = null, Landmine mine = null) { t.Abilities = abilities; t.NetId = netId; t.Anchor = anchor; t.Turret = turret; t.Mine = mine; return t; } private void Awake() { Instance = this; } private static bool GhostReady(out SpectatorCamController ghostCam, out PlayerControllerB player) { if (_readyFrame != Time.frameCount) { _readyFrame = Time.frameCount; _readyCam = SpectatorCamController.instance; _readyPlayer = GameNetworkManager.Instance?.localPlayerController; _readyResult = (Object)(object)_readyCam != (Object)null && (Object)(object)_readyPlayer != (Object)null && _readyPlayer.isPlayerDead && ((Behaviour)_readyCam).enabled && !PoltergeistBridge.InVanillaMode() && !_readyPlayer.isTypingChat && !_readyPlayer.quickMenuManager.isMenuOpen; } ghostCam = _readyCam; player = _readyPlayer; return _readyResult; } private static T Find<T>(Collider col) where T : Component { T componentInParent = ((Component)col).GetComponentInParent<T>(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } NetworkObject componentInParent2 = ((Component)col).GetComponentInParent<NetworkObject>(); return ((Object)(object)componentInParent2 != (Object)null) ? ((Component)componentInParent2).GetComponentInChildren<T>() : default(T); } private static Info Classify(Collider col) { if (ClassCache.TryGetValue(col, out var value)) { return value; } if (ClassCache.Count > 512) { ClassCache.Clear(); } Info info = default(Info); Turret val = Find<Turret>(col); if ((Object)(object)val != (Object)null) { info.Kind = Kind.Turret; info.Comp = (Component)(object)val; } else { Landmine val2 = Find<Landmine>(col); if ((Object)(object)val2 != (Object)null) { info.Kind = Kind.Mine; info.Comp = (Component)(object)val2; } else { PlayerControllerB componentInParent = ((Component)col).GetComponentInParent<PlayerControllerB>(); if ((Object)(object)componentInParent != (Object)null) { info.Kind = Kind.Player; info.Comp = (Component)(object)componentInParent; } } } ClassCache[col] = info; return info; } private static Target FindTarget(SpectatorCamController ghostCam, PlayerControllerB self, Target result) { //IL_0009: 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_00e1: 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_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Expected O, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown Transform transform = ((Component)ghostCam).transform; RaycastHit val = default(RaycastHit); if (Physics.Raycast(transform.position, transform.forward, ref val, 5f, 525120) && ((Component)((RaycastHit)(ref val)).collider).gameObject.layer != 8) { Collider collider = ((RaycastHit)(ref val)).collider; if (!EnemyCache.TryGetValue(collider, out var value)) { if (EnemyCache.Count > 256) { EnemyCache.Clear(); } value = ((Component)collider).GetComponentInParent<EnemyAI>(); EnemyCache[collider] = value; } if ((Object)(object)value != (Object)null && !value.isEnemyDead) { return Fill(result, EnemyAbilities, ((NetworkBehaviour)value).NetworkObjectId, (Component)(object)value); } } int num = Physics.RaycastNonAlloc(transform.position, transform.forward, Buffer, 5f, -1, (QueryTriggerInteraction)2); if (num > 1) { Array.Sort(Buffer, 0, num, ByDistance); } for (int i = 0; i < num; i++) { Collider collider2 = ((RaycastHit)(ref Buffer[i])).collider; Info info = Classify(collider2); if (info.Kind != Kind.None && (Object)(object)info.Comp != (Object)null) { switch (info.Kind) { case Kind.Turret: { Turret val3 = (Turret)info.Comp; return Fill(result, TurretAbilities, ((NetworkBehaviour)val3).NetworkObjectId, (Component)(object)val3, val3); } case Kind.Mine: { Landmine val4 = (Landmine)info.Comp; return Fill(result, MineAbilities, ((NetworkBehaviour)val4).NetworkObjectId, (Component)(object)val4, null, val4); } case Kind.Player: { PlayerControllerB val2 = (PlayerControllerB)info.Comp; if ((Object)(object)val2 != (Object)(object)self && val2.isPlayerControlled && !val2.isPlayerDead) { return Fill(result, PlayerAbilities, ((NetworkBehaviour)val2).NetworkObjectId, (Component)(object)val2); } break; } } } if (!collider2.isTrigger) { break; } } return null; } private static string BlockReason(Target target, Ability ability) { if ((Object)(object)target.Turret != (Object)null) { if (AbilityEffects.IsTurretBerserk(target.Turret)) { return "Turret is berserk"; } if (!AbilityEffects.IsTurretActive(target.Turret)) { return "Turret is off"; } } if ((Object)(object)target.Mine != (Object)null) { if (AbilityEffects.IsMineExploded(target.Mine)) { return "Landmine already exploded"; } if (ability.Type == AbilityType.MineOff && !AbilityEffects.IsMineActive(target.Mine)) { return "Landmine is already off"; } } return null; } private static bool Pressed(Ability a, bool interactPressed, bool secondaryPressed) { return a.UsesInteractKey ? interactPressed : secondaryPressed; } private void Update() { long start = PerfMonitor.Begin(); UpdateInner(); PerfMonitor.End(start); PerfMonitor.Frame(); } private void UpdateInner() { NetSync.Tick(); GhostGlow.Tick(); if (!GhostReady(out var ghostCam, out var player)) { return; } bool flag = PoltergeistBridge.InteractPressed(); bool flag2 = PlusInputs.Instance.SecondaryInteraction.WasPressedThisFrame(); if (!flag && !flag2) { return; } Target target = FindTarget(ghostCam, player, _pressTarget); if (target == null) { return; } Ability[] abilities = target.Abilities; foreach (Ability ability in abilities) { if (Pressed(ability, flag, flag2)) { string text = BlockReason(target, ability); if (text != null) { HUDManager.Instance.DisplayTip("Can't Do That", text, true, false, "LC_Tip1"); } else { TryUse(ability, ghostCam, target.NetId); } break; } } } private void LateUpdate() { long start = PerfMonitor.Begin(); LateUpdateInner(); PerfMonitor.End(start); } private void LateUpdateInner() { //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_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_006f: 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_0077: 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_008c: 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_00c3: 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_00cb: 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) if (!GhostReady(out var ghostCam, out var player)) { _cachedTarget = null; _ours = null; _sigAbilities = null; return; } float unscaledTime = Time.unscaledTime; if (unscaledTime >= _nextScan) { _nextScan = unscaledTime + 0.07f; Transform transform = ((Component)ghostCam).transform; Vector3 position = transform.position; Vector3 forward = transform.forward; Vector3 val = position - _scanPos; if (((Vector3)(ref val)).sqrMagnitude > 0.0004f || Vector3.Dot(forward, _scanForward) < 0.99999f || unscaledTime - _lastRaycast > 0.35f) { _scanPos = position; _scanForward = forward; _lastRaycast = unscaledTime; _cachedTarget = FindTarget(ghostCam, player, _scanTarget); } RebuildTip(ghostCam, _cachedTarget); } else if (_cachedTarget != null && (Object)(object)_cachedTarget.Anchor == (Object)null) { _cachedTarget = null; _ours = null; _sigAbilities = null; } if (_ours != null) { ApplyTip(player); } } private void RebuildTip(SpectatorCamController ghostCam, Target target) { if (target == null) { _ours = null; _sigAbilities = null; return; } Ability[] abilities = target.Abilities; bool flag = abilities == _sigAbilities && _ours != null; for (int i = 0; i < abilities.Length; i++) { string text = BlockReason(target, abilities[i]); int num = NetSync.CostOf(abilities[i].Type); bool flag2 = ghostCam.Power >= (float)num; _tmpReason[i] = text; _tmpCost[i] = num; _tmpAfford[i] = flag2; if (text != _sigReason[i] || num != _sigCost[i] || flag2 != _sigAfford[i]) { flag = false; } } if (flag) { return; } _sigAbilities = abilities; _lines.Clear(); for (int j = 0; j < abilities.Length; j++) { _sigReason[j] = _tmpReason[j]; _sigCost[j] = _tmpCost[j]; _sigAfford[j] = _tmpAfford[j]; string item = _tmpReason[j] ?? ((!_tmpAfford[j]) ? $"Not Enough Power ({_tmpCost[j]})" : $"{abilities[j].Label} : [{abilities[j].KeyLabel}] ({_tmpCost[j]})"); if (!_lines.Contains(item)) { _lines.Add(item); } } _ours = string.Join("\n", _lines); } private void ApplyTip(PlayerControllerB player) { string text = ((TMP_Text)player.cursorTip).text; if (text.EndsWith(_ours, StringComparison.Ordinal)) { return; } if (text.Length == 0) { ((TMP_Text)player.cursorTip).text = _ours; return; } if (text != _lastExisting || _ours != _lastOurs) { _lastExisting = text; _lastOurs = _ours; _lastCombined = text + "\n" + _ours; } ((TMP_Text)player.cursorTip).text = _lastCombined; } private void TryUse(Ability ability, SpectatorCamController ghostCam, ulong netId) { int num = NetSync.CostOf(ability.Type); if (ghostCam.Power < (float)num) { HUDManager.Instance.DisplayTip("Not Enough Power", $"{ability.Label} costs {num}", true, false, "LC_Tip1"); return; } NetSync.RequestAbility(ability.Type, netId); PoltergeistBridge.Spend(ghostCam, num); } } internal static class AbilityEffects { private static object _berserkValue; private static readonly string[] ToggleNames = new string[2] { "ToggleMine", "ToggleMineEnabled" }; private static bool IsHost => (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer; private static void Run(IEnumerator routine) { if ((Object)(object)AbilityController.Instance != (Object)null) { ((MonoBehaviour)AbilityController.Instance).StartCoroutine(routine); } } public static void Stun(EnemyAI enemy, float seconds) { enemy.SetEnemyStunned(true, seconds, (PlayerControllerB)null); } public static bool IsTurretActive(Turret turret) { return Reflect.GetField(turret, "turretActive", fallback: true); } public static bool IsTurretBerserk(Turret turret) { object fieldObject = Reflect.GetFieldObject(turret, "turretMode"); if (fieldObject == null) { return false; } if (_berserkValue == null) { try { _berserkValue = Enum.Parse(fieldObject.GetType(), "Berserk"); } catch { return false; } } return fieldObject.Equals(_berserkValue); } public static void TurretOff(Turret turret, float seconds) { if (IsTurretActive(turret) && !IsTurretBerserk(turret)) { Reflect.Invoke(turret, "ToggleTurretEnabled", false); Run(TurretReEnable(turret, seconds)); } } private static IEnumerator TurretReEnable(Turret turret, float seconds) { yield return (object)new WaitForSeconds(seconds); if ((Object)(object)turret != (Object)null) { Reflect.Invoke(turret, "ToggleTurretEnabled", true); } } public static void TurretBerserk(Turret turret, float seconds) { if (IsTurretActive(turret) && !IsTurretBerserk(turret)) { if (IsHost) { Reflect.Invoke(turret, "EnterBerserkModeServerRpc", -1); } if (Mathf.Abs(seconds - 9f) > 0.05f) { Run(AdjustBerserk(turret, seconds)); } } } private static IEnumerator AdjustBerserk(Turret turret, float seconds) { yield return (object)new WaitForSeconds(0.5f); if ((Object)(object)turret != (Object)null) { Reflect.SetField(turret, "berserkTimer", Mathf.Max(0.1f, seconds - 0.5f)); } } public static bool IsMineExploded(Landmine mine) { return Reflect.GetField(mine, "hasExploded", fallback: false); } public static bool IsMineActive(Landmine mine) { return Reflect.GetField(mine, "mineActivated", fallback: true); } public static void MineOff(Landmine mine, float seconds) { if (!IsMineExploded(mine) && IsMineActive(mine)) { Reflect.InvokeFirst(mine, ToggleNames, false); if (seconds > 0f) { Run(MineReEnable(mine, seconds)); } } } private static IEnumerator MineReEnable(Landmine mine, float seconds) { yield return (object)new WaitForSeconds(seconds); if ((Object)(object)mine != (Object)null && !IsMineExploded(mine)) { Reflect.InvokeFirst(mine, ToggleNames, true); } } public static void MineDetonate(Landmine mine, float damage) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!IsMineExploded(mine)) { GhostMines.Register(((Component)mine).transform.position, Mathf.RoundToInt(damage)); if (IsHost) { Reflect.Invoke(mine, "ExplodeMineServerRpc"); } } } private static bool IsLocal(PlayerControllerB player) { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; return (Object)(object)val != (Object)null && (Object)(object)player == (Object)(object)val && !player.isPlayerDead; } private static void Notify(string message) { Plugin.Debug("Showing pop-up: " + message); if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(message, "", false, false, "LC_Tip1"); } } public static void PlayerHeal(PlayerControllerB player, int amount) { if (!IsLocal(player)) { return; } int health = player.health; int num = Mathf.Min(100, health + amount); if (num > health) { player.health = num; Reflect.Invoke(HUDManager.Instance, "UpdateHealthUI", num, false); if (num >= 20 && Reflect.GetField(player, "criticallyInjured", fallback: false)) { Reflect.Invoke(player, "MakeCriticallyInjured", false); } Notify("A presence heals you"); } } public static void PlayerStamina(PlayerControllerB player, float amount) { if (IsLocal(player)) { float num = Mathf.Clamp01(Reflect.GetField(player, "sprintMeter", 0f) + amount); Reflect.SetField(player, "sprintMeter", num); if (num > 0.2f && Reflect.GetField(player, "isExhausted", fallback: false)) { Reflect.SetField(player, "isExhausted", false); } Notify("A presence energizes you"); } } } internal static class PoltergeistBridge { private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Type CamType = typeof(SpectatorCamController); private static readonly FieldInfo PowerField = CamType.GetField("power", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo VanillaModeField = CamType.Assembly.GetType("Poltergeist.Patches")?.GetField("vanillaMode", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo LightsField = CamType.GetField("lights", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly Type InputsType = CamType.Assembly.GetType("Poltergeist.PoltergeistCustomInputs"); private static readonly MethodInfo InteractStringMethod = InputsType?.GetMethod("GetInteractString", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); private static InputAction _interactAction; private static bool _interactLookupFailed; private static readonly Func<bool> VanillaModeGetter = BuildStaticBoolGetter(VanillaModeField); private static SpectatorCamController _lightsFor; private static Light[] _lights; private static bool _getterBroken; private static Func<bool> BuildStaticBoolGetter(FieldInfo field) { if (field == null) { return null; } try { return Expression.Lambda<Func<bool>>(Expression.Field(null, field), Array.Empty<ParameterExpression>()).Compile(); } catch { return () => (bool)field.GetValue(null); } } public static Light[] GetLights(SpectatorCamController cam) { if (cam != _lightsFor || _lights == null) { _lights = LightsField?.GetValue(cam) as Light[]; _lightsFor = cam; } return _lights; } public static bool InVanillaMode() { if (!_getterBroken && VanillaModeGetter != null) { try { return VanillaModeGetter(); } catch { _getterBroken = true; } } return VanillaModeField != null && (bool)VanillaModeField.GetValue(null); } public static bool InteractPressed() { InputAction interactAction = GetInteractAction(); if (interactAction != null) { return interactAction.WasPressedThisFrame(); } Keyboard current = Keyboard.current; return current != null && ((ButtonControl)current[(Key)19]).wasPressedThisFrame; } public static string InteractLabel() { try { if (InteractStringMethod != null) { return (string)InteractStringMethod.Invoke(null, null); } } catch { } return "E"; } private static InputAction GetInteractAction() { if (_interactAction != null) { return _interactAction; } if (_interactLookupFailed || InputsType == null) { return null; } try { object obj = InputsType.GetField("instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) ?? InputsType.GetProperty("instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null) { return null; } object obj2 = InputsType.GetField("InteractButton", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? InputsType.GetProperty("InteractButton", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); _interactAction = (InputAction)((obj2 is InputAction) ? obj2 : null); if (_interactAction == null) { _interactLookupFailed = true; } } catch { _interactLookupFailed = true; } return _interactAction; } public static bool Spend(SpectatorCamController cam, float cost) { if (PowerField == null) { Plugin.Log.LogWarning((object)"Could not find Poltergeist's power field; no power was spent."); return false; } float num = (float)PowerField.GetValue(cam); PowerField.SetValue(cam, num - cost); return true; } } internal static class Reflect { private const BindingFlags All = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly HashSet<string> Warned = new HashSet<string>(); private static readonly Dictionary<Type, Dictionary<string, FieldInfo>> FieldCache = new Dictionary<Type, Dictionary<string, FieldInfo>>(); private static readonly Dictionary<string, MethodInfo> MethodCache = new Dictionary<string, MethodInfo>(); private static FieldInfo FindField(Type type, string name) { if (!FieldCache.TryGetValue(type, out var value)) { value = new Dictionary<string, FieldInfo>(); FieldCache[type] = value; } if (!value.TryGetValue(name, out var value2)) { value2 = (value[name] = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); } return value2; } private static MethodInfo FindMethod(Type type, string name, int argCount) { string key = type.FullName + "." + name + "/" + argCount; if (MethodCache.TryGetValue(key, out var value)) { return value; } MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == name && methodInfo2.GetParameters().Length == argCount) { methodInfo = methodInfo2; break; } } MethodCache[key] = methodInfo; return methodInfo; } public static bool Invoke(object target, string method, params object[] args) { Type type = target.GetType(); MethodInfo methodInfo = FindMethod(type, method, args.Length); if (methodInfo == null) { WarnMissing(type, method); return false; } try { methodInfo.Invoke(target, args); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)(type.Name + "." + method + " threw: " + (ex.InnerException?.Message ?? ex.Message))); return false; } } public static bool InvokeFirst(object target, string[] methods, params object[] args) { Type type = target.GetType(); foreach (string text in methods) { MethodInfo methodInfo = FindMethod(type, text, args.Length); if (!(methodInfo == null)) { try { methodInfo.Invoke(target, args); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)(type.Name + "." + text + " threw: " + (ex.InnerException?.Message ?? ex.Message))); return false; } } } WarnMissing(type, string.Join("/", methods)); return false; } public static T GetField<T>(object target, string name, T fallback) { FieldInfo fieldInfo = FindField(target.GetType(), name); if (fieldInfo == null) { WarnMissing(target.GetType(), name); return fallback; } return (T)fieldInfo.GetValue(target); } public static object GetFieldObject(object target, string name) { FieldInfo fieldInfo = FindField(target.GetType(), name); if (fieldInfo == null) { WarnMissing(target.GetType(), name); return null; } return fieldInfo.GetValue(target); } public static bool SetField(object target, string name, object value) { FieldInfo fieldInfo = FindField(target.GetType(), name); if (fieldInfo == null) { WarnMissing(target.GetType(), name); return false; } fieldInfo.SetValue(target, value); return true; } private static void WarnMissing(Type type, string member) { if (Warned.Add(type.Name + "." + member)) { List<string> list = new List<string>(); MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { list.Add(methodInfo.Name); } FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { list.Add(fieldInfo.Name); } Plugin.Log.LogWarning((object)("Could not find '" + member + "' on " + type.Name + ". Members found: " + string.Join(", ", list))); } } } internal static class ControlsPatch { private const string Title = "Poltergeist Plus Controls;"; private static MethodInfo _updateMethod; private static FieldInfo _textField; private static FieldInfo _hiddenField; public static void Install(Harmony harmony) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown MethodInfo methodInfo = (_updateMethod = AccessTools.Method(typeof(SpectatorCamController), "UpdateControlText", (Type[])null, (Type[])null)); _textField = AccessTools.Field(typeof(SpectatorCamController), "controlsText"); _hiddenField = AccessTools.Field(typeof(SpectatorCamController), "controlsHidden"); if (methodInfo == null || _textField == null || _hiddenField == null) { Plugin.Log.LogWarning((object)"Could not hook Poltergeist's controls list; the music keys won't be listed."); return; } MethodInfo method = typeof(ControlsPatch).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static void Refresh() { SpectatorCamController instance = SpectatorCamController.instance; if ((Object)(object)instance == (Object)null || _updateMethod == null) { return; } try { _updateMethod.Invoke(instance, null); } catch { } } private static void Postfix(SpectatorCamController __instance) { if (!(bool)_hiddenField.GetValue(__instance)) { object? value = _textField.GetValue(__instance); TMP_Text val = (TMP_Text)((value is TMP_Text) ? value : null); if (!((Object)(object)val == (Object)null)) { PluginConfig modConfig = Plugin.ModConfig; StringBuilder stringBuilder = new StringBuilder(val.text, val.text.Length + 160); stringBuilder.Append("\n\n").Append(BuildTitle(modConfig)).Append('\n'); PlusInputs instance = PlusInputs.Instance; stringBuilder.Append("Secondary Interaction; [").Append(PlusInputs.Label(instance.SecondaryInteraction)).Append("]\n"); stringBuilder.Append("Turn on/off Music; [").Append(PlusInputs.Label(instance.MusicToggle)).Append("]\n"); stringBuilder.Append("Switch Song; [").Append(PlusInputs.Label(instance.NextSong)).Append(']'); val.text = stringBuilder.ToString(); } } } private static string BuildTitle(PluginConfig cfg) { //IL_0019: 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_007c: 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) if (!cfg.RainbowHeader.Value) { return "<color=#" + ColorUtility.ToHtmlStringRGB(cfg.HeaderColorValue) + ">Poltergeist Plus Controls;</color>"; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < "Poltergeist Plus Controls;".Length; i++) { if ("Poltergeist Plus Controls;"[i] == ' ') { stringBuilder.Append(' '); continue; } Color val = Color.HSVToRGB((float)i / (float)"Poltergeist Plus Controls;".Length, 1f, 1f); stringBuilder.Append("<color=#").Append(ColorUtility.ToHtmlStringRGB(val)).Append('>') .Append("Poltergeist Plus Controls;"[i]) .Append("</color>"); } return stringBuilder.ToString(); } } internal class GhostAudio : MonoBehaviour { public static GhostAudio Instance; private const int TrackCount = 12; private static readonly Assembly Asm = typeof(GhostAudio).Assembly; private AudioSource _sfx; private AudioSource _music; private AudioClip _nvOn; private AudioClip _nvOff; private readonly string[] _trackResources = new string[13]; private bool _anyTrack; private int _current = 1; private bool _musicOn; private bool _loadingTrack; private bool _pausedMidTrack; private float _nextCheck; public static void Create() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown GameObject val = new GameObject("PoltergeistPlusAudio"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; Instance = val.AddComponent<GhostAudio>(); } private void Awake() { _sfx = MakeSource(); _music = MakeSource(); _music.loop = true; HashSet<string> hashSet = new HashSet<string>(Asm.GetManifestResourceNames()); for (int i = 1; i <= 12; i++) { string text = "Audio.music" + i + ".mp3"; _trackResources[i] = (hashSet.Contains(text) ? text : null); if (_trackResources[i] != null) { _anyTrack = true; } } int num = 0; for (int j = 1; j <= 12; j++) { if (_trackResources[j] != null) { num++; } } Plugin.Log.LogInfo((object)($"Audio built into the addon: {num} of {12} music tracks, " + string.Format("NV on sound: {0}, NV off sound: {1}", hashSet.Contains("Audio.NV_turn_on.mp3"), hashSet.Contains("Audio.NV_turn_off.mp3")))); if (!_anyTrack) { Plugin.Log.LogWarning((object)("No music is built into this addon. Audio resources found: " + string.Join(", ", hashSet))); } ((MonoBehaviour)this).StartCoroutine(LoadSfx("Audio.NV_turn_on.mp3", () => Beep("nv_on", 300f, 1400f, 0.28f), delegate(AudioClip clip) { _nvOn = clip; })); ((MonoBehaviour)this).StartCoroutine(LoadSfx("Audio.NV_turn_off.mp3", () => Beep("nv_off", 1200f, 180f, 0.32f), delegate(AudioClip clip) { _nvOff = clip; })); } private AudioSource MakeSource() { AudioSource val = ((Component)this).gameObject.AddComponent<AudioSource>(); val.playOnAwake = false; val.loop = false; val.spatialBlend = 0f; val.bypassEffects = true; val.bypassListenerEffects = true; val.bypassReverbZones = true; val.dopplerLevel = 0f; return val; } private static string Extract(string resourceName) { using Stream stream = Asm.GetManifestResourceStream(resourceName); if (stream == null) { return null; } string text = Path.Combine(Paths.CachePath, "PoltergeistPlus"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, resourceName.Substring("Audio.".Length)); if (!File.Exists(text2) || new FileInfo(text2).Length != stream.Length) { using FileStream destination = File.Create(text2); stream.CopyTo(destination); } return text2; } public void PlayNightVision(bool on) { PlaySfx(on ? _nvOn : _nvOff); } private void PlaySfx(AudioClip clip) { PluginConfig modConfig = Plugin.ModConfig; if (!((Object)(object)clip == (Object)null) && modConfig.SoundEffects.Value) { _sfx.PlayOneShot(clip, modConfig.SoundVolume.Value); } } private IEnumerator LoadSfx(string resourceName, Func<AudioClip> fallback, Action<AudioClip> done) { string path = null; try { path = Extract(resourceName); } catch (Exception ex) { Exception e = ex; Plugin.Log.LogWarning((object)("Could not unpack " + resourceName + ": " + e.Message)); } AudioClip loaded = null; if (path != null) { yield return Load(path, stream: false, delegate(AudioClip c) { loaded = c; }); } if ((Object)(object)loaded == (Object)null) { Plugin.Log.LogWarning((object)(resourceName + " could not be loaded, using a plain beep instead.")); loaded = fallback(); } done(loaded); } private void Update() { long start = PerfMonitor.Begin(); UpdateInner(); PerfMonitor.End(start); } private void UpdateInner() { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return; } if (!val.isPlayerDead) { if (_musicOn) { StopMusic(withSound: false); } } else if (!val.isTypingChat && !val.quickMenuManager.isMenuOpen) { PlusInputs instance = PlusInputs.Instance; if (instance.MusicToggle.WasPressedThisFrame()) { ToggleMusic(); } else if (instance.NextSong.WasPressedThisFrame()) { SwitchSong(); } float unscaledTime = Time.unscaledTime; if (unscaledTime >= _nextCheck) { _nextCheck = unscaledTime + 0.25f; _music.volume = Plugin.ModConfig.MusicVolume.Value; } } } private void ToggleMusic() { if (_musicOn) { StopMusic(withSound: true); } else { StartMusic(); } } private void StartMusic() { if (!_anyTrack) { HUDManager.Instance.DisplayTip("No Music Found", "This build of the addon has no music inside it.", true, false, "LC_Tip1"); return; } _musicOn = true; _music.volume = Plugin.ModConfig.MusicVolume.Value; if (!_loadingTrack) { if ((Object)(object)_music.clip != (Object)null && _pausedMidTrack) { _music.UnPause(); _pausedMidTrack = false; } else if ((Object)(object)_music.clip != (Object)null) { _music.Play(); } else { PlayTrack(FindTrack(_current, 0)); } } } private void StopMusic(bool withSound) { _musicOn = false; if (_music.isPlaying) { _music.Pause(); _pausedMidTrack = true; } if (withSound) { PlaySfx(_nvOff); } } private void SwitchSong() { if (_musicOn && !_loadingTrack) { int num = FindTrack(_current, 1); if (num >= 0) { PlayTrack(num); } } } private int FindTrack(int start, int offset) { for (int i = offset; i < offset + 12; i++) { int num = (start - 1 + i) % 12 + 1; if (_trackResources[num] != null) { return num; } } return -1; } private void PlayTrack(int index) { if (index < 0) { StopMusic(withSound: false); return; } _current = index; string path = null; try { path = Extract(_trackResources[index]); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not unpack " + _trackResources[index] + ": " + ex.Message)); } if (path == null) { _trackResources[index] = null; if (_musicOn) { PlayTrack(FindTrack(index, 1)); } return; } _loadingTrack = true; ((MonoBehaviour)this).StartCoroutine(Load(path, stream: true, delegate(AudioClip clip) { _loadingTrack = false; if ((Object)(object)clip == (Object)null) { Plugin.Log.LogWarning((object)("Could not load " + path + ", skipping it.")); HUDManager.Instance.DisplayTip("Music Error", "Could not load a song. Check the BepInEx log for details.", true, false, "LC_Tip1"); _trackResources[index] = null; if (_musicOn) { PlayTrack(FindTrack(index, 1)); } } else { AudioClip clip2 = _music.clip; _music.clip = clip; _pausedMidTrack = false; if ((Object)(object)clip2 != (Object)null) { Object.Destroy((Object)(object)clip2); } if (_musicOn) { _music.Play(); Plugin.Log.LogInfo((object)("Playing " + ((Object)clip).name)); } } })); } private static IEnumerator Load(string path, bool stream, Action<AudioClip> done) { UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)13); try { ((DownloadHandlerAudioClip)request.downloadHandler).streamAudio = stream; yield return request.SendWebRequest(); if ((int)request.result != 1) { Plugin.Log.LogWarning((object)("Audio load failed for " + path + ": " + request.error)); done(null); yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(request); ((Object)clip).name = Path.GetFileNameWithoutExtension(path); done(clip); } finally { ((IDisposable)request)?.Dispose(); } } private static AudioClip Beep(string name, float startHz, float endHz, float seconds) { int num = (int)(44100f * seconds); float[] array = new float[num]; float num2 = 0f; for (int i = 0; i < num; i++) { float num3 = (float)i / (float)num; float num4 = Mathf.Lerp(startHz, endHz, num3); num2 += MathF.PI * 2f * num4 / 44100f; float num5 = Mathf.Pow(Mathf.Sin(MathF.PI * num3), 0.6f); float num6 = Mathf.Sin(num2) * 0.6f + Mathf.Sin(num2 * 2f) * 0.2f; array[i] = num6 * num5 * 0.5f; } AudioClip val = AudioClip.Create(name, num, 1, 44100, false); val.SetData(array, 0); return val; } } internal static class GhostGlow { private const string Section = "Night Vision"; private static Volume _volume; private static VolumeProfile _profile; private static Bloom _bloom; private static ColorAdjustments _color; private static LiftGammaGain _lgg; private static GameObject _overlayRoot; private static Image _wash; private static RawImage _vignette; private static RawImage _grain; private static RawImage _scanlines; private static float _overlayShown = -1f; private static float _nextGrain; private static int _screenHeightUsed = -1; private static bool _overlayVisible; private const float MinVisibleAlpha = 0.012f; private static Camera _camera; private static bool _lightsDirty = true; private static bool _paramsDirty = true; private static Light[] _styledFor; private static Color _appliedColor; private static float _nextLightCheck; private static bool _warnedLightReset; private static float _weight; private static bool _volumeEnabled; private static bool _worldTinted; private static bool _prevActive; private static bool _lightOn; private static bool _wasOn; private static bool _lightStateDirty = true; private static bool _polling; internal static bool IsOn => _wasOn; public static void Init(PluginConfig cfg, ConfigFile file) { file.SettingChanged += delegate(object _, SettingChangedEventArgs args) { if (args.ChangedSetting.Definition.Section == "Night Vision") { _lightsDirty = true; _paramsDirty = true; } }; } public static void Install(Harmony harmony) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(SpectatorCamController), "SwitchLight", (Type[])null, (Type[])null); if (methodInfo == null) { _polling = true; Plugin.Log.LogWarning((object)"Could not hook Poltergeist's light switch; checking the lights every frame instead."); } else { MethodInfo method = typeof(GhostGlow).GetMethod("SwitchLightPostfix", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void SwitchLightPostfix() { _lightStateDirty = true; } public static void Tick() { //IL_0204: 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) PluginConfig modConfig = Plugin.ModConfig; if (modConfig == null) { return; } SpectatorCamController instance = SpectatorCamController.instance; bool flag = modConfig.GlowEnabled.Value && (Object)(object)instance != (Object)null && ((Behaviour)instance).enabled && !PoltergeistBridge.InVanillaMode(); if (flag != _prevActive) { _lightStateDirty = true; } if (flag) { if ((Object)(object)_camera == (Object)null) { _camera = ((Component)instance).GetComponent<Camera>(); } Light[] lights = PoltergeistBridge.GetLights(instance); if (lights != null) { if (_lightOn && Time.unscaledTime >= _nextLightCheck) { _nextLightCheck = Time.unscaledTime + 0.5f; if (lights.Length != 0 && (Object)(object)lights[0] != (Object)null && lights[0].color != _appliedColor) { if (!_warnedLightReset) { _warnedLightReset = true; Plugin.Log.LogWarning((object)"The ghost light's color was changed back by something else; re-applying it."); } _lightsDirty = true; } } if (_lightsDirty || lights != _styledFor) { StyleLights(lights, modConfig); } if (_lightStateDirty || _polling) { _lightStateDirty = false; _lightOn = false; for (int i = 0; i < lights.Length; i++) { if ((Object)(object)lights[i] != (Object)null && ((Behaviour)lights[i]).enabled) { _lightOn = true; break; } } } } else { _lightOn = false; } } else { _lightOn = false; } bool flag2 = flag && _lightOn; bool flag3 = flag2 && modConfig.GlowWorldLights.Value; if (flag3 != _worldTinted) { _worldTinted = flag3; WorldLights.SetActive(flag3, _appliedColor, _styledFor); } else if (_worldTinted) { WorldLights.Tick(); } if (flag && _prevActive && flag2 != _wasOn) { Plugin.Log.LogInfo((object)("Night vision " + (flag2 ? "on" : "off"))); if ((Object)(object)GhostAudio.Instance != (Object)null) { GhostAudio.Instance.PlayNightVision(flag2); } } _prevActive = flag; _wasOn = flag2; Fade(flag2); if (_overlayVisible) { AnimateOverlay(); } } private static Color PureColor(Color c) { //IL_0001: 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_000d: 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_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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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 = Mathf.Max(c.r, Mathf.Max(c.g, c.b)); return (Color)((num > 0.0001f) ? new Color(c.r / num, c.g / num, c.b / num, 1f) : Color.white); } private static void StyleLights(Light[] lights, PluginConfig cfg) { //IL_0001: 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_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_0027: 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_0040: 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_00cb: 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_00ab: Unknown result type (might be due to invalid IL or missing references) Color val = Color.Lerp(Color.white, PureColor(cfg.NightVisionColor), cfg.GlowTint.Value); float num = 0.299f * val.r + 0.587f * val.g + 0.114f * val.b; float num2 = Mathf.Clamp(1f / Mathf.Max(num, 0.0001f), 1f, 3.5f); float intensity = Poltergeist.Config.LightIntensity.Value * cfg.GlowLightMultiplier.Value * num2; foreach (Light val2 in lights) { if (!((Object)(object)val2 == (Object)null)) { val2.color = val; val2.intensity = intensity; } } _appliedColor = val; _styledFor = lights; _lightsDirty = false; WorldLights.SetTint(val); } private static void Fade(bool on) { if ((Object)(object)_volume == (Object)null) { if (!on) { return; } Create(_camera); } float num = (on ? 1f : 0f); if (_weight != num) { _weight = Mathf.MoveTowards(_weight, num, Time.unscaledDeltaTime * 4f); _volume.weight = _weight; } UpdateOverlay(_paramsDirty); bool flag = on || _weight > 0.001f; if (flag != _volumeEnabled) { ((Behaviour)_volume).enabled = flag; _volumeEnabled = flag; } if (flag && _paramsDirty) { UpdateParameters(); } } private static int PickLayer(Camera camera) { try { HDAdditionalCameraData val = (((Object)(object)camera != (Object)null) ? ((Component)camera).GetComponent<HDAdditionalCameraData>() : null); if ((Object)(object)val != (Object)null) { int value = ((LayerMask)(ref val.volumeLayerMask)).value; if ((value & 1) != 0) { return 0; } for (int i = 0; i < 32; i++) { if ((value & (1 << i)) != 0) { return i; } } } } catch { } try { GameObject colorVolObject = Poltergeist.colorVolObject; if ((Object)(object)colorVolObject != (Object)null) { return colorVolObject.layer; } } catch { } return 0; } private static void Create(Camera camera) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown GameObject val = new GameObject("PoltergeistPlusGlow"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; val.layer = PickLayer(camera); _profile = ScriptableObject.CreateInstance<VolumeProfile>(); ((Object)_profile).hideFlags = (HideFlags)61; _bloom = _profile.Add<Bloom>(false); _color = _profile.Add<ColorAdjustments>(false); _lgg = _profile.Add<LiftGammaGain>(false); SetOptional((VolumeComponent)(object)_bloom, "highQualityFiltering", false); SetOptional((VolumeComponent)(object)_bloom, "highQualityPrefiltering", false); SetOptional((VolumeComponent)(object)_bloom, "resolution", "Quarter"); _volume = val.AddComponent<Volume>(); _volume.isGlobal = true; _volume.priority = 10000f; _volume.sharedProfile = _profile; _volume.weight = 0f; _weight = 0f; _volumeEnabled = true; CreateOverlay(); Plugin.Log.LogInfo((object)$"Night vision created (volume layer {val.layer})."); _paramsDirty = true; } private static RawImage MakeLayer(string name, Texture2D texture) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0048: 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_0060: 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) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); ((Object)val).hideFlags = (HideFlags)61; val.transform.SetParent(_overlayRoot.transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; RawImage val3 = val.AddComponent<RawImage>(); val3.texture = (Texture)(object)texture; ((Graphic)val3).raycastTarget = false; return val3; } private static Texture2D MakeVignetteTexture() { //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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_00e4: 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) Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, hideFlags = (HideFlags)61 }; Color32[] array = (Color32[])(object)new Color32[16384]; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float num = ((float)j + 0.5f) / 128f * 2f - 1f; float num2 = ((float)i + 0.5f) / 128f * 2f - 1f; float num3 = Mathf.Sqrt(num * num + num2 * num2) / 1.4142f; float num4 = Mathf.Clamp01((num3 - 0.45f) / 0.55f); float num5 = num4 * num4 * (3f - 2f * num4); array[i * 128 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num5 * 255f)); } } val.SetPixels32(array); val.Apply(false, true); return val; } private static Texture2D MakeGrainTexture() { //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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //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) Texture2D val = new Texture2D(256, 256, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)0, filterMode = (FilterMode)0, hideFlags = (HideFlags)61 }; Color32[] array = (Color32[])(object)new Color32[65536]; for (int i = 0; i < array.Length; i++) { float value = Random.value; array[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(value * value * value * 255f)); } val.SetPixels32(array); val.Apply(false, true); return val; } private static Texture2D MakeScanlineTexture() { //IL_0005: 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_0012: 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_0024: Expected O, but got Unknown //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_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_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_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) Texture2D val = new Texture2D(1, 4, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)0, filterMode = (FilterMode)0, hideFlags = (HideFlags)61 }; val.SetPixels32((Color32[])(object)new Color32[4] { new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0), new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0), new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0), new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue) }); val.Apply(false, true); return val; } private static void CreateOverlay() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0092: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) _overlayRoot = new GameObject("PoltergeistPlusOverlay"); Object.DontDestroyOnLoad((Object)(object)_overlayRoot); ((Object)_overlayRoot).hideFlags = (HideFlags)61; Canvas val = _overlayRoot.AddComponent<Canvas>(); val.renderMode = (RenderMode)0; val.sortingOrder = -1000; GameObject val2 = new GameObject("Wash", new Type[1] { typeof(RectTransform) }); ((Object)val2).hideFlags = (HideFlags)61; val2.transform.SetParent(_overlayRoot.transform, false); RectTransform val3 = (RectTransform)val2.transform; val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = Vector2.zero; val3.offsetMax = Vector2.zero; _wash = val2.AddComponent<Image>(); ((Graphic)_wash).raycastTarget = false; _vignette = MakeLayer("Vignette", MakeVignetteTexture()); _grain = MakeLayer("Static", MakeGrainTexture()); ((Component)_grain).gameObject.AddComponent<Canvas>(); _scanlines = MakeLayer("Scanlines", MakeScanlineTexture()); _overlayRoot.SetActive(false); _overlayVisible = false; } private static void UpdateOverlay(bool force) { //IL_0087: 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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_overlayRoot == (Object)null || (!force && Mathf.Approximately(_weight, _overlayShown))) { return; } _overlayShown = _weight; if (!(_weight > 0.001f)) { if (_overlayRoot.activeSelf) { _overlayRoot.SetActive(false); } _overlayVisible = false; return; } PluginConfig modConfig = Plugin.ModConfig; Color val = PureColor(modConfig.NightVisionColor); bool value = modConfig.PerformanceMode.Value; float num = _weight * modConfig.GlowOverlay.Value; float num2 = _weight * Mathf.Clamp01(modConfig.GlowVignette.Value * 2f); float num3 = _weight * modConfig.GlowGrain.Value * 0.5f; float num4 = _weight * modConfig.GlowScanlines.Value * 0.5f; ((Behaviour)_wash).enabled = !value && num >= 0.012f; ((Behaviour)_vignette).enabled = !value && num2 >= 0.012f; ((Behaviour)_grain).enabled = !value && num3 >= 0.012f; ((Behaviour)_scanlines).enabled = !value && num4 >= 0.012f; bool flag = ((Behaviour)_wash).enabled || ((Behaviour)_vignette).enabled || ((Behaviour)_grain).enabled || ((Behaviour)_scanlines).enabled; if (_overlayRoot.activeSelf != flag) { _overlayRoot.SetActive(flag); } _overlayVisible = flag; if (flag) { ((Graphic)_wash).color = new Color(val.r, val.g, val.b, num); ((Graphic)_vignette).color = new Color(val.r * 0.12f, val.g * 0.12f, val.b * 0.12f, num2); ((Graphic)_grain).color = new Color(val.r, val.g, val.b, num3); ((Graphic)_scanlines).color = new Color(val.r * 0.2f, val.g * 0.2f, val.b * 0.2f, num4); _screenHeightUsed = -1; FitTextures(); } } private static void FitTextures() { //IL_003d: 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) if (_screenHeightUsed != Screen.height) { _screenHeightUsed = Screen.height; _scanlines.uvRect = new Rect(0f, 0f, 1f, (float)Screen.height / 4f); _grain.uvRect = new Rect(0f, 0f, (float)Screen.width / 512f, (float)Screen.height / 512f); } } private static void AnimateOverlay() { //IL_0064: 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_0089: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_grain == (Object)null || !((Behaviour)_grain).enabled) { return; } float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextGrain)) { _nextGrain = unscaledTime + 0.1f; if (_screenHeightUsed != Screen.height) { FitTextures(); } Rect uvRect = _grain.uvRect; ((Rect)(ref uvRect)).x = Random.value; ((Rect)(ref uvRect)).y = Random.value; _grain.uvRect = uvRect; } } private static void SetOptional(VolumeComponent component, string field, object value) { try { FieldInfo field2 = ((object)component).GetType().GetField(field); if (field2 == null) { return; } object value2 = field2.GetValue(component); PropertyInfo property = value2.GetType().GetProperty("value"); PropertyInfo property2 = value2.GetType().GetProperty("overrideState"); if (!(property == null) && !(property2 == null)) { if (value is string value3 && property.PropertyType.IsEnum) { value = Enum.Parse(property.PropertyType, value3); } property.SetValue(value2, value); property2.SetValue(value2, true); } } catch { } } private static void UpdateParameters() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) PluginConfig modConfig = Plugin.ModConfig; float value = modConfig.GlowBloom.Value; ((VolumeComponent)_bloom).active = !modConfig.PerformanceMode.Value && value > 0.001f; ((VolumeParameter<float>)(object)_bloom.threshold).Override(modConfig.GlowThreshold.Value); ((VolumeParameter<float>)(object)_bloom.intensity).Override(value); ((VolumeParameter<float>)(object)_bloom.scatter).Override(0.7f); ((VolumeParameter<Color>)(object)_bloom.tint).Override(PureColor(modConfig.NightVisionColor)); ((VolumeParameter<float>)(object)_color.saturation).Override(0f); ((VolumeParameter<float>)(object)_color.postExposure).Override(modConfig.GlowBrightness.Value); ((VolumeParameter<float>)(object)_color.contrast).Override(10f); SetOptional((VolumeComponent)(object)_lgg, "gamma", (object)new Vector4(1f, 1f, 1f, modConfig.GlowBoost.Value)); _paramsDirty = false; } } internal static class LethalConfigSupport { public static void Register(PluginConfig cfg) { try { Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { if (assembly2.GetName().Name == "LethalConfig") { assembly = assembly2; break; } } if (assembly == null) { Plugin.Log.LogInfo((object)"LethalConfig is not installed; the normal config menus are used."); return; } Type type = assembly.GetType("LethalConfig.LethalConfigManager"); if (type == null) { Plugin.Log.LogInfo((object)"LethalConfig was found, but its settings API wasn't; using its automatic menu."); return; } SetDescription(type); Type type2 = assembly.GetType("LethalConfig.ConfigItems.HexColorInputFieldConfigItem"); int num = 0; if (type2 != null) { if (AddItem(type, type2, (ConfigEntryBase)(object)cfg.GlowColorText)) { num++; } if (AddItem(type, type2, (ConfigEntryBase)(object)cfg.HeaderColorText)) { num++; } } Plugin.Log.LogInfo((object)((num > 0) ? "LethalConfig: added color fields for the night vision and title colors." : "LethalConfig: its automatic menu is used (color settings are plain text fields).")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("LethalConfig support could not be set up (" + ex.Message + "); its automatic menu is used.")); } } private static void SetDescription(Type manager) { try { manager.GetMethod("SetModDescription", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null)?.Invoke(null, new object[1] { "Poltergeist Plus: extra things for ghosts to do (stun, turrets, landmines, players), a night vision, and music. Settings marked [Host-sided] use the host's value for everyone; [Client-sided] ones are only yours." }); } catch { } } private static bool AddItem(Type manager, Type itemType, ConfigEntryBase entry) { object obj = Create(itemType, entry); if (obj == null) { return false; } MethodInfo[] methods = manager.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "AddConfigItem")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType.IsInstanceOfType(obj)) { methodInfo.Invoke(null, new object[1] { obj }); return true; } } } return false; } private static object Create(Type itemType, ConfigEntryBase entry) { ConstructorInfo[] constructors = itemType.GetConstructors(); foreach (ConstructorInfo constructorInfo in constructors) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length == 0 || !parameters[0].ParameterType.IsInstanceOfType(entry)) { continue; } object[] array = new object[parameters.Length]; array[0] = entry; bool flag = true; for (int j = 1; j < parameters.Length && flag; j++) { Type parameterType = parameters[j].ParameterType; if (parameters[j].HasDefaultValue) { array[j] = parameters[j].DefaultValue; } else if (parameterType == typeof(bool)) { array[j] = false; } else if (parameterType.IsClass && parameterType.GetConstructor(Type.EmptyTypes) != null) { array[j] = Activator.CreateInstance(parameterType); } else { flag = false; } } if (flag) { return constructorInfo.Invoke(array); } } return null; } } internal static class GhostMines { private struct Entry { public Vector3 Position; public int Damage; public float Expires; } private static readonly List<Entry> Entries = new List<Entry>(); public static void Register(Vector3 minePosition, int damage) { //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) Entries.Add(new Entry { Position = minePosition, Damage = damage, Expires = Time.time + 15f }); } public static bool TryConsume(Vector3 explosionPosition, out int damage) { //IL_003e: 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_0054: Unknown result type (might be due to invalid IL or missing references) damage = 0; for (int num = Entries.Count - 1; num >= 0; num--) { if (Entries[num].Expires < Time.time) { Entries.RemoveAt(num); } else { Vector3 val = explosionPosition - Entries[num].Position; if (((Vector3)(ref val)).sqrMagnitude < 9f) { damage = Entries[num].Damage; Entries.RemoveAt(num); return true; } } } return false; } } internal static class MinePatches { private static bool _resolved; private static int _posIndex = -1; private static int _killIndex = -1; private static int _damageIndex = -1; public static void Install(Harmony harmony) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown MethodInfo methodInfo = null; MethodInfo[] methods = typeof(Landmine).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == "SpawnExplosion") { methodInfo = methodInfo2; break; } } if (methodInfo == null) { Plugin.Log.LogWarning((object)"Could not find Landmine.SpawnExplosion; ghost landmine damage will be vanilla."); return; } MethodInfo method = typeof(MinePatches).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Landmine explosion patch installed."); } private static void Prefix(MethodBase __originalMethod, object[] __args) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) if (!_resolved) { _resolved = true; ParameterInfo[] parameters = __originalMethod.GetParameters(); for (int i = 0; i < parameters.Length; i++) { if (_posIndex < 0 && parameters[i].ParameterType == typeof(Vector3)) { _posIndex = i; } if (parameters[i].Name == "killRange") { _killIndex = i; } if (parameters[i].Name == "nonLethalDamage") { _damageIndex = i; } } if (_posIndex < 0 || _killIndex < 0 || _damageIndex < 0) { List<string> list = new List<string>(); ParameterInfo[] array = parameters; foreach (ParameterInfo parameterInfo in array) { list.Add(parameterInfo.ParameterType.Name + " " + parameterInfo.Name); } Plugin.Log.LogWarning((object)("SpawnExplosion parameters not as expected: " + string.Join(", ", list))); } } if (_posIndex >= 0 && _killIndex >= 0 && _damageIndex >= 0 && GhostMines.TryConsume((Vector3)__args[_posIndex], out var damage)) { __args[_killIndex] = 0f; __args[_damageIndex] = damage; } } } public enum AbilityType : byte { Stun, TurretOff, TurretBerserk, MineOff, MineDetonate, PlayerHeal, PlayerStamina } internal static class NetSync { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnCostRequest; public static HandleNamedMessageDelegate <1>__OnAbilityRequest; public static HandleNamedMessageDelegate <2>__OnCosts; public static HandleNamedMessageDelegate <3>__OnAbility; } private const string CostRequestMsg = "PoltergeistPlus_CostRequest"; private const string CostMsg = "PoltergeistPlus_Costs"; private const string AbilityRequestMsg = "PoltergeistPlus_AbilityRequest"; private const string AbilityMsg = "PoltergeistPlus_Ability"; private static CustomMessagingManager _registeredOn; private static readonly Dictionary<AbilityType, int> HostCosts = new Dictionary<AbilityType, int>(); public static int CostOf(AbilityType type) { int value; return HostCosts.TryGetValue(type, out value) ? value : Plugin.ModConfig.CostEntry(type).Value; } public unsafe static void Tick() { //IL_00e1: 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_00ec: Expected O, but got Unknown //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: Expected O, but got Unknown //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_0113: Expected O, but got Unknown //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_00be: Expected O, but got Unknown //IL_012c: 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) NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if ((Object)(object)singleton == (Object)null || val == null || !singleton.IsListening) { _registeredOn = null; HostCosts.Clear(); } else { if (val == _registeredOn) { return; } _registeredOn = val; HostCosts.Clear(); if (!singleton.IsServer) { object obj = <>O.<2>__OnCosts; if (obj == null) { HandleNamedMessageDelegate val2 = OnCosts; <>O.<2>__OnCosts = val2; obj = (object)val2; } val.RegisterNamedMessageHandler("PoltergeistPlus_Costs", (HandleNamedMessageDelegate)obj); object obj2 = <>O.<3>__OnAbility; if (obj2 == null) { HandleNamedMessageDelegate val3 = OnAbility; <>O.<3>__OnAbility = val3; obj2 = (object)val3; } val.RegisterNamedMessageHandler("PoltergeistPlus_Ability", (HandleNamedMessageDelegate)obj2); FastBufferWriter val4 = default(FastBufferWriter); ((FastBufferWriter)(ref val4))..ctor(1, (Allocator)2, -1); try { bool flag = true; ((FastBufferWriter)(ref val4)).WriteValueSafe<bool>(ref flag, default(ForPrimitives)); val.SendNamedMessage("PoltergeistPlus_CostRequest", 0uL, val4, (NetworkDelivery)3); return; } finally { ((IDisposable)(*(FastBufferWriter*)(&val4))/*cast due to .constrained prefix*/).Dispose(); } } object obj3 = <>O.<0>__OnCostRequest; if (obj3 == null) { HandleNamedMessageDelegate val5 = OnCostRequest; <>O.<0>__OnCostRequest = val5; obj3 = (object)val5; } val.RegisterNamedMessageHandler("PoltergeistPlus_CostRequest", (HandleNamedMessageDelegate)obj3); object obj4 = <>O.<1>__OnAbilityRequest; if (obj4 == null) { HandleNamedMessageDelegate val6 = OnAbilityRequest; <>O.<1>__OnAbilityRequest = val6; obj4 = (object)val6; } val.RegisterNamedMessageHandler("PoltergeistPlus_AbilityRequest", (HandleNamedMessageDelegate)obj4); } } public unsafe static void RequestAbility(AbilityType type, ulong targetNetId) { //IL_0055: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if ((Object)(object)singleton == (Object)null || val == null) { return; } if (singleton.IsServer) { BroadcastAbility(type, targetNetId); return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(9, (Allocator)2, -1); try { byte b = (byte)type; ((FastBufferWriter)(ref val2)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteValueSafe<ulong>(ref targetNetId, default(ForPrimitives)); val.SendNamedMessage("PoltergeistPlus_AbilityRequest", 0uL, val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void BroadcastAbility(AbilityType type, ulong targetNetId) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_007e: 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_0092: Unknown result type (might be due to invalid IL or missing references) float num = Plugin.ModConfig.ValueOf(type); Plugin.Debug($"Host: {type} on object {targetNetId} (value {num}); applying it and telling the other players."); ApplyAbility(type, targetNetId, num); CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(13, (Allocator)2, -1); try { byte b = (byte)type; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref targetNetId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref num, default(ForPrimitives)); customMessagingManager.SendNamedMessageToAll("PoltergeistPlus_Ability", val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnAbilityRequest(ulong senderId, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0020: Unknown result type (might be due to invalid IL or missing references) byte type = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref type, default(ForPrimitives)); ulong targetNetId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref targetNetId, default(ForPrimitives)); BroadcastAbility((AbilityType)type, targetNetId); } private static void OnAbility(ulong senderId, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0020: 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_0033: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num2, default(ForPrimitives)); Plugin.Debug($"Received {(AbilityType)b} on object {num} (value {num2}) from the host."); ApplyAbility((AbilityType)b, num, num2); } private static void ApplyAbility(AbilityType type, ulong targetNetId, float value) { Dictionary<ulong, NetworkObject> spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; if (!spawnedObjects.TryGetValue(targetNetId, out var value2)) { Plugin.Debug($"{type}: object {targetNetId} does not exist on this machine, so nothing was done."); return; } switch (type) { case AbilityType.Stun: { EnemyAI component = ((Component)value2).GetComponent<EnemyAI>(); if ((Object)(object)component != (Object)null && !component.isEnemyDead) { AbilityEffects.Stun(component, value); } break; } case AbilityType.TurretOff: { Turret componentInChildren2 = ((Component)value2).GetComponentInChildren<Turret>(true); if ((Object)(object)componentInChildren2 != (Object)null) { AbilityEffects.TurretOff(componentInChildren2, value); } break; } case AbilityType.TurretBerserk: { Turret componentInChildren4 = ((Component)value2).GetComponentInChildren<Turret>(true); if ((Object)(object)componentInChildren4 != (Object)null) { AbilityEffects.TurretBerserk(componentInChildren4, value); } break; } case AbilityType.MineOff: { Landmine componentInChildren6 = ((Component)value2).GetComponentInChildren<Landmine>(true); if ((Object)(object)componentInChildren6 != (Object)null) { AbilityEffects.MineOff(componentInChildren6, value); } break; } case AbilityType.MineDetonate: { Landmine componentInChildren5 = ((Component)value2).GetComponentInChildren<Landmine>(true); if ((Object)(object)componentInChildren5 != (Object)null) { AbilityEffects.MineDetonate(componentInChildren5, value); } break; } case AbilityType.PlayerHeal: { PlayerControllerB componentInChildren3 = ((Component)value2).GetComponentInChildren<PlayerControllerB>(true); if ((Object)(object)componentInChildren3 != (Object)null) { AbilityEffects.PlayerHeal(componentInChildren3, (int)value); } break; } case AbilityType.PlayerStamina: { PlayerControllerB componentInChildren = ((Component)value2).GetComponentInChildren<PlayerControllerB>(true); if ((Object)(object)componentInChildren != (Object)null) { AbilityEffects.PlayerStamina(componentInChildren, value); } break; } } } private static FastBufferWriter BuildCostWriter() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_007d: 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_0099: 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_009e: Unknown result type (might be due to invalid IL or missing references) AbilityType[] array = (AbilityType[])Enum.GetValues(typeof(AbilityType)); FastBufferWriter result = default(FastBufferWriter); ((FastBufferWriter)(ref result))..ctor(4 + array.Length * 5, (Allocator)2, -1); int num = array.Length; ((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref num, default(ForPrimitives)); AbilityType[] array2 = array; foreach (AbilityType abilityType in array2) { byte b = (byte)abilityType; ((FastBufferWriter)(ref result)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); num = Plugin.ModConfig.CostEntry(abilityType).Value; ((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref num, default(ForPrimitives)); } return result; } private unsafe static void OnCostRequest(ulong senderId, FastBufferReader reader) { //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_0019: Unknown result type (might be due to invalid IL or missing references) CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; FastBufferWriter val = BuildCostWriter(); try { customMessagingManager.SendNamedMessage("PoltergeistPlus_Costs", senderId, val, (NetworkDelivery)3); Plugin.Debug($"Host sent its ability costs to player {senderId}."); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public unsafe static void BroadcastCosts() { //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_0046: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if ((Object)(object)singleton == (Object)null || val == null || !singleton.IsListening || !singleton.IsServer) { return; } FastBufferWriter val2 = BuildCostWriter(); try { val.SendNamedMessageToAll("PoltergeistPlus_Costs", val2, (NetworkDelivery)3); Plugin.Debug("Host sent its ability costs to everyone (a host setting changed)."); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnCosts(ulong senderId, FastBufferReader reader) { //IL_0012: 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_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_003d: 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) HostCosts.Clear(); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); byte key = default(byte); int value = default(int); for (int i = 0; i < num; i++) { ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref key, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref value, default(ForPrimitives)); HostCosts[(AbilityType)key] = value; } Plugin.Log.LogInfo((object)$"Received {num} host ability costs."); } } internal static class PerfMonitor { private const float WindowSeconds = 30f; private static long _addonTicks; private static int _frames; private static int _framesOn; private static int _framesOff; private static float _timeOn; private static float _timeOff; private static float _windowStart = -1f; private static bool _explained; private static bool Enabled => Plugin.ModConfig != null && Plugin.ModConfig.LogPerformance.Value; public static long Begin() { return Enabled ? Stopwatch.GetTimestamp() : 0; } public static void End(long start) { if (start != 0) { _addonTicks += Stopwatch.GetTimestamp() - start; } } public static void Frame() { if (!Enabled) { _windowStart = -1f; return; } float unscaledTime = Time.unscaledTime; if (_windowStart < 0f) { Reset(); _windowStart = unscaledTime; } float unscaledDeltaTime = Time.unscaledDeltaTime; _frames++; if (GhostGlow.IsOn) { _framesOn++; _timeOn += unscaledDeltaTime; } else { _framesOff++; _timeOff += unscaledDeltaTime; } if (unscaledTime - _windowStart >= 30f) { Report(); Reset(); _windowStart = unscaledTime; } } private static void Reset() { _addonTicks = 0L; _frames = 0; _framesOn = 0; _framesOff = 0; _timeOn = 0f; _timeOff = 0f; } private static void Report() { float num = _timeOn + _timeOff; if (_frames >= 10 && !(num <= 0f)) { double num2 = (float)_frames / num; double num3 = (double)num * 1000.0 / (double)_frames; double num4 = (double)_addonTicks * 1000.0 / (double)Stopwatch.Frequency / (double)_frames; double num5 = num4 / num3 * 100.0; string arg = ""; if (_framesOn >= 30 && _framesOff >= 30) { arg = $" Night vision off: {(float)_framesOff / _timeOff:F0} fps, on: {(float)_framesOn / _timeOn:F0} fps."; } else if (_framesOn >= 30) { arg = " (Night vision was on the whole time.)"; } Plugin.Log.LogInfo((object)($"Performance, last {num:F0}s: {num2:F0} fps average. " + $"Poltergeist Plus used {num4:F3} ms of processor time per frame ({num5:F2}% of a frame).{arg}")); if (!_explained) { _explained = true; Plugin.Log.LogInfo((object)"About these numbers: the processor time is measured exactly. The night vision's screen effects run on the graphics card, which isn't included in it, so compare the night vision on/off fps to see their cost (other things in the scene change fps too, so use several readings)."); } } } } [BepInPlugin("PoltergeistPlus", "Poltergeist Plus", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static PluginConfig ModConfig; private static readonly string[] HostSections = new string[4] { "Stun", "Turret", "Landmine", "Player" }; internal static void Debug(string message) { if (ModConfig != null && ModConfig.ShowDebugLogs.Value) { Log.LogInfo((object)("[debug] " + message)); } } private void Awake() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig = new PluginConfig(((BaseUnityPlugin)this).Config); PlusInputs instance = PlusInputs.Instance; GhostGlow.Init(ModConfig, ((BaseUnityPlugin)this).Config); GhostAudio.Create(); Harmony harmony = new Harmony("PoltergeistPlus"); MinePatches.Install(harmony); ControlsPatch.Install(harmony); GhostGlow.Install(harmony); ((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged; GameObject val = new GameObject("PoltergeistPlusController"); ((Object)val).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<AbilityController>(); Log.LogInfo((object)"Poltergeist Plus loaded."); } private void Start() { LethalConfigSupport.Register(ModConfig); } private static void OnSettingChanged(object sender, SettingChangedEventArgs args) { string section = args.ChangedSetting.Definition.Section; if (Array.IndexOf(HostSections, section) >= 0) { NetSync.BroadcastCosts(); } else if (section == "Controls") { ControlsPatch.Refresh(); } } } public class PluginConfig { public ConfigEntry<int> StunCost; public ConfigEntry<float> StunTime; public ConfigEntry<int> TurretOffCost; public ConfigEntry<float> TurretOffTime; public ConfigEntry<int> TurretBerserkCost; public ConfigEntry<float> TurretBerserkTime; public ConfigEntry<int> MineOffCost; public ConfigEntry<float> MineOffTime; public ConfigEntry<int> MineDetonateCost; public ConfigEntry<int> MineGhostDamage; public ConfigEntry<int> HealCost; public ConfigEntry<int> HealAmount; public ConfigEntry<int> StaminaCost; public ConfigEntry<float> StaminaAmount; public ConfigEntry<bool> GlowEnabled; public ConfigEntry<string> GlowColorText; public ConfigEntry<float> GlowLightMultiplier; public ConfigEntry<float> GlowBrightness; public ConfigEntry<float> GlowBoost; public ConfigEntry<float> GlowBloom; public ConfigEntry<float> GlowThreshold; public ConfigEntry<float> GlowTint; public ConfigEntry<float> GlowVignette; public ConfigEntry<float> GlowGrain; public ConfigEntry<float> GlowScanlines; public ConfigEntry<float> GlowOverlay; public ConfigEntry<bool> GlowWorldLights; public ConfigEntry<bool> PerformanceMode; public ConfigEntry<bool> SoundEffects; public ConfigEntry<float> SoundVolume; public ConfigEntry<float> MusicVolume; public ConfigEntry<string> HeaderColorText; public ConfigEntry<bool> RainbowHeader; public ConfigEntry<bool> ShowDebugLogs; public ConfigEntry<bool> LogPerformance; private string _nvText; private Color _nvColor; private string _headerText; private Color _headerColor; public Color NightVisionColor { get { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) string value = GlowColorText.Value; if (value != _nvText) { _nvText = value; _nvColor = ParseColor(value, new Color(0.62f, 1f, 0.12f, 1f)); } return _nvColor; } } public Color HeaderColorValue { get { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) string value = HeaderColorText.Value; if (value != _headerText) { _headerText = value; _headerColor = ParseColor(value, new Color(1f, 0.85f, 0.1f, 1f)); } return _headerColor; } } private static ConfigDescription Client(string text, AcceptableValueBase range = null) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return new ConfigDescription("[Client-sided] " + text, range, Array.Empty<object>()); } private static ConfigDescription Host(string text, AcceptableValueBase range = null) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown return new ConfigDescription("[Host-sided] " + text + " Everyone uses the host's value.", range, Array.Empty<object>()); } private static ConfigDescription Cost(string text) { return Host(text, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000)); } private static ConfigDescription Seconds(string text) { return Host(text, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 600f)); } public PluginConfig(ConfigFile cfg) { GlowEnabled = cfg.Bind<bool>("Night Vision", "Enabled", true, Client("Turns Poltergeist's ghost light (left click) into a night vision look instead of a plain white light.")); GlowColorText = cfg.Bind<string>("Night Vision", "ColorHex", "9EFF1F", Client("The night vision color, as a hex code like 9EFF1F (lime) or a name like lime, green, cyan, purple, red, orange, yellow or white. It colors everything: the lights, the glow, the static, the scanlines and the screen wash. Every color is balanced to be about equally bright.")); GlowTint = cfg.Bind<float>("Night Vision", "ColorStrength", 1f, Client("How strongly the light takes on the night vision color. 0 = plain white light.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))); GlowWorldLights = cfg.Bind<bool>("Night Vision", "TintGameLights", true, Client("Also turns the game's own lights (lamps, the sun, flashlights) the night vision color while it is on.")); GlowLightMultiplier = cfg.Bind<float>("Night Vision", "LightStrength", 1f, Client("How strong the light is that lets you see in the dark. 1 = same as Poltergeist's own ghost light. Raise it to see even more.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f))); GlowBoost = cfg.Bind<float>("Night Vision", "DarkBoost", 0.05f, Client("Brightens the dark parts of the picture so you can see in the dark, without washing out bright parts. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))); GlowBrightness = cfg.Bind<float>("Night Vision", "Brightness", 0.05f, Client("Extra brightness for the whole picture in exposure steps. Too high washes the picture out to white; try DarkBoost first.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 3f))); GlowBloom = cfg.Bind<float>("Night Vision", "GlowStrength", 0f, Client("How strongly very bright things glow. 0 = off (cheapest on performance).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f))); GlowThreshold = cfg.Bind<float>("Night Vision", "GlowThreshold", 1f, Client("How bright something must be before it glows. Lower = more things glow (and the picture can wash out).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f))); GlowVignette = cfg.Bind<float>("Night Vision", "Vignette", 0.35f, Client("Dark corners strength.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))); GlowGrain = cfg.Bind<float>("Night Vision", "Static", 0.05f, Client("Night vision static (flickering speckles). 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))); GlowScanlines = cfg.Bind<float>("Night Vision", "Scanlines", 0.12f, Client("Fine scanlines across the screen. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f))); GlowOverlay = cfg.Bind<float>("Night Vision", "ColorWash", 0.01f, Client("A flat wash of the night vision color over the whole screen. Off by default because it makes the picture look flat; only use it if the light's color isn't showing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.8f))); PerformanceMode = cfg.Bind<bool>("Night Vision", "PerformanceMode", false, Client("For low-end computers. Turns off the static, scanlines, dark corners, color wash and glow, which are the parts of the night vision that cost the most. The colored light and the dark boost stay.")); SoundEffects = cfg.Bind<bool>("Audio", "NightVisionSounds", true, Cl