using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ComputerysModdingUtilities; using FishNet; using FishNet.Broadcast; using FishNet.Connection; using FishNet.Managing; using FishNet.Managing.Client; using FishNet.Managing.Object; using FishNet.Managing.Server; using FishNet.Object; using FishNet.Object.Synchronizing; using FishNet.Serializing; using FishNet.Transporting; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using TMPro; using Unity.Collections; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: StraftatMod(false)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PropHunt { internal static class Disguise { internal sealed class HiderSlot { internal int PlayerId; internal bool IsLocal; internal GameObject Clone; internal readonly List HiddenRenderers = new List(); internal readonly List HiddenColliders = new List(); internal PlayerHealth HiddenGraphicsOwner; internal readonly List OwnedMeshes = new List(4); internal readonly List OwnedMaterials = new List(8); internal bool CloneHitboxOk; internal bool CleanupPending; internal float CleanupPendingSince; internal float CleanupExtraSeconds; internal bool DeathHandled; } private enum MeshTier { None, SharedMesh, Asset, BatchExtract, ColliderProxy } private const float PickRange = 6f; private const int MaxAncestorHops = 4; private const string CloneName = "PropHunt_Disguise"; private const string HitboxName = "PropHunt_Hitbox"; private const int ShootableLayer = 11; private const string WalkBoxName = "PropHunt_WalkBox"; private const int WalkableLayer = 14; private const string PartName = "PropHunt_Part"; private const string DummyName = "PropHunt_WeaponDummy"; private const string LightName = "PropHunt_SpawnerLight"; private const string WalkColName = "PropHunt_WalkCollider"; private const string ProbeAnchorName = "PropHunt_ProbeAnchor"; private const string OutlineWidthProp = "_OutlineWidth"; private const string RuntimeMeshPrefix = "PropHunt_"; internal static readonly int PickMask = -2558413; private static readonly Dictionary _slots = new Dictionary(8); private static readonly List _slotList = new List(8); private static Dictionary> _assetMeshesByName; private static int _assetCacheSceneHandle; private static string _assetCacheSceneName; private static PropertyInfo _staticBatchRootProp; private static bool _staticBatchRootResolved; private static readonly RaycastHit[] _rayScratch = (RaycastHit[])(object)new RaycastHit[256]; private const float SpatialFallbackRange = 0.75f; private const float MaxGroundSlack = 0.6f; private const float WallMountMaxGap = 0.1f; private const float WallProbeMargin = 0.3f; internal const float WallNormalMaxY = 0.35f; private const float PendingCleanupMaxSeconds = 10f; internal static List SlotList => _slotList; private static HiderSlot GetOrCreateSlot(int playerId) { if (_slots.TryGetValue(playerId, out var value)) { return value; } value = new HiderSlot { PlayerId = playerId }; _slots[playerId] = value; _slotList.Add(value); return value; } private static void RemoveSlot(HiderSlot slot) { _slots.Remove(slot.PlayerId); _slotList.Remove(slot); } internal static bool SlotHasLiveClone(int playerId) { if (_slots.TryGetValue(playerId, out var value)) { return (Object)(object)value.Clone != (Object)null; } return false; } internal static bool RaycastNearestIgnoringRoot(Vector3 origin, Vector3 dir, float range, Transform ignoreRoot, out RaycastHit best, bool ignoreAllClones = false) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) best = default(RaycastHit); int num = Physics.RaycastNonAlloc(origin, dir, _rayScratch, range, PickMask, (QueryTriggerInteraction)1); float num2 = float.MaxValue; for (int i = 0; i < num; i++) { RaycastHit val = _rayScratch[i]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !(((RaycastHit)(ref val)).distance >= num2) && (!((Object)(object)ignoreRoot != (Object)null) || !((Component)((RaycastHit)(ref val)).collider).transform.IsChildOf(ignoreRoot)) && (!ignoreAllClones || !IsUnderClone(((Component)((RaycastHit)(ref val)).collider).transform))) { best = val; num2 = ((RaycastHit)(ref val)).distance; } } return num2 < float.MaxValue; } internal static bool SphereCastNearestIgnoringRoot(Vector3 origin, float radius, Vector3 dir, float range, Transform ignoreRoot, out float distance) { //IL_0008: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) distance = 0f; int num = Physics.SphereCastNonAlloc(origin, radius, dir, _rayScratch, range, PickMask, (QueryTriggerInteraction)1); float num2 = float.MaxValue; for (int i = 0; i < num; i++) { RaycastHit val = _rayScratch[i]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !(((RaycastHit)(ref val)).distance >= num2) && (!((Object)(object)ignoreRoot != (Object)null) || !((Component)((RaycastHit)(ref val)).collider).transform.IsChildOf(ignoreRoot))) { num2 = ((RaycastHit)(ref val)).distance; } } if (num2 == float.MaxValue) { return false; } distance = num2; return true; } internal static bool IsUnderClone(Transform t) { if ((Object)(object)t == (Object)null) { return false; } for (int i = 0; i < _slotList.Count; i++) { GameObject clone = _slotList[i].Clone; if ((Object)(object)clone != (Object)null && t.IsChildOf(clone.transform)) { return true; } } return false; } internal static bool TryPick(FirstPersonController fpc, out string path, out string meshName, out Vector3 lossyScale, out int spawnerObjectId, out string fail) { //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_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) path = null; meshName = null; lossyScale = Vector3.one; spawnerObjectId = -1; fail = "internal error"; try { Camera playerCamera = fpc.playerCamera; if ((Object)(object)playerCamera == (Object)null) { fail = "no camera"; return PickFail(fail); } GameObject val; if (!RaycastNearestIgnoringRoot(((Component)playerCamera).transform.position, ((Component)playerCamera).transform.forward, 6f, ((Component)fpc).transform.root, out var best, ignoreAllClones: true)) { val = RayMissFallback(((Component)playerCamera).transform.position, ((Component)playerCamera).transform.forward); if ((Object)(object)val == (Object)null) { fail = "nothing in range (6 m)"; PickFail(fail); fail = "Nothing there to disguise as."; return false; } } else { val = ChooseCandidate(best, out fail); if ((Object)(object)val == (Object)null) { PickFail(fail); fail = FriendlyPickFail(fail); return false; } } if (!TryRecoverRenderMesh(val, out var mesh, out var _, out var meshKey, out var ownsMesh, out fail)) { Plugin.Activity("Disguise: pick '" + ((Object)val).name + "' failed mesh recovery - " + fail); return false; } if (ownsMesh && (Object)(object)mesh != (Object)null) { Object.Destroy((Object)(object)mesh); } try { ItemSpawner componentInParent = val.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)((NetworkBehaviour)componentInParent).NetworkObject != (Object)null) { spawnerObjectId = ((NetworkBehaviour)componentInParent).NetworkObject.ObjectId; Plugin.Activity($"Disguise: pick resolved spawner '{((Object)componentInParent).name}' (ObjectId {spawnerObjectId})."); } } catch (Exception e) { Plugin.LogOnce("Disguise.SpawnerDetect", e); spawnerObjectId = -1; } path = BuildScenePath(val.transform); meshName = meshKey; lossyScale = val.transform.lossyScale; return true; } catch (Exception e2) { Plugin.LogOnce("Disguise.TryPick", e2); fail = "error (see log)"; return false; } } private static bool PickFail(string fail) { Plugin.Activity("Disguise: pick failed - " + fail); return false; } private static string FriendlyPickFail(string fail) { try { if (fail == null || !fail.StartsWith("too big (", StringComparison.Ordinal) || !fail.EndsWith(" m)", StringComparison.Ordinal)) { return fail; } if (float.TryParse(fail.Substring("too big (".Length, fail.Length - "too big (".Length - " m)".Length), out var result) && result > 50f) { return "That's part of the map - try a smaller object."; } return fail; } catch (Exception e) { Plugin.LogOnce("Disguise.FriendlyFail", e); return fail; } } private static GameObject ChooseCandidate(RaycastHit hit, out string fail) { //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) fail = "that's not a solid prop"; try { HashSet hashSet = new HashSet(); List list = new List(16); Transform val = ((Component)((RaycastHit)(ref hit)).collider).transform; int num = 0; while (num <= 4 && (Object)(object)val != (Object)null) { if (hashSet.Add(((Component)val).gameObject)) { list.Add(((Component)val).gameObject); } num++; val = val.parent; } MeshFilter[] componentsInChildren = ((Component)((RaycastHit)(ref hit)).collider).GetComponentsInChildren(true); foreach (MeshFilter val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && hashSet.Add(((Component)val2).gameObject)) { list.Add(((Component)val2).gameObject); } } GameObject val3 = null; float num2 = -1f; string strB = null; GameObject val4 = null; float num3 = float.MaxValue; string text = null; float num4 = float.MaxValue; int num5 = 0; int num6 = 0; foreach (GameObject item in list) { MeshFilter component = item.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null || ((Object)item).name == "PropHunt_Disguise" || IsUnderClone(item.transform)) { continue; } MeshRenderer component2 = item.GetComponent(); if ((Object)(object)component2 == (Object)null) { continue; } if (!((Renderer)component2).enabled || !item.activeInHierarchy) { if (num5 < 12) { num5++; Plugin.Activity("Disguise: pick candidate '" + ((Object)item).name + "' skipped - not visible."); } else { num6++; } continue; } Bounds bounds = ((Renderer)component2).bounds; float num7 = Vector3.Distance(((RaycastHit)(ref hit)).point, ((Bounds)(ref bounds)).ClosestPoint(((RaycastHit)(ref hit)).point)); if (!ValidateSource(item, out var fail2)) { if (num5 < 12) { num5++; Plugin.Activity("Disguise: pick candidate '" + ((Object)item).name + "' rejected - " + fail2); } else { num6++; } if (num7 < num4) { num4 = num7; text = fail2; } continue; } Bounds val5 = bounds; ((Bounds)(ref val5)).Expand(0.6f); if (((Bounds)(ref val5)).Contains(((RaycastHit)(ref hit)).point)) { Vector3 size = ((Bounds)(ref bounds)).size; float num8 = size.x * size.y * size.z; string text2 = BuildScenePath(item.transform); if ((Object)(object)val3 == (Object)null || num8 > num2 || (num8 == num2 && string.CompareOrdinal(text2, strB) < 0)) { val3 = item; num2 = num8; strB = text2; } } else if (num7 < num3) { val4 = item; num3 = num7; } } if (num6 > 0) { Plugin.Activity($"Disguise: {num6} more candidate rejections suppressed (large subtree)."); } if ((Object)(object)val3 != (Object)null) { Plugin.Activity($"Disguise: pick chose '{((Object)val3).name}' (contains hit, vol {num2:0.###})"); return val3; } if ((Object)(object)val4 != (Object)null) { Plugin.Activity($"Disguise: pick chose '{((Object)val4).name}' (nearest to hit, {num3:0.##} m)"); return val4; } GameObject val6 = SpatialFallback(((RaycastHit)(ref hit)).point); if ((Object)(object)val6 != (Object)null) { return val6; } if (text != null) { fail = text; } return null; } catch (Exception e) { Plugin.LogOnce("Disguise.ChooseCandidate", e); return null; } } private static GameObject SpatialFallback(Vector3 hitPoint) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) try { float bestSqrOut; GameObject val = ScanNearestValidRenderer((Bounds b) => ((Bounds)(ref b)).SqrDistance(hitPoint), 0.5625f, out bestSqrOut); if ((Object)(object)val != (Object)null) { Plugin.Activity($"Disguise: spatial fallback chose '{((Object)val).name}' (dist {Mathf.Sqrt(bestSqrOut):0.00})"); } return val; } catch (Exception e) { Plugin.LogOnce("Disguise.SpatialFallback", e); return null; } } private static GameObject RayMissFallback(Vector3 origin, Vector3 dir) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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) try { float bestSqrOut; GameObject val = ScanNearestValidRenderer(delegate(Bounds b) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp(Vector3.Dot(((Bounds)(ref b)).center - origin, dir), 0f, 6f); return ((Bounds)(ref b)).SqrDistance(origin + dir * num); }, 0.5625f, out bestSqrOut); if ((Object)(object)val != (Object)null) { Plugin.Activity($"Disguise: pick ray-miss fallback chose '{((Object)val).name}' (dist {Mathf.Sqrt(bestSqrOut):0.00})"); } return val; } catch (Exception e) { Plugin.LogOnce("Disguise.RayMissFallback", e); return null; } } private static GameObject ScanNearestValidRenderer(Func sqrDistOf, float maxSqr, out float bestSqrOut) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) GameObject val = null; float num = float.MaxValue; string text = null; MeshRenderer[] array = Object.FindObjectsOfType(); foreach (MeshRenderer val2 in array) { if ((Object)(object)val2 == (Object)null || !((Renderer)val2).enabled) { continue; } GameObject gameObject = ((Component)val2).gameObject; if (!gameObject.activeInHierarchy || ((Object)gameObject).name == "PropHunt_Disguise" || IsUnderClone(gameObject.transform)) { continue; } MeshFilter component = gameObject.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null) { continue; } float num2 = sqrDistOf(((Renderer)val2).bounds); if (num2 > maxSqr || num2 > num || !ValidateSource(gameObject, out var _)) { continue; } if (num2 < num) { val = gameObject; num = num2; text = null; continue; } if (text == null) { text = BuildScenePath(val.transform); } string text2 = BuildScenePath(gameObject.transform); if (string.CompareOrdinal(text2, text) < 0) { val = gameObject; text = text2; } } bestSqrOut = num; return val; } private static bool ValidateSource(GameObject go, out string fail) { //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) fail = null; if ((Object)(object)go.GetComponentInParent() != (Object)null) { fail = "that's a weapon/item"; return false; } if ((Object)(object)go.GetComponentInParent() != (Object)null) { fail = "that's a player"; return false; } string name = ((Object)go).name; switch (name) { case "PROC_PLANE": case "SKINNED_PROC_PLANE": case "GlassGib": case "Bullet Hole": fail = "that's combat debris"; Plugin.Activity("Disguise: leftover excluded '" + name + "' (debris GO name)."); return false; default: { string name2 = ((Object)go.transform.root).name; if (name2.EndsWith("(Clone)", StringComparison.Ordinal)) { fail = "that's combat debris"; Plugin.Activity("Disguise: leftover excluded '" + name + "' (runtime-spawned root '" + name2 + "')."); return false; } if ((Object)(object)go.GetComponent() != (Object)null) { fail = "that's combat debris"; Plugin.Activity("Disguise: leftover excluded '" + name + "' (bullet-hole decal)."); return false; } if ((Object)(object)go.GetComponentInParent() != (Object)null) { fail = "that's combat debris"; Plugin.Activity("Disguise: leftover excluded '" + name + "' (dismemberment gib)."); return false; } NetworkObject componentInParent = go.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && !componentInParent.IsSceneObject) { fail = "that's a networked object"; return false; } if (go.tag == "vfx" || go.tag == "Hat") { fail = "that's not a solid prop"; return false; } MeshRenderer component = go.GetComponent(); if ((Object)(object)component == (Object)null) { fail = "that's not a solid prop"; return false; } Bounds bounds = ((Renderer)component).bounds; Vector3 size = ((Bounds)(ref bounds)).size; float num = Mathf.Max(size.x, Mathf.Max(size.y, size.z)); float value = Plugin.CfgMinPropSize.Value; float value2 = Plugin.CfgMaxPropSize.Value; if (num < value) { fail = $"too small ({num:0.0} m)"; return false; } if (num > value2) { fail = $"too big ({num:0.0} m)"; return false; } return true; } } } private static string BuildScenePath(Transform t) { StringBuilder stringBuilder = new StringBuilder(128); BuildScenePathInto(stringBuilder, t); return stringBuilder.ToString(); } private static void BuildScenePathInto(StringBuilder sb, Transform t) { if ((Object)(object)t.parent != (Object)null) { BuildScenePathInto(sb, t.parent); sb.Append('/'); } sb.Append(((Object)t).name); } internal static bool TryRecoverRenderMesh(GameObject go, out Mesh mesh, out Material[] materials, out string meshKey, out bool ownsMesh, out string fail) { mesh = null; materials = null; meshKey = null; ownsMesh = false; fail = "that prop can't be copied - pick a movable one"; MeshFilter component = go.GetComponent(); MeshRenderer component2 = go.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null || (Object)(object)component2 == (Object)null) { return false; } Mesh assetMesh; MeshTier meshTier = DecideTier(go, component, component2, out assetMesh); meshKey = KeyForTier(go, component, meshTier); switch (meshTier) { case MeshTier.SharedMesh: mesh = component.sharedMesh; materials = ((Renderer)component2).sharedMaterials; Plugin.Activity("Disguise: tier A (sharedMesh) serves '" + ((Object)go).name + "'."); return true; case MeshTier.Asset: mesh = assetMesh; materials = ((Renderer)component2).sharedMaterials; Plugin.Activity("Disguise: tier ASSET ('" + ((Object)assetMesh).name + "') serves '" + ((Object)go).name + "'."); return true; case MeshTier.BatchExtract: try { if (TryExtractSubMeshes(go, component2, component.sharedMesh, out mesh)) { materials = ((Renderer)component2).sharedMaterials; ownsMesh = true; Plugin.Activity("Disguise: tier C (batch extract) serves '" + ((Object)go).name + "'."); return true; } } catch (Exception e) { Plugin.LogOnce("Disguise.TierCExtract", e); mesh = null; } break; default: meshKey = null; return false; case MeshTier.ColliderProxy: break; } MeshCollider component3 = go.GetComponent(); if ((Object)(object)component3 != (Object)null && (Object)(object)component3.sharedMesh != (Object)null && ColliderMeshLooksRenderable(go, component3, component2)) { mesh = component3.sharedMesh; materials = ((Renderer)component2).sharedMaterials; Plugin.Activity("Disguise: tier B (collider proxy) serves '" + ((Object)go).name + "'."); return true; } meshKey = null; mesh = null; return false; } private static MeshTier DecideTier(GameObject go, MeshFilter mf, MeshRenderer mr, out Mesh assetMesh) { assetMesh = null; if (!((Renderer)mr).isPartOfStaticBatch) { return MeshTier.SharedMesh; } if (TryFindAssetMesh(go, mr, out assetMesh)) { return MeshTier.Asset; } if (mf.sharedMesh.isReadable) { return MeshTier.BatchExtract; } MeshCollider component = go.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { return MeshTier.ColliderProxy; } return MeshTier.None; } private static string KeyForTier(GameObject go, MeshFilter mf, MeshTier tier) { switch (tier) { case MeshTier.SharedMesh: return ((Object)mf.sharedMesh).name; case MeshTier.Asset: return "asset:" + BaseAssetName(((Object)go).name); case MeshTier.BatchExtract: return "batch:" + ((Object)mf.sharedMesh).name; case MeshTier.ColliderProxy: { MeshCollider component = go.GetComponent(); if (!((Object)(object)component != (Object)null) || !((Object)(object)component.sharedMesh != (Object)null)) { return null; } return "col:" + ((Object)component.sharedMesh).name; } default: return null; } } private static bool ColliderMeshLooksRenderable(GameObject go, MeshCollider mc, MeshRenderer mr) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) try { string text = (((Object)mc.sharedMesh).name ?? "").ToLowerInvariant(); if (text.Contains("collider") || text.Contains("collision") || text.Contains("ucx") || text.Contains("phys")) { return false; } Bounds bounds = mc.sharedMesh.bounds; Vector3 val = Vector3.Scale(((Bounds)(ref bounds)).size, go.transform.lossyScale); float magnitude = ((Vector3)(ref val)).magnitude; bounds = ((Renderer)mr).bounds; val = ((Bounds)(ref bounds)).size; float magnitude2 = ((Vector3)(ref val)).magnitude; if (magnitude < 0.0001f || magnitude2 < 0.0001f) { return false; } float num = magnitude2 / magnitude; return num >= 0.75f && num <= 1.35f; } catch (Exception e) { Plugin.LogOnce("Disguise.TierBGate", e); return false; } } private static string GetMeshKey(GameObject go) { MeshFilter component = go.GetComponent(); MeshRenderer component2 = go.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null || (Object)(object)component2 == (Object)null) { return null; } Mesh assetMesh; return KeyForTier(go, component, DecideTier(go, component, component2, out assetMesh)); } private static bool TryFindAssetMesh(GameObject go, MeshRenderer mr, out Mesh mesh) { mesh = null; try { string text = BaseAssetName(((Object)go).name); if (string.IsNullOrEmpty(text)) { return false; } Dictionary> assetMeshCache = GetAssetMeshCache(); if (assetMeshCache == null || !assetMeshCache.TryGetValue(text, out var value) || value == null) { return false; } foreach (Mesh item in value) { if (!((Object)(object)item == (Object)null) && AssetMeshMatchesRenderer(item, go, mr)) { mesh = item; return true; } } return false; } catch (Exception e) { Plugin.LogOnce("Disguise.AssetLookup", e); return false; } } private static string BaseAssetName(string goName) { if (goName == null) { return null; } string text = goName.Trim(); bool flag = true; while (flag && text.Length > 0) { flag = false; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length).TrimEnd(Array.Empty()); flag = true; } else { if (text[text.Length - 1] != ')') { continue; } int num = text.LastIndexOf('('); if (num <= 0 || text[num - 1] != ' ' || num + 1 >= text.Length - 1) { continue; } bool flag2 = true; for (int i = num + 1; i < text.Length - 1; i++) { if (!char.IsDigit(text[i])) { flag2 = false; break; } } if (flag2) { text = text.Substring(0, num - 1).TrimEnd(Array.Empty()); flag = true; } } } return text; } private static Dictionary> GetAssetMeshCache() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); int handle = ((Scene)(ref activeScene)).handle; string name = ((Scene)(ref activeScene)).name; if (_assetMeshesByName != null && handle == _assetCacheSceneHandle && name == _assetCacheSceneName) { return _assetMeshesByName; } Dictionary> dictionary = new Dictionary>(512, StringComparer.Ordinal); Mesh[] array = Resources.FindObjectsOfTypeAll(); foreach (Mesh val in array) { if ((Object)(object)val == (Object)null) { continue; } string name2 = ((Object)val).name; if (!string.IsNullOrEmpty(name2) && !name2.StartsWith("PropHunt_", StringComparison.Ordinal)) { if (!dictionary.TryGetValue(name2, out var value)) { value = (dictionary[name2] = new List(1)); } value.Add(val); } } foreach (List value2 in dictionary.Values) { if (value2.Count > 1) { value2.Sort(CompareAssetMeshes); } } _assetMeshesByName = dictionary; _assetCacheSceneHandle = handle; _assetCacheSceneName = name; Plugin.Activity($"Disguise: asset mesh cache built ({dictionary.Count} names)."); return dictionary; } private static int CompareAssetMeshes(Mesh a, Mesh b) { //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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)a == (Object)null; bool flag2 = (Object)(object)b == (Object)null; if (flag || flag2) { if (flag != flag2) { if (!flag) { return -1; } return 1; } return 0; } int num = a.vertexCount.CompareTo(b.vertexCount); if (num == 0) { Bounds bounds = a.bounds; Vector3 size = ((Bounds)(ref bounds)).size; float sqrMagnitude = ((Vector3)(ref size)).sqrMagnitude; bounds = b.bounds; size = ((Bounds)(ref bounds)).size; return sqrMagnitude.CompareTo(((Vector3)(ref size)).sqrMagnitude); } return num; } private static bool AssetMeshMatchesRenderer(Mesh mesh, GameObject go, MeshRenderer mr) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Bounds val = TransformBounds(go.transform.localToWorldMatrix, mesh.bounds); Vector3 size = ((Bounds)(ref val)).size; Bounds bounds = ((Renderer)mr).bounds; Vector3 size2 = ((Bounds)(ref bounds)).size; for (int i = 0; i < 3; i++) { if (((Vector3)(ref size))[i] < 0.0001f || ((Vector3)(ref size2))[i] < 0.0001f) { return false; } float num = ((Vector3)(ref size2))[i] / ((Vector3)(ref size))[i]; if (num < 0.85f || num > 1.18f) { return false; } } return true; } private static bool TryExtractSubMeshes(GameObject go, MeshRenderer mr, Mesh combined, out Mesh extracted) { //IL_003d: 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_0042: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Expected O, but got Unknown //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_044c: Unknown result type (might be due to invalid IL or missing references) //IL_0451: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Unknown result type (might be due to invalid IL or missing references) extracted = null; Material[] sharedMaterials = ((Renderer)mr).sharedMaterials; int num = ((sharedMaterials != null) ? sharedMaterials.Length : 0); int subMeshCount = combined.subMeshCount; if (num <= 0 || subMeshCount < num) { return false; } Transform staticBatchRoot = GetStaticBatchRoot((Renderer)(object)mr); Matrix4x4 val = (((Object)(object)staticBatchRoot != (Object)null) ? staticBatchRoot.localToWorldMatrix : Matrix4x4.identity); Bounds bounds = ((Renderer)mr).bounds; int num2 = -1; float num3 = float.MaxValue; float num4 = float.MaxValue; for (int i = 0; i + num <= subMeshCount; i++) { Bounds val2 = default(Bounds); for (int j = 0; j < num; j++) { SubMeshDescriptor subMesh = combined.GetSubMesh(i + j); Bounds val3 = TransformBounds(val, ((SubMeshDescriptor)(ref subMesh)).bounds); if (j == 0) { val2 = val3; } else { ((Bounds)(ref val2)).Encapsulate(val3); } } Vector3 val4 = ((Bounds)(ref val2)).center - ((Bounds)(ref bounds)).center; Vector3 val5 = ((Bounds)(ref val2)).size - ((Bounds)(ref bounds)).size; float num5 = Mathf.Abs(val4.x) + Mathf.Abs(val4.y) + Mathf.Abs(val4.z) + Mathf.Abs(val5.x) + Mathf.Abs(val5.y) + Mathf.Abs(val5.z); if (num5 < num3) { num4 = num3; num3 = num5; num2 = i; } else if (num5 < num4) { num4 = num5; } } Vector3 size = ((Bounds)(ref bounds)).size; float num6 = 0.05f * Mathf.Max(1f, ((Vector3)(ref size)).magnitude); bool flag = num4 == float.MaxValue || (num4 >= 2f * num3 && num4 > 0f); if (num2 < 0 || num3 >= num6 || !flag) { Plugin.Activity("Disguise: batched submesh match rejected for '" + ((Object)go).name + "' " + $"(best {num3:0.###}, second {num4:0.###}, tol {num6:0.###})."); return false; } Vector3[] vertices = combined.vertices; Vector3[] normals = combined.normals; Vector2[] uv = combined.uv; Vector2[] uv2 = combined.uv2; bool flag2 = normals != null && normals.Length == combined.vertexCount; bool flag3 = uv != null && uv.Length == combined.vertexCount; bool flag4 = uv2 != null && uv2.Length == combined.vertexCount; Matrix4x4 worldToLocalMatrix = go.transform.worldToLocalMatrix; Matrix4x4 val6 = worldToLocalMatrix * val; val6 = ((Matrix4x4)(ref val6)).inverse; Matrix4x4 transpose = ((Matrix4x4)(ref val6)).transpose; int[] array = new int[combined.vertexCount]; for (int k = 0; k < array.Length; k++) { array[k] = -1; } List list = new List(); List list2 = (flag2 ? new List() : null); List list3 = (flag3 ? new List() : null); List list4 = (flag4 ? new List() : null); List list5 = new List(num); for (int l = 0; l < num; l++) { int[] indices = combined.GetIndices(num2 + l); int[] array2 = new int[indices.Length]; for (int m = 0; m < indices.Length; m++) { int num7 = indices[m]; int num8 = array[num7]; if (num8 < 0) { num8 = (array[num7] = list.Count); list.Add(((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(((Matrix4x4)(ref val)).MultiplyPoint3x4(vertices[num7]))); if (flag2) { Vector3 val7 = ((Matrix4x4)(ref transpose)).MultiplyVector(normals[num7]); float magnitude = ((Vector3)(ref val7)).magnitude; list2.Add((magnitude > 0.0001f) ? (val7 / magnitude) : Vector3.up); } if (flag3) { list3.Add(uv[num7]); } if (flag4) { list4.Add(uv2[num7]); } } array2[m] = num8; } list5.Add(array2); } Mesh val8 = new Mesh(); try { ((Object)val8).name = "PropHunt_Extract_" + ((Object)go).name; if (list.Count > 65534) { val8.indexFormat = (IndexFormat)1; } val8.SetVertices(list); if (flag2) { val8.SetNormals(list2); } if (flag3) { val8.SetUVs(0, list3); } if (flag4) { val8.SetUVs(1, list4); } val8.subMeshCount = num; bool flag5 = true; for (int n = 0; n < num; n++) { MeshTopology topology = combined.GetTopology(num2 + n); if ((int)topology != 0) { flag5 = false; } val8.SetIndices(list5[n], topology, n); } if (!flag2 && flag5) { val8.RecalculateNormals(); } if (flag3 && flag5) { val8.RecalculateTangents(); } val8.RecalculateBounds(); } catch { Object.Destroy((Object)(object)val8); throw; } extracted = val8; return true; } private static Bounds TransformBounds(Matrix4x4 m, Bounds b) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_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_00d0: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Matrix4x4)(ref m)).MultiplyPoint3x4(((Bounds)(ref b)).center); Vector3 extents = ((Bounds)(ref b)).extents; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(Mathf.Abs(m.m00) * extents.x + Mathf.Abs(m.m01) * extents.y + Mathf.Abs(m.m02) * extents.z, Mathf.Abs(m.m10) * extents.x + Mathf.Abs(m.m11) * extents.y + Mathf.Abs(m.m12) * extents.z, Mathf.Abs(m.m20) * extents.x + Mathf.Abs(m.m21) * extents.y + Mathf.Abs(m.m22) * extents.z); return new Bounds(val, val2 * 2f); } private static Transform GetStaticBatchRoot(Renderer r) { if (!_staticBatchRootResolved) { _staticBatchRootResolved = true; try { _staticBatchRootProp = typeof(Renderer).GetProperty("staticBatchRootTransform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } catch { _staticBatchRootProp = null; } } if (_staticBatchRootProp == null) { return null; } try { object? value = _staticBatchRootProp.GetValue(r, null); return (Transform)((value is Transform) ? value : null); } catch { return null; } } internal static bool ApplyFromMessage(PropHuntNet msg, bool logResolutionFailure = true) { //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) HiderSlot hiderSlot = null; try { if (!Plugin.CfgEnabled.Value) { return true; } PropHuntManager instance = PropHuntManager.Instance; if ((Object)(object)instance == (Object)null) { return true; } int a = msg.A; int num = PropHuntManager.ConnIdForPlayer(a); if (num < 0) { if (logResolutionFailure) { Plugin.Activity($"Disguise: op-4 for unknown PlayerId {a} - ignored."); } return false; } PlayerHealth val = instance.FindHealthByConn(num); if ((Object)(object)val != (Object)null && !instance.IsBindableHealth(val)) { if (logResolutionFailure) { Plugin.Log.LogWarning((object)$"Disguise: player object for prop {a} is stale/despawning - that hider stays visible."); } return false; } if ((Object)(object)val == (Object)null) { if (logResolutionFailure) { Plugin.Log.LogWarning((object)$"Disguise: player object for prop {a} not found - that hider stays visible."); } return false; } hiderSlot = GetOrCreateSlot(a); hiderSlot.IsLocal = PropHuntManager.LocalRole == Role.Hider && a == PropHuntManager.LocalPlayerId(); string text = msg.S ?? ""; int num2 = text.IndexOf('\n'); string text2 = ((num2 >= 0) ? text.Substring(0, num2) : text); string text3 = ((num2 >= 0) ? text.Substring(num2 + 1) : ""); GameObject val2 = ResolveByPath(text2); if ((Object)(object)val2 != (Object)null && !HasRenderMesh(val2)) { val2 = null; } if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(text3)) { string meshKey = GetMeshKey(val2); if (meshKey == null || meshKey != text3) { Plugin.Log.LogWarning((object)("Disguise: path '" + text2 + "' resolved mesh key '" + (meshKey ?? "") + "' instead of '" + text3 + "' - falling back to mesh search.")); val2 = null; } } if ((Object)(object)val2 == (Object)null && !string.IsNullOrEmpty(text3)) { val2 = FindUniqueByMeshName(text3); } ItemSpawner val3 = ((msg.B > 0) ? ResolveSpawner(msg.B - 1, val2) : null); if ((Object)(object)val2 == (Object)null && (Object)(object)val3 == (Object)null) { Plugin.Log.LogWarning((object)("Disguise: couldn't resolve prop '" + text2 + "' (mesh '" + text3 + "') - hider stays visible.")); return true; } if ((Object)(object)val2 != (Object)null) { Vector3 lossyScale = val2.transform.lossyScale; Vector3 val4 = lossyScale - msg.V; if (((Vector3)(ref val4)).sqrMagnitude > 0.01f * Mathf.Max(0.01f, ((Vector3)(ref msg.V)).sqrMagnitude)) { Plugin.Log.LogWarning((object)$"Disguise: resolved scale {lossyScale} differs from sender's {msg.V} - possible mismatched resolution."); } } DestroyClone(hiderSlot); bool isLocal = hiderSlot.IsLocal; if (!isLocal || Plugin.CfgShowOwnProp.Value) { bool flag = false; if ((Object)(object)val3 != (Object)null) { flag = BuildCloneSpawner(hiderSlot, val, val3); if (!flag && (Object)(object)val2 != (Object)null) { Plugin.Log.LogWarning((object)("Disguise: spawner composite failed for '" + ((Object)val3).name + "' - plain prop fallback.")); flag = BuildClone(hiderSlot, val, val2); } } else { flag = BuildClone(hiderSlot, val, val2); } if (!flag) { RestoreGraphics(hiderSlot); return true; } } HideGraphics(hiderSlot, val); if (hiderSlot.CloneHitboxOk) { HideHitboxColliders(hiderSlot, val); } else { RestoreHiddenColliders(hiderSlot); } if (isLocal) { PropHuntManager.ClientDisguisedThisTake = true; } Plugin.Activity($"Disguise: applied '{(((Object)(object)val3 != (Object)null) ? ((Object)val3).name : ((Object)val2).name)}' onto prop {a}."); return true; } catch (Exception e) { Plugin.LogOnce("Disguise.Apply", e); try { if (hiderSlot != null) { CleanupSlot(hiderSlot); } } catch { } return true; } } private static bool HasRenderMesh(GameObject go) { MeshFilter component = go.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { return (Object)(object)go.GetComponent() != (Object)null; } return false; } private static GameObject ResolveByPath(string path) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(path)) { return null; } string[] array = path.Split(new char[1] { '/' }); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); if (!((Scene)(ref sceneAt)).isLoaded) { continue; } GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if (((Object)val).name != array[0]) { continue; } Transform val2 = val.transform; bool flag = true; for (int k = 1; k < array.Length; k++) { val2 = val2.Find(array[k]); if ((Object)(object)val2 == (Object)null) { flag = false; break; } } if (flag) { return ((Component)val2).gameObject; } } } return null; } private static GameObject FindUniqueByMeshName(string meshKey) { GameObject result = null; int num = 0; MeshFilter[] array = Object.FindObjectsOfType(true); foreach (MeshFilter val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.sharedMesh == (Object)null) { continue; } GameObject gameObject = ((Component)val).gameObject; if (!(((Object)gameObject).name == "PropHunt_Disguise") && !IsUnderClone(gameObject.transform) && !((Object)(object)gameObject.GetComponent() == (Object)null) && !(GetMeshKey(gameObject) != meshKey) && ValidateSource(gameObject, out var _)) { result = gameObject; num++; if (num > 1) { return null; } } } if (num != 1) { return null; } return result; } private static bool BuildClone(HiderSlot slot, PlayerHealth hiderPh, GameObject source) { //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: 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_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Expected O, but got Unknown //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) try { MeshRenderer component = source.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } if (!TryRecoverRenderMesh(source, out var mesh, out var materials, out var _, out var ownsMesh, out var fail)) { Plugin.Log.LogWarning((object)("Disguise: mesh recovery failed for '" + ((Object)source).name + "' (" + fail + ") - hider stays visible.")); return false; } if (ownsMesh) { slot.OwnedMeshes.Add(mesh); } PropLighting.LogSceneInventoryOnce(); float num = ComputeGroundSlack(source, component, ((Component)hiderPh).transform); if (num > 0.01f) { Plugin.Activity($"Disguise: ground slack for '{((Object)source).name}' = {num:0.00}m"); } Transform transform = ((Component)hiderPh).transform; GameObject val = (slot.Clone = new GameObject("PropHunt_Disguise")); val.transform.SetParent(transform, false); val.AddComponent().sharedMesh = mesh; MeshRenderer val2 = val.AddComponent(); ((Renderer)val2).sharedMaterials = materials; ((Renderer)val2).shadowCastingMode = ((Renderer)component).shadowCastingMode; ((Renderer)val2).receiveShadows = ((Renderer)component).receiveShadows; Vector3 lossyScale = transform.lossyScale; Vector3 lossyScale2 = source.transform.lossyScale; val.transform.localScale = new Vector3(SafeDiv(lossyScale2.x, lossyScale.x), SafeDiv(lossyScale2.y, lossyScale.y), SafeDiv(lossyScale2.z, lossyScale.z)); Quaternion val3 = YawOnly(transform.rotation); Quaternion val4 = Quaternion.Inverse(val3) * source.transform.rotation; val.transform.rotation = val3 * val4; val.transform.position = transform.position; CharacterController component2 = ((Component)hiderPh).GetComponent(); float y; Bounds bounds; if (!((Object)(object)component2 != (Object)null)) { y = transform.position.y; } else { bounds = ((Collider)component2).bounds; y = ((Bounds)(ref bounds)).min.y; } float num2 = y; Bounds bounds2 = ((Renderer)val2).bounds; Transform transform2 = val.transform; transform2.position += new Vector3(transform.position.x - ((Bounds)(ref bounds2)).center.x, num2 - num - ((Bounds)(ref bounds2)).min.y, transform.position.z - ((Bounds)(ref bounds2)).center.z); try { GameObject val5 = new GameObject("PropHunt_Hitbox"); val5.layer = 11; val5.transform.SetParent(val.transform, false); BoxCollider val6 = val5.AddComponent(); ((Collider)val6).isTrigger = false; bounds = mesh.bounds; val6.center = ((Bounds)(ref bounds)).center; bounds = mesh.bounds; val6.size = ((Bounds)(ref bounds)).size; if ((Object)(object)component2 != (Object)null) { Physics.IgnoreCollision((Collider)(object)val6, (Collider)(object)component2, true); } if (HitboxResolvesHider(val5, hiderPh)) { slot.CloneHitboxOk = true; } else { Plugin.Log.LogWarning((object)"Disguise: prop hitbox wouldn't resolve the hider's PlayerHealth - keeping the humanoid hitbox."); Object.Destroy((Object)(object)val5); } } catch (Exception e) { Plugin.LogOnce("Disguise.Hitbox", e); slot.CloneHitboxOk = false; } bounds = mesh.bounds; Vector3 center = ((Bounds)(ref bounds)).center; bounds = mesh.bounds; AddWalkBox(val, center, ((Bounds)(ref bounds)).size, component2); DisguiseFollower disguiseFollower = val.AddComponent(); disguiseFollower.Root = transform; disguiseFollower.DeltaRot = val4; disguiseFollower.SrcLossyScale = lossyScale2; disguiseFollower.Cc = component2; disguiseFollower.GraphicsT = ResolveGraphicsAnchor(hiderPh); disguiseFollower.CloneMr = val2; disguiseFollower.GroundSlack = num; if (ComputeWallMount(source.transform, ((Renderer)component).bounds, transform, out var backLocal, out var gap, out var halfExtent)) { disguiseFollower.WallMounted = true; disguiseFollower.WallBackLocal = backLocal; disguiseFollower.WallGap = gap; disguiseFollower.WallHalfExtent = halfExtent; } disguiseFollower.SetManualYaw(disguiseFollower.SeedYaw = ((Quaternion)(ref val3)).eulerAngles.y); disguiseFollower.SlotPlayerId = slot.PlayerId; if (slot.IsLocal) { disguiseFollower.IsLocalHider = true; disguiseFollower.Fpc = ((Component)hiderPh).GetComponent(); } return true; } catch (Exception e2) { Plugin.LogOnce("Disguise.BuildClone", e2); DestroyClone(slot); return false; } } private static bool HitboxResolvesHider(GameObject hitboxGo, PlayerHealth hiderPh) { PlayerHealth val = null; Transform root = hitboxGo.transform.root; if (((Component)root).CompareTag("Player")) { ((Component)root).TryGetComponent(ref val); } else { val = hitboxGo.GetComponentInParent(); } return (Object)(object)val == (Object)(object)hiderPh; } private static void AddWalkBox(GameObject clone, Vector3 localCenter, Vector3 localSize, CharacterController hiderCc) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) GameObject val = null; try { if (Plugin.CfgSolidProps.Value) { val = new GameObject("PropHunt_WalkBox"); val.layer = 14; val.transform.SetParent(clone.transform, false); BoxCollider val2 = val.AddComponent(); ((Collider)val2).isTrigger = false; val2.center = localCenter; val2.size = localSize; if ((Object)(object)hiderCc != (Object)null) { Physics.IgnoreCollision((Collider)(object)val2, (Collider)(object)hiderCc, true); } } } catch (Exception e) { Plugin.LogOnce("Disguise.WalkBox", e); if ((Object)(object)val != (Object)null) { try { Object.Destroy((Object)(object)val); return; } catch { return; } } } } private static ItemSpawner ResolveSpawner(int objectId, GameObject source) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) try { NetworkObject value = null; NetworkManager networkManager = InstanceFinder.NetworkManager; if ((Object)(object)networkManager != (Object)null) { if ((Object)(object)networkManager.ClientManager != (Object)null && networkManager.ClientManager.Objects != null) { ((ManagedObjects)networkManager.ClientManager.Objects).Spawned.TryGetValue(objectId, out value); } if ((Object)(object)value == (Object)null && networkManager.IsServer && (Object)(object)networkManager.ServerManager != (Object)null && networkManager.ServerManager.Objects != null) { ((ManagedObjects)networkManager.ServerManager.Objects).Spawned.TryGetValue(objectId, out value); } } if ((Object)(object)value != (Object)null) { ItemSpawner component = ((Component)value).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } } Plugin.Log.LogWarning((object)$"Disguise: spawner ObjectId {objectId} didn't resolve - trying nearest spawner."); if ((Object)(object)source != (Object)null) { Vector3 position = source.transform.position; ItemSpawner val = null; float num = 1f; ItemSpawner[] array = Object.FindObjectsOfType(); foreach (ItemSpawner val2 in array) { if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = ((Component)val2).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { val = val2; num = sqrMagnitude; } } } if ((Object)(object)val != (Object)null) { Plugin.Activity($"Disguise: nearest-spawner fallback chose '{((Object)val).name}' ({Mathf.Sqrt(num):0.00} m)."); return val; } } Plugin.Log.LogWarning((object)"Disguise: spawner resolution failed entirely - plain prop fallback."); return null; } catch (Exception e) { Plugin.LogOnce("Disguise.ResolveSpawner", e); return null; } } private static bool BuildCloneSpawner(HiderSlot slot, PlayerHealth hiderPh, ItemSpawner spawner) { //IL_05e5: Unknown result type (might be due to invalid IL or missing references) //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_05f6: Unknown result type (might be due to invalid IL or missing references) //IL_05f8: Unknown result type (might be due to invalid IL or missing references) //IL_05fd: Unknown result type (might be due to invalid IL or missing references) //IL_06af: Unknown result type (might be due to invalid IL or missing references) //IL_06b1: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Unknown result type (might be due to invalid IL or missing references) //IL_06ba: Unknown result type (might be due to invalid IL or missing references) //IL_06e7: Unknown result type (might be due to invalid IL or missing references) //IL_0878: Unknown result type (might be due to invalid IL or missing references) //IL_087a: Unknown result type (might be due to invalid IL or missing references) //IL_0881: Unknown result type (might be due to invalid IL or missing references) //IL_0883: Unknown result type (might be due to invalid IL or missing references) //IL_088a: Unknown result type (might be due to invalid IL or missing references) //IL_0701: Unknown result type (might be due to invalid IL or missing references) //IL_0703: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_0748: Unknown result type (might be due to invalid IL or missing references) //IL_074d: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_075f: Unknown result type (might be due to invalid IL or missing references) //IL_0780: Unknown result type (might be due to invalid IL or missing references) //IL_079c: Unknown result type (might be due to invalid IL or missing references) //IL_07bb: Unknown result type (might be due to invalid IL or missing references) //IL_07d1: Unknown result type (might be due to invalid IL or missing references) //IL_07f0: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Unknown result type (might be due to invalid IL or missing references) //IL_0808: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_0495: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Unknown result type (might be due to invalid IL or missing references) //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_03eb: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03fa: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: Unknown result type (might be due to invalid IL or missing references) //IL_04c4: Unknown result type (might be due to invalid IL or missing references) //IL_04e7: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_04fa: Unknown result type (might be due to invalid IL or missing references) //IL_050b: Unknown result type (might be due to invalid IL or missing references) //IL_0518: Unknown result type (might be due to invalid IL or missing references) //IL_0524: Unknown result type (might be due to invalid IL or missing references) //IL_052f: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_0546: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Expected O, but got Unknown //IL_057f: Unknown result type (might be due to invalid IL or missing references) //IL_058d: Unknown result type (might be due to invalid IL or missing references) try { Transform transform = ((Component)spawner).transform; List list = new List(8); MeshRenderer[] componentsInChildren = ((Component)transform).GetComponentsInChildren(false); foreach (MeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && ((Renderer)val).enabled) { GameObject gameObject = ((Component)val).gameObject; MeshFilter component = gameObject.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh == (Object)null) && !((Object)(object)gameObject.GetComponentInParent() != (Object)null) && !AnyAncestorOnLayer(gameObject.transform, transform, 7, 8)) { list.Add(val); } } } if (list.Count == 0) { Plugin.Log.LogWarning((object)("Disguise: spawner '" + ((Object)spawner).name + "' has no usable pedestal renderer.")); return false; } PropLighting.LogSceneInventoryOnce(); Bounds bounds = ((Renderer)list[0]).bounds; for (int j = 1; j < list.Count; j++) { ((Bounds)(ref bounds)).Encapsulate(((Renderer)list[j]).bounds); } float num = ComputeSpawnerBurial(transform, bounds, ((Component)hiderPh).transform); if (num > 0.01f) { Plugin.Activity($"Disguise: spawner burial for '{((Object)spawner).name}' = {num:0.00}m"); } Transform transform2 = ((Component)hiderPh).transform; GameObject val2 = (slot.Clone = new GameObject("PropHunt_Disguise")); val2.transform.SetParent(transform2, false); Vector3 lossyScale = transform2.lossyScale; Vector3 lossyScale2 = transform.lossyScale; val2.transform.localScale = new Vector3(SafeDiv(lossyScale2.x, lossyScale.x), SafeDiv(lossyScale2.y, lossyScale.y), SafeDiv(lossyScale2.z, lossyScale.z)); Quaternion val3 = YawOnly(transform2.rotation); Quaternion val4 = Quaternion.Inverse(val3) * transform.rotation; val2.transform.rotation = val3 * val4; val2.transform.position = transform2.position; List list2 = new List(list.Count); MeshRenderer val5 = null; float num2 = -1f; Bounds localBase = default(Bounds); bool flag = false; Matrix4x4 worldToLocalMatrix = transform.worldToLocalMatrix; Vector3 lossyScale3 = val2.transform.lossyScale; Bounds bounds2; foreach (MeshRenderer item in list) { if (!TryRecoverRenderMesh(((Component)item).gameObject, out var mesh, out var materials, out var _, out var ownsMesh, out var fail)) { Plugin.Activity("Disguise: spawner part '" + ((Object)((Component)item).gameObject).name + "' skipped - " + fail); continue; } if (ownsMesh) { slot.OwnedMeshes.Add(mesh); } Transform transform3 = ((Component)item).transform; GameObject val6 = new GameObject("PropHunt_Part") { layer = ((Component)item).gameObject.layer }; val6.transform.SetParent(val2.transform, false); val6.transform.localPosition = ((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(transform3.position); val6.transform.localRotation = Quaternion.Inverse(transform.rotation) * transform3.rotation; Vector3 lossyScale4 = transform3.lossyScale; val6.transform.localScale = new Vector3(SafeDiv(lossyScale4.x, lossyScale3.x), SafeDiv(lossyScale4.y, lossyScale3.y), SafeDiv(lossyScale4.z, lossyScale3.z)); val6.AddComponent().sharedMesh = mesh; MeshRenderer val7 = val6.AddComponent(); ((Renderer)val7).sharedMaterials = materials; ((Renderer)val7).shadowCastingMode = ((Renderer)item).shadowCastingMode; ((Renderer)val7).receiveShadows = ((Renderer)item).receiveShadows; list2.Add(val7); Bounds val8 = TransformBounds(worldToLocalMatrix * transform3.localToWorldMatrix, mesh.bounds); if (!flag) { localBase = val8; flag = true; } else { ((Bounds)(ref localBase)).Encapsulate(val8); } bounds2 = ((Renderer)item).bounds; Vector3 size = ((Bounds)(ref bounds2)).size; float num3 = size.x * size.y * size.z; if (num3 > num2) { num2 = num3; val5 = val7; } } if (list2.Count == 0) { Plugin.Log.LogWarning((object)("Disguise: every pedestal part of '" + ((Object)spawner).name + "' failed mesh recovery.")); DestroyClone(slot); return false; } CopySpawnerLights(val2, transform, worldToLocalMatrix); CharacterController component2 = ((Component)hiderPh).GetComponent(); float y; if (!((Object)(object)component2 != (Object)null)) { y = transform2.position.y; } else { bounds2 = ((Collider)component2).bounds; y = ((Bounds)(ref bounds2)).min.y; } float num4 = y; Bounds bounds3 = ((Renderer)list2[0]).bounds; for (int k = 1; k < list2.Count; k++) { ((Bounds)(ref bounds3)).Encapsulate(((Renderer)list2[k]).bounds); } Transform transform4 = val2.transform; transform4.position += new Vector3(transform2.position.x - ((Bounds)(ref bounds3)).center.x, num4 - num - ((Bounds)(ref bounds3)).min.y, transform2.position.z - ((Bounds)(ref bounds3)).center.z); bool flag2 = false; try { GameObject val9 = new GameObject("PropHunt_Hitbox"); val9.layer = 11; val9.transform.SetParent(val2.transform, false); BoxCollider val10 = val9.AddComponent(); ((Collider)val10).isTrigger = false; val10.center = ((Bounds)(ref localBase)).center; val10.size = ((Bounds)(ref localBase)).size; if ((Object)(object)component2 != (Object)null) { Physics.IgnoreCollision((Collider)(object)val10, (Collider)(object)component2, true); } if (HitboxResolvesHider(val9, hiderPh)) { flag2 = true; } else { Plugin.Log.LogWarning((object)"Disguise: spawner base hitbox wouldn't resolve the hider - keeping the humanoid hitbox."); Object.Destroy((Object)(object)val9); } } catch (Exception e) { Plugin.LogOnce("Disguise.SpawnerHitbox", e); } AddSpawnerWalkColliders(val2, transform, localBase, component2); Transform dummyT = null; Vector3 bobBase = Vector3.zero; Vector3 bobOffset = Vector3.zero; List list3 = new List(list2); bool flag3 = true; try { ItemBehaviour val11 = null; ItemBehaviour[] componentsInChildren2 = ((Component)transform).GetComponentsInChildren(false); foreach (ItemBehaviour val12 in componentsInChildren2) { if ((Object)(object)val12 != (Object)null && (Object)(object)val12.rootObject == (Object)null && !val12.isTaken) { val11 = val12; break; } } if ((Object)(object)val11 != (Object)null) { flag3 = BuildWeaponDummy(slot, val2, transform, val11, hiderPh, component2, list3, out dummyT, out bobBase, out bobOffset); } } catch (Exception e2) { Plugin.LogOnce("Disguise.WeaponDummy", e2); dummyT = null; } slot.CloneHitboxOk = flag2 && flag3; DisguiseFollower disguiseFollower = val2.AddComponent(); disguiseFollower.Root = transform2; disguiseFollower.DeltaRot = val4; disguiseFollower.SrcLossyScale = lossyScale2; disguiseFollower.Cc = component2; disguiseFollower.GraphicsT = ResolveGraphicsAnchor(hiderPh); disguiseFollower.CloneMr = val5; disguiseFollower.GroundSlack = num; if (ComputeWallMount(transform, bounds, transform2, out var backLocal, out var gap, out var halfExtent)) { disguiseFollower.WallMounted = true; disguiseFollower.WallBackLocal = backLocal; disguiseFollower.WallGap = gap; disguiseFollower.WallHalfExtent = halfExtent; } disguiseFollower.BoundsRenderers = list2.ToArray(); list3.Remove(val5); disguiseFollower.ExtraLitRenderers = list3.ToArray(); try { Bounds bounds4 = ((Renderer)list2[0]).bounds; for (int l = 1; l < list2.Count; l++) { ((Bounds)(ref bounds4)).Encapsulate(((Renderer)list2[l]).bounds); } float num5 = num4; float num6 = Mathf.Max(((Bounds)(ref bounds4)).min.y, num5); float num7 = Mathf.Max(0.5f * (num6 + ((Bounds)(ref bounds4)).max.y), num5 + 0.15f); num7 = Mathf.Min(num7, ((Bounds)(ref bounds4)).max.y); Transform transform5 = new GameObject("PropHunt_ProbeAnchor").transform; transform5.SetParent(val2.transform, false); transform5.position = new Vector3(transform2.position.x, num7, transform2.position.z); ((Renderer)val5).probeAnchor = transform5; for (int m = 0; m < list3.Count; m++) { if ((Object)(object)list3[m] != (Object)null) { ((Renderer)list3[m]).probeAnchor = transform5; } } disguiseFollower.ProbeAnchorT = transform5; } catch (Exception e3) { Plugin.LogOnce("Disguise.ProbeAnchor", e3); } disguiseFollower.DummyT = dummyT; disguiseFollower.DummyBobBase = bobBase; disguiseFollower.DummyBobOffset = bobOffset; disguiseFollower.SetManualYaw(disguiseFollower.SeedYaw = ((Quaternion)(ref val3)).eulerAngles.y); disguiseFollower.SlotPlayerId = slot.PlayerId; if (slot.IsLocal) { disguiseFollower.IsLocalHider = true; disguiseFollower.Fpc = ((Component)hiderPh).GetComponent(); } Plugin.Activity($"Disguise: spawner composite built ({list2.Count} parts, " + $"dummy={(Object)(object)dummyT != (Object)null}, burial {num:0.00}m)."); return true; } catch (Exception e4) { Plugin.LogOnce("Disguise.BuildSpawner", e4); DestroyClone(slot); return false; } } private static bool BuildWeaponDummy(HiderSlot slot, GameObject clone, Transform spawnerT, ItemBehaviour sitting, PlayerHealth hiderPh, CharacterController cc, List extraLit, out Transform dummyT, out Vector3 bobBase, out Vector3 bobOffset) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_0098: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Expected O, but got Unknown //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: 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_01f6: 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_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) dummyT = null; Transform transform = ((Component)sitting).transform; bobBase = spawnerT.InverseTransformPoint(spawnerT.position + Vector3.up * 0.5f); try { if (Reflect.ItemBobBaseField != null && (Object)(object)transform.parent == (Object)(object)spawnerT) { bobBase = (Vector3)Reflect.ItemBobBaseField.GetValue(sitting); } } catch (Exception e) { Plugin.LogOnce("Disguise.BobBase", e); } bobOffset = spawnerT.up * 0.5f; GameObject val = new GameObject("PropHunt_WeaponDummy"); Bounds val2 = default(Bounds); try { val.transform.SetParent(clone.transform, false); val.transform.localPosition = bobBase; val.transform.localRotation = Quaternion.Inverse(spawnerT.rotation) * transform.rotation; val.transform.localScale = transform.localScale; Matrix4x4 worldToLocalMatrix = transform.worldToLocalMatrix; Vector3 lossyScale = val.transform.lossyScale; bool flag = false; int num = 0; MeshRenderer[] componentsInChildren = ((Component)transform).GetComponentsInChildren(false); foreach (MeshRenderer val3 in componentsInChildren) { if ((Object)(object)val3 == (Object)null || !((Renderer)val3).enabled) { continue; } GameObject gameObject = ((Component)val3).gameObject; if (gameObject.tag == "vfx") { continue; } MeshFilter component = gameObject.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh == (Object)null)) { GameObject val4 = new GameObject("PropHunt_Part"); val4.transform.SetParent(val.transform, false); val4.transform.localPosition = ((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(((Component)val3).transform.position); val4.transform.localRotation = Quaternion.Inverse(transform.rotation) * ((Component)val3).transform.rotation; Vector3 lossyScale2 = ((Component)val3).transform.lossyScale; val4.transform.localScale = new Vector3(SafeDiv(lossyScale2.x, lossyScale.x), SafeDiv(lossyScale2.y, lossyScale.y), SafeDiv(lossyScale2.z, lossyScale.z)); val4.AddComponent().sharedMesh = component.sharedMesh; MeshRenderer val5 = val4.AddComponent(); ((Renderer)val5).sharedMaterials = CloneDummyMaterials(slot, ((Renderer)val3).sharedMaterials); ((Renderer)val5).shadowCastingMode = ((Renderer)val3).shadowCastingMode; ((Renderer)val5).receiveShadows = ((Renderer)val3).receiveShadows; extraLit.Add(val5); Bounds val6 = TransformBounds(worldToLocalMatrix * ((Component)val3).transform.localToWorldMatrix, component.sharedMesh.bounds); if (!flag) { val2 = val6; flag = true; } else { ((Bounds)(ref val2)).Encapsulate(val6); } num++; } } if (num == 0) { Object.Destroy((Object)(object)val); Plugin.Activity("Disguise: sitting weapon '" + ((Object)sitting).name + "' had no copyable renderer - bare pedestal."); return true; } dummyT = val.transform; Plugin.Activity($"Disguise: weapon dummy '{((Object)sitting).name}' copied ({num} parts)."); } catch (Exception e2) { Plugin.LogOnce("Disguise.WeaponDummyCopy", e2); dummyT = null; try { Object.Destroy((Object)(object)val); } catch { } return true; } try { GameObject val7 = new GameObject("PropHunt_Hitbox"); val7.layer = 11; val7.transform.SetParent(val.transform, false); BoxCollider val8 = val7.AddComponent(); ((Collider)val8).isTrigger = false; val8.center = ((Bounds)(ref val2)).center; val8.size = ((Bounds)(ref val2)).size; if ((Object)(object)cc != (Object)null) { Physics.IgnoreCollision((Collider)(object)val8, (Collider)(object)cc, true); } if (HitboxResolvesHider(val7, hiderPh)) { return true; } Plugin.Log.LogWarning((object)"Disguise: weapon-dummy hitbox wouldn't resolve the hider - keeping the humanoid hitbox."); Object.Destroy((Object)(object)val7); return false; } catch (Exception e3) { Plugin.LogOnce("Disguise.WeaponHitbox", e3); return false; } } private static Material[] CloneDummyMaterials(HiderSlot slot, Material[] src) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if (src == null) { return null; } Material[] array = (Material[])(object)new Material[src.Length]; for (int i = 0; i < src.Length; i++) { Material val = src[i]; if ((Object)(object)val == (Object)null) { continue; } try { Material val2 = new Material(val); slot.OwnedMaterials.Add(val2); if (val2.HasProperty("_OutlineWidth")) { val2.SetFloat("_OutlineWidth", 0f); } array[i] = val2; } catch (Exception e) { Plugin.LogOnce("Disguise.DummyMaterials", e); array[i] = val; } } return array; } private static bool AnyAncestorOnLayer(Transform t, Transform stopAt, int layerA, int layerB) { Transform val = t; while ((Object)(object)val != (Object)null) { int layer = ((Component)val).gameObject.layer; if (layer == layerA || layer == layerB) { return true; } if ((Object)(object)val == (Object)(object)stopAt) { break; } val = val.parent; } return false; } private static void CopySpawnerLights(GameObject clone, Transform spawnerT, Matrix4x4 w2l) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) int num = 0; try { Light[] componentsInChildren = ((Component)spawnerT).GetComponentsInChildren(false); foreach (Light val in componentsInChildren) { if ((Object)(object)val == (Object)null || !((Behaviour)val).enabled) { continue; } GameObject gameObject = ((Component)val).gameObject; if ((Object)(object)gameObject.GetComponentInParent() != (Object)null || AnyAncestorOnLayer(gameObject.transform, spawnerT, 7, 8)) { continue; } GameObject val2 = null; try { val2 = new GameObject("PropHunt_SpawnerLight"); val2.transform.SetParent(clone.transform, false); Transform transform = ((Component)val).transform; val2.transform.localPosition = ((Matrix4x4)(ref w2l)).MultiplyPoint3x4(transform.position); val2.transform.localRotation = Quaternion.Inverse(spawnerT.rotation) * transform.rotation; Light obj = val2.AddComponent(); obj.type = val.type; obj.color = val.color; obj.colorTemperature = val.colorTemperature; obj.useColorTemperature = val.useColorTemperature; obj.intensity = val.intensity; obj.bounceIntensity = val.bounceIntensity; obj.range = val.range; obj.spotAngle = val.spotAngle; obj.innerSpotAngle = val.innerSpotAngle; obj.shadows = val.shadows; obj.shadowStrength = val.shadowStrength; obj.shadowBias = val.shadowBias; obj.shadowNormalBias = val.shadowNormalBias; obj.shadowNearPlane = val.shadowNearPlane; obj.renderMode = val.renderMode; obj.cullingMask = val.cullingMask; obj.renderingLayerMask = val.renderingLayerMask; obj.cookie = val.cookie; obj.cookieSize = val.cookieSize; obj.bakingOutput = val.bakingOutput; num++; } catch (Exception e) { Plugin.LogOnce("Disguise.SpawnerLight", e); if ((Object)(object)val2 != (Object)null) { try { Object.Destroy((Object)(object)val2); } catch { } } } } } catch (Exception e2) { Plugin.LogOnce("Disguise.SpawnerLights", e2); } if (num > 0) { Plugin.Activity($"Disguise: spawner lights copied ({num})."); } } private static void AddSpawnerWalkColliders(GameObject clone, Transform spawnerT, Bounds localBase, CharacterController hiderCc) { //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) try { if (!Plugin.CfgSolidProps.Value) { return; } Matrix4x4 worldToLocalMatrix = spawnerT.worldToLocalMatrix; Vector3 lossyScale = clone.transform.lossyScale; int num = 0; Collider[] componentsInChildren = ((Component)spawnerT).GetComponentsInChildren(false); foreach (Collider val in componentsInChildren) { if ((Object)(object)val == (Object)null || !val.enabled || val.isTrigger) { continue; } GameObject gameObject = ((Component)val).gameObject; if ((Object)(object)gameObject.GetComponentInParent() != (Object)null || AnyAncestorOnLayer(gameObject.transform, spawnerT, 7, 8)) { continue; } GameObject val2 = null; try { val2 = new GameObject("PropHunt_WalkCollider"); val2.layer = 14; val2.transform.SetParent(clone.transform, false); Transform transform = ((Component)val).transform; val2.transform.localPosition = ((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(transform.position); val2.transform.localRotation = Quaternion.Inverse(spawnerT.rotation) * transform.rotation; Vector3 lossyScale2 = transform.lossyScale; val2.transform.localScale = new Vector3(SafeDiv(lossyScale2.x, lossyScale.x), SafeDiv(lossyScale2.y, lossyScale.y), SafeDiv(lossyScale2.z, lossyScale.z)); Collider val3 = null; BoxCollider val4 = (BoxCollider)(object)((val is BoxCollider) ? val : null); if (val4 != null) { BoxCollider obj = val2.AddComponent(); obj.center = val4.center; obj.size = val4.size; val3 = (Collider)(object)obj; } else { SphereCollider val5 = (SphereCollider)(object)((val is SphereCollider) ? val : null); if (val5 != null) { SphereCollider obj2 = val2.AddComponent(); obj2.center = val5.center; obj2.radius = val5.radius; val3 = (Collider)(object)obj2; } else { CapsuleCollider val6 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null); if (val6 != null) { CapsuleCollider obj3 = val2.AddComponent(); obj3.center = val6.center; obj3.radius = val6.radius; obj3.height = val6.height; obj3.direction = val6.direction; val3 = (Collider)(object)obj3; } else { MeshCollider val7 = (MeshCollider)(object)((val is MeshCollider) ? val : null); if (val7 != null && (Object)(object)val7.sharedMesh != (Object)null) { MeshCollider obj4 = val2.AddComponent(); obj4.sharedMesh = val7.sharedMesh; obj4.convex = val7.convex; val3 = (Collider)(object)obj4; } } } } if ((Object)(object)val3 == (Object)null) { Object.Destroy((Object)(object)val2); continue; } val3.isTrigger = false; val3.sharedMaterial = val.sharedMaterial; if ((Object)(object)hiderCc != (Object)null) { Physics.IgnoreCollision(val3, (Collider)(object)hiderCc, true); } num++; } catch (Exception e) { Plugin.LogOnce("Disguise.WalkColliderCopy", e); if ((Object)(object)val2 != (Object)null) { try { Object.Destroy((Object)(object)val2); } catch { } } } } if (num > 0) { Plugin.Activity($"Disguise: spawner walk colliders: copied {num}."); return; } Plugin.Activity("Disguise: spawner walk colliders: none on source - box fallback."); AddWalkBox(clone, ((Bounds)(ref localBase)).center, ((Bounds)(ref localBase)).size, hiderCc); } catch (Exception e2) { Plugin.LogOnce("Disguise.SpawnerWalkCols", e2); AddWalkBox(clone, ((Bounds)(ref localBase)).center, ((Bounds)(ref localBase)).size, hiderCc); } } private static float ComputeSpawnerBurial(Transform spawnerT, Bounds baseWorld, Transform hiderRoot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = new Vector3(((Bounds)(ref baseWorld)).center.x, ((Bounds)(ref baseWorld)).max.y + 0.25f, ((Bounds)(ref baseWorld)).center.z); float num = ((Bounds)(ref baseWorld)).size.y + 1.75f; RaycastHit[] array = Physics.RaycastAll(val, Vector3.down, num, PickMask, (QueryTriggerInteraction)1); float num2 = float.MaxValue; float num3 = 0f; bool flag = false; RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val2 = array2[i]; Collider collider = ((RaycastHit)(ref val2)).collider; if (!((Object)(object)collider == (Object)null) && !SharesLineage(((Component)collider).transform, spawnerT) && (!((Object)(object)hiderRoot != (Object)null) || !((Component)collider).transform.IsChildOf(hiderRoot)) && ((RaycastHit)(ref val2)).distance < num2) { num2 = ((RaycastHit)(ref val2)).distance; num3 = ((RaycastHit)(ref val2)).point.y; flag = true; } } if (!flag) { return 0f; } float num4 = num3 - ((Bounds)(ref baseWorld)).min.y; if (num4 < 0.005f) { return 0f; } return Mathf.Min(num4, 0.95f * ((Bounds)(ref baseWorld)).size.y); } catch (Exception e) { Plugin.LogOnce("Disguise.SpawnerBurial", e); return 0f; } } private static float ComputeGroundSlack(GameObject source, MeshRenderer srcMr, Transform hiderRoot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) try { Bounds bounds = ((Renderer)srcMr).bounds; RaycastHit[] array = Physics.RaycastAll(new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y + 1f, ((Bounds)(ref bounds)).center.z), Vector3.down, 1.5f, PickMask, (QueryTriggerInteraction)1); float num = float.MaxValue; float num2 = 0f; bool flag = false; RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val = array2[i]; Collider collider = ((RaycastHit)(ref val)).collider; if (!((Object)(object)collider == (Object)null) && !SharesLineage(((Component)collider).transform, source.transform) && (!((Object)(object)hiderRoot != (Object)null) || !((Component)collider).transform.IsChildOf(hiderRoot)) && ((RaycastHit)(ref val)).distance < num) { num = ((RaycastHit)(ref val)).distance; num2 = ((RaycastHit)(ref val)).point.y; flag = true; } } if (!flag) { Plugin.Activity("Disguise: ground slack for '" + ((Object)source).name + "' = 0 (no qualifying hit)."); return 0f; } float num3 = num2 - ((Bounds)(ref bounds)).min.y; if (num3 < 0.005f) { Plugin.Activity("Disguise: ground slack for '" + ((Object)source).name + "' = 0 (AABB bottom at/below the surface)."); return 0f; } return Mathf.Min(num3, 0.6f); } catch (Exception e) { Plugin.LogOnce("Disguise.GroundSlack", e); return 0f; } } private static bool ComputeWallMount(Transform sourceT, Bounds srcWorld, Transform hiderRoot, out Vector3 backLocal, out float gap, out float halfExtent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) backLocal = Vector3.zero; gap = 0f; halfExtent = 0f; try { Vector3 center = ((Bounds)(ref srcWorld)).center; Vector3 extents = ((Bounds)(ref srcWorld)).extents; bool flag = false; for (int i = 0; i < 4; i++) { Vector3 val = (Vector3)(i switch { 0 => sourceT.right, 1 => -sourceT.right, 2 => sourceT.forward, _ => -sourceT.forward, }); val.y = 0f; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.5f) { continue; } val /= magnitude; float num = Mathf.Abs(val.x) * extents.x + Mathf.Abs(val.z) * extents.z; RaycastHit[] array = Physics.RaycastAll(center, val, num + 0.3f, PickMask, (QueryTriggerInteraction)1); float num2 = float.MaxValue; float num3 = 1f; RaycastHit[] array2 = array; for (int j = 0; j < array2.Length; j++) { RaycastHit val2 = array2[j]; Collider collider = ((RaycastHit)(ref val2)).collider; if (!((Object)(object)collider == (Object)null) && !SharesLineage(((Component)collider).transform, sourceT) && (!((Object)(object)hiderRoot != (Object)null) || !((Component)collider).transform.IsChildOf(hiderRoot)) && !IsUnderClone(((Component)collider).transform) && ((RaycastHit)(ref val2)).distance < num2) { num2 = ((RaycastHit)(ref val2)).distance; num3 = ((RaycastHit)(ref val2)).normal.y; } } if (num2 == float.MaxValue || Mathf.Abs(num3) >= 0.35f) { continue; } float num4 = num2 - num; if (!(num4 > 0.1f)) { num4 = Mathf.Max(0f, num4); if (!flag || num4 < gap) { flag = true; gap = num4; halfExtent = num; backLocal = Quaternion.Inverse(sourceT.rotation) * val; } } } if (flag) { Plugin.Activity($"Disguise: wall-mount: back={backLocal}, gap={gap:0.00}m"); } return flag; } catch (Exception e) { Plugin.LogOnce("Disguise.WallMount", e); return false; } } private static bool SharesLineage(Transform a, Transform b) { Transform val = a; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)b) { return true; } val = val.parent; } Transform val2 = b; while ((Object)(object)val2 != (Object)null) { if ((Object)(object)val2 == (Object)(object)a) { return true; } val2 = val2.parent; } return false; } internal static float SafeDiv(float a, float b) { if (!(Mathf.Abs(b) < 0.0001f)) { return a / b; } return a; } internal static Quaternion YawOnly(Quaternion q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Vector3 val = q * Vector3.forward; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { return Quaternion.identity; } return Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); } private static Transform ResolveGraphicsAnchor(PlayerHealth hiderPh) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = (((Object)(object)hiderPh != (Object)null) ? hiderPh.graphics : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Disguise: hider graphics unresolved - ground-anchor fallback keeps the local CC skinWidth."); return null; } Transform transform = val.transform; float y = transform.localPosition.y; if (y < -0.5f || y >= 0f) { Plugin.Log.LogWarning((object)$"Disguise: graphics localPosition.y = {y:0.###} outside the expected hover band - ground-anchor fallback keeps the local CC skinWidth."); return null; } Plugin.Activity($"Disguise: ground anchor uses graphics hover {y:0.###}m."); return transform; } catch (Exception e) { Plugin.LogOnce("Disguise.GraphicsAnchor", e); return null; } } private static void HideGraphics(HiderSlot slot, PlayerHealth hiderPh) { GameObject graphics = hiderPh.graphics; if ((Object)(object)graphics == (Object)null) { return; } Renderer[] componentsInChildren = graphics.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled) { val.enabled = false; slot.HiddenRenderers.Add(val); } } slot.HiddenGraphicsOwner = hiderPh; } private static void HideHitboxColliders(HiderSlot slot, PlayerHealth hiderPh) { GameObject graphics = hiderPh.graphics; if ((Object)(object)graphics == (Object)null) { return; } Collider[] componentsInChildren = graphics.GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled && !val.isTrigger && ((Component)val).gameObject.layer == 11) { val.enabled = false; slot.HiddenColliders.Add(val); } } } internal static void ApplyRotate(int hiderPlayerId, float yawDeltaDegrees) { try { if (float.IsNaN(yawDeltaDegrees) || float.IsInfinity(yawDeltaDegrees)) { return; } if (!_slots.TryGetValue(hiderPlayerId, out var value) || (Object)(object)value.Clone == (Object)null) { Plugin.Activity($"Disguise: op-6 for unknown/cloneless prop {hiderPlayerId} - ignored."); return; } DisguiseFollower component = value.Clone.GetComponent(); if ((Object)(object)component != (Object)null) { component.SetManualYawDelta(yawDeltaDegrees); } } catch (Exception e) { Plugin.LogOnce("Disguise.ApplyRotate", e); } } internal static void DeferCleanupAll(float extraSeconds = 0f) { for (int i = 0; i < _slotList.Count; i++) { DeferCleanupSlot(_slotList[i], freezeFollower: false, extraSeconds); } Plugin.Activity("Disguise: cleanup deferred (resolved take end - kill cams show the props)."); } internal static void DeferCleanupSlot(HiderSlot slot, bool freezeFollower = false, float extraSeconds = 0f) { if (slot == null || slot.CleanupPending) { return; } slot.CleanupPending = true; slot.CleanupPendingSince = Time.time; slot.CleanupExtraSeconds = extraSeconds; if (freezeFollower) { try { if ((Object)(object)slot.Clone != (Object)null) { DisguiseFollower component = slot.Clone.GetComponent(); if ((Object)(object)component != (Object)null) { component.PositionFrozen = true; } } } catch (Exception e) { Plugin.LogOnce("Disguise.DeferFreeze", e); } } try { if (!((Object)(object)slot.Clone != (Object)null)) { return; } Collider[] componentsInChildren = slot.Clone.GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if ((Object)(object)val != (Object)null) { val.enabled = false; } } } catch (Exception e2) { Plugin.LogOnce("Disguise.DeferColliders", e2); } } internal static void TickPendingCleanup() { for (int num = _slotList.Count - 1; num >= 0; num--) { HiderSlot hiderSlot = _slotList[num]; if (hiderSlot.CleanupPending && !(Time.time - hiderSlot.CleanupPendingSince < 10f + hiderSlot.CleanupExtraSeconds)) { Plugin.Activity($"Disguise: deferred cleanup watchdog fired for prop {hiderSlot.PlayerId}."); CleanupSlot(hiderSlot); } } } internal static void Cleanup() { for (int num = _slotList.Count - 1; num >= 0; num--) { CleanupSlot(_slotList[num]); } } internal static void CleanupSlot(HiderSlot slot) { if (slot != null) { DestroyClone(slot); RestoreGraphics(slot); RemoveSlot(slot); } } internal static void CleanupSlot(int playerId) { if (_slots.TryGetValue(playerId, out var value)) { CleanupSlot(value); } } private static void DestroyClone(HiderSlot slot) { if (slot.IsLocal) { DisguiseFollower.RestoreThirdPersonCamera(); } if ((Object)(object)slot.Clone != (Object)null) { try { Object.Destroy((Object)(object)slot.Clone); } catch { } } slot.Clone = null; slot.CloneHitboxOk = false; foreach (Mesh ownedMesh in slot.OwnedMeshes) { if ((Object)(object)ownedMesh != (Object)null) { try { Object.Destroy((Object)(object)ownedMesh); } catch { } } } slot.OwnedMeshes.Clear(); foreach (Material ownedMaterial in slot.OwnedMaterials) { if ((Object)(object)ownedMaterial != (Object)null) { try { Object.Destroy((Object)(object)ownedMaterial); } catch { } } } slot.OwnedMaterials.Clear(); } private static void RestoreGraphics(HiderSlot slot) { if (slot.HiddenRenderers.Count > 0 && HiddenOwnerAlive(slot)) { foreach (Renderer hiddenRenderer in slot.HiddenRenderers) { try { if ((Object)(object)hiddenRenderer != (Object)null) { hiddenRenderer.enabled = true; } } catch { } } } slot.HiddenRenderers.Clear(); RestoreHiddenColliders(slot); slot.HiddenGraphicsOwner = null; } private static void RestoreHiddenColliders(HiderSlot slot) { if (slot.HiddenColliders.Count > 0 && HiddenOwnerAlive(slot)) { foreach (Collider hiddenCollider in slot.HiddenColliders) { try { if ((Object)(object)hiddenCollider != (Object)null) { hiddenCollider.enabled = true; } } catch { } } } slot.HiddenColliders.Clear(); } private static bool HiddenOwnerAlive(HiderSlot slot) { try { return (Object)(object)slot.HiddenGraphicsOwner != (Object)null && slot.HiddenGraphicsOwner.health > 0f; } catch { return false; } } } internal class DisguiseFollower : MonoBehaviour { internal Transform Root; internal Quaternion DeltaRot = Quaternion.identity; internal Vector3 SrcLossyScale = Vector3.one; internal CharacterController Cc; internal MeshRenderer CloneMr; internal float GroundSlack; internal bool WallMounted; internal Vector3 WallBackLocal; internal float WallGap; internal float WallHalfExtent; private float _wallOffset; private const float WallFlushReach = 1f; private const float WallFlushMaxOffset = 1f; private const float WallFlushLerpRate = 8f; internal Transform GraphicsT; internal bool PositionFrozen; internal MeshRenderer[] BoundsRenderers; internal MeshRenderer[] ExtraLitRenderers; private bool _samplePointValid; private Vector3 _samplePoint; private const float MinSampleHeight = 0.15f; internal Transform ProbeAnchorT; internal Transform DummyT; internal Vector3 DummyBobBase; internal Vector3 DummyBobOffset; private float _dummyBobClock; private const float DummySpinDegPerSec = 20f; private const float DummyBobLegSeconds = 1.4f; private readonly PropLightSampler _lightSampler = new PropLightSampler(); private GroundHitInfo _groundHit; internal bool IsLocalHider; internal FirstPersonController Fpc; internal int SlotPlayerId = -1; internal float SeedYaw; private bool _manual; private float _manualYaw; private const float RotateSendInterval = 0.15f; private float _nextRotateSend; private bool _wasRotating; private bool _leanSuppressed; private static FieldInfo _isLeaningLeftField; private static FieldInfo _isLeaningRightField; private static bool _leanFieldsResolved; private const float MaxGroundSnap = 0.6f; private static Transform _tpCamT; private static Vector3 _tpCamOrigLocalPos; internal void SetManualYaw(float yawDegrees) { _manual = true; _manualYaw = yawDegrees; } internal void SetManualYawDelta(float deltaDegrees) { SetManualYaw(SeedYaw + deltaDegrees); } private void Update() { if (!IsLocalHider) { return; } try { TickRotateInput(); } catch (Exception e) { Plugin.LogOnce("Follower.RotateInput", e); } try { SuppressLean(); } catch (Exception e2) { Plugin.LogOnce("Follower.LeanSuppress", e2); } } private void SuppressLean() { FirstPersonController fpc = Fpc; if ((Object)(object)fpc == (Object)null) { return; } if (fpc.leanLeft != null) { fpc.leanLeft.Disable(); } if (fpc.leanRight != null) { fpc.leanRight.Disable(); } _leanSuppressed = true; if (!_leanFieldsResolved) { _leanFieldsResolved = true; _isLeaningLeftField = typeof(FirstPersonController).GetField("isLeaningLeft", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _isLeaningRightField = typeof(FirstPersonController).GetField("isLeaningRight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_isLeaningLeftField == null || _isLeaningRightField == null) { Plugin.Log.LogWarning((object)"Follower: isLeaningLeft/Right field(s) not found - a toggled lean may persist while disguised."); } } _isLeaningLeftField?.SetValue(fpc, false); _isLeaningRightField?.SetValue(fpc, false); } private void OnDestroy() { if (!IsLocalHider) { return; } if (!Disguise.SlotHasLiveClone(SlotPlayerId)) { RestoreThirdPersonCamera(); } if (!_leanSuppressed) { return; } try { FirstPersonController fpc = Fpc; if (!((Object)(object)fpc == (Object)null)) { if (fpc.leanLeft != null) { fpc.leanLeft.Enable(); } if (fpc.leanRight != null) { fpc.leanRight.Enable(); } } } catch (Exception e) { Plugin.LogOnce("Follower.LeanRestore", e); } } private void TickRotateInput() { //IL_0031: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) bool flag = false; PauseManager instance = PauseManager.Instance; if ((Object)(object)instance != (Object)null && (instance.pause || instance.chatting)) { flag = true; } float num = 0f; if (!flag) { if (Input.GetKey(Plugin.CfgRotateLeftKey.Value)) { num -= 1f; } if (Input.GetKey(Plugin.CfgRotateRightKey.Value)) { num += 1f; } } if (num != 0f) { if (!_manual) { _manual = true; Quaternion val; float y; if (!((Object)(object)Root != (Object)null)) { val = ((Component)this).transform.rotation; y = ((Quaternion)(ref val)).eulerAngles.y; } else { val = Disguise.YawOnly(Root.rotation); y = ((Quaternion)(ref val)).eulerAngles.y; } _manualYaw = y; } _manualYaw = Mathf.Repeat(_manualYaw + num * Mathf.Max(0f, Plugin.CfgRotateSpeed.Value) * Time.deltaTime, 360f); if (Time.time >= _nextRotateSend) { _nextRotateSend = Time.time + 0.15f; SendRotate(); } _wasRotating = true; } else if (_wasRotating) { _wasRotating = false; SendRotate(); } } private void SendRotate() { Net.ClientToServer(new PropHuntNet { Op = 6, F = _manualYaw - SeedYaw }); } private void LateUpdate() { //IL_005a: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0114: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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) if ((Object)(object)Root == (Object)null) { RestoreThirdPersonCamera(); Object.Destroy((Object)(object)((Component)this).gameObject); return; } ((Component)this).transform.rotation = (_manual ? (Quaternion.Euler(0f, _manualYaw, 0f) * DeltaRot) : (Disguise.YawOnly(Root.rotation) * DeltaRot)); Vector3 lossyScale = Root.lossyScale; ((Component)this).transform.localScale = new Vector3(Disguise.SafeDiv(SrcLossyScale.x, lossyScale.x), Disguise.SafeDiv(SrcLossyScale.y, lossyScale.y), Disguise.SafeDiv(SrcLossyScale.z, lossyScale.z)); try { TickPositionFollow(); } catch (Exception e) { Plugin.LogOnce("Follower.GroundFollow", e); } try { TickWeaponDummy(); } catch (Exception e2) { Plugin.LogOnce("Follower.WeaponDummy", e2); } try { _lightSampler.Tick(CloneMr, ExtraLitRenderers, _samplePointValid, _samplePoint, (BoundsRenderers != null) ? Root : null, Root, _groundHit); } catch (Exception e3) { Plugin.LogOnce("Follower.PropLighting", e3); } if (!IsLocalHider) { return; } try { TickThirdPersonCamera(); } catch (Exception e4) { Plugin.LogOnce("Follower.ThirdPerson", e4); } } private void TickPositionFollow() { //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: 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_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) if (PositionFrozen) { return; } _samplePointValid = false; MeshRenderer[] boundsRenderers = BoundsRenderers; Bounds b; if (boundsRenderers != null) { if (!TryUnionBounds(boundsRenderers, out b)) { return; } } else { MeshRenderer cloneMr = CloneMr; if ((Object)(object)cloneMr == (Object)null) { return; } b = ((Renderer)cloneMr).bounds; } Vector3 position = Root.position; float num = position.x - ((Bounds)(ref b)).center.x; float num2 = position.z - ((Bounds)(ref b)).center.z; float num3 = 0f; CharacterController cc = Cc; if ((Object)(object)cc != (Object)null) { Bounds bounds = ((Collider)cc).bounds; Transform graphicsT = GraphicsT; float num4 = (((Object)(object)graphicsT != (Object)null) ? graphicsT.localPosition.y : (0f - cc.skinWidth)); float num5 = ((Bounds)(ref bounds)).min.y + num4; if (Disguise.RaycastNearestIgnoringRoot(new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).min.y + 0.5f, ((Bounds)(ref bounds)).center.z), Vector3.down, 2f, Root, out var best)) { _groundHit.Valid = true; _groundHit.Col = ((RaycastHit)(ref best)).collider; _groundHit.Tri = ((RaycastHit)(ref best)).triangleIndex; _groundHit.Bary = ((RaycastHit)(ref best)).barycentricCoordinate; if (Mathf.Abs(((RaycastHit)(ref best)).point.y - num5) <= 0.6f) { num5 = ((RaycastHit)(ref best)).point.y; } } float num6 = num5; num5 -= GroundSlack; num3 = num5 - ((Bounds)(ref b)).min.y; if (boundsRenderers != null) { float num7 = ((Bounds)(ref b)).max.y + num3; float num8 = Mathf.Max(((Bounds)(ref b)).min.y + num3, num6); float num9 = Mathf.Max(0.5f * (num8 + num7), num6 + 0.15f); num9 = Mathf.Min(num9, num7); _samplePoint = new Vector3(position.x, num9, position.z); _samplePointValid = true; } } if (WallMounted) { if (!Plugin.CfgWallFlushProps.Value) { _wallOffset = 0f; } else { try { Vector3 val = ((Component)this).transform.rotation * WallBackLocal; val.y = 0f; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude > 0.0001f) { val /= Mathf.Sqrt(sqrMagnitude); Vector3 origin = new Vector3(((Bounds)(ref b)).center.x + num, ((Bounds)(ref b)).center.y + num3, ((Bounds)(ref b)).center.z + num2); float num10 = 0f; if (Disguise.RaycastNearestIgnoringRoot(origin, val, WallHalfExtent + 1f, Root, out var best2, ignoreAllClones: true) && Mathf.Abs(((RaycastHit)(ref best2)).normal.y) < 0.35f) { num10 = Mathf.Clamp(((RaycastHit)(ref best2)).distance - WallHalfExtent - WallGap, 0f, 1f); } _wallOffset = Mathf.Lerp(_wallOffset, num10, Mathf.Min(1f, 8f * Time.deltaTime)); num += val.x * _wallOffset; num2 += val.z * _wallOffset; if (_samplePointValid) { _samplePoint.x += val.x * _wallOffset; _samplePoint.z += val.z * _wallOffset; } } } catch (Exception e) { Plugin.LogOnce("Follower.WallFlush", e); } } } if (num != 0f || num3 != 0f || num2 != 0f) { Transform transform = ((Component)this).transform; transform.position += new Vector3(num, num3, num2); } if (_samplePointValid && (Object)(object)ProbeAnchorT != (Object)null) { ProbeAnchorT.position = _samplePoint; } } private static bool TryUnionBounds(MeshRenderer[] rs, out Bounds b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) b = default(Bounds); bool flag = false; foreach (MeshRenderer val in rs) { if (!((Object)(object)val == (Object)null)) { if (!flag) { b = ((Renderer)val).bounds; flag = true; } else { ((Bounds)(ref b)).Encapsulate(((Renderer)val).bounds); } } } return flag; } private void TickWeaponDummy() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Transform dummyT = DummyT; if (!((Object)(object)dummyT == (Object)null)) { _dummyBobClock += Time.deltaTime; float num = 0.5f * (1f - Mathf.Cos((float)Math.PI * _dummyBobClock / 1.4f)); dummyT.localPosition = DummyBobBase + DummyBobOffset * num; dummyT.Rotate(0f, 20f * Time.deltaTime, 0f); } } internal static void RestoreThirdPersonCamera() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_tpCamT != (Object)null) { _tpCamT.localPosition = _tpCamOrigLocalPos; } } catch (Exception e) { Plugin.LogOnce("Follower.CamRestore", e); } _tpCamT = null; } private void TickThirdPersonCamera() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) FirstPersonController fpc = Fpc; if ((Object)(object)fpc == (Object)null) { return; } Camera playerCamera = fpc.playerCamera; if ((Object)(object)playerCamera == (Object)null) { return; } Transform transform = ((Component)playerCamera).transform; if (_tpCamT != transform) { RestoreThirdPersonCamera(); _tpCamT = transform; _tpCamOrigLocalPos = transform.localPosition; } else { transform.localPosition = _tpCamOrigLocalPos; } if (!Plugin.CfgThirdPersonCamera.Value) { return; } float num = Mathf.Max(0f, Plugin.CfgThirdPersonDistance.Value); if (!(num <= 0f)) { Vector3 position = transform.position; Vector3 val = -(transform.rotation * Vector3.forward); float num2 = num; if (Disguise.SphereCastNearestIgnoringRoot(position, 0.25f, val, num, Root, out var distance)) { num2 = Mathf.Max(0f, distance - 0.1f); } transform.position = position + val * num2; } } } internal static class Hud { private struct ToastItem { public string Text; public float Until; } private static readonly List _toasts = new List(8); private const float ToastSeconds = 4f; private const int MaxToasts = 4; private static GUIStyle _bannerStyle; private static GUIStyle _subStyle; private static GUIStyle _bigStyle; private static GUIStyle _toastStyle; private static bool _stylesReady; private static Phase _cachedPhase = Phase.Idle; private static Role _cachedRole = Role.None; private static int _cachedSecs = -1; private static string _bannerLine = ""; private static string _subLine = ""; private static string _bigSecs = ""; internal static float TauntCueAt; private static int _tauntCueCachedSecs = -1; private static string _tauntCueLine = ""; private static GameObject _blindGo; private static Canvas _blindCanvas; private static bool _blindShown; private static FieldInfo _pmPauseMenuField; private static FieldInfo _pmChatBoxField; private static bool _pmFieldsResolved; internal static void Toast(string text) { if (!string.IsNullOrEmpty(text)) { if (_toasts.Count >= 4) { _toasts.RemoveAt(0); } _toasts.Add(new ToastItem { Text = text, Until = Time.time + 4f }); } } private static void EnsureStyles() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0032: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) if (!_stylesReady) { _bannerStyle = new GUIStyle(GUI.skin.label) { fontSize = 26, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _bannerStyle.normal.textColor = Color.white; _subStyle = new GUIStyle(GUI.skin.label) { fontSize = 16, alignment = (TextAnchor)4 }; _subStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f, 1f); _bigStyle = new GUIStyle(GUI.skin.label) { fontSize = 72, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _bigStyle.normal.textColor = Color.white; _toastStyle = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _toastStyle.normal.textColor = new Color(1f, 0.85f, 0.4f, 1f); _stylesReady = true; } } internal static void Draw() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) EnsureStyles(); Phase clientPhase = PropHuntManager.ClientPhase; Role localRole = PropHuntManager.LocalRole; if (clientPhase != Phase.Idle) { int secs = Mathf.Max(0, Mathf.CeilToInt(PropHuntManager.ClientPhaseEnd - Time.time)); RebuildBannerIfNeeded(clientPhase, localRole, secs); if (clientPhase == Phase.Hide && localRole == Role.Seeker && Plugin.CfgBlindOverlay.Value && !PauseUiOpen()) { GUI.Label(new Rect(0f, (float)Screen.height * 0.3f, (float)Screen.width, 100f), "THE PROPS ARE HIDING", _bannerStyle); GUI.Label(new Rect(0f, (float)Screen.height * 0.4f, (float)Screen.width, 120f), _bigSecs, _bigStyle); GUI.Label(new Rect(0f, (float)Screen.height * 0.55f, (float)Screen.width, 40f), "You'll be released when the hunt begins.", _subStyle); } else { GUI.Label(new Rect(0f, 12f, (float)Screen.width, 34f), _bannerLine, _bannerStyle); GUI.Label(new Rect(0f, 46f, (float)Screen.width, 24f), _subLine, _subStyle); } } float y = 84f; DrawTauntCue(ref y); DrawToasts(y); } private static void DrawTauntCue(ref float y) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (TauntCueAt <= 0f || PropHuntManager.ClientPhase != Phase.Hunt) { return; } float num = TauntCueAt - Time.time; if (!(num <= 0f)) { int num2 = Mathf.CeilToInt(num); if (num2 != _tauntCueCachedSecs) { _tauntCueCachedSecs = num2; _tauntCueLine = $"Taunt in {num2}s..."; } GUI.Label(new Rect(0f, y, (float)Screen.width, 24f), _tauntCueLine, _toastStyle); y += 24f; } } private static bool PauseUiOpen() { try { PauseManager instance = PauseManager.Instance; return (Object)(object)instance != (Object)null && (instance.pause || instance.chatting); } catch { return false; } } internal static void TickBlind() { try { if (PropHuntManager.ClientPhase != Phase.Hide || PropHuntManager.LocalRole != Role.Seeker || !Plugin.CfgBlindOverlay.Value) { _blindShown = false; if ((Object)(object)_blindCanvas != (Object)null && ((Behaviour)_blindCanvas).enabled) { ((Behaviour)_blindCanvas).enabled = false; } return; } if ((Object)(object)_blindGo == (Object)null) { CreateBlind(); if ((Object)(object)_blindGo == (Object)null) { return; } _blindShown = false; } if (!_blindShown) { _blindCanvas.sortingOrder = ComputeBlindSortingOrder(); _blindShown = true; } if (!((Behaviour)_blindCanvas).enabled) { ((Behaviour)_blindCanvas).enabled = true; } } catch (Exception e) { Plugin.LogOnce("Hud.TickBlind", e); } } private static void CreateBlind() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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) try { GameObject val = new GameObject("PropHunt_Blind"); Object.DontDestroyOnLoad((Object)(object)val); Canvas obj = val.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = -1; ((Behaviour)obj).enabled = false; GameObject val2 = new GameObject("Fill"); val2.transform.SetParent(val.transform, false); Image obj2 = val2.AddComponent(); ((Graphic)obj2).color = new Color(0.02f, 0.02f, 0.03f, 1f); ((Graphic)obj2).raycastTarget = false; RectTransform rectTransform = ((Graphic)obj2).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; _blindGo = val; _blindCanvas = obj; } catch (Exception e) { Plugin.LogOnce("Hud.CreateBlind", e); try { if ((Object)(object)_blindGo != (Object)null) { Object.Destroy((Object)(object)_blindGo); } } catch { } _blindGo = null; _blindCanvas = null; } } private static int ComputeBlindSortingOrder() { try { if (!_pmFieldsResolved) { _pmFieldsResolved = true; _pmPauseMenuField = typeof(PauseManager).GetField("pauseMenu", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _pmChatBoxField = typeof(PauseManager).GetField("ChatBox", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_pmPauseMenuField == null || _pmChatBoxField == null) { Plugin.Log.LogWarning((object)"Hud: PauseManager.pauseMenu/ChatBox field(s) not found - blind sorts at -1."); } } PauseManager instance = PauseManager.Instance; if ((Object)(object)instance == (Object)null) { return -1; } int min = int.MaxValue; MinCanvasOrder(_pmPauseMenuField, instance, ref min); MinCanvasOrder(_pmChatBoxField, instance, ref min); return (min == int.MaxValue) ? (-1) : (min - 1); } catch (Exception e) { Plugin.LogOnce("Hud.BlindSorting", e); return -1; } } private static void MinCanvasOrder(FieldInfo field, PauseManager pm, ref int min) { if (field == null) { return; } try { object? value = field.GetValue(pm); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null) { return; } Canvas val2 = val.GetComponentInParent(true); if (!((Object)(object)val2 == (Object)null)) { if (!val2.overrideSorting) { val2 = val2.rootCanvas; } if ((Object)(object)val2 != (Object)null && val2.sortingOrder < min) { min = val2.sortingOrder; } } } catch { } } private static void RebuildBannerIfNeeded(Phase phase, Role role, int secs) { //IL_006a: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (phase == _cachedPhase && role == _cachedRole && secs == _cachedSecs) { return; } _cachedPhase = phase; _cachedRole = role; _cachedSecs = secs; _bigSecs = secs.ToString(); switch (role) { case Role.Hider: if (phase == Phase.Hide) { _bannerLine = $"PROP - HIDE! {secs}s"; _subLine = $"Press {Plugin.CfgDisguiseKey.Value} while looking at a prop to disguise. " + $"{Plugin.CfgRotateLeftKey.Value}/{Plugin.CfgRotateRightKey.Value}: rotate."; } else { _bannerLine = $"PROP - SURVIVE! {secs}s"; _subLine = (Plugin.CfgAllowReDisguise.Value ? $"Hold still. {Plugin.CfgDisguiseKey.Value} re-disguises (cooldown)." : "Hold still."); } break; case Role.Seeker: _bannerLine = ((phase == Phase.Hide) ? $"SEEKER - WAIT... {secs}s" : $"SEEKER - HUNT! {secs}s"); _subLine = ((phase == Phase.Hide) ? "The props are hiding. You are frozen." : "Find the props and kill them. Missed shots hurt YOU."); break; default: _bannerLine = $"PROP HUNT {secs}s"; _subLine = ""; break; } } private static void DrawToasts(float y) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (_toasts.Count == 0) { return; } float time = Time.time; for (int num = _toasts.Count - 1; num >= 0; num--) { if (_toasts[num].Until <= time) { _toasts.RemoveAt(num); } } for (int i = 0; i < _toasts.Count; i++) { GUI.Label(new Rect(0f, y, (float)Screen.width, 24f), _toasts[i].Text, _toastStyle); y += 24f; } } } internal static class PropLighting { internal enum Regime { EngineProbes, Dim, AmbientNearBlack } internal struct CachedLight { internal Light Source; internal LightType Type; internal Vector3 Pos; internal Vector3 Forward; internal float Range; internal float Intensity; internal float SpotCos; } private const float RefreshInterval = 5f; private const float NearBlackDc = 0.01f; private const int MaxInventoryLights = 20; private static readonly List _lights = new List(32); private static float _nextRefresh; private static int _cacheSceneHandle = -1; private static string _cacheSceneName; private static bool _mapFallback; private static int _fallbackLoggedSceneHandle = -1; private static Regime _regime = Regime.Dim; private static SphericalHarmonicsL2 _baseSh; private static bool _baseShNearBlack; private static int _regimeLoggedSceneHandle = -1; private static Regime _loggedRegime; private static Vector3[] _probePositions; private static SphericalHarmonicsL2[] _probeSh; private static bool _probeCacheValid; private static int _probeCacheSceneHandle = -1; private static int _probeCacheCount = -1; private static string _probeCacheSceneName; private static int _probeCacheWarnedSceneHandle = -1; private static int _inventorySceneHandle = -1; internal static bool MapFallbackActive { get { EnsureFresh(); return _mapFallback; } } internal static Regime CurrentRegime { get { EnsureFresh(); return _regime; } } internal static SphericalHarmonicsL2 BaseSH { get { //IL_0005: Unknown result type (might be due to invalid IL or missing references) EnsureFresh(); return _baseSh; } } internal static List Lights { get { EnsureFresh(); return _lights; } } internal static bool ProbeCacheValid { get { EnsureFresh(); return _probeCacheValid; } } internal static Vector3[] ProbePositions => _probePositions; internal static SphericalHarmonicsL2[] ProbeShs => _probeSh; private static void EnsureFresh() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Invalid comparison between Unknown and I4 //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Invalid comparison between Unknown and I4 Scene activeScene = SceneManager.GetActiveScene(); int handle = ((Scene)(ref activeScene)).handle; if (handle == _cacheSceneHandle && Time.time < _nextRefresh) { return; } string name = ((Scene)(ref activeScene)).name; bool num = handle != _cacheSceneHandle || name != _cacheSceneName; _cacheSceneHandle = handle; _cacheSceneName = name; _nextRefresh = Time.time + 5f; if (num) { _baseSh = RenderSettings.ambientProbe; _baseShNearBlack = Mathf.Max(Mathf.Abs(((SphericalHarmonicsL2)(ref _baseSh))[0, 0]), Mathf.Max(Mathf.Abs(((SphericalHarmonicsL2)(ref _baseSh))[1, 0]), Mathf.Abs(((SphericalHarmonicsL2)(ref _baseSh))[2, 0]))) < 0.01f; _probeCacheWarnedSceneHandle = -1; _inventorySceneHandle = -1; _regimeLoggedSceneHandle = -1; _fallbackLoggedSceneHandle = -1; GroundLightmap.OnSceneChanged(); } LightProbes lightProbes = LightmapSettings.lightProbes; int num2 = (((Object)(object)lightProbes != (Object)null) ? lightProbes.count : 0); Regime regime = (_regime = ((num2 <= 0) ? ((!_baseShNearBlack) ? Regime.Dim : Regime.AmbientNearBlack) : Regime.EngineProbes)); if (regime == Regime.EngineProbes) { if (_probeCacheSceneHandle != handle || _probeCacheCount != num2 || _probeCacheSceneName != name) { _probeCacheSceneHandle = handle; _probeCacheCount = num2; _probeCacheSceneName = name; _probeCacheValid = false; _probePositions = null; _probeSh = null; try { Vector3[] positions = lightProbes.positions; SphericalHarmonicsL2[] bakedProbes = lightProbes.bakedProbes; if (positions == null || bakedProbes == null || positions.Length == 0 || positions.Length != bakedProbes.Length) { if (_probeCacheWarnedSceneHandle != handle) { _probeCacheWarnedSceneHandle = handle; Plugin.Log.LogWarning((object)("PropLighting: probe data unusable (positions " + ((positions == null) ? "null" : positions.Length.ToString()) + ", bakedProbes " + ((bakedProbes == null) ? "null" : bakedProbes.Length.ToString()) + ") - falling back to engine probe lighting.")); } } else { _probePositions = positions; _probeSh = bakedProbes; _probeCacheValid = true; } } catch (Exception e) { Plugin.LogOnce("PropLighting.ProbeCache", e); } } } else { _probeCacheValid = false; _probeCacheSceneHandle = -1; _probeCacheCount = -1; _probeCacheSceneName = null; } if (_regimeLoggedSceneHandle != handle || _loggedRegime != regime) { _regimeLoggedSceneHandle = handle; _loggedRegime = regime; switch (regime) { case Regime.EngineProbes: Plugin.Activity(_probeCacheValid ? ("PropLighting: probe-select mode (" + num2 + " probes).") : ("PropLighting: " + num2 + " light probes present but the probe cache is unusable - using engine probe lighting.")); break; case Regime.AmbientNearBlack: Plugin.Activity("PropLighting: ambient probe near-black - leaving engine lighting alone."); break; } } _lights.Clear(); Light[] array = Object.FindObjectsOfType(); foreach (Light val in array) { if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled && !(val.intensity <= 0f)) { LightType type = val.type; if (((int)type == 2 || (int)type == 0 || (int)type == 1) && (val.cullingMask & 1) != 0) { Transform transform = ((Component)val).transform; _lights.Add(new CachedLight { Source = val, Type = type, Pos = transform.position, Forward = transform.forward, Range = Mathf.Max(0.01f, val.range), Intensity = val.intensity, SpotCos = Mathf.Cos(0.5f * val.spotAngle * ((float)Math.PI / 180f)) }); } } } bool num3 = _lights.Count == 0; if (num3 && regime == Regime.Dim && _fallbackLoggedSceneHandle != handle) { _fallbackLoggedSceneHandle = handle; Plugin.Activity("PropLighting: no active scene lights on this map - nothing to test visibility against; the disguise keeps vanilla lighting here."); } _mapFallback = num3; } private static string RegimeInventoryLabel(Regime r) { switch (r) { case Regime.EngineProbes: if (!_probeCacheValid) { return "engine probes (probe cache unusable)"; } return "probe-select"; case Regime.AmbientNearBlack: return "ambient near-black, engine"; default: return "dim mode"; } } internal static void LogSceneInventoryOnce() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected I4, but got Unknown //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) try { Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).handle == _inventorySceneHandle) { return; } _inventorySceneHandle = ((Scene)(ref activeScene)).handle; StringBuilder stringBuilder = new StringBuilder(1024); stringBuilder.Append("PropLighting inventory for scene '").Append(((Scene)(ref activeScene)).name).Append("':\n"); LightmapData[] lightmaps = LightmapSettings.lightmaps; int num = 0; int num2 = ((lightmaps != null) ? lightmaps.Length : 0); for (int i = 0; i < num2; i++) { Texture2D val = ((lightmaps[i] != null) ? lightmaps[i].lightmapColor : null); if ((Object)(object)val != (Object)null && ((Texture)val).isReadable) { num++; } } stringBuilder.Append(" lightmaps: ").Append(num2).Append(" (CPU-readable color: ") .Append(num) .Append('/') .Append(num2) .Append(")\n"); stringBuilder.Append(" groundLM: ").Append(GroundLightmap.InventoryLine()).Append('\n'); LightProbes lightProbes = LightmapSettings.lightProbes; stringBuilder.Append(" lightProbes: ").Append(((Object)(object)lightProbes == (Object)null) ? "null" : lightProbes.count.ToString()).Append('\n'); stringBuilder.Append(" regime: ").Append(RegimeInventoryLabel(CurrentRegime)).Append('\n'); stringBuilder.Append(" ambientMode: ").Append(RenderSettings.ambientMode).Append(", ambientLight: ") .Append(RenderSettings.ambientLight) .Append(", ambientIntensity: ") .Append(RenderSettings.ambientIntensity.ToString("0.###")) .Append('\n'); Light[] array = Object.FindObjectsOfType(true); int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; Light[] array2 = array; foreach (Light val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { LightType type = val2.type; switch ((int)type) { case 2: num3++; break; case 0: num4++; break; case 1: num5++; break; default: num6++; break; } if (val2.bakingOutput.isBaked) { num7++; } } } stringBuilder.Append(" lights: ").Append(array.Length).Append(" total (Point ") .Append(num3) .Append(", Spot ") .Append(num4) .Append(", Directional ") .Append(num5) .Append(", other ") .Append(num6) .Append("; baked ") .Append(num7) .Append(", realtime ") .Append(array.Length - num7) .Append(")\n"); int num8 = 0; array2 = array; foreach (Light val3 in array2) { if (!((Object)(object)val3 == (Object)null)) { if (num8 >= 20) { stringBuilder.Append(" (+").Append(array.Length - num8).Append(" more)\n"); break; } num8++; LightBakingOutput bakingOutput = val3.bakingOutput; stringBuilder.Append(" '").Append(((Object)val3).name).Append("' ") .Append(val3.type) .Append(" intensity=") .Append(val3.intensity.ToString("0.##")) .Append(" range=") .Append(val3.range.ToString("0.#")) .Append(" color=") .Append(val3.color) .Append(" enabled=") .Append(((Behaviour)val3).isActiveAndEnabled) .Append(" bakeType=") .Append(bakingOutput.lightmapBakeType) .Append(" isBaked=") .Append(bakingOutput.isBaked) .Append('\n'); } } Plugin.Activity(stringBuilder.ToString().TrimEnd(new char[1] { '\n' })); } catch (Exception e) { Plugin.LogOnce("PropLighting.Inventory", e); } } } internal struct GroundHitInfo { internal bool Valid; internal Collider Col; internal int Tri; internal Vector3 Bary; } internal static class GroundLightmap { internal enum CacheState { Building, Ready, Failed } internal sealed class MeshUv2Cache { internal CacheState State; internal Vector2[] Uv2; internal int[] Tri; internal string Name; internal int PartsLeft = 2; internal int MaxIndex = -1; } internal sealed class RendererEntry { internal MeshRenderer Mr; internal Texture2D LmTex; } private static readonly Dictionary _meshCache = new Dictionary(8); private static readonly Dictionary _rendererByCol = new Dictionary(16); private static RenderTexture _rt; private static int _cntOk; private static int _cntFailed; private static int _cntNoUv2; private static int _cntUnbaked; private static bool _supportChecked; private static bool _supported; internal static bool Supported { get { if (!_supportChecked) { _supportChecked = true; try { _supported = SystemInfo.supportsAsyncGPUReadback; } catch (Exception e) { Plugin.LogOnce("GroundLM.Support", e); _supported = false; } if (!_supported) { Plugin.Activity("GroundLM: async GPU readback unsupported on this device - ground-lightmap lighting off (probe-select fallback everywhere)."); } } return _supported; } } internal static void OnSceneChanged() { _meshCache.Clear(); _rendererByCol.Clear(); _cntOk = (_cntFailed = (_cntNoUv2 = (_cntUnbaked = 0))); } internal static string InventoryLine() { return (Supported ? "async readback ok" : "async readback UNSUPPORTED") + ", config " + (Plugin.CfgGroundLightmapLighting.Value ? "on" : "off"); } internal static RendererEntry GetRendererEntry(Collider col) { if (_rendererByCol.TryGetValue(col, out var value)) { return value; } value = new RendererEntry(); try { MeshRenderer component = ((Component)col).GetComponent(); if ((Object)(object)component == (Object)null) { _cntUnbaked++; Plugin.Activity("GroundLM: floor collider '" + ((Object)col).name + "' has no MeshRenderer - probe fallback under it."); } else { int lightmapIndex = ((Renderer)component).lightmapIndex; LightmapData[] lightmaps = LightmapSettings.lightmaps; Texture2D val = ((lightmapIndex >= 0 && lightmapIndex < 65534 && lightmaps != null && lightmapIndex < lightmaps.Length && lightmaps[lightmapIndex] != null) ? lightmaps[lightmapIndex].lightmapColor : null); if ((Object)(object)val != (Object)null) { value.Mr = component; value.LmTex = val; } else { _cntUnbaked++; Plugin.Activity("GroundLM: floor renderer '" + ((Object)component).name + "' unbaked " + $"(lightmapIndex {lightmapIndex}) - probe fallback under it."); } } } catch (Exception e) { Plugin.LogOnce("GroundLM.RendererEntry", e); } _rendererByCol[col] = value; return value; } internal static MeshUv2Cache GetMeshCache(Mesh mesh) { if (_meshCache.TryGetValue(mesh, out var value)) { return value; } value = new MeshUv2Cache { Name = ((Object)mesh).name }; _meshCache[mesh] = value; StartBuild(mesh, value); return value; } private static void StartBuild(Mesh mesh, MeshUv2Cache e) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Invalid comparison between Unknown and I4 //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) try { if (!mesh.HasVertexAttribute((VertexAttribute)5)) { e.State = CacheState.Failed; _cntNoUv2++; Plugin.Activity("GroundLM: mesh '" + e.Name + "' has no UV2 - probe fallback on it permanently."); return; } VertexAttributeFormat fmt = mesh.GetVertexAttributeFormat((VertexAttribute)5); if (((int)fmt != 0 && (int)fmt != 1) || mesh.GetVertexAttributeDimension((VertexAttribute)5) < 2) { FailBuild(e, "uv2 format " + ((object)Unsafe.As(ref fmt)/*cast due to .constrained prefix*/).ToString() + " unsupported"); return; } int vertexAttributeStream = mesh.GetVertexAttributeStream((VertexAttribute)5); int offset = mesh.GetVertexAttributeOffset((VertexAttribute)5); int stride = mesh.GetVertexBufferStride(vertexAttributeStream); int vcount = mesh.vertexCount; bool idx16 = (int)mesh.indexFormat == 0; mesh.indexBufferTarget = (Target)(mesh.indexBufferTarget | 0x20); mesh.vertexBufferTarget = (Target)(mesh.vertexBufferTarget | 0x20); GraphicsBuffer ib = mesh.GetIndexBuffer(); GraphicsBuffer vb; try { vb = mesh.GetVertexBuffer(vertexAttributeStream); } catch { GraphicsBuffer obj2 = ib; if (obj2 != null) { obj2.Dispose(); } throw; } if (ib == null || vb == null) { GraphicsBuffer obj3 = ib; if (obj3 != null) { obj3.Dispose(); } GraphicsBuffer obj4 = vb; if (obj4 != null) { obj4.Dispose(); } FailBuild(e, "no GPU buffer (collider-only mesh?)"); return; } try { AsyncGPUReadback.Request(ib, (Action)delegate(AsyncGPUReadbackRequest req) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) try { OnIndexData(e, req, idx16); } catch (Exception ex2) { FailBuild(e, "index decode: " + ex2.Message); } finally { ib.Dispose(); } }); AsyncGPUReadback.Request(vb, (Action)delegate(AsyncGPUReadbackRequest req) { //IL_0006: 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) try { OnVertexData(e, req, stride, offset, vcount, fmt); } catch (Exception ex2) { FailBuild(e, "vertex decode: " + ex2.Message); } finally { vb.Dispose(); } }); } catch { ib.Dispose(); vb.Dispose(); throw; } } catch (Exception ex) { FailBuild(e, ex.GetType().Name + ": " + ex.Message); } } private static void OnIndexData(MeshUv2Cache e, AsyncGPUReadbackRequest req, bool idx16) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (e.State == CacheState.Failed) { return; } if (((AsyncGPUReadbackRequest)(ref req)).hasError) { FailBuild(e, "index readback error"); return; } int num = -1; int[] array; if (idx16) { NativeArray data = ((AsyncGPUReadbackRequest)(ref req)).GetData(0); int num2 = data.Length - data.Length % 3; array = new int[num2]; for (int i = 0; i < num2; i++) { int num3 = (array[i] = data[i]); if (num3 > num) { num = num3; } } } else { NativeArray data2 = ((AsyncGPUReadbackRequest)(ref req)).GetData(0); int num4 = data2.Length - data2.Length % 3; array = new int[num4]; for (int j = 0; j < num4; j++) { int num5 = (array[j] = data2[j]); if (num5 > num) { num = num5; } } } e.Tri = array; e.MaxIndex = num; PartDone(e); } private static void OnVertexData(MeshUv2Cache e, AsyncGPUReadbackRequest req, int stride, int offset, int vcount, VertexAttributeFormat fmt) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if (e.State == CacheState.Failed) { return; } if (((AsyncGPUReadbackRequest)(ref req)).hasError) { FailBuild(e, "vertex readback error"); return; } byte[] array = ((AsyncGPUReadbackRequest)(ref req)).GetData(0).ToArray(); int num = (((int)fmt == 0) ? 8 : 4); if (vcount <= 0 || stride <= 0 || (long)(vcount - 1) * (long)stride + offset + num > array.Length) { FailBuild(e, "vertex stream shorter than expected"); return; } Vector2[] array2 = (Vector2[])(object)new Vector2[vcount]; if ((int)fmt == 0) { for (int i = 0; i < vcount; i++) { int num2 = i * stride + offset; array2[i] = new Vector2(BitConverter.ToSingle(array, num2), BitConverter.ToSingle(array, num2 + 4)); } } else { for (int j = 0; j < vcount; j++) { int num3 = j * stride + offset; array2[j] = new Vector2(Mathf.HalfToFloat((ushort)(array[num3] | (array[num3 + 1] << 8))), Mathf.HalfToFloat((ushort)(array[num3 + 2] | (array[num3 + 3] << 8)))); } } e.Uv2 = array2; PartDone(e); } private static void PartDone(MeshUv2Cache e) { e.PartsLeft--; if (e.PartsLeft <= 0 && e.State != CacheState.Failed) { if (e.Tri == null || e.Uv2 == null || e.Tri.Length == 0 || e.Uv2.Length == 0) { FailBuild(e, "empty index/vertex data"); return; } if (e.MaxIndex >= e.Uv2.Length) { FailBuild(e, "index range " + e.MaxIndex + " exceeds uv2 count " + e.Uv2.Length); return; } e.State = CacheState.Ready; _cntOk++; Plugin.Activity($"GroundLM: mesh '{e.Name}' uv2 cache ready ({e.Tri.Length / 3} tris, " + $"{e.Uv2.Length} verts; scene: {_cntOk} ok / {_cntFailed} failed / " + $"{_cntNoUv2} no-uv2 / {_cntUnbaked} unbaked)."); } } private static void FailBuild(MeshUv2Cache e, string why) { if (e.State != CacheState.Failed) { e.State = CacheState.Failed; e.Uv2 = null; e.Tri = null; _cntFailed++; Plugin.Activity("GroundLM: mesh '" + e.Name + "' uv2 cache failed (" + why + ") - probe fallback on it permanently."); } } internal static bool BlitAndRequest(Texture2D lm, Vector2 uv, Action cb) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_001c: Expected O, but got Unknown try { if ((Object)(object)_rt == (Object)null) { _rt = new RenderTexture(1, 1, 0, (RenderTextureFormat)2, (RenderTextureReadWrite)1); _rt.Create(); } RenderTexture active = RenderTexture.active; Graphics.Blit((Texture)(object)lm, _rt, Vector2.zero, uv); RenderTexture.active = active; AsyncGPUReadback.Request((Texture)(object)_rt, 0, (TextureFormat)20, cb); return true; } catch (Exception e) { Plugin.LogOnce("GroundLM.BlitRequest", e); return false; } } } internal sealed class PropLightSampler { private enum Mode { None, Dim, Probe, GroundLm } private const float SampleInterval = 0.2f; private const float SampleMoveSqr = 0.25f; private const float LerpSpeed = 5f; private const float MinBrightness = 0.3f; private const float DirectionalRayLength = 200f; private const int MaxSampledLights = 8; private const float ProbeOcclusionSlack = 0.3f; private const float ProbeMinWeightDist = 0.5f; private MaterialPropertyBlock _mpb; private readonly SphericalHarmonicsL2[] _shScratch = (SphericalHarmonicsL2[])(object)new SphericalHarmonicsL2[1]; private Mode _mode; private Action _lmReadbackCb; private bool _lmPending; private bool _lmSeeded; private int _lmErrStreak; private bool _lmBroken; private float _current; private float _target; private bool _seeded; private bool _applied; private bool _broken; private SphericalHarmonicsL2 _curSh; private SphericalHarmonicsL2 _targetSh; private Vector3 _lastSamplePos; private float _nextSampleTime; private readonly int[] _selIdx = new int[8]; private readonly float[] _selDist = new float[8]; internal void Tick(MeshRenderer mr, MeshRenderer[] extras, bool hasSamplePoint, Vector3 samplePointOverride, Transform selfRoot, Transform hiderRoot, GroundHitInfo ground) { //IL_0058: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) if (_broken || (Object)(object)mr == (Object)null) { return; } try { if (!Plugin.CfgDynamicPropLighting.Value) { Restore(mr, extras); return; } PropLighting.Regime currentRegime = PropLighting.CurrentRegime; if ((currentRegime == PropLighting.Regime.EngineProbes || currentRegime == PropLighting.Regime.Dim) && !_lmBroken && Plugin.CfgGroundLightmapLighting.Value && GroundLightmap.Supported) { TickGroundLm(mr, extras, hasSamplePoint, samplePointOverride, selfRoot, hiderRoot, ground, currentRegime); return; } Bounds bounds; Vector3 val3; float num2; switch (currentRegime) { case PropLighting.Regime.EngineProbes: { if (!PropLighting.ProbeCacheValid) { Restore(mr, extras); break; } Vector3 val4; if (!hasSamplePoint) { bounds = ((Renderer)mr).bounds; val4 = ((Bounds)(ref bounds)).center; } else { val4 = samplePointOverride; } Vector3 val5 = val4; if (_mode != Mode.Probe) { bool num = _mode == Mode.GroundLm && _lmSeeded; _mode = Mode.Probe; _lastSamplePos = val5; _nextSampleTime = Time.time + 0.2f; _targetSh = SampleProbeSh(val5, hiderRoot); if (!num) { _curSh = _targetSh; } } else { if (!(Time.time >= _nextSampleTime)) { val3 = val5 - _lastSamplePos; if (!(((Vector3)(ref val3)).sqrMagnitude > 0.25f)) { goto IL_0145; } } _nextSampleTime = Time.time + 0.2f; _lastSamplePos = val5; _targetSh = SampleProbeSh(val5, hiderRoot); } goto IL_0145; } case PropLighting.Regime.Dim: { if (PropLighting.MapFallbackActive) { goto default; } if (_mode != Mode.Dim) { _mode = Mode.Dim; _seeded = false; } Vector3 val; if (!hasSamplePoint) { bounds = ((Renderer)mr).bounds; val = ((Bounds)(ref bounds)).center; } else { val = samplePointOverride; } Vector3 val2 = val; if (!_seeded) { _seeded = true; _current = 1f; _target = 1f; _lastSamplePos = val2; _nextSampleTime = 0f; } if (!(Time.time >= _nextSampleTime)) { val3 = val2 - _lastSamplePos; if (!(((Vector3)(ref val3)).sqrMagnitude > 0.25f)) { goto IL_0268; } } _nextSampleTime = Time.time + 0.2f; _lastSamplePos = val2; _target = SampleBrightness(val2, selfRoot); goto IL_0268; } default: { Restore(mr, extras); break; } IL_0268: _current = Mathf.Lerp(_current, _target, Time.deltaTime * 5f); Publish(mr, extras, PropLighting.BaseSH * _current); break; IL_0145: num2 = Time.deltaTime * 5f; if (num2 > 1f) { num2 = 1f; } _curSh = _curSh * (1f - num2) + _targetSh * num2; Publish(mr, extras, _curSh); break; } } catch (Exception e) { Plugin.LogOnce("PropLightSampler.Tick", e); _broken = true; try { Restore(mr, extras); } catch { } } } private void Publish(MeshRenderer mr, MeshRenderer[] extras, SphericalHarmonicsL2 sh) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (_mpb == null) { _mpb = new MaterialPropertyBlock(); } _shScratch[0] = sh; _mpb.CopySHCoefficientArraysFrom(_shScratch); ((Renderer)mr).SetPropertyBlock(_mpb); if (extras != null) { MeshRenderer[] array = extras; foreach (MeshRenderer val in array) { if ((Object)(object)val != (Object)null) { ((Renderer)val).SetPropertyBlock(_mpb); } } } if (_applied) { return; } ((Renderer)mr).lightProbeUsage = (LightProbeUsage)4; if (extras != null) { MeshRenderer[] array = extras; foreach (MeshRenderer val2 in array) { if ((Object)(object)val2 != (Object)null) { ((Renderer)val2).lightProbeUsage = (LightProbeUsage)4; } } } _applied = true; } private void Restore(MeshRenderer mr, MeshRenderer[] extras) { _mode = Mode.None; if (!_applied) { return; } _applied = false; _seeded = false; ((Renderer)mr).lightProbeUsage = (LightProbeUsage)1; if (_mpb != null) { _mpb.Clear(); ((Renderer)mr).SetPropertyBlock(_mpb); } if (extras == null) { return; } foreach (MeshRenderer val in extras) { if (!((Object)(object)val == (Object)null)) { ((Renderer)val).lightProbeUsage = (LightProbeUsage)1; if (_mpb != null) { ((Renderer)val).SetPropertyBlock(_mpb); } } } } private void TickGroundLm(MeshRenderer mr, MeshRenderer[] extras, bool hasSamplePoint, Vector3 samplePointOverride, Transform selfRoot, Transform hiderRoot, GroundHitInfo ground, PropLighting.Regime regime) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) Vector3 val; if (!hasSamplePoint) { Bounds bounds = ((Renderer)mr).bounds; val = ((Bounds)(ref bounds)).center; } else { val = samplePointOverride; } Vector3 val2 = val; if (_mode != Mode.GroundLm) { Mode mode = _mode; _mode = Mode.GroundLm; _lastSamplePos = val2; _nextSampleTime = Time.time; switch (mode) { case Mode.Probe: _lmSeeded = true; break; case Mode.Dim: _curSh = PropLighting.BaseSH * _current; _targetSh = _curSh; _lmSeeded = true; break; default: { if (TrySampleL2(val2, selfRoot, hiderRoot, regime, out var sh)) { _curSh = sh; _targetSh = sh; _lmSeeded = true; } else { _lmSeeded = false; } break; } } if (_lmReadbackCb == null) { _lmReadbackCb = OnLmReadback; } } if (!_lmPending) { if (!(Time.time >= _nextSampleTime)) { Vector3 val3 = val2 - _lastSamplePos; if (!(((Vector3)(ref val3)).sqrMagnitude > 0.25f)) { goto IL_0169; } } _nextSampleTime = Time.time + 0.2f; _lastSamplePos = val2; if (!TryStartGroundSample(ground)) { if (TrySampleL2(val2, selfRoot, hiderRoot, regime, out var sh2)) { _targetSh = sh2; if (!_lmSeeded) { _curSh = sh2; _lmSeeded = true; } } else if (!_lmSeeded) { Restore(mr, extras); _mode = Mode.GroundLm; return; } } } goto IL_0169; IL_0169: if (_lmSeeded) { float num = Time.deltaTime * 5f; if (num > 1f) { num = 1f; } _curSh = _curSh * (1f - num) + _targetSh * num; Publish(mr, extras, _curSh); } } private bool TrySampleL2(Vector3 pp, Transform selfRoot, Transform hiderRoot, PropLighting.Regime regime, out SphericalHarmonicsL2 sh) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (regime == PropLighting.Regime.EngineProbes && PropLighting.ProbeCacheValid) { sh = SampleProbeSh(pp, hiderRoot); return true; } if (regime == PropLighting.Regime.Dim && !PropLighting.MapFallbackActive) { sh = PropLighting.BaseSH * SampleBrightness(pp, selfRoot); return true; } sh = default(SphericalHarmonicsL2); return false; } private bool TryStartGroundSample(GroundHitInfo g) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) try { if (!g.Valid || g.Tri < 0) { return false; } Collider col = g.Col; MeshCollider val = (MeshCollider)(object)((col is MeshCollider) ? col : null); if ((Object)(object)val == (Object)null) { return false; } Mesh sharedMesh = val.sharedMesh; if ((Object)(object)sharedMesh == (Object)null) { return false; } GroundLightmap.RendererEntry rendererEntry = GroundLightmap.GetRendererEntry((Collider)(object)val); if (rendererEntry == null || (Object)(object)rendererEntry.LmTex == (Object)null || (Object)(object)rendererEntry.Mr == (Object)null) { return false; } GroundLightmap.MeshUv2Cache meshCache = GroundLightmap.GetMeshCache(sharedMesh); if (meshCache == null || meshCache.State != GroundLightmap.CacheState.Ready) { return false; } int num = g.Tri * 3; int[] tri = meshCache.Tri; Vector2[] uv = meshCache.Uv2; if (num + 2 >= tri.Length) { return false; } Vector2 val2 = uv[tri[num]] * g.Bary.x + uv[tri[num + 1]] * g.Bary.y + uv[tri[num + 2]] * g.Bary.z; Vector4 lightmapScaleOffset = ((Renderer)rendererEntry.Mr).lightmapScaleOffset; val2.x = Mathf.Clamp01(val2.x * lightmapScaleOffset.x + lightmapScaleOffset.z); val2.y = Mathf.Clamp01(val2.y * lightmapScaleOffset.y + lightmapScaleOffset.w); if (!GroundLightmap.BlitAndRequest(rendererEntry.LmTex, val2, _lmReadbackCb)) { return false; } _lmPending = true; return true; } catch (Exception e) { Plugin.LogOnce("PropLightSampler.GroundSample", e); return false; } } private void OnLmReadback(AsyncGPUReadbackRequest req) { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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) _lmPending = false; try { if (_broken || _mode != Mode.GroundLm) { return; } if (((AsyncGPUReadbackRequest)(ref req)).hasError) { if (++_lmErrStreak >= 3 && !_lmBroken) { _lmBroken = true; Plugin.Activity("PropLighting: ground-lightmap readback failed 3x in a row - this clone falls back to probe lighting."); } return; } _lmErrStreak = 0; NativeArray data = ((AsyncGPUReadbackRequest)(ref req)).GetData(0); if (data.Length >= 1) { SphericalHarmonicsL2 val = default(SphericalHarmonicsL2); ((SphericalHarmonicsL2)(ref val)).AddAmbientLight(data[0]); _targetSh = val; if (!_lmSeeded) { _curSh = val; _lmSeeded = true; } } } catch (Exception e) { Plugin.LogOnce("PropLightSampler.LmReadback", e); _lmBroken = true; } } private SphericalHarmonicsL2 SampleProbeSh(Vector3 p, Transform hiderRoot) { //IL_0016: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) Vector3[] probePositions = PropLighting.ProbePositions; SphericalHarmonicsL2[] probeShs = PropLighting.ProbeShs; if (probePositions == null || probeShs == null || probePositions.Length == 0) { return RenderSettings.ambientProbe; } int num = 0; for (int i = 0; i < probePositions.Length; i++) { float num2 = Vector3.Distance(p, probePositions[i]); if (num < _selIdx.Length) { int num3 = num++; while (num3 > 0 && _selDist[num3 - 1] > num2) { _selDist[num3] = _selDist[num3 - 1]; _selIdx[num3] = _selIdx[num3 - 1]; num3--; } _selDist[num3] = num2; _selIdx[num3] = i; } else if (num2 < _selDist[num - 1]) { int num4 = num - 1; while (num4 > 0 && _selDist[num4 - 1] > num2) { _selDist[num4] = _selDist[num4 - 1]; _selIdx[num4] = _selIdx[num4 - 1]; num4--; } _selDist[num4] = num2; _selIdx[num4] = i; } } SphericalHarmonicsL2 val = default(SphericalHarmonicsL2); float num5 = 0f; for (int j = 0; j < num; j++) { float num6 = _selDist[j]; float num7 = num6 - 0.3f; if (num7 > 0.001f) { Vector3 dir = (probePositions[_selIdx[j]] - p) / num6; if (Disguise.RaycastNearestIgnoringRoot(p, dir, num7, hiderRoot, out var _, ignoreAllClones: true)) { continue; } } float num8 = 1f / Mathf.Max(num6, 0.5f); val += probeShs[_selIdx[j]] * num8; num5 += num8; } if (num5 > 0f) { return val * (1f / num5); } return probeShs[_selIdx[0]]; } private float SampleBrightness(Vector3 p, Transform selfRoot) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_004e: 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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Invalid comparison between Unknown and I4 //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: 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_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) List lights = PropLighting.Lights; int num = 0; for (int i = 0; i < lights.Count; i++) { PropLighting.CachedLight cachedLight = lights[i]; if ((Object)(object)cachedLight.Source == (Object)null || !((Behaviour)cachedLight.Source).isActiveAndEnabled) { continue; } float num2; if ((int)cachedLight.Type == 1) { num2 = 0f; } else { num2 = Vector3.Distance(p, cachedLight.Pos); if (num2 >= cachedLight.Range) { continue; } if ((int)cachedLight.Type == 0) { Vector3 val = p - cachedLight.Pos; float num3 = ((Vector3)(ref val)).magnitude; if (num3 < 0.001f) { num3 = 0.001f; } if (Vector3.Dot(val / num3, cachedLight.Forward) < cachedLight.SpotCos) { continue; } } } if (num < _selIdx.Length) { int num4 = num++; while (num4 > 0 && _selDist[num4 - 1] > num2) { _selDist[num4] = _selDist[num4 - 1]; _selIdx[num4] = _selIdx[num4 - 1]; num4--; } _selDist[num4] = num2; _selIdx[num4] = i; } else if (num2 < _selDist[num - 1]) { int num5 = num - 1; while (num5 > 0 && _selDist[num5 - 1] > num2) { _selDist[num5] = _selDist[num5 - 1]; _selIdx[num5] = _selIdx[num5 - 1]; num5--; } _selDist[num5] = num2; _selIdx[num5] = i; } } float num6 = 0f; for (int j = 0; j < num; j++) { PropLighting.CachedLight value = lights[_selIdx[j]]; Light source = value.Source; if ((Object)(object)source == (Object)null) { continue; } Transform transform = ((Component)source).transform; value.Pos = transform.position; value.Forward = transform.forward; lights[_selIdx[j]] = value; RaycastHit best; if ((int)value.Type == 1) { if (!(((Object)(object)selfRoot != (Object)null) ? Disguise.RaycastNearestIgnoringRoot(p, -value.Forward, 200f, selfRoot, out best) : Physics.Raycast(p, -value.Forward, 200f, Disguise.PickMask, (QueryTriggerInteraction)1))) { float num7 = Mathf.Min(1f, value.Intensity); if (num7 > num6) { num6 = num7; } } continue; } float num8 = Vector3.Distance(p, value.Pos); float num9 = num8 - 0.15f; if (num9 > 0.001f) { Vector3 val2 = (value.Pos - p) / num8; if (((Object)(object)selfRoot != (Object)null) ? Disguise.RaycastNearestIgnoringRoot(p, val2, num9, selfRoot, out best) : Physics.Raycast(p, val2, num9, Disguise.PickMask, (QueryTriggerInteraction)1)) { continue; } } float num10 = 1f - num8 * num8 / (value.Range * value.Range); if (!(num10 <= 0f)) { float num11 = num10 * num10; float num12 = Mathf.Min(1f, value.Intensity) * num11; if (num12 > num6) { num6 = num12; } } } return Mathf.Clamp(num6, 0.3f, 1f); } } public struct PropHuntNet : IBroadcast { public byte Op; public int A; public int B; public float F; public string S; public Vector3 V; } internal static class Ops { public const byte Hello = 1; public const byte HelloAck = 2; public const byte State = 3; public const byte Disguise = 4; public const byte Notice = 5; public const byte RotateProp = 6; public const byte TauntCue = 7; public const string StateResolved = "resolved"; } internal struct ModdedClientInfo { public string Version; public ulong SteamId; } internal static class Net { private static NetworkManager _boundNm; internal static readonly Dictionary ModdedClients = new Dictionary(); internal static bool HostModded; internal static string HostVersion = ""; internal static bool Registered => (Object)(object)_boundNm != (Object)null; internal static bool TryRegister() { try { NetworkManager val = ResolveLiveManager(); if ((Object)(object)val == (Object)null) { return false; } if ((Object)(object)val == (Object)(object)_boundNm) { return true; } Bind(val); return true; } catch (Exception e) { Plugin.LogOnce("Net.TryRegister", e); return false; } } private static NetworkManager ResolveLiveManager() { IReadOnlyCollection instances = NetworkManager.Instances; if (instances != null) { foreach (NetworkManager item in instances) { if ((Object)(object)item != (Object)null && (item.IsServer || item.IsClient)) { return item; } } } return InstanceFinder.NetworkManager; } private static void Bind(NetworkManager nm) { bool flag = _boundNm != null; if ((Object)(object)_boundNm != (Object)null) { try { _boundNm.ClientManager.UnregisterBroadcast((Action)OnClientMessage); _boundNm.ServerManager.UnregisterBroadcast((Action)OnServerMessage); _boundNm.ServerManager.OnRemoteConnectionState -= OnRemoteConnectionState; } catch (Exception e) { Plugin.LogOnce("Net.Unbind", e); } } GenericWriter.Write = WriteMsg; GenericReader.Read = ReadMsg; nm.ClientManager.RegisterBroadcast((Action)OnClientMessage); nm.ServerManager.RegisterBroadcast((Action)OnServerMessage, true); nm.ServerManager.OnRemoteConnectionState += OnRemoteConnectionState; _boundNm = nm; if (flag) { Plugin.Activity($"Net: rebound to new NetworkManager (server={nm.IsServer} client={nm.IsClient})."); ResetClientHandshake(); ResetServerHandshake(); try { PropHuntManager.Instance?.OnNetworkManagerSwapped(); return; } catch (Exception e2) { Plugin.LogOnce("Net.SwapReset", e2); return; } } Plugin.Activity("Net: broadcast + serializer registered."); } private static void WriteMsg(Writer w, PropHuntNet m) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) w.WriteByte(m.Op); w.WriteInt32(m.A, (AutoPackType)1); w.WriteInt32(m.B, (AutoPackType)1); w.WriteSingle(m.F, (AutoPackType)0); w.WriteString(m.S); w.WriteVector3(m.V); } private static PropHuntNet ReadMsg(Reader r) { //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) return new PropHuntNet { Op = r.ReadByte(), A = r.ReadInt32((AutoPackType)1), B = r.ReadInt32((AutoPackType)1), F = r.ReadSingle((AutoPackType)0), S = r.ReadString(), V = r.ReadVector3() }; } internal static void ServerToAll(PropHuntNet msg) { try { NetworkManager boundNm = _boundNm; if ((Object)(object)boundNm == (Object)null || !boundNm.IsServer) { return; } boundNm.ServerManager.Broadcast(msg, true, (Channel)0); } catch (Exception e) { Plugin.LogOnce("Net.ServerToAll.send", e); } try { PropHuntManager.ApplyFromServer(msg); } catch (Exception e2) { Plugin.LogOnce("Net.ServerToAll.localApply", e2); } } internal static void ServerTo(NetworkConnection conn, PropHuntNet msg) { try { NetworkManager boundNm = _boundNm; if (!((Object)(object)boundNm == (Object)null) && boundNm.IsServer && !(conn == (NetworkConnection)null)) { boundNm.ServerManager.Broadcast(conn, msg, true, (Channel)0); } } catch (Exception e) { Plugin.LogOnce("Net.ServerTo", e); } } internal static void ClientToServer(PropHuntNet msg) { try { NetworkManager boundNm = _boundNm; if (!((Object)(object)boundNm == (Object)null)) { if (boundNm.IsServer) { OnServerMessage(boundNm.ClientManager.Connection, msg); } else if (boundNm.IsClient) { boundNm.ClientManager.Broadcast(msg, (Channel)0); } } } catch (Exception e) { Plugin.LogOnce("Net.ClientToServer", e); } } private static void OnRemoteConnectionState(NetworkConnection conn, RemoteConnectionStateArgs args) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) try { if ((int)args.ConnectionState == 0) { ModdedClients.Remove(args.ConnectionId); } } catch (Exception e) { Plugin.LogOnce("Net.RemoteConnState", e); } } private static void OnServerMessage(NetworkConnection conn, PropHuntNet msg) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) try { Plugin.Activity($"Net: server recv op={msg.Op} from ClientId {((conn != (NetworkConnection)null) ? conn.ClientId : (-1))}."); switch (msg.Op) { case 1: { int num = ((conn != (NetworkConnection)null) ? conn.ClientId : (-1)); if (num >= 0) { ulong num2 = PropHuntManager.SteamIdForConn(num); if (num2 == 0L && ModdedClients.TryGetValue(num, out var value)) { num2 = value.SteamId; } ModdedClients[num] = new ModdedClientInfo { Version = (msg.S ?? ""), SteamId = num2 }; Plugin.Activity($"Net: Hello from ClientId {num} (mod v{msg.S})."); ServerTo(conn, new PropHuntNet { Op = 2, S = "1.2.1", V = Vector3.zero }); } break; } case 4: { if (!PropHuntManager.ServerValidateProp(conn, out var playerId2)) { Plugin.Activity($"Net: rejected Disguise from ClientId {((conn != (NetworkConnection)null) ? conn.ClientId : (-1))}."); break; } msg.A = playerId2; PropHuntManager.Instance?.ServerOnPropDisguised(playerId2); ServerToAll(msg); break; } case 6: { if (!PropHuntManager.ServerValidateProp(conn, out var playerId)) { Plugin.Activity($"Net: rejected RotateProp from ClientId {((conn != (NetworkConnection)null) ? conn.ClientId : (-1))}."); break; } msg.A = playerId; ServerToAll(msg); break; } } } catch (Exception e) { Plugin.LogOnce("Net.OnServerMessage", e); } } private static void OnClientMessage(PropHuntNet msg) { try { Plugin.Activity($"Net: client recv op={msg.Op}."); if (msg.Op == 2) { HostModded = true; HostVersion = msg.S ?? ""; Plugin.Activity("Net: HelloAck from host (v" + HostVersion + ")."); } else if (!((Object)(object)_boundNm != (Object)null) || !_boundNm.IsServer) { PropHuntManager.ApplyFromServer(msg); } } catch (Exception e) { Plugin.LogOnce("Net.OnClientMessage", e); } } internal static void ResetClientHandshake() { HostModded = false; HostVersion = ""; } internal static void ResetServerHandshake() { ModdedClients.Clear(); } } internal static class Patches { private const float ModeGraceSeconds = 5f; private const float ShotDedupeSeconds = 0.05f; private static readonly Dictionary _lastShotByInstance = new Dictionary(); private const float KillfeedDedupeSeconds = 1.5f; private static readonly Dictionary _recentDeathLines = new Dictionary(); private static bool _changeSceneReentry; private static bool ModeOnLocally(bool withGrace) { bool flag = PropHuntManager.ClientModeLive; if (!flag && InstanceFinder.IsServer) { PropHuntManager instance = PropHuntManager.Instance; flag = (Object)(object)instance != (Object)null && instance.Armed; } if (!flag && withGrace) { flag = Time.unscaledTime - PropHuntManager.LastModeLiveTime < 5f; } return flag; } internal static bool HandleInteractionPrefix(PlayerPickup __instance) { try { if (!Plugin.CfgEnabled.Value) { return true; } if (PropHuntManager.ClientPhase == Phase.Idle || PropHuntManager.LocalRole != Role.Hider) { return true; } if (!((NetworkBehaviour)__instance).IsOwner) { return true; } return false; } catch (Exception e) { Plugin.LogOnce("Patches.HandleInteraction", e); return true; } } internal static void RemoveHealthPostfix(PlayerHealth __instance) { try { if (InstanceFinder.IsServer) { PropHuntManager.Instance?.ServerOnHealthRemoved(__instance); } } catch (Exception e) { Plugin.LogOnce("Patches.RemoveHealth", e); } } internal static void ShootObserversEffectPostfix(Weapon __instance) { try { if (!InstanceFinder.IsServer || !Plugin.CfgEnabled.Value || (Object)(object)__instance == (Object)null || __instance is MeleeWeapon) { return; } int instanceID = ((Object)__instance).GetInstanceID(); float time = Time.time; if (!_lastShotByInstance.TryGetValue(instanceID, out var value) || !(time - value < 0.05f)) { if (_lastShotByInstance.Count > 256) { _lastShotByInstance.Clear(); } _lastShotByInstance[instanceID] = time; PropHuntManager.Instance?.ServerOnWeaponFired(__instance); } } catch (Exception e) { Plugin.LogOnce("Patches.ShootObserversEffect", e); } } internal static bool PauseWriteLogPrefix(string text) { try { if (!Plugin.CfgEnabled.Value) { return true; } if (!ModeOnLocally(withGrace: true) || string.IsNullOrEmpty(text) || !LooksLikeDeathLine(text)) { return true; } int hashCode = text.GetHashCode(); float time = Time.time; if (_recentDeathLines.TryGetValue(hashCode, out var value) && time - value < 1.5f) { Plugin.Activity("Patches: duplicate killfeed line suppressed."); return false; } if (_recentDeathLines.Count > 64) { _recentDeathLines.Clear(); } _recentDeathLines[hashCode] = time; return true; } catch (Exception e) { Plugin.LogOnce("Patches.PauseWriteLog", e); return true; } } private static bool LooksLikeDeathLine(string text) { if (!text.Contains(" was killed") && !text.Contains(" was headshot") && !text.Contains(" was slain") && !text.Contains(" was beheaded")) { return text.Contains(" died"); } return true; } internal static bool KillServerLogicPrefix(PlayerHealth enemyHealth) { try { if (!Plugin.CfgEnabled.Value) { return true; } PropHuntManager instance = PropHuntManager.Instance; if ((Object)(object)instance == (Object)null || !instance.Armed) { return true; } if ((Object)(object)enemyHealth == (Object)null) { return true; } if (enemyHealth.health <= 0f) { Plugin.Activity("Patches: duplicate KillServer on a dead victim skipped."); return false; } return true; } catch (Exception e) { Plugin.LogOnce("Patches.KillServerLogic", e); return true; } } internal static bool TauntObserversLogicPrefix(FirstPersonController __instance, int clip) { try { if (!Plugin.CfgEnabled.Value) { return true; } if (!ModeOnLocally(withGrace: true)) { return true; } object? value = Reflect.FpcAudioField.GetValue(__instance); AudioSource val = (AudioSource)((value is AudioSource) ? value : null); AudioClip[] array = Reflect.FpcTauntClipField.GetValue(__instance) as AudioClip[]; if ((Object)(object)val == (Object)null || array == null || clip < 0 || clip >= array.Length || (Object)(object)array[clip] == (Object)null) { return true; } float num = (float)Reflect.FpcNextTauntPlayField.GetValue(__instance); bool flag = Time.unscaledTime < num; if (!flag) { Plugin.Activity($"Patches: taunt heard (clip {clip})."); } float num2 = Mathf.Clamp(Plugin.CfgTauntVolume.Value, 1f, 5f); if (num2 <= 1.01f) { return true; } if (flag) { return false; } Reflect.FpcNextTauntPlayField.SetValue(__instance, Time.unscaledTime + 0.2f); val.PlayOneShot(array[clip], num2); return false; } catch (Exception e) { Plugin.LogOnce("Patches.TauntVolume", e); return true; } } internal static void SetStartTimePrefix(ref float serverTimeTillStart) { try { if (Plugin.CfgEnabled.Value && Plugin.CfgSkipIntro.Value && InstanceFinder.IsServer) { PropHuntManager instance = PropHuntManager.Instance; if (!((Object)(object)instance == (Object)null) && instance.Armed) { serverTimeTillStart = 0.05f; } } } catch (Exception e) { Plugin.LogOnce("Patches.SetStartTime", e); } } internal static bool StartRoundDelayPrefix(PauseManager __instance) { try { if (!Plugin.CfgEnabled.Value || !Plugin.CfgSkipIntro.Value) { return true; } if (!ModeOnLocally(withGrace: false)) { return true; } __instance.InvokeRoundStarted(); Plugin.Activity("Client: skipped map intro (SkipIntro)."); return false; } catch (Exception e) { Plugin.LogOnce("Patches.StartRoundDelay", e); return true; } } internal static bool ChangeNetworkScenePrefix() { try { if (_changeSceneReentry) { return true; } if (!InstanceFinder.IsServer) { return true; } if (!Plugin.CfgEnabled.Value) { return true; } PropHuntManager instance = PropHuntManager.Instance; if ((Object)(object)instance == (Object)null || !instance.Armed) { return true; } if (!instance.TryConsumeRevealHold(out var deferSeconds)) { return true; } ((MonoBehaviour)instance).StartCoroutine(DeferredChangeNetworkScene(deferSeconds)); Plugin.Activity($"Patches: holding map change {deferSeconds:0.#}s for the props-win reveal."); return false; } catch (Exception e) { Plugin.LogOnce("Patches.ChangeNetworkScene", e); return true; } } private static IEnumerator DeferredChangeNetworkScene(float seconds) { float until = Time.time + seconds; while (Time.time < until) { yield return null; } try { _changeSceneReentry = true; SceneMotor instance = SceneMotor.Instance; if ((Object)(object)instance != (Object)null) { instance.ChangeNetworkScene(); } else { Plugin.Log.LogError((object)"PropHunt: SceneMotor.Instance gone at reveal-hold end - map change NOT issued."); } } catch (Exception ex) { Plugin.LogOnce("Patches.DeferredChangeScene", ex); Plugin.Log.LogError((object)("PropHunt: deferred map change threw - the lobby may be stuck on this map: " + ex.Message)); } finally { _changeSceneReentry = false; } } internal static bool ShowLoadingScreenPrefix() { try { if (!Plugin.CfgEnabled.Value) { return true; } if (Time.time >= PropHuntManager.ClientRevealHoldUntil) { return true; } Plugin.Activity("Patches: early loading screen skipped (props-win reveal hold)."); return false; } catch (Exception e) { Plugin.LogOnce("Patches.ShowLoadingScreen", e); return true; } } internal static void SetGamemodePostfix(SteamLobby __instance, TMP_Dropdown dropdown) { try { if ((Object)(object)__instance == (Object)null || (Object)(object)dropdown == (Object)null) { return; } PropHuntManager instance = PropHuntManager.Instance; if ((Object)(object)instance == (Object)null) { return; } NetworkManager networkManager = InstanceFinder.NetworkManager; if ((Object)(object)networkManager == (Object)null || !networkManager.IsServer) { return; } int num = PropHuntManager.FindPropHuntOptionIndex(dropdown); if (num < 0) { return; } if (dropdown.value == num) { PropHuntManager.UndoTeamsSideEffect(__instance); if (!instance.TryArm("dropdown") && !Plugin.CfgEnabled.Value) { dropdown.SetValueWithoutNotify(0); } } else if (instance.Armed) { instance.Disarm("dropdown"); } } catch (Exception e) { Plugin.LogOnce("Patches.SetGamemode", e); } } } [BepInPlugin("landa.straftat.prophunt", "PropHunt", "1.2.1")] [BepInProcess("STRAFTAT.exe")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "landa.straftat.prophunt"; public const string PluginName = "PropHunt"; public const string PluginVersion = "1.2.1"; internal static Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry CfgEnabled; internal static ConfigEntry CfgToggleModeKey; internal static ConfigEntry CfgDisguiseKey; internal static ConfigEntry CfgSkipIntro; internal static ConfigEntry CfgDynamicPropLighting; internal static ConfigEntry CfgGroundLightmapLighting; internal static ConfigEntry CfgSolidProps; internal static ConfigEntry CfgWallFlushProps; internal static ConfigEntry CfgAutoTauntInterval; internal static ConfigEntry CfgTauntVolume; internal static ConfigEntry CfgPropHealthMultiplier; internal static ConfigEntry CfgSeekerCount; internal static ConfigEntry CfgHideSeconds; internal static ConfigEntry CfgHuntSeconds; internal static ConfigEntry CfgShotPenalty; internal static ConfigEntry CfgPenaltyWindowSeconds; internal static ConfigEntry CfgHitGraceSeconds; internal static ConfigEntry CfgAllowReDisguise; internal static ConfigEntry CfgReDisguiseCooldown; internal static ConfigEntry CfgMinPropSize; internal static ConfigEntry CfgMaxPropSize; internal static ConfigEntry CfgWinRevealExtraSeconds; internal static ConfigEntry CfgShowOwnProp; internal static ConfigEntry CfgBlindOverlay; internal static ConfigEntry CfgThirdPersonCamera; internal static ConfigEntry CfgThirdPersonDistance; internal static ConfigEntry CfgRotateLeftKey; internal static ConfigEntry CfgRotateRightKey; internal static ConfigEntry CfgRotateSpeed; internal static ConfigEntry CfgLogActivity; private static readonly HashSet _loggedSites = new HashSet(); private Harmony _harmony; internal static void LogOnce(string site, Exception e) { if (_loggedSites.Add(site)) { Log.LogError((object)("[" + site + "] exception (suppressing further occurrences): " + e)); } } internal static void Activity(string msg) { if (CfgLogActivity != null && CfgLogActivity.Value) { string text; try { text = $"[{Time.unscaledTime:0.0}] "; } catch { text = ""; } Log.LogInfo((object)(text + msg)); } } private void Awake() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected O, but got Unknown //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Expected O, but got Unknown //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Expected O, but got Unknown //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Expected O, but got Unknown //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { BindConfig(); } catch (Exception ex) { Log.LogError((object)("Config bind failed, using defaults: " + ex)); } if (!Reflect.Init()) { Log.LogWarning((object)"PropHunt: required game members not found - mod INERT. Re-decompile and check PlayerPickup/PlayerHealth/Weapon if the game updated."); return; } try { _harmony = new Harmony("landa.straftat.prophunt"); _harmony.Patch(Reflect.PickupHandleInteraction, new HarmonyMethod(typeof(Patches), "HandleInteractionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch(Reflect.RemoveHealthLogic, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RemoveHealthPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch(Reflect.SetGamemodeDropdown, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SetGamemodePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch(Reflect.GameSetStartTime, new HarmonyMethod(typeof(Patches), "SetStartTimePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch(Reflect.PauseStartRoundDelay, new HarmonyMethod(typeof(Patches), "StartRoundDelayPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); foreach (MethodBase shootObserversEffectLogic in Reflect.ShootObserversEffectLogics) { _harmony.Patch(shootObserversEffectLogic, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "ShootObserversEffectPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } _harmony.Patch(Reflect.PauseWriteLog, new HarmonyMethod(typeof(Patches), "PauseWriteLogPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); foreach (MethodBase killServerLogic in Reflect.KillServerLogics) { _harmony.Patch(killServerLogic, new HarmonyMethod(typeof(Patches), "KillServerLogicPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } _harmony.Patch(Reflect.FpcTauntObserversLogic, new HarmonyMethod(typeof(Patches), "TauntObserversLogicPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch(Reflect.SceneChangeNetworkScene, new HarmonyMethod(typeof(Patches), "ChangeNetworkScenePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch(Reflect.SceneShowLoadingScreen, new HarmonyMethod(typeof(Patches), "ShowLoadingScreenPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)($"Harmony patches applied ({9 + Reflect.ShootObserversEffectLogics.Count + Reflect.KillServerLogics.Count} targets, " + $"{Reflect.ShootObserversEffectLogics.Count} shoot-effect + {Reflect.KillServerLogics.Count} kill-server overloads).")); } catch (Exception ex2) { Log.LogError((object)("Harmony patching failed - mod INERT: " + ex2)); return; } try { GameObject val = new GameObject("PropHunt_Runtime"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent(); Log.LogInfo((object)"Runtime behaviour hosted."); } catch (Exception ex3) { Log.LogError((object)("Failed to host runtime behaviour - mod INERT: " + ex3)); return; } Log.LogInfo((object)("PropHunt v1.2.1 loaded. Host picks 'Prop Hunt' in the lobby " + $"gamemode dropdown (or presses {CfgToggleModeKey.Value} in-map) to arm the mode " + "(every player in the lobby needs the mod).")); } private void BindConfig() { CfgEnabled = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "Enabled", true, "Master toggle. When false the mod never arms and all patches pass through."); CfgToggleModeKey = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "ToggleModeKey", (KeyCode)287, "HOST-only fallback: arm/disarm the Prop Hunt mode in-map (the primary way is the 'Prop Hunt' entry in the lobby gamemode dropdown). Takes effect at the NEXT take."); CfgDisguiseKey = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "DisguiseKey", (KeyCode)102, "HIDER: disguise as the world prop you are looking at (6 m range)."); CfgSkipIntro = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "SkipIntro", true, "Skip the map intro (welcome banner + TAKE X + GET/READY/GO) while Prop Hunt is armed - the hide phase starts sooner. Movement still unlocks through the game's own ready-handshake (~0.5-1 s after the map loads)."); CfgDynamicPropLighting = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "DynamicPropLighting", true, "Occlusion-aware probe lighting for the disguise on all maps (1.1.2): the prop blends only the baked light probes it can actually SEE, so a prop in a dark corner no longer glows with light interpolated through the wall. On the one map without baked probes, dims by local light visibility instead (never brighter or off-hue vs vanilla). Since 1.1.5 the primary mode samples the baked lightmap of the floor under the prop instead (see GroundLightmapLighting). Off = the engine's vanilla lighting everywhere."); CfgGroundLightmapLighting = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "GroundLightmapLighting", false, "EXPERIMENTAL / opt-in (default off): light the disguise with the baked lightmap color of the floor point under it (1.1.5): the ground's texel IS the map's authored lighting at that exact spot, and every machine samples the same texel. Wherever the floor has no usable lightmap (unbaked maps, non-mesh floors) the 1.1.2 probe/dim lighting takes over per sample. Requires DynamicPropLighting; off = the occlusion-aware probe-select lighting (the tested default)."); CfgSolidProps = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "SolidProps", true, "The disguised prop is solid to the hunter: stand on it, jump off it, wall-jump against it - like real geometry. Never blocks bullets (shots still resolve against the prop hitbox only). Off = 1.0.11 behavior (props are walk-through)."); CfgWallFlushProps = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "WallFlushProps", true, "Wall-mounted disguises (light fixtures, signs) press flush against walls instead of hovering at player-collider distance. Purely visual placement, computed identically on every machine - no networking."); CfgAutoTauntInterval = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "AutoTauntInterval", 30f, "HOST-controlled (1.0.11): seconds between forced prop auto-taunts during the hunt phase (the vanilla taunt sound, audible to both players - classic prop-hunt pressure). The HOST's value schedules the taunts for BOTH players' hiding turns, aligned to the hunt timer (one taunt per interval mark). 0 = off; nonzero values are clamped to at least 10."); CfgTauntVolume = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "TauntVolume", 2.5f, "Taunt loudness multiplier while Prop Hunt is on (applies to ALL taunts on this machine while the mode is live - a LOCAL playback setting, per machine). 1.0 = vanilla; clamped to [1, 5]."); CfgPropHealthMultiplier = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "PropHealthMultiplier", 2f, "Multiply every PROP's health when the hunt begins (applied by the host, synced to all players) so a discovered prop can survive the first burst and run. 1.0 = vanilla pool; clamped to [1, 4]."); CfgSeekerCount = ((BaseUnityPlugin)this).Config.Bind("PropHunt.General", "SeekerCount", 1, "HOST-controlled (1.1.0): how many players seek each take; everyone else is a prop. Live-read at every take start and clamped to [1, playerCount - 1], so a value too big for the current lobby simply caps out. Seeker duty rotates round-robin through the players across takes."); CfgHideSeconds = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "HideSeconds", 25f, "Hide phase length: the seeker is frozen (and blinded, see BlindOverlay) while the prop runs and picks a disguise."); CfgHuntSeconds = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "HuntSeconds", 120f, "Hunt phase length. If it expires with both players alive, the seeker dies and the prop wins the take."); CfgShotPenalty = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "ShotPenalty", 1f, "HP of self-damage the seeker takes for a non-melee shot that does NOT hit the prop. (Players have 10 HP total; guns deal ~3-4.)"); if (CfgShotPenalty.Value == 2f || CfgShotPenalty.Value == 5f) { float value = CfgShotPenalty.Value; CfgShotPenalty.Value = 1f; ((BaseUnityPlugin)this).Config.Save(); Log.LogInfo((object)$"Config: migrated ShotPenalty {value:0} -> 1 (rebalanced 1.0.6 default)."); } CfgPenaltyWindowSeconds = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "PenaltyWindowSeconds", 0.4f, "Minimum seconds between two shot penalties (so miniguns/shotguns don't insta-kill)."); CfgHitGraceSeconds = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "HitGraceSeconds", 0.15f, "A shot's penalty is cancelled if the prop took damage within this window after the shot."); CfgAllowReDisguise = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "AllowReDisguise", true, "Whether the prop may pick a new disguise after the first one."); CfgReDisguiseCooldown = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "ReDisguiseCooldown", 2f, "Seconds between disguise picks."); CfgMinPropSize = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "MinPropSize", 0.3f, "Smallest accepted prop (largest bounds dimension, metres)."); CfgMaxPropSize = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "MaxPropSize", 6.5f, "Largest accepted prop (largest bounds dimension, metres)."); if (CfgMaxPropSize.Value == 4.5f) { CfgMaxPropSize.Value = 6.5f; ((BaseUnityPlugin)this).Config.Save(); Log.LogInfo((object)"Config: migrated MaxPropSize 4.5 -> 6.5 (raised 1.0.5 default)."); } CfgWinRevealExtraSeconds = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Rules", "WinRevealExtraSeconds", 4f, "Extra seconds the next-map change is held after a PROP-TEAM win so the dead seekers (free-spectating through the kill cam) can see where the props were hiding. HOST-controlled: the host's value drives the timing for everyone. 0 disables; live-read at every take end and clamped to [0, 10]."); CfgShowOwnProp = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "ShowOwnProp", true, "Prop player also sees their own disguise clone (helps alignment; the camera sits near the prop's top)."); CfgBlindOverlay = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "BlindOverlay", true, "Seeker gets a fullscreen blind overlay during the hide phase."); CfgThirdPersonCamera = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "ThirdPersonCamera", true, "Hider: pull the camera back into third person while disguised (local-only visual)."); CfgThirdPersonDistance = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "ThirdPersonDistance", 2.5f, "Third-person camera distance behind the head (metres); walls shorten it."); CfgRotateLeftKey = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "RotateLeftKey", (KeyCode)113, "Hider: hold to rotate the disguise prop counter-clockwise."); CfgRotateRightKey = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "RotateRightKey", (KeyCode)101, "Hider: hold to rotate the disguise prop clockwise."); CfgRotateSpeed = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Visuals", "RotateSpeed", 120f, "Prop rotation speed while a rotate key is held (degrees/second)."); CfgLogActivity = ((BaseUnityPlugin)this).Config.Bind("PropHunt.Diagnostics", "LogActivity", false, "Verbose diagnostic logging - enable when reporting a bug."); } } internal enum Phase { Idle, Hide, Hunt } internal enum Role { None, Hider, Seeker } internal class PropHuntManager : MonoBehaviour { private sealed class SvSeeker { public int PlayerId; public int ConnId; public PlayerHealth Ph; public PlayerManager Pm; public float PendingPenaltyDue = -1f; public float PendingShotTime; public float LastPenaltyTime = -999f; public bool TimeoutKilled; public int UnfreezeFlips; } private sealed class SvProp { public int PlayerId; public int ConnId; public PlayerHealth Ph; public float LastHealth; } private struct PendingDisguise { public PropHuntNet Msg; public float Deadline; public float NextAttempt; } internal static PropHuntManager Instance; internal static Phase ClientPhase = Phase.Idle; internal static int ClientSeekerTeamId = -1; internal static float ClientPhaseEnd; internal static Role LocalRole = Role.None; internal static float ClientRevealHoldUntil; private const float RevealHoldVanillaFlowSeconds = 7f; internal static bool ClientDisguisedThisTake; internal static float LastModeLiveTime = float.NegativeInfinity; private bool _armed; private Phase _svPhase; private readonly List _svSeekers = new List(4); private readonly List _svProps = new List(8); private readonly HashSet _svSeekerConnIds = new HashSet(); private readonly HashSet _svPropConnIds = new HashSet(); private int _svSeekerTeamId = -1; private int _svPropTeamId = -1; private float _svPhaseEnd; private float _svRevealHoldUntil; private float _svRevealHoldExtra; private const float RevealHoldArmWindowSeconds = 15f; private int _svRotationOffset = -1; private readonly HashSet _svDisguisedProps = new HashSet(); private int _svTauntRotIdx; private readonly Dictionary _svTeamSnapshot = new Dictionary(); private bool _svTeamSnapshotValid; private bool _svTeamsRestorePending; private float _svTeamsRestoreNotBefore; private const float TeamRestoreDelaySeconds = 3.5f; private float _svHuntStart; private int _svNextTauntK; private const float TauntWarnLeadSeconds = 3f; private readonly HashSet _svPrevTakePhIds = new HashSet(); private int _prevAlive; private bool _takePending; private float _takePendingSince; private bool _pendingGateNoticeSent; private const float TakePendingTimeout = 20f; private float _nextFreezeAssert; private const float FreezeAssertInterval = 0.25f; private bool _svExternalUnfreezeLogged; private readonly List _svUnfreezeConnIds = new List(4); private float _svUnfreezeDeadline; private float _svUnfreezeNextAssert; private const float HuntUnfreezeRetrySeconds = 2f; private const float EndTakeUnfreezeRetrySeconds = 5f; private float _lastPropDamageTime = -999f; private bool _wasServer; private bool _wasClient; private float _nextHelloRetry; private bool _clNeedWeaponDrop; private float _clWeaponDropDeadline; private float _clWeaponDropNotBefore; private const float WeaponDropConfirmSeconds = 0.6f; private float _clDisguiseReadyAt; private float _clNextHiderWatch; private const float StalePhaseGrace = 30f; private float _clNextRoleCheck; private const float RoleCheckInterval = 0.5f; private FirstPersonController _clLocalFpc; private float _clNextLocalScan; private const float LocalScanInterval = 0.5f; private readonly List _clPendingDisguises = new List(4); private const float DisguiseRetrySeconds = 1.5f; private const float DisguiseRetryInterval = 0.25f; private float _clTauntCueDue; private const int AutoTauntClipIndex = 4; private static readonly object[] _autoTauntArgs = new object[1] { 4 }; internal const string DropdownLabel = "Prop Hunt"; private float _nextDropdownScan; private string _lastQuietArmRefusal; private PlayerHealth[] _phCache; private float _phCacheRefreshedAt = -999f; private PlayerManager[] _pmCache; private float _pmCacheRefreshedAt = -999f; private readonly List _rosterScratch = new List(16); private bool _svTimeoutNoticeSent; internal static bool ClientModeLive => ClientPhase != Phase.Idle; internal bool Armed => _armed; private void Awake() { Instance = this; try { SceneManager.activeSceneChanged += OnActiveSceneChanged; } catch (Exception e) { Plugin.LogOnce("Manager.Awake", e); } } private void OnDestroy() { try { SceneManager.activeSceneChanged -= OnActiveSceneChanged; } catch { } } private void OnActiveSceneChanged(Scene from, Scene to) { try { MarkCachesDirty(); _clLocalFpc = null; ClientRevealHoldUntil = 0f; Disguise.Cleanup(); if (InstanceFinder.IsServer && _svPhase != Phase.Idle) { EndTakeServer("scene change"); } if (InstanceFinder.IsClient && !InstanceFinder.IsServer) { SendHello(); } } catch (Exception e) { Plugin.LogOnce("Manager.SceneChanged", e); } } private void MarkCachesDirty() { _phCacheRefreshedAt = -999f; _pmCacheRefreshedAt = -999f; } private void Update() { try { Net.TryRegister(); } catch (Exception e) { Plugin.LogOnce("Manager.NetRegister", e); } try { ClientTick(); } catch (Exception e2) { Plugin.LogOnce("Manager.ClientTick", e2); } try { Hud.TickBlind(); } catch (Exception e3) { Plugin.LogOnce("Manager.BlindTick", e3); } try { TickGamemodeDropdown(); } catch (Exception e4) { Plugin.LogOnce("Manager.GamemodeDropdown", e4); } try { bool isServer = InstanceFinder.IsServer; if (isServer) { ServerTick(); } else if (_wasServer) { ResetServerState("server stopped"); } _wasServer = isServer; } catch (Exception e5) { Plugin.LogOnce("Manager.ServerTick", e5); } } private void OnGUI() { try { Hud.Draw(); } catch (Exception e) { Plugin.LogOnce("Manager.OnGUI", e); } } private void ServerTick() { HandleToggleKey(); TickUnfreezeRetry(); if (_armed) { LastModeLiveTime = Time.unscaledTime; } GameManager instance = GameManager.Instance; if ((Object)(object)instance == (Object)null) { return; } int count = instance.alivePlayers.Count; if (_svPhase != Phase.Idle) { int num = CountAlive(instance, seekers: true); int num2 = CountAlive(instance, seekers: false); if (num == 0 || num2 == 0) { bool flag = num2 > 0 && num == 0; EndTakeServer(flag ? "take resolved - props won" : "take resolved - seekers won", flag); } else if (MidTakePlayerMissing()) { EndTakeServer("player left mid-take"); } else if (InMenuOrVictory()) { EndTakeServer("menu/victory"); } else { RunPhasesServer(); } } if (count >= 2 && count > _prevAlive) { _takePending = true; _takePendingSince = Time.time; _pendingGateNoticeSent = false; MarkCachesDirty(); } _prevAlive = count; if (_takePending && _svPhase == Phase.Idle) { TryStartPendingTake(); } if (_svTeamsRestorePending && _svPhase == Phase.Idle && Time.time >= _svTeamsRestoreNotBefore) { _svTeamsRestorePending = false; RestoreTeamsServer("deferred"); } } private int CountAlive(GameManager gm, bool seekers) { int num = 0; if (seekers) { for (int i = 0; i < _svSeekers.Count; i++) { if (gm.alivePlayers.Contains(_svSeekers[i].PlayerId)) { num++; } } } else { for (int j = 0; j < _svProps.Count; j++) { if (gm.alivePlayers.Contains(_svProps[j].PlayerId)) { num++; } } } return num; } private bool MidTakePlayerMissing() { if (ClientInstance.playerInstances.Count < 2) { return true; } ServerManager serverManager = InstanceFinder.ServerManager; if ((Object)(object)serverManager == (Object)null) { return false; } for (int i = 0; i < _svSeekers.Count; i++) { if (!serverManager.Clients.ContainsKey(_svSeekers[i].ConnId)) { return true; } } for (int j = 0; j < _svProps.Count; j++) { if (!serverManager.Clients.ContainsKey(_svProps[j].ConnId)) { return true; } } return false; } private void HandleToggleKey() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!Input.GetKeyDown(Plugin.CfgToggleModeKey.Value)) { return; } PauseManager instance = PauseManager.Instance; if (!((Object)(object)instance != (Object)null) || (!instance.chatting && !instance.inMainMenu && !instance.pause && !instance.inVictoryMenu)) { if (_armed) { Disarm("F6"); } else { TryArm("F6"); } } } internal bool TryArm(string via, bool quiet = false) { if (_armed) { return true; } if (!Plugin.CfgEnabled.Value) { if (!quiet) { Hud.Toast("Prop Hunt is disabled in config (PropHunt.General.Enabled)."); } return false; } if (!GatesOk(out var reason)) { if (!quiet) { Hud.Toast("Can't start Prop Hunt: " + reason); Plugin.Log.LogWarning((object)("Prop Hunt arm refused: " + reason)); } else if (reason != _lastQuietArmRefusal) { _lastQuietArmRefusal = reason; Plugin.Activity("Server: lazy arm waiting - " + reason); } return false; } _lastQuietArmRefusal = null; _armed = true; EnsureTeamSnapshot(); Plugin.Activity("Server: mode ARMED by host (" + via + ")."); Net.ServerToAll(new PropHuntNet { Op = 5, S = "Prop Hunt enabled - starts next take!" }); SyncDropdownShownValue(armed: true); SetLobbyGamemodeData(armed: true); return true; } internal void Disarm(string via) { if (_armed) { _armed = false; Plugin.Activity("Server: mode DISARMED by host (" + via + ")."); Net.ServerToAll(new PropHuntNet { Op = 5, S = "Prop Hunt disabled" + ((_svPhase != Phase.Idle) ? " - ends after this take" : "") }); SyncDropdownShownValue(armed: false); SetLobbyGamemodeData(armed: false); RequestTeamRestore(); } } private void EnsureTeamSnapshot() { if (_svTeamSnapshotValid) { return; } try { ScoreManager instance = ScoreManager.Instance; if ((Object)(object)instance == (Object)null) { return; } _svTeamSnapshot.Clear(); foreach (KeyValuePair item in instance.PlayerIdToTeamId) { _svTeamSnapshot[item.Key] = item.Value; } _svTeamSnapshotValid = true; Plugin.Activity($"Server: team snapshot taken ({_svTeamSnapshot.Count} entries)."); } catch (Exception e) { Plugin.LogOnce("Manager.TeamSnapshot", e); } } private void RequestTeamRestore() { if (_svTeamSnapshotValid) { _svTeamsRestorePending = true; if (_svTeamsRestoreNotBefore < Time.time) { _svTeamsRestoreNotBefore = Time.time; } } } private void RestoreTeamsServer(string via) { if (!_svTeamSnapshotValid) { return; } _svTeamSnapshotValid = false; _svTeamsRestorePending = false; try { if (!InstanceFinder.IsServer) { Plugin.Activity("Server: team restore skipped - server no longer running."); _svTeamSnapshot.Clear(); return; } ScoreManager instance = ScoreManager.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Activity("Server: team restore skipped - no ScoreManager."); _svTeamSnapshot.Clear(); return; } instance.ResetTeams(); foreach (KeyValuePair item in _svTeamSnapshot) { instance.SetTeamId(item.Key, item.Value); } Plugin.Activity($"Server: vanilla teams restored ({_svTeamSnapshot.Count} entries, {via})."); } catch (Exception e) { Plugin.LogOnce("Manager.TeamRestore", e); } _svTeamSnapshot.Clear(); } private static void SetLobbyGamemodeData(bool armed) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) try { SteamLobby instance = SteamLobby.Instance; if (!((Object)(object)instance == (Object)null) && instance.inSteamLobby) { if (armed) { SteamMatchmaking.SetLobbyData(new CSteamID(instance.CurrentLobbyID), "gamemode", "Prop Hunt"); } else { instance.SetGamemodeString(); } } } catch (Exception e) { Plugin.LogOnce("Manager.LobbyGamemodeData", e); } } private bool GatesOk(out string reason) { reason = null; int count = ClientInstance.playerInstances.Count; if (count < 2) { reason = $"need at least 2 players (have {count})"; return false; } SteamLobby instance = SteamLobby.Instance; if ((Object)(object)instance != (Object)null && instance.players.Count != count) { reason = $"players still connecting ({count} spawned of {instance.players.Count} in the lobby)"; return false; } int num = LocalClientId(); foreach (ClientInstance value2 in ClientInstance.playerInstances.Values) { if ((Object)(object)value2 == (Object)null) { reason = "player list still settling"; return false; } if (value2.ConnectionID != num) { string text = (string.IsNullOrEmpty(value2.PlayerName) ? $"player {value2.PlayerId}" : value2.PlayerName); if (!Net.ModdedClients.TryGetValue(value2.ConnectionID, out var value)) { reason = "waiting on handshake from " + text + " (PropHunt mod missing?)"; return false; } if (value.Version != "1.2.1") { reason = text + " runs " + value.Version + ", need 1.2.1"; return false; } ulong playerSteamID = value2.PlayerSteamID; if (value.SteamId == 0L && playerSteamID != 0L) { value.SteamId = playerSteamID; Net.ModdedClients[value2.ConnectionID] = value; } else if (value.SteamId != 0L && playerSteamID != 0L && value.SteamId != playerSteamID) { reason = "handshake for " + text + " belongs to a different player (reconnect?)"; return false; } } } return true; } private void TryStartPendingTake() { if (!_armed || !Plugin.CfgEnabled.Value) { _takePending = false; RequestTeamRestore(); return; } if (InMenuOrVictory()) { _takePending = false; return; } if (Time.time - _takePendingSince > 20f) { _takePending = false; Plugin.Log.LogWarning((object)"Server: pending take timed out before all players spawned - vanilla take."); return; } if (!GatesOk(out var reason)) { if (!_pendingGateNoticeSent) { _pendingGateNoticeSent = true; Plugin.Log.LogWarning((object)("Prop Hunt suspended this take: " + reason)); Net.ServerToAll(new PropHuntNet { Op = 5, S = "Prop Hunt suspended: " + reason }); } _takePending = false; RequestTeamRestore(); return; } _rosterScratch.Clear(); foreach (KeyValuePair playerInstance in ClientInstance.playerInstances) { if ((Object)(object)playerInstance.Value == (Object)null) { return; } _rosterScratch.Add(playerInstance.Key); } int count = _rosterScratch.Count; if (count < 2) { return; } _rosterScratch.Sort(); int num = Mathf.Clamp(Plugin.CfgSeekerCount.Value, 1, count - 1); if (_svRotationOffset < 0) { _svRotationOffset = Random.Range(0, count); } _svSeekers.Clear(); _svProps.Clear(); _svSeekerConnIds.Clear(); _svPropConnIds.Clear(); for (int i = 0; i < count; i++) { int num2 = _rosterScratch[i]; bool flag = false; for (int j = 0; j < num; j++) { if (i == (_svRotationOffset + j) % count) { flag = true; break; } } if (!ClientInstance.playerInstances.TryGetValue(num2, out var value) || (Object)(object)value == (Object)null) { RollbackTakeScratch(); return; } PlayerHealth val = FindHealthByConn(value.ConnectionID); if (!IsAlive(val)) { RollbackTakeScratch(); return; } if (_svPrevTakePhIds.Contains(((Object)val).GetInstanceID())) { RollbackTakeScratch(); return; } if (flag) { PlayerManager val2 = FindManagerByConn(value.ConnectionID); if ((Object)(object)val2 == (Object)null) { RollbackTakeScratch(); return; } _svSeekers.Add(new SvSeeker { PlayerId = num2, ConnId = value.ConnectionID, Ph = val, Pm = val2 }); _svSeekerConnIds.Add(value.ConnectionID); } else { _svProps.Add(new SvProp { PlayerId = num2, ConnId = value.ConnectionID, Ph = val, LastHealth = val.health }); _svPropConnIds.Add(value.ConnectionID); } } if (_svSeekers.Count == 0 || _svProps.Count == 0) { RollbackTakeScratch(); return; } if (!AssignTeamsServer()) { RollbackTakeScratch(); _takePending = false; Plugin.Log.LogWarning((object)"Prop Hunt suspended this take: team assignment failed (see log)."); Net.ServerToAll(new PropHuntNet { Op = 5, S = "Prop Hunt suspended: team assignment failed" }); return; } _takePending = false; _svTeamsRestorePending = false; _svUnfreezeConnIds.Clear(); _svPhase = Phase.Hide; _svPhaseEnd = Time.time + Mathf.Max(1f, Plugin.CfgHideSeconds.Value); _svRotationOffset++; _svDisguisedProps.Clear(); _svTauntRotIdx = 0; _svTimeoutNoticeSent = false; _svExternalUnfreezeLogged = false; _lastPropDamageTime = -999f; _nextFreezeAssert = 0f; Plugin.Activity("Server: take start - seekers=" + DescribeGroup(_svSeekers) + " " + $"props={DescribeGroup(_svProps)} teams(seek={_svSeekerTeamId} prop={_svPropTeamId}) " + $"hide={Plugin.CfgHideSeconds.Value}s."); Net.ServerToAll(new PropHuntNet { Op = 3, A = _svSeekerTeamId, B = 1, F = Mathf.Max(1f, Plugin.CfgHideSeconds.Value) }); } private void RollbackTakeScratch() { _svSeekers.Clear(); _svProps.Clear(); _svSeekerConnIds.Clear(); _svPropConnIds.Clear(); } private static string DescribeGroup(List group) { StringBuilder stringBuilder = new StringBuilder(64); for (int i = 0; i < group.Count; i++) { AppendPlayer(stringBuilder, i, group[i].PlayerId); } return stringBuilder.ToString(); } private static string DescribeGroup(List group) { StringBuilder stringBuilder = new StringBuilder(64); for (int i = 0; i < group.Count; i++) { AppendPlayer(stringBuilder, i, group[i].PlayerId); } return stringBuilder.ToString(); } private static void AppendPlayer(StringBuilder sb, int index, int pid) { if (index > 0) { sb.Append(", "); } ClientInstance value2; string value = ((ClientInstance.playerInstances.TryGetValue(pid, out value2) && (Object)(object)value2 != (Object)null) ? value2.PlayerName : "?"); sb.Append(value).Append('(').Append(pid) .Append(')'); } private bool AssignTeamsServer() { try { ScoreManager instance = ScoreManager.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Log.LogWarning((object)"Server: no ScoreManager - can't assign prop-hunt teams."); return false; } EnsureTeamSnapshot(); int num = int.MaxValue; for (int i = 0; i < _svSeekers.Count; i++) { num = Mathf.Min(num, _svSeekers[i].PlayerId); } int num2 = int.MaxValue; for (int j = 0; j < _svProps.Count; j++) { num2 = Mathf.Min(num2, _svProps[j].PlayerId); } for (int k = 0; k < _svSeekers.Count; k++) { instance.SetTeamId(_svSeekers[k].PlayerId, num); } for (int l = 0; l < _svProps.Count; l++) { instance.SetTeamId(_svProps[l].PlayerId, num2); } _svSeekerTeamId = num; _svPropTeamId = num2; return true; } catch (Exception e) { Plugin.LogOnce("Manager.AssignTeams", e); return false; } } private void RunPhasesServer() { if (_svPhase == Phase.Hide) { if (Time.time >= _nextFreezeAssert) { _nextFreezeAssert = Time.time + 0.25f; for (int i = 0; i < _svSeekers.Count; i++) { SvSeeker svSeeker = _svSeekers[i]; try { if ((Object)(object)svSeeker.Pm != (Object)null && (Object)(object)svSeeker.Pm.player != (Object)null && svSeeker.Pm.player.canMove) { svSeeker.UnfreezeFlips++; if (svSeeker.UnfreezeFlips >= 2 && !_svExternalUnfreezeLogged) { _svExternalUnfreezeLogged = true; Plugin.Activity("Server: freeze re-asserted (external unfreeze detected - MatchFixes?)."); } } } catch (Exception e) { Plugin.LogOnce("Manager.FreezeDetect", e); } if (!TrySetSeekerMove(svSeeker, state: false)) { EndTakeServer("lost a seeker object during hide"); return; } } } if (!(Time.time >= _svPhaseEnd)) { return; } for (int j = 0; j < _svSeekers.Count; j++) { if (!TrySetSeekerMove(_svSeekers[j], state: true)) { ArmUnfreezeRetry(_svSeekers[j].ConnId, 2f); } } _svPhase = Phase.Hunt; _svHuntStart = Time.time; _svNextTauntK = 1; _svPhaseEnd = Time.time + Mathf.Max(5f, Plugin.CfgHuntSeconds.Value); ApplyPropHealthMultiplier(); Plugin.Activity($"Server: HUNT begins ({Plugin.CfgHuntSeconds.Value}s)."); Net.ServerToAll(new PropHuntNet { Op = 3, A = _svSeekerTeamId, B = 2, F = Mathf.Max(5f, Plugin.CfgHuntSeconds.Value) }); } else { if (_svPhase != Phase.Hunt) { return; } for (int k = 0; k < _svProps.Count; k++) { SvProp svProp = _svProps[k]; if ((Object)(object)svProp.Ph == (Object)null) { svProp.Ph = FindHealthByConn(svProp.ConnId); if ((Object)(object)svProp.Ph != (Object)null) { svProp.LastHealth = svProp.Ph.health; } } if ((Object)(object)svProp.Ph != (Object)null) { float health = svProp.Ph.health; if (health < svProp.LastHealth - 0.0001f) { _lastPropDamageTime = Time.time; } svProp.LastHealth = health; } } for (int l = 0; l < _svSeekers.Count; l++) { SvSeeker svSeeker2 = _svSeekers[l]; if ((Object)(object)svSeeker2.Ph == (Object)null) { svSeeker2.Ph = FindHealthByConn(svSeeker2.ConnId); } if (svSeeker2.PendingPenaltyDue > 0f && Time.time >= svSeeker2.PendingPenaltyDue) { svSeeker2.PendingPenaltyDue = -1f; if (_lastPropDamageTime >= svSeeker2.PendingShotTime) { Plugin.Activity("Server: shot penalty cancelled (a prop was hit)."); } else if ((Object)(object)svSeeker2.Ph != (Object)null && IsAlive(svSeeker2.Ph)) { svSeeker2.LastPenaltyTime = Time.time; float num = Mathf.Max(0f, Plugin.CfgShotPenalty.Value); Plugin.Activity($"Server: shot penalty -{num} HP to seeker {svSeeker2.PlayerId}."); svSeeker2.Ph.RpcLogic___RemoveHealth_431000436(num); } } } TickAutoTauntCueServer(); if (Time.time >= _svPhaseEnd) { TimeoutKillSeekers(); } } } private void TickAutoTauntCueServer() { try { float value = Plugin.CfgAutoTauntInterval.Value; if (value <= 0f) { return; } float num = Mathf.Max(10f, value); float time = Time.time; float num2; while (true) { num2 = _svHuntStart + (float)_svNextTauntK * num; if (num2 >= _svPhaseEnd) { return; } if (!(time >= num2)) { break; } _svNextTauntK++; } if (time < num2 - 3f) { return; } GameManager instance = GameManager.Instance; int count = _svProps.Count; for (int i = 0; i < count; i++) { int num3 = (_svTauntRotIdx + i) % count; SvProp svProp = _svProps[num3]; if (_svDisguisedProps.Contains(svProp.PlayerId) && !((Object)(object)instance == (Object)null) && instance.alivePlayers.Contains(svProp.PlayerId)) { Plugin.Activity($"Server: taunt cue -> prop {svProp.PlayerId} ({_svNextTauntK}x{num:0}s)."); Net.ServerToAll(new PropHuntNet { Op = 7, A = svProp.PlayerId, F = num2 - time }); _svTauntRotIdx = num3 + 1; _svNextTauntK++; break; } } } catch (Exception e) { Plugin.LogOnce("Manager.TauntCue", e); } } private void ApplyPropHealthMultiplier() { try { float num = Mathf.Clamp(Plugin.CfgPropHealthMultiplier.Value, 1f, 4f); if (num <= 1.001f) { return; } for (int i = 0; i < _svProps.Count; i++) { SvProp svProp = _svProps[i]; try { if ((Object)(object)svProp.Ph == (Object)null) { svProp.Ph = FindHealthByConn(svProp.ConnId); } if (!((Object)(object)svProp.Ph == (Object)null) && IsAlive(svProp.Ph)) { float health = svProp.Ph.health; svProp.Ph.sync___set_value_health(health * num, true); svProp.LastHealth = svProp.Ph.health; Plugin.Activity($"Server: prop {svProp.PlayerId} health {health:0.#} -> {svProp.Ph.health:0.#} (x{num:0.##})."); } } catch (Exception e) { Plugin.LogOnce("Manager.PropHealthMult.One", e); } } } catch (Exception e2) { Plugin.LogOnce("Manager.PropHealthMult", e2); } } private void TimeoutKillSeekers() { if (!_svTimeoutNoticeSent) { _svTimeoutNoticeSent = true; Plugin.Activity("Server: hunt timer expired - seekers die, props win the take."); Net.ServerToAll(new PropHuntNet { Op = 5, S = ((_svProps.Count > 1) ? "Time's up - the props survived!" : "Time's up - the prop survived!") }); } Transform val = null; GameManager instance = GameManager.Instance; for (int i = 0; i < _svProps.Count; i++) { SvProp svProp = _svProps[i]; if (!((Object)(object)instance != (Object)null) || instance.alivePlayers.Contains(svProp.PlayerId)) { if ((Object)(object)svProp.Ph == (Object)null) { svProp.Ph = FindHealthByConn(svProp.ConnId); } if ((Object)(object)svProp.Ph != (Object)null) { val = ((Component)svProp.Ph).transform; break; } } } for (int j = 0; j < _svSeekers.Count; j++) { SvSeeker svSeeker = _svSeekers[j]; if (svSeeker.TimeoutKilled) { continue; } try { if ((Object)(object)svSeeker.Ph == (Object)null) { svSeeker.Ph = FindHealthByConn(svSeeker.ConnId); } PlayerHealth ph = svSeeker.Ph; if (!((Object)(object)ph == (Object)null) && IsAlive(ph)) { ph.sync___set_value_isShot(true, true); ph.sync___set_value_health(-8f, true); GameManager.Instance.PlayerDied(svSeeker.PlayerId); if ((Object)(object)val != (Object)null) { ph.sync___set_value_killer(val, true); } svSeeker.TimeoutKilled = true; } } catch (Exception e) { Plugin.LogOnce("Manager.TimeoutKill", e); svSeeker.TimeoutKilled = true; } } } private void EndTakeServer(string reason, bool resolved = false) { if (_svPhase == Phase.Idle) { return; } Plugin.Activity("Server: take end (" + reason + ")."); if (_svPhase == Phase.Hide) { for (int i = 0; i < _svSeekers.Count; i++) { if (!TrySetSeekerMove(_svSeekers[i], state: true)) { ArmUnfreezeRetry(_svSeekers[i].ConnId, 5f); } } } _svPrevTakePhIds.Clear(); for (int j = 0; j < _svSeekers.Count; j++) { if ((Object)(object)_svSeekers[j].Ph != (Object)null) { _svPrevTakePhIds.Add(((Object)_svSeekers[j].Ph).GetInstanceID()); } } for (int k = 0; k < _svProps.Count; k++) { if ((Object)(object)_svProps[k].Ph != (Object)null) { _svPrevTakePhIds.Add(((Object)_svProps[k].Ph).GetInstanceID()); } } _svPhase = Phase.Idle; _svTimeoutNoticeSent = false; _svSeekers.Clear(); _svProps.Clear(); _svSeekerConnIds.Clear(); _svPropConnIds.Clear(); _svDisguisedProps.Clear(); _svSeekerTeamId = -1; _svPropTeamId = -1; _svTeamsRestoreNotBefore = Time.time + 3.5f; if (!_armed) { RequestTeamRestore(); } float num = (resolved ? Mathf.Clamp(Plugin.CfgWinRevealExtraSeconds.Value, 0f, 10f) : 0f); if (num > 0f) { _svRevealHoldUntil = Time.time + 15f; _svRevealHoldExtra = num; Plugin.Activity($"Server: props-win reveal hold armed (+{num:0.#}s on the next map change)."); } else { _svRevealHoldUntil = 0f; _svRevealHoldExtra = 0f; } Net.ServerToAll(new PropHuntNet { Op = 3, A = -1, B = 0, F = num, S = (resolved ? "resolved" : null) }); } internal bool TryConsumeRevealHold(out float deferSeconds) { deferSeconds = 0f; if (_svRevealHoldUntil <= 0f || Time.time >= _svRevealHoldUntil) { return false; } deferSeconds = _svRevealHoldExtra; _svRevealHoldUntil = 0f; _svRevealHoldExtra = 0f; return deferSeconds > 0f; } private void ResetServerState(string reason) { Plugin.Activity("Server: full reset (" + reason + ")."); _armed = false; _takePending = false; _svPhase = Phase.Idle; _svRotationOffset = -1; _svTimeoutNoticeSent = false; _svSeekers.Clear(); _svProps.Clear(); _svSeekerConnIds.Clear(); _svPropConnIds.Clear(); _svDisguisedProps.Clear(); _svSeekerTeamId = -1; _svPropTeamId = -1; _svRevealHoldUntil = 0f; _svRevealHoldExtra = 0f; _svUnfreezeConnIds.Clear(); _svPrevTakePhIds.Clear(); _prevAlive = 0; RestoreTeamsServer(reason); Net.ResetServerHandshake(); } internal void OnNetworkManagerSwapped() { ResetServerState("NetworkManager swapped"); ResetClientState("NetworkManager swapped"); _wasServer = false; _wasClient = false; } private void TickGamemodeDropdown() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown if (Time.time < _nextDropdownScan) { return; } _nextDropdownScan = Time.time + 1f; PauseManager instance = PauseManager.Instance; if ((Object)(object)instance == (Object)null || !instance.inMainMenu) { return; } SteamLobby instance2 = SteamLobby.Instance; if ((Object)(object)instance2 == (Object)null) { return; } TMP_Dropdown gamemodeDropdown = instance2.GamemodeDropdown; if ((Object)(object)gamemodeDropdown == (Object)null) { return; } int num = FindPropHuntOptionIndex(gamemodeDropdown); if (num < 0) { gamemodeDropdown.options.Add(new OptionData("Prop Hunt")); gamemodeDropdown.RefreshShownValue(); Plugin.Activity("Ui: added 'Prop Hunt' to the lobby gamemode dropdown."); } else { if (gamemodeDropdown.value != num || _armed || !InstanceFinder.IsServer || !instance2.inSteamLobby) { return; } if (!Plugin.CfgEnabled.Value) { gamemodeDropdown.SetValueWithoutNotify(0); UndoTeamsSideEffect(instance2); Plugin.Activity("Ui: cleared 'Prop Hunt' dropdown selection (mod disabled in config)."); return; } if (instance2.playingTeams) { UndoTeamsSideEffect(instance2); } TryArm("dropdown-lazy", quiet: true); } } internal static int FindPropHuntOptionIndex(TMP_Dropdown dd) { List list = (((Object)(object)dd != (Object)null) ? dd.options : null); if (list == null) { return -1; } for (int i = 0; i < list.Count; i++) { OptionData val = list[i]; if (val != null && val.text == "Prop Hunt") { return i; } } return -1; } internal static void UndoTeamsSideEffect(SteamLobby lobby) { try { if ((Object)(object)lobby == (Object)null) { return; } lobby.playingTeams = false; if ((Object)(object)GameManager.Instance != (Object)null) { GameManager.Instance.sync___set_value_playingTeams(false, true); if ((Object)(object)ScoreManager.Instance != (Object)null) { ScoreManager.Instance.ResetTeams(); } } if (lobby.inSteamLobby) { lobby.SetGamemodeString(); } } catch (Exception e) { Plugin.LogOnce("Manager.UndoTeams", e); } } private void SyncDropdownShownValue(bool armed) { try { SteamLobby instance = SteamLobby.Instance; TMP_Dropdown val = (((Object)(object)instance != (Object)null) ? instance.GamemodeDropdown : null); if ((Object)(object)val == (Object)null) { return; } int num = FindPropHuntOptionIndex(val); if (num >= 0) { if (armed && val.value != num) { val.SetValueWithoutNotify(num); } else if (!armed && val.value == num) { val.SetValueWithoutNotify(0); } } } catch (Exception e) { Plugin.LogOnce("Manager.SyncDropdown", e); } } private bool TrySetSeekerMove(SvSeeker seeker, bool state) { try { if ((Object)(object)seeker.Pm == (Object)null) { seeker.Pm = FindManagerByConn(seeker.ConnId); } if ((Object)(object)seeker.Pm == (Object)null) { return false; } seeker.Pm.RpcLogic___SetPlayerMove_1140765316(state); return true; } catch (Exception e) { Plugin.LogOnce("Manager.SetSeekerMove", e); return false; } } private void ArmUnfreezeRetry(int connId, float seconds) { if (connId >= 0) { if (!_svUnfreezeConnIds.Contains(connId)) { _svUnfreezeConnIds.Add(connId); } _svUnfreezeDeadline = Time.time + seconds; _svUnfreezeNextAssert = 0f; } } private void TickUnfreezeRetry() { if (_svUnfreezeConnIds.Count == 0) { return; } if (Time.time > _svUnfreezeDeadline) { _svUnfreezeConnIds.Clear(); Plugin.Log.LogWarning((object)"Server: gave up re-asserting canMove=true (player object(s) gone?)."); } else { if (Time.time < _svUnfreezeNextAssert) { return; } _svUnfreezeNextAssert = Time.time + 0.25f; for (int num = _svUnfreezeConnIds.Count - 1; num >= 0; num--) { if (TrySetMoveByConn(_svUnfreezeConnIds[num], state: true)) { Plugin.Activity("Server: deferred unfreeze landed."); _svUnfreezeConnIds.RemoveAt(num); } } } } private bool TrySetMoveByConn(int connId, bool state) { try { PlayerManager val = FindManagerByConn(connId); if ((Object)(object)val == (Object)null) { return false; } val.RpcLogic___SetPlayerMove_1140765316(state); return true; } catch (Exception e) { Plugin.LogOnce("Manager.SetMoveByConn", e); return false; } } internal void ServerOnWeaponFired(Weapon weapon) { if (_svPhase != Phase.Hunt || !Plugin.CfgEnabled.Value) { return; } FirstPersonController playerController = weapon.playerController; if ((Object)(object)playerController == (Object)null || ((NetworkBehaviour)playerController).Owner == (NetworkConnection)null) { return; } int clientId = ((NetworkBehaviour)playerController).Owner.ClientId; if (!_svSeekerConnIds.Contains(clientId)) { return; } SvSeeker svSeeker = null; for (int i = 0; i < _svSeekers.Count; i++) { if (_svSeekers[i].ConnId == clientId) { svSeeker = _svSeekers[i]; break; } } if (svSeeker != null && !(svSeeker.PendingPenaltyDue > 0f) && !(Time.time - svSeeker.LastPenaltyTime < Mathf.Max(0.05f, Plugin.CfgPenaltyWindowSeconds.Value))) { svSeeker.PendingShotTime = Time.time; svSeeker.PendingPenaltyDue = Time.time + Mathf.Max(0.01f, Plugin.CfgHitGraceSeconds.Value); } } internal void ServerOnHealthRemoved(PlayerHealth ph) { if (_svPhase != Phase.Hunt || (Object)(object)ph == (Object)null) { return; } for (int i = 0; i < _svProps.Count; i++) { if ((Object)(object)_svProps[i].Ph == (Object)(object)ph) { _lastPropDamageTime = Time.time; break; } } } internal static bool ServerValidateProp(NetworkConnection conn, out int playerId) { playerId = -1; PropHuntManager instance = Instance; if ((Object)(object)instance == (Object)null || conn == (NetworkConnection)null) { return false; } if (instance._svPhase == Phase.Idle) { return false; } int clientId = conn.ClientId; if (!instance._svPropConnIds.Contains(clientId)) { return false; } for (int i = 0; i < instance._svProps.Count; i++) { if (instance._svProps[i].ConnId == clientId) { playerId = instance._svProps[i].PlayerId; return true; } } return false; } internal void ServerOnPropDisguised(int playerId) { try { if (_svPhase != Phase.Idle) { _svDisguisedProps.Add(playerId); } } catch (Exception e) { Plugin.LogOnce("Manager.PropDisguised", e); } } internal static void ApplyFromServer(PropHuntNet msg) { PropHuntManager instance = Instance; switch (msg.Op) { case 3: { Phase b = (Phase)msg.B; ClientSeekerTeamId = msg.A; ClientPhaseEnd = Time.time + ((b == Phase.Idle) ? 0f : Mathf.Max(0f, msg.F)); ClientPhase = b; LocalRole = ((b != Phase.Idle) ? ComputeLocalRole() : Role.None); if (b != Phase.Idle && LocalRole == Role.None) { Plugin.Log.LogWarning((object)"Client: couldn't resolve local role - degrading to spectator (no gates apply; the 0.5s recheck retries)."); } Plugin.Activity($"Client: state -> {b} (seekerTeam={msg.A}, {msg.F:0}s, localRole={LocalRole})."); if (b != Phase.Idle) { LastModeLiveTime = Time.unscaledTime; } switch (b) { case Phase.Hide: Disguise.Cleanup(); instance?.OnClientHideStart(); break; case Phase.Idle: if (msg.S == "resolved") { float num = Mathf.Clamp(msg.F, 0f, 10f); if (num > 0f) { ClientRevealHoldUntil = Time.time + num + 7f; Plugin.Activity($"Client: props-win reveal hold {num:0.#}s (early loading screen deferred)."); } Disguise.DeferCleanupAll(num); } else { Disguise.Cleanup(); } instance?.OnClientIdle(); break; } break; } case 4: if (ClientPhase != Phase.Idle) { if (Disguise.ApplyFromMessage(msg, logResolutionFailure: false)) { instance?.ClearPendingDisguise(msg.A); } else { instance?.QueueDisguiseRetry(msg); } } break; case 6: if (ClientPhase != Phase.Idle && (LocalRole != Role.Hider || msg.A != LocalPlayerId())) { Disguise.ApplyRotate(msg.A, msg.F); } break; case 7: if (ClientPhase == Phase.Hunt && LocalRole == Role.Hider && msg.A == LocalPlayerId()) { instance?.OnTauntCue(msg.F); } break; case 5: Hud.Toast(msg.S); Plugin.Activity("Client: notice - " + msg.S); break; } } private void OnClientHideStart() { _clDisguiseReadyAt = 0f; _clPendingDisguises.Clear(); ClientDisguisedThisTake = false; _clNeedWeaponDrop = LocalRole == Role.Hider; _clWeaponDropDeadline = Time.time + 8f; _clWeaponDropNotBefore = Time.time + 0.6f; _clTauntCueDue = 0f; Hud.TauntCueAt = 0f; _clNextRoleCheck = Time.time + 0.5f; MarkCachesDirty(); } private void OnClientIdle() { _clNeedWeaponDrop = false; ClientDisguisedThisTake = false; _clTauntCueDue = 0f; Hud.TauntCueAt = 0f; _clPendingDisguises.Clear(); } private void ClientTick() { if (!InstanceFinder.IsClient) { if (_wasClient) { ResetClientState("client stopped"); } _wasClient = false; return; } if (!_wasClient) { _wasClient = true; if (!InstanceFinder.IsServer) { SendHello(); } } if (!InstanceFinder.IsServer && !Net.HostModded && Time.time >= _nextHelloRetry) { _nextHelloRetry = Time.time + 5f; SendHello(); } Disguise.TickPendingCleanup(); if (!ClientModeLive) { return; } LastModeLiveTime = Time.unscaledTime; if (Time.time > ClientPhaseEnd + 30f) { Plugin.Log.LogWarning((object)"Client: phase went stale with no server update - degrading to vanilla."); ClientPhase = Phase.Idle; ClientSeekerTeamId = -1; LocalRole = Role.None; Disguise.Cleanup(); OnClientIdle(); return; } TickRoleRecheck(); if (_clNeedWeaponDrop && LocalRole == Role.Hider) { TickWeaponDrop(); } TickPendingDisguises(); WatchHidersAlive(); if (LocalRole == Role.Hider) { TickDisguiseInput(); TickTauntCue(); } } private void TickRoleRecheck() { if (Time.time < _clNextRoleCheck) { return; } _clNextRoleCheck = Time.time + 0.5f; Role role = ComputeLocalRole(); if (role != Role.None && role != LocalRole) { Plugin.Activity($"Client: local role re-derived {LocalRole} -> {role} (late team sync)."); LocalRole = role; if (role == Role.Hider && ClientPhase == Phase.Hide && !ClientDisguisedThisTake) { _clNeedWeaponDrop = true; _clWeaponDropDeadline = Time.time + 8f; _clWeaponDropNotBefore = Time.time + 0.6f; } else if (role == Role.Seeker) { _clNeedWeaponDrop = false; } } } private void OnTauntCue(float lead) { _clTauntCueDue = Time.time + Mathf.Clamp(lead, 0.05f, 10f); Hud.TauntCueAt = _clTauntCueDue; Plugin.Activity($"Client: taunt cue received (fires in {lead:0.0}s)."); } private void TickTauntCue() { if (_clTauntCueDue <= 0f) { return; } if (ClientPhase != Phase.Hunt) { _clTauntCueDue = 0f; Hud.TauntCueAt = 0f; } else { if (Time.time < _clTauntCueDue) { return; } _clTauntCueDue = 0f; Hud.TauntCueAt = 0f; if (!ClientDisguisedThisTake) { Plugin.Activity("Client: taunt cue skipped (not disguised)."); return; } FirstPersonController localFpc = GetLocalFpc(); if ((Object)(object)localFpc == (Object)null) { return; } PlayerHealth component = ((Component)localFpc).GetComponent(); if (!((Object)(object)component == (Object)null) && !(component.health <= 0f)) { try { Reflect.FpcTauntServer.Invoke(localFpc, _autoTauntArgs); Plugin.Activity("Client: auto-taunt fired (server cue)."); } catch (Exception e) { Plugin.LogOnce("Manager.AutoTaunt", e); } } } } private void SendHello() { if (!InstanceFinder.IsServer) { Net.ClientToServer(new PropHuntNet { Op = 1, S = "1.2.1" }); } } private void ResetClientState(string reason) { Plugin.Activity("Client: full reset (" + reason + ")."); ClientPhase = Phase.Idle; ClientSeekerTeamId = -1; LocalRole = Role.None; ClientDisguisedThisTake = false; ClientRevealHoldUntil = 0f; _clNeedWeaponDrop = false; _clTauntCueDue = 0f; Hud.TauntCueAt = 0f; _clPendingDisguises.Clear(); _clLocalFpc = null; Disguise.Cleanup(); Net.ResetClientHandshake(); MarkCachesDirty(); } private void TickWeaponDrop() { if (Time.time < _clWeaponDropNotBefore) { return; } if (Time.time > _clWeaponDropDeadline) { _clNeedWeaponDrop = false; Plugin.Log.LogWarning((object)"Client: couldn't drop held weapons in time (player object missing?)."); return; } FirstPersonController localFpc = GetLocalFpc(); if ((Object)(object)localFpc == (Object)null) { return; } PlayerPickup playerPickupScript = localFpc.playerPickupScript; if ((Object)(object)playerPickupScript == (Object)null) { return; } try { if (playerPickupScript.hasObjectInHand) { playerPickupScript.RightHandDrop(); } if (playerPickupScript.hasObjectInLeftHand) { playerPickupScript.LeftHandDrop(); } if (!playerPickupScript.hasObjectInHand && !playerPickupScript.hasObjectInLeftHand) { _clNeedWeaponDrop = false; Plugin.Activity("Client: hider hands emptied."); } } catch (Exception e) { Plugin.LogOnce("Manager.WeaponDrop", e); _clNeedWeaponDrop = false; } } private void WatchHidersAlive() { if (Time.time < _clNextHiderWatch) { return; } _clNextHiderWatch = Time.time + 0.25f; List slotList = Disguise.SlotList; for (int num = slotList.Count - 1; num >= 0; num--) { Disguise.HiderSlot hiderSlot = slotList[num]; if (hiderSlot != null && !hiderSlot.DeathHandled && !hiderSlot.CleanupPending) { int num2 = ConnIdForPlayer(hiderSlot.PlayerId); if (num2 >= 0) { PlayerHealth val = FindHealthByConn(num2); if (!((Object)(object)val == (Object)null) && (val.health <= 0f || !((Component)val).gameObject.activeInHierarchy)) { hiderSlot.DeathHandled = true; if (AnyOtherLivingProp(hiderSlot.PlayerId)) { Plugin.Activity($"Client: prop {hiderSlot.PlayerId} died mid-take - deferring its disguise cleanup (props still alive)."); Disguise.DeferCleanupSlot(hiderSlot, freezeFollower: true); } else { Plugin.Activity($"Client: prop {hiderSlot.PlayerId} died resolving the take - immediate cleanup (1.0.11 rule)."); Disguise.CleanupSlot(hiderSlot); } } } } } } private bool AnyOtherLivingProp(int deadPlayerId) { try { ScoreManager instance = ScoreManager.Instance; int num2 = default(int); foreach (KeyValuePair playerInstance in ClientInstance.playerInstances) { int key = playerInstance.Key; if (key == deadPlayerId) { continue; } ClientInstance value = playerInstance.Value; if ((Object)(object)value == (Object)null) { continue; } int num = key; try { if ((Object)(object)instance != (Object)null && ((SyncIDictionary)(object)instance.PlayerIdToTeamId).TryGetValue(key, ref num2)) { num = num2; } } catch { } if (num != ClientSeekerTeamId) { PlayerHealth val = FindHealthByConn(value.ConnectionID); if ((Object)(object)val != (Object)null && val.health > 0f && ((Component)val).gameObject.activeInHierarchy) { return true; } } } return false; } catch (Exception e) { Plugin.LogOnce("Manager.OtherLivingProp", e); return true; } } private void QueueDisguiseRetry(PropHuntNet msg) { try { for (int i = 0; i < _clPendingDisguises.Count; i++) { if (_clPendingDisguises[i].Msg.A == msg.A) { _clPendingDisguises[i] = new PendingDisguise { Msg = msg, Deadline = Time.time + 1.5f, NextAttempt = Time.time + 0.25f }; return; } } _clPendingDisguises.Add(new PendingDisguise { Msg = msg, Deadline = Time.time + 1.5f, NextAttempt = Time.time + 0.25f }); Plugin.Activity($"Client: op-4 for prop {msg.A} didn't resolve - queued for retry ({1.5f:0.#}s)."); } catch (Exception e) { Plugin.LogOnce("Manager.QueueDisguise", e); } } private void ClearPendingDisguise(int propPlayerId) { for (int num = _clPendingDisguises.Count - 1; num >= 0; num--) { if (_clPendingDisguises[num].Msg.A == propPlayerId) { _clPendingDisguises.RemoveAt(num); } } } private void TickPendingDisguises() { for (int num = _clPendingDisguises.Count - 1; num >= 0; num--) { PendingDisguise value = _clPendingDisguises[num]; if (!(Time.time < value.NextAttempt)) { bool flag = Time.time >= value.Deadline; bool flag2 = Disguise.ApplyFromMessage(value.Msg, flag); if (flag2 || flag) { if (flag2) { Plugin.Activity($"Client: op-4 retry for prop {value.Msg.A} settled."); } _clPendingDisguises.RemoveAt(num); } else { value.NextAttempt = Time.time + 0.25f; _clPendingDisguises[num] = value; } } } } private void TickDisguiseInput() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) if (!Input.GetKeyDown(Plugin.CfgDisguiseKey.Value)) { return; } PauseManager instance = PauseManager.Instance; if ((Object)(object)instance != (Object)null && (instance.pause || instance.chatting)) { return; } if (ClientDisguisedThisTake && !Plugin.CfgAllowReDisguise.Value) { Hud.Toast("Re-disguise is disabled."); return; } if (Time.time < _clDisguiseReadyAt) { Hud.Toast($"Re-disguise in {Mathf.CeilToInt(_clDisguiseReadyAt - Time.time)}s..."); return; } FirstPersonController localFpc = GetLocalFpc(); if ((Object)(object)localFpc == (Object)null) { return; } PlayerHealth component = ((Component)localFpc).GetComponent(); if (!((Object)(object)component == (Object)null) && !(component.health <= 0f)) { if (!Disguise.TryPick(localFpc, out var path, out var meshName, out var lossyScale, out var spawnerObjectId, out var fail)) { Hud.Toast((fail != null && fail.EndsWith(".", StringComparison.Ordinal)) ? fail : ("Can't disguise: " + fail)); return; } _clDisguiseReadyAt = Time.time + Mathf.Max(0.2f, Plugin.CfgReDisguiseCooldown.Value); Plugin.Activity($"Client: disguise pick '{path}' (mesh '{meshName}', spawner {spawnerObjectId})."); Net.ClientToServer(new PropHuntNet { Op = 4, A = LocalPlayerId(), B = spawnerObjectId + 1, S = path + "\n" + meshName, V = lossyScale }); } } internal static int LocalClientId() { try { ClientManager clientManager = InstanceFinder.ClientManager; if ((Object)(object)clientManager != (Object)null && clientManager.Connection != (NetworkConnection)null) { return clientManager.Connection.ClientId; } } catch { } return -1; } internal static int ConnIdForPlayer(int playerId) { if (playerId >= 0 && ClientInstance.playerInstances.TryGetValue(playerId, out var value) && (Object)(object)value != (Object)null) { return value.ConnectionID; } return -1; } internal static int LocalPlayerId() { int num = LocalClientId(); if (num < 0) { return -1; } try { foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && value.ConnectionID == num) { return value.PlayerId; } } } catch { } return -1; } internal static ulong SteamIdForConn(int connId) { try { foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && value.ConnectionID == connId) { return value.PlayerSteamID; } } } catch { } return 0uL; } private static Role ComputeLocalRole() { int num = LocalPlayerId(); if (num < 0 || ClientSeekerTeamId < 0) { return Role.None; } int num2 = num; try { ScoreManager instance = ScoreManager.Instance; int num3 = default(int); if ((Object)(object)instance != (Object)null && ((SyncIDictionary)(object)instance.PlayerIdToTeamId).TryGetValue(num, ref num3)) { num2 = num3; } } catch (Exception e) { Plugin.LogOnce("Manager.LocalRoleTeam", e); } if (num2 != ClientSeekerTeamId) { return Role.Hider; } return Role.Seeker; } private static bool IsAlive(PlayerHealth ph) { if ((Object)(object)ph != (Object)null && ph.health > 0f) { return ((Component)ph).gameObject.activeInHierarchy; } return false; } private static bool InMenuOrVictory() { PauseManager instance = PauseManager.Instance; if ((Object)(object)instance != (Object)null) { if (!instance.inMainMenu) { return instance.inVictoryMenu; } return true; } return false; } internal PlayerHealth FindHealthByConn(int connId) { if (connId < 0) { return null; } return ScanCache(ref _phCache, ref _phCacheRefreshedAt, connId); } internal bool IsBindableHealth(PlayerHealth ph) { try { if ((Object)(object)ph == (Object)null) { return false; } if (_svPrevTakePhIds.Contains(((Object)ph).GetInstanceID())) { return false; } if (!((NetworkBehaviour)ph).IsSpawned) { return false; } if (!((Component)ph).gameObject.activeInHierarchy) { return false; } return true; } catch { return false; } } internal PlayerManager FindManagerByConn(int connId) { if (connId < 0) { return null; } return ScanCache(ref _pmCache, ref _pmCacheRefreshedAt, connId); } private static T ScanCache(ref T[] cache, ref float refreshedAt, int connId) where T : NetworkBehaviour { T val = ScanFor(cache, connId); if ((Object)(object)val != (Object)null) { return val; } if (Time.time - refreshedAt < 1f) { return default(T); } refreshedAt = Time.time; cache = Object.FindObjectsOfType(); return ScanFor(cache, connId); } private static T ScanFor(T[] cache, int connId) where T : NetworkBehaviour { if (cache == null) { return default(T); } foreach (T val in cache) { if ((Object)(object)val == (Object)null) { continue; } try { if (((NetworkBehaviour)val).Owner != (NetworkConnection)null && ((NetworkBehaviour)val).Owner.ClientId == connId) { return val; } } catch { } } return default(T); } private FirstPersonController GetLocalFpc() { if ((Object)(object)_clLocalFpc != (Object)null) { try { if (((NetworkBehaviour)_clLocalFpc).IsOwner) { return _clLocalFpc; } } catch { } _clLocalFpc = null; } if (Time.time < _clNextLocalScan) { return null; } _clNextLocalScan = Time.time + 0.5f; try { FirstPersonController[] array = Object.FindObjectsOfType(); foreach (FirstPersonController val in array) { if ((Object)(object)val != (Object)null && ((NetworkBehaviour)val).IsOwner) { _clLocalFpc = val; return val; } } } catch (Exception e) { Plugin.LogOnce("Manager.FindLocalFpc", e); } return null; } } internal static class Reflect { internal static MethodBase PickupHandleInteraction; internal static MethodBase RemoveHealthLogic; internal static MethodBase SetGamemodeDropdown; internal static MethodBase GameSetStartTime; internal static MethodBase PauseStartRoundDelay; internal static MethodBase SceneChangeNetworkScene; internal static MethodBase SceneShowLoadingScreen; internal static MethodBase PauseWriteLog; internal static MethodInfo FpcTauntServer; internal static MethodBase FpcTauntObserversLogic; internal static FieldInfo ItemBobBaseField; internal static FieldInfo FpcAudioField; internal static FieldInfo FpcTauntClipField; internal static FieldInfo FpcNextTauntPlayField; internal static readonly List ShootObserversEffectLogics = new List(); internal static readonly List KillServerLogics = new List(); private const string ShootEffectPrefix = "RpcLogic___ShootObserversEffect_"; private const string KillServerPrefix = "RpcLogic___KillServer_"; private const string TauntObserversPrefix = "RpcLogic___AboubiPlayObservers_"; internal static bool Init() { try { List list = new List(); MethodInfo[] methods = typeof(PlayerPickup).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "HandleInteraction") { list.Add(methodInfo); } } if (list.Count != 1) { Plugin.Log.LogWarning((object)$"Reflect: expected exactly 1 PlayerPickup.HandleInteraction, found {list.Count}."); return false; } PickupHandleInteraction = list[0]; RemoveHealthLogic = AccessTools.Method(typeof(PlayerHealth), "RpcLogic___RemoveHealth_431000436", (Type[])null, (Type[])null); if (RemoveHealthLogic == null) { Plugin.Log.LogWarning((object)"Reflect: PlayerHealth.RpcLogic___RemoveHealth_431000436 not found."); return false; } SetGamemodeDropdown = AccessTools.Method(typeof(SteamLobby), "SetGamemode", new Type[1] { typeof(TMP_Dropdown) }, (Type[])null); if (SetGamemodeDropdown == null) { Plugin.Log.LogWarning((object)"Reflect: SteamLobby.SetGamemode(TMP_Dropdown) not found."); return false; } GameSetStartTime = AccessTools.Method(typeof(GameManager), "SetStartTime", new Type[1] { typeof(float) }, (Type[])null); if (GameSetStartTime == null) { Plugin.Log.LogWarning((object)"Reflect: GameManager.SetStartTime(float) not found."); return false; } PauseStartRoundDelay = AccessTools.Method(typeof(PauseManager), "StartRoundDelay", new Type[1] { typeof(float) }, (Type[])null); if (PauseStartRoundDelay == null) { Plugin.Log.LogWarning((object)"Reflect: PauseManager.StartRoundDelay(float) not found."); return false; } SceneChangeNetworkScene = AccessTools.Method(typeof(SceneMotor), "ChangeNetworkScene", Type.EmptyTypes, (Type[])null); if (SceneChangeNetworkScene == null) { Plugin.Log.LogWarning((object)"Reflect: SceneMotor.ChangeNetworkScene() not found."); return false; } SceneShowLoadingScreen = AccessTools.Method(typeof(SceneMotor), "ShowLoadingScreen", Type.EmptyTypes, (Type[])null); if (SceneShowLoadingScreen == null) { Plugin.Log.LogWarning((object)"Reflect: SceneMotor.ShowLoadingScreen() not found."); return false; } PauseWriteLog = AccessTools.Method(typeof(PauseManager), "WriteLog", new Type[1] { typeof(string) }, (Type[])null); if (PauseWriteLog == null) { Plugin.Log.LogWarning((object)"Reflect: PauseManager.WriteLog(string) not found."); return false; } FpcTauntServer = typeof(FirstPersonController).GetMethod("AboubiPlayServer", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null); FpcAudioField = typeof(FirstPersonController).GetField("audio", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FpcTauntClipField = typeof(FirstPersonController).GetField("tauntClip", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FpcNextTauntPlayField = typeof(FirstPersonController).GetField("nextTauntObserverPlayTime", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FpcTauntObserversLogic = null; methods = typeof(FirstPersonController).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (!methodInfo2.Name.StartsWith("RpcLogic___AboubiPlayObservers_", StringComparison.Ordinal)) { continue; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length != 1 || parameters[0].ParameterType != typeof(int)) { Plugin.Log.LogWarning((object)$"Reflect: skipping unexpected overload FirstPersonController.{methodInfo2.Name} ({parameters.Length} params)."); continue; } if (FpcTauntObserversLogic != null) { Plugin.Log.LogWarning((object)"Reflect: multiple RpcLogic___AboubiPlayObservers_*(int) overloads found."); return false; } FpcTauntObserversLogic = methodInfo2; } if (FpcTauntServer == null || FpcTauntObserversLogic == null || FpcAudioField == null || FpcTauntClipField == null || FpcNextTauntPlayField == null) { Plugin.Log.LogWarning((object)"Reflect: FirstPersonController taunt member(s) not found (AboubiPlayServer(int) / RpcLogic___AboubiPlayObservers_*(int) / audio / tauntClip / nextTauntObserverPlayTime)."); return false; } ItemBobBaseField = typeof(ItemBehaviour).GetField("initialLocalPosition", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (ItemBobBaseField == null || ItemBobBaseField.FieldType != typeof(Vector3)) { ItemBobBaseField = null; Plugin.Log.LogWarning((object)"Reflect: ItemBehaviour.initialLocalPosition not found - spawner-disguise weapon bob uses the reconstructed base (cosmetic)."); } Type[] types; try { types = typeof(Weapon).Assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; Plugin.Log.LogWarning((object)"Reflect: Assembly.GetTypes() partially failed (modded install?) - scanning loadable types only."); } ShootObserversEffectLogics.Clear(); KillServerLogics.Clear(); Type[] array = types; foreach (Type type in array) { if (type == null || !typeof(Weapon).IsAssignableFrom(type)) { continue; } methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo3 in methods) { if (methodInfo3.Name.StartsWith("RpcLogic___ShootObserversEffect_", StringComparison.Ordinal)) { ParameterInfo[] parameters2 = methodInfo3.GetParameters(); if (parameters2.Length == 0 || (parameters2.Length == 1 && parameters2[0].ParameterType == typeof(int))) { ShootObserversEffectLogics.Add(methodInfo3); } else { Plugin.Log.LogWarning((object)$"Reflect: skipping unexpected overload {type.Name}.{methodInfo3.Name} ({parameters2.Length} params)."); } } else if (methodInfo3.Name.StartsWith("RpcLogic___KillServer_", StringComparison.Ordinal)) { ParameterInfo[] parameters3 = methodInfo3.GetParameters(); if (parameters3.Length == 1 && parameters3[0].ParameterType == typeof(PlayerHealth)) { KillServerLogics.Add(methodInfo3); } else { Plugin.Log.LogWarning((object)$"Reflect: skipping unexpected overload {type.Name}.{methodInfo3.Name} ({parameters3.Length} params)."); } } } } if (ShootObserversEffectLogics.Count == 0) { Plugin.Log.LogWarning((object)"Reflect: no RpcLogic___ShootObserversEffect_* methods found on Weapon subclasses."); return false; } if (KillServerLogics.Count == 0) { Plugin.Log.LogWarning((object)"Reflect: no RpcLogic___KillServer_* methods found on Weapon subclasses."); return false; } Plugin.Activity($"Reflect: resolved OK ({ShootObserversEffectLogics.Count} shoot-effect targets, " + $"{KillServerLogics.Count} kill-server targets)."); return true; } catch (Exception ex2) { Plugin.Log.LogError((object)("Reflect.Init failed: " + ex2)); return false; } } } }