using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Api; using Microsoft.CodeAnalysis; using TMPro; using UltimateRevive.Patches; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("UltimateRevive")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("UltimateRevive")] [assembly: AssemblyTitle("UltimateRevive")] [assembly: AssemblyVersion("1.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 UltimateRevive { public static class CadaverBloomTracker { public static readonly Dictionary PlayerBlooms = new Dictionary(); public static readonly Dictionary PlayerBodies = new Dictionary(); public static readonly HashSet PendingBodyCheck = new HashSet(); } public static class DeathMarkerManager { [CompilerGenerated] private sealed class d__31 : IEnumerable<(string Path, bool FromConfiguredPath)>, IEnumerable, IEnumerator<(string Path, bool FromConfiguredPath)>, IEnumerator, IDisposable { private int <>1__state; private (string Path, bool FromConfiguredPath) <>2__current; private int <>l__initialThreadId; private HashSet 5__2; private (string RawPath, bool FromConfiguredPath)[] <>7__wrap2; private int <>7__wrap3; private (string RawPath, bool FromConfiguredPath) 5__5; private string 5__6; (string, bool) IEnumerator<(string, bool)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__31(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>7__wrap2 = null; 5__5 = default((string, bool)); 5__6 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; 5__2 = new HashSet(StringComparer.OrdinalIgnoreCase); (string, bool)[] array = new(string, bool)[2] { (ModConfig.DeathTombAssetBundlePath, true), ("chaps-UltimateRevive/sm_tombstone_3_dark_rip.bundle", false) }; <>7__wrap2 = array; <>7__wrap3 = 0; goto IL_016d; } case 1: <>1__state = -1; goto IL_00f9; case 2: { <>1__state = -1; goto IL_014c; } IL_016d: if (<>7__wrap3 < <>7__wrap2.Length) { 5__5 = <>7__wrap2[<>7__wrap3]; string item = 5__5.RawPath; if (!string.IsNullOrWhiteSpace(item)) { 5__6 = (Path.IsPathRooted(item) ? item : Path.Combine(Paths.PluginPath, item)); if (5__2.Add(5__6)) { <>2__current = (5__6, 5__5.FromConfiguredPath); <>1__state = 1; return true; } goto IL_00f9; } goto IL_015f; } <>7__wrap2 = null; return false; IL_00f9: if (!Path.HasExtension(5__6)) { string text = 5__6 + ".bundle"; if (5__2.Add(text)) { <>2__current = (text, 5__5.FromConfiguredPath); <>1__state = 2; return true; } } goto IL_014c; IL_014c: 5__6 = null; 5__5 = default((string, bool)); goto IL_015f; IL_015f: <>7__wrap3++; goto IL_016d; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(string Path, bool FromConfiguredPath)> IEnumerable<(string, bool)>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__31(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(string, bool)>)this).GetEnumerator(); } } private const float TombWidth = 0.6f; private const float TombHeight = 1.35f; private const float TombDepth = 0.35f; private static readonly Dictionary ActiveMarkers = new Dictionary(); private static readonly Dictionary LastMarkerDecision = new Dictionary(); private static readonly Dictionary MarkerRadarIndexes = new Dictionary(); private static readonly Dictionary TombDeadBodies = new Dictionary(); private static ManualLogSource _log; private static AssetBundle _assetBundle; private static bool _assetBundleLoadAttempted; private static GameObject _cachedPrefab; public static IReadOnlyDictionary TombDeadBodiesPublic => TombDeadBodies; public static void SetLogSource(ManualLogSource logSource) { _log = logSource; } public static void InvalidateBundleCache() { _assetBundleLoadAttempted = false; _cachedPrefab = null; if ((Object)(object)_assetBundle != (Object)null) { _assetBundle.Unload(false); _assetBundle = null; } } public static bool IsTombDeadBody(int playerClientId, DeadBodyInfo deadBody) { if ((Object)(object)deadBody != (Object)null && TombDeadBodies.TryGetValue(playerClientId, out var value)) { return (Object)(object)value == (Object)(object)deadBody; } return false; } public static bool TryGetTombOwnerPlayerId(DeadBodyInfo deadBody, out int playerClientId) { playerClientId = -1; if ((Object)(object)deadBody == (Object)null) { return false; } foreach (KeyValuePair tombDeadBody in TombDeadBodies) { if ((Object)(object)tombDeadBody.Value == (Object)(object)deadBody) { playerClientId = tombDeadBody.Key; return true; } } return false; } private static void DebugLog(string message) { if (ModConfig.EnableDebugLogs) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)message); } } } public static bool IsTombRagdollGrabbable(RagdollGrabbableObject body) { if ((Object)(object)body == (Object)null) { return false; } foreach (DeadBodyInfo value in TombDeadBodies.Values) { if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject == (Object)(object)((Component)body).gameObject) { return true; } } return false; } public static bool TrySpawnTombForPlayer(PlayerControllerB player, Vector3 position, string reason = null) { //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: 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_01d9: Unknown result type (might be due to invalid IL or missing references) if (!ModState.Active || !ModConfig.EnableDeathTombFallback || (Object)(object)player == (Object)null) { return false; } int num = (int)player.playerClientId; if (CadaverBloomTracker.PendingBodyCheck.Contains(num)) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[DeathMarker] Prevent tomb for player={num} (CadaverBloom pending body)"); } return false; } if (ActiveMarkers.TryGetValue(num, out var value) && (Object)(object)value != (Object)null) { DeadBodyInfo component = value.GetComponent(); if ((Object)(object)component != (Object)null) { TombDeadBodies[num] = component; } GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Body, 0uL, value.transform.position, string.IsNullOrWhiteSpace(reason) ? "TombAlreadyActive" : (reason + ":alreadyActive")); ReviveNetworkSync.TryApplyPendingTombPosition(num); return true; } GameObject val = null; try { GameObject markerPrefab = GetMarkerPrefab(); val = (((Object)(object)markerPrefab != (Object)null) ? Object.Instantiate(markerPrefab) : CreateProceduralTombMarker()); } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("Tomb prefab instantiate failed, using procedural marker: " + ex.Message)); } val = CreateProceduralTombMarker(); } if ((Object)(object)val == (Object)null) { return false; } ((Object)val).name = $"UltimateReviveTomb_{num}"; position = ProjectTombPositionToGround(position, num); val.transform.position = position; KeepMarkerUpright(val); DeadBodyInfo val2 = TombDeadBodySetup.SetupTombAsDeadBody(player, val, position); if ((Object)(object)val2 == (Object)null) { Object.Destroy((Object)(object)val); return false; } ActiveMarkers[num] = val; TombDeadBodies[num] = val2; GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Body, 0uL, position, string.IsNullOrWhiteSpace(reason) ? "TombSpawned" : reason); ReviveNetworkSync.TryApplyPendingTombPosition(num); ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)string.Format("[DeathMarker] tombSpawned player={0} pos=({1:0.00},{2:0.00},{3:0.00}) reason={4}", num, position.x, position.y, position.z, string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason)); } return true; } private static Vector3 ProjectTombPositionToGround(Vector3 deathPosition, int playerClientId) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_004d: 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_0078: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_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_0100: 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) float num = Mathf.Max(0f, ModConfig.DeathTombGroundOffset); float num2 = Mathf.Max(1f, ModConfig.DeathTombSpawnHeight); if (TryRaycastGroundBelow(deathPosition + Vector3.up * num2, out var point)) { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(deathPosition.x, point.y + num, deathPosition.z); DebugLog($"[DeathMarker] tombGroundSnap player={playerClientId} " + $"from=({deathPosition.x:0.00},{deathPosition.y:0.00},{deathPosition.z:0.00}) " + $"to=({val.x:0.00},{val.y:0.00},{val.z:0.00})"); return val; } ManualLogSource log = _log; if (log != null) { log.LogWarning((object)($"[DeathMarker] tombGroundSnapFailed player={playerClientId} " + $"pos=({deathPosition.x:0.00},{deathPosition.y:0.00},{deathPosition.z:0.00})")); } return deathPosition; } private static bool TryRaycastGroundBelow(Vector3 origin, out Vector3 point) { //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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) point = Vector3.zero; RaycastHit[] array = Physics.RaycastAll(origin, Vector3.down, 120f, -1, (QueryTriggerInteraction)1); if (array == null || array.Length == 0) { return false; } RaycastHit val = default(RaycastHit); float num = float.PositiveInfinity; bool flag = false; for (int i = 0; i < array.Length; i++) { RaycastHit val2 = array[i]; Collider collider = ((RaycastHit)(ref val2)).collider; if (!((Object)(object)collider == (Object)null) && collider.enabled && !collider.isTrigger && !((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) && ((Component)collider).gameObject.layer != LayerMask.NameToLayer("Enemies")) { Transform root = ((Component)collider).transform.root; if ((!((Object)(object)root != (Object)null) || !((Object)root).name.StartsWith("UltimateReviveTomb_")) && ((RaycastHit)(ref val2)).distance < num) { num = ((RaycastHit)(ref val2)).distance; val = val2; flag = true; } } } if (!flag) { return false; } point = ((RaycastHit)(ref val)).point; return true; } public static void ClearMarker(int playerClientId) { ReviveNetworkSync.ClearPendingTombPosition(playerClientId); if (!ActiveMarkers.TryGetValue(playerClientId, out var value)) { return; } if (TombDeadBodies.TryGetValue(playerClientId, out var value2)) { PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(playerClientId); if ((Object)(object)playerByClientId != (Object)null && (Object)(object)playerByClientId.deadBody == (Object)(object)value2) { playerByClientId.deadBody = null; } TombDeadBodies.Remove(playerClientId); } UnregisterMarkerFromRadar(playerClientId); if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } ActiveMarkers.Remove(playerClientId); } public static bool HasActiveMarker(int playerClientId) { if (ActiveMarkers.TryGetValue(playerClientId, out var value)) { return (Object)(object)value != (Object)null; } return false; } public static bool TryGetMarkerObject(int playerClientId, out GameObject markerObject) { if (ActiveMarkers.TryGetValue(playerClientId, out var value) && (Object)(object)value != (Object)null) { markerObject = value; return true; } markerObject = null; return false; } public static bool TryGetMarkerPosition(int playerClientId, out Vector3 position) { //IL_003a: 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) if (ActiveMarkers.TryGetValue(playerClientId, out var value) && (Object)(object)value != (Object)null && (Object)(object)value.transform != (Object)null) { position = value.transform.position; return true; } position = default(Vector3); return false; } public static void Reset() { ReviveNetworkSync.ClearAllPendingTombPositions(); foreach (KeyValuePair tombDeadBody in TombDeadBodies) { PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(tombDeadBody.Key); if ((Object)(object)playerByClientId != (Object)null && (Object)(object)playerByClientId.deadBody == (Object)(object)tombDeadBody.Value) { playerByClientId.deadBody = null; } } TombDeadBodies.Clear(); foreach (GameObject value in ActiveMarkers.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } ActiveMarkers.Clear(); LastMarkerDecision.Clear(); MarkerRadarIndexes.Clear(); InvalidateBundleCache(); } private static void UnregisterMarkerFromRadar(int playerClientId) { MarkerRadarIndexes.Remove(playerClientId); } private static void KeepMarkerUpright(GameObject marker) { //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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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) if (!((Object)(object)marker == (Object)null) && !((Object)(object)marker.transform == (Object)null)) { Quaternion rotation = marker.transform.rotation; Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles; marker.transform.rotation = Quaternion.Euler(0f, eulerAngles.y, 0f); } } private static GameObject CreateProceduralTombMarker() { //IL_0026: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "ReviveDeathMarker_ProceduralCube"; obj.transform.localScale = new Vector3(0.6f, 1.35f, 0.35f); obj.transform.localPosition = new Vector3(0f, 0.675f, 0f); Renderer component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Material val = new Material(Shader.Find("Standard")); val.color = new Color(0.42f, 0.42f, 0.42f, 1f); component.material = val; } return obj; } private static GameObject GetMarkerPrefab() { if ((Object)(object)_cachedPrefab != (Object)null) { return _cachedPrefab; } if (_assetBundleLoadAttempted) { return null; } _assetBundleLoadAttempted = true; List<(string, bool)> list = BuildCandidateBundlePaths().ToList(); if (list.Count == 0) { return null; } for (int i = 0; i < list.Count; i++) { string item = list[i].Item1; bool flag = !list[i].Item2; if (!File.Exists(item)) { continue; } try { _assetBundle = AssetBundle.LoadFromFile(item); if ((Object)(object)_assetBundle == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Failed to load death marker AssetBundle: " + item)); } continue; } string deathTombPrefabName = ModConfig.DeathTombPrefabName; if (!string.IsNullOrWhiteSpace(deathTombPrefabName)) { _cachedPrefab = _assetBundle.LoadAsset(deathTombPrefabName); } if ((Object)(object)_cachedPrefab == (Object)null) { _cachedPrefab = _assetBundle.LoadAsset("sm_tombstone_3_dark_rip"); } if ((Object)(object)_cachedPrefab == (Object)null) { GameObject[] array = _assetBundle.LoadAllAssets(); _cachedPrefab = ((array != null && array.Length != 0) ? array[0] : null); } if ((Object)(object)_cachedPrefab != (Object)null) { if (flag) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("Configured death marker bundle failed, fallback loaded: " + item)); } } return _cachedPrefab; } ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)("Death marker AssetBundle has no GameObject prefab: " + item)); } _assetBundle.Unload(false); _assetBundle = null; } catch (Exception ex) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogWarning((object)("Death marker loading failed for " + item + ": " + ex.Message)); } if ((Object)(object)_assetBundle != (Object)null) { _assetBundle.Unload(false); _assetBundle = null; } } } ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)"No valid death marker bundle found. Using primitive fallback marker."); } return null; } [IteratorStateMachine(typeof(d__31))] private static IEnumerable<(string Path, bool FromConfiguredPath)> BuildCandidateBundlePaths() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__31(-2); } } public static class GeneralUtil { private static ManualLogSource _log; private static readonly BindingFlags InstanceFieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly FieldInfo BodyPlayerScriptField = typeof(RagdollGrabbableObject).GetField("playerScript", InstanceFieldFlags); private static readonly FieldInfo BodyPlayerObjectScriptField = typeof(RagdollGrabbableObject).GetField("playerObjectScript", InstanceFieldFlags); private static readonly FieldInfo BodySourcePlayerField = typeof(RagdollGrabbableObject).GetField("sourcePlayer", InstanceFieldFlags); public static void SetLogSource(ManualLogSource logSource) { _log = logSource; } public static PlayerControllerB GetPlayerByClientId(int playerClientId) { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return null; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && (int)val.playerClientId == playerClientId) { return val; } } return null; } public static void TryMarkTeleportedIfDead(int playerClientId) { PlayerControllerB playerByClientId = GetPlayerByClientId(playerClientId); if ((Object)(object)playerByClientId != (Object)null && playerByClientId.isPlayerDead) { MarkTeleported(playerClientId); } } public static PlayerControllerB GetClosestAlivePlayer(Vector3 position) { //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) if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return null; } PlayerControllerB result = null; float num = float.MaxValue; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && val.isPlayerControlled) { float num2 = Vector3.Distance(((Component)val).transform.position, position); if (num2 < num) { num = num2; result = val; } } } return result; } public static RagdollGrabbableObject GetBodyForPlayer(int playerClientId) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId); if (orCreateInfo.LastDeathNetworkObjectId != 0L) { RagdollGrabbableObject bodyByNetworkObjectId = GetBodyByNetworkObjectId(orCreateInfo.LastDeathNetworkObjectId); if ((Object)(object)bodyByNetworkObjectId != (Object)null) { return bodyByNetworkObjectId; } } if (orCreateInfo.HasLastDeathPosition) { RagdollGrabbableObject bodyNearPosition = GetBodyNearPosition(orCreateInfo.LastDeathPosition); if ((Object)(object)bodyNearPosition != (Object)null) { return bodyNearPosition; } } RagdollGrabbableObject bodyFromDeadBodyReference = GetBodyFromDeadBodyReference(GetPlayerByClientId(playerClientId)); if ((Object)(object)bodyFromDeadBodyReference != (Object)null) { return bodyFromDeadBodyReference; } RagdollGrabbableObject[] array = Object.FindObjectsOfType(); foreach (RagdollGrabbableObject val in array) { if (TryGetBodyOwner(val, out var targetPlayerClientId, out var _) && targetPlayerClientId == playerClientId) { return val; } } return null; } public static RagdollGrabbableObject GetBodyFromDeadBodyReference(PlayerControllerB player) { if ((Object)(object)player?.deadBody == (Object)null) { return null; } DeadBodyInfo deadBody = player.deadBody; RagdollGrabbableObject val = ((Component)deadBody).GetComponent() ?? ((Component)deadBody).GetComponentInChildren() ?? ((Component)deadBody).GetComponentInParent(); if ((Object)(object)val != (Object)null) { return val; } if ((Object)(object)((Component)deadBody).transform == (Object)null) { return null; } RagdollGrabbableObject[] array = Object.FindObjectsOfType(); foreach (RagdollGrabbableObject val2 in array) { if (!((Object)(object)((val2 != null) ? ((Component)val2).transform : null) == (Object)null) && ((Object)(object)((Component)val2).transform == (Object)(object)((Component)deadBody).transform || ((Component)val2).transform.IsChildOf(((Component)deadBody).transform) || ((Component)deadBody).transform.IsChildOf(((Component)val2).transform))) { return val2; } } return null; } public static RagdollGrabbableObject GetBodyByNetworkObjectId(ulong networkObjectId) { if (networkObjectId == 0L) { return null; } if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.SpawnManager != null && NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var value) && (Object)(object)value != (Object)null) { RagdollGrabbableObject component = ((Component)value).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)value).GetComponentInChildren(); if ((Object)(object)component != (Object)null) { return component; } } RagdollGrabbableObject[] array = Object.FindObjectsOfType(); foreach (RagdollGrabbableObject val in array) { if ((Object)(object)((val != null) ? ((NetworkBehaviour)val).NetworkObject : null) != (Object)null && ((NetworkBehaviour)val).NetworkObject.NetworkObjectId == networkObjectId) { return val; } } return null; } public static RagdollGrabbableObject GetBodyNearPosition(Vector3 position, float maxDistance = 12f) { //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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) RagdollGrabbableObject[] array = Object.FindObjectsOfType(); RagdollGrabbableObject result = null; float num = maxDistance * maxDistance; RagdollGrabbableObject[] array2 = array; foreach (RagdollGrabbableObject val in array2) { if (!((Object)(object)((val != null) ? ((Component)val).transform : null) == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = val; } } } return result; } public static DeadBodyInfo GetDeadBodyNearPosition(Vector3 position, float maxDistance = 12f) { //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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) DeadBodyInfo[] array = Object.FindObjectsOfType(); DeadBodyInfo result = null; float num = maxDistance * maxDistance; DeadBodyInfo[] array2 = array; foreach (DeadBodyInfo val in array2) { if (!((Object)(object)((val != null) ? ((Component)val).transform : null) == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = val; } } } return result; } public static bool TryGetBodyOwner(RagdollGrabbableObject body, out int targetPlayerClientId, out PlayerControllerB targetPlayer) { targetPlayerClientId = -1; targetPlayer = null; if ((Object)(object)body == (Object)null) { return false; } targetPlayer = body.ragdoll?.playerScript; if ((Object)(object)targetPlayer != (Object)null) { targetPlayerClientId = (int)targetPlayer.playerClientId; return true; } object? obj = BodyPlayerScriptField?.GetValue(body); PlayerControllerB val = (PlayerControllerB)(((obj is PlayerControllerB) ? obj : null) ?? ((object)(/*isinst with value type is only supported in some contexts*/ ?? /*isinst with value type is only supported in some contexts*/))); if ((Object)(object)val != (Object)null) { targetPlayer = val; targetPlayerClientId = (int)val.playerClientId; return true; } if (StartOfRound.Instance?.allPlayerScripts != null) { PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (!((Object)(object)val2 == (Object)null)) { DeadBodyInfo deadBody = val2.deadBody; if (!((Object)(object)deadBody == (Object)null) && (deadBody == body || ((Object)(object)((Component)deadBody).transform != (Object)null && (Object)(object)((Component)body).transform != (Object)null && ((Object)(object)((Component)deadBody).transform == (Object)(object)((Component)body).transform || ((Component)deadBody).transform.IsChildOf(((Component)body).transform) || ((Component)body).transform.IsChildOf(((Component)deadBody).transform))))) { targetPlayer = val2; targetPlayerClientId = (int)val2.playerClientId; return true; } } } } return false; } public static PlayerReviveInfo GetOrCreateInfo(int playerClientId) { //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) PlayerReviveInfo playerReviveInfo = ModState.PlayerInfos.FirstOrDefault((PlayerReviveInfo x) => x.PlayerClientId == playerClientId); if (playerReviveInfo != null) { return playerReviveInfo; } playerReviveInfo = new PlayerReviveInfo { PlayerClientId = playerClientId, TimeDiedAt = Time.time, LastDeathType = PlayerDeathType.Unknown, LastDeathNetworkObjectId = 0uL, LastDeathPosition = Vector3.zero, HasLastDeathPosition = false }; ModState.PlayerInfos.Add(playerReviveInfo); return playerReviveInfo; } public static void ResetAllPlayerInfos() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) ModState.PlayerInfos.Clear(); if (StartOfRound.Instance?.allPlayerScripts == null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null)) { ModState.PlayerInfos.Add(new PlayerReviveInfo { PlayerClientId = (int)val.playerClientId, TimeDiedAt = Time.time, HasBeenTeleported = false, TimesRevivedThisLevel = 0, LastDeathType = PlayerDeathType.Unknown, LastDeathNetworkObjectId = 0uL, LastDeathPosition = Vector3.zero, HasLastDeathPosition = false }); } } } public static void SetPlayerDeathState(int playerClientId, PlayerDeathType deathType, ulong networkObjectId, Vector3 deathPosition, string reason = null) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) SetPlayerDeathState(playerClientId, deathType, networkObjectId, deathPosition, hasPosition: true, reason, skipDeathTimeUpdate: false); } public static void SetPlayerDeathState(int playerClientId, PlayerDeathType deathType, ulong networkObjectId, Vector3 deathPosition, bool hasPosition, string reason) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) SetPlayerDeathState(playerClientId, deathType, networkObjectId, deathPosition, hasPosition, reason, skipDeathTimeUpdate: false); } public static void SetPlayerDeathState(int playerClientId, PlayerDeathType deathType, ulong networkObjectId, Vector3 deathPosition, bool hasPosition, string reason, bool skipDeathTimeUpdate) { //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_0055: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId); PlayerDeathType lastDeathType = orCreateInfo.LastDeathType; ulong lastDeathNetworkObjectId = orCreateInfo.LastDeathNetworkObjectId; bool hasLastDeathPosition = orCreateInfo.HasLastDeathPosition; Vector3 lastDeathPosition = orCreateInfo.LastDeathPosition; if (!skipDeathTimeUpdate) { orCreateInfo.TimeDiedAt = Time.time; } orCreateInfo.LastDeathType = deathType; orCreateInfo.LastDeathNetworkObjectId = networkObjectId; orCreateInfo.HasLastDeathPosition = hasPosition; orCreateInfo.LastDeathPosition = (hasPosition ? deathPosition : Vector3.zero); if (lastDeathType == deathType && lastDeathNetworkObjectId == networkObjectId && hasLastDeathPosition == hasPosition) { if (!hasPosition) { return; } if (hasLastDeathPosition) { Vector3 val = lastDeathPosition - deathPosition; if (!(((Vector3)(ref val)).sqrMagnitude > 0.0025f)) { return; } } } string text = (hasLastDeathPosition ? $"{lastDeathType}/obj:{lastDeathNetworkObjectId}/pos:{FormatVector(lastDeathPosition)}" : $"{lastDeathType}/obj:{lastDeathNetworkObjectId}/pos:none"); string text2 = (hasPosition ? $"{deathType}/obj:{networkObjectId}/pos:{FormatVector(deathPosition)}" : $"{deathType}/obj:{networkObjectId}/pos:none"); Plugin.DebugLog(string.Format("[DeathState] player={0} from={1} to={2} reason={3}", playerClientId, text, text2, string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason)); } private static string FormatVector(Vector3 value) { //IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references) return $"({value.x:0.00},{value.y:0.00},{value.z:0.00})"; } public static PlayerDeathType GetPlayerLastDeathType(int playerClientId) { return GetOrCreateInfo(playerClientId).LastDeathType; } public static ulong GetPlayerLastDeathNetworkObjectId(int playerClientId) { return GetOrCreateInfo(playerClientId).LastDeathNetworkObjectId; } public static bool TryGetPlayerLastDeathPosition(int playerClientId, out Vector3 position) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId); position = orCreateInfo.LastDeathPosition; return orCreateInfo.HasLastDeathPosition; } public static void ClearPlayerDeathState(int playerClientId) { //IL_0016: 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) PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId); orCreateInfo.LastDeathType = PlayerDeathType.Unknown; orCreateInfo.LastDeathNetworkObjectId = 0uL; orCreateInfo.LastDeathPosition = Vector3.zero; orCreateInfo.HasLastDeathPosition = false; orCreateInfo.TimeDiedAt = 0f; } public static float GetPlayerDiedAtTime(int playerClientId) { return GetOrCreateInfo(playerClientId).TimeDiedAt; } public static void MarkTeleported(int playerClientId) { GetOrCreateInfo(playerClientId).HasBeenTeleported = true; } public static bool HasPlayerTeleported(int playerClientId) { return GetOrCreateInfo(playerClientId).HasBeenTeleported; } public static int IncrementReviveCountAndGetHealth(int playerClientId) { PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId); orCreateInfo.TimesRevivedThisLevel++; return Mathf.Max(1, ModConfig.ReviveHealth - (orCreateInfo.TimesRevivedThisLevel - 1) * ModConfig.ExtraHealthLostPerRevive); } public static bool CanRevivePlayer(int targetPlayerClientId, out string reason) { reason = null; switch (ModConfig.CurrentReviveMode) { case ModConfig.ReviveMode.Unlimited: return true; case ModConfig.ReviveMode.PerLevelMultiplier: if (ModState.RemainingRevives > 0) { return true; } reason = "no revives left "; return false; case ModConfig.ReviveMode.FixedPerLevel: if (ModState.RemainingRevives > 0) { return true; } reason = "no revives left"; return false; case ModConfig.ReviveMode.PerPlayer: if (GetOrCreateInfo(targetPlayerClientId).RemainingRevivesForPlayer > 0) { return true; } reason = "no revives left for this player"; return false; default: reason = "unknown revive limit mode"; return false; } } public static bool TryConsumePlayerRevive(int playerClientId, out int revivesLeft) { PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId); if (ModConfig.CurrentReviveMode != ModConfig.ReviveMode.PerPlayer) { revivesLeft = int.MaxValue; return true; } if (orCreateInfo.RemainingRevivesForPlayer > 0) { orCreateInfo.RemainingRevivesForPlayer--; revivesLeft = orCreateInfo.RemainingRevivesForPlayer; return true; } revivesLeft = 0; return false; } public static void InitializeReviveLimits() { int num = 0; if (StartOfRound.Instance?.allPlayerScripts != null) { num = StartOfRound.Instance.allPlayerScripts.Count((PlayerControllerB p) => (Object)(object)p != (Object)null && p.isPlayerControlled); } switch (ModConfig.CurrentReviveMode) { case ModConfig.ReviveMode.Unlimited: { ModState.RemainingRevives = int.MaxValue; foreach (PlayerReviveInfo playerInfo in ModState.PlayerInfos) { playerInfo.RemainingRevivesForPlayer = int.MaxValue; } ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)"[UltimateRevive] ReviveMode=Unlimited, RemainingRevives=∞"); } break; } case ModConfig.ReviveMode.PerLevelMultiplier: { ModState.RemainingRevives = Mathf.RoundToInt((float)num * ModConfig.RevivesPerLevelMultiplier); foreach (PlayerReviveInfo playerInfo2 in ModState.PlayerInfos) { playerInfo2.RemainingRevivesForPlayer = int.MaxValue; } ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)$"[UltimateRevive] ReviveMode=PerLevelMultiplier, RemainingRevives={ModState.RemainingRevives}"); } break; } case ModConfig.ReviveMode.FixedPerLevel: { ModState.RemainingRevives = ModConfig.FixedRevivesPerLevel; foreach (PlayerReviveInfo playerInfo3 in ModState.PlayerInfos) { playerInfo3.RemainingRevivesForPlayer = int.MaxValue; } ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"[UltimateRevive] ReviveMode=FixedPerLevel, RemainingRevives={ModState.RemainingRevives}"); } break; } case ModConfig.ReviveMode.PerPlayer: { ModState.RemainingRevives = int.MaxValue; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null)) { GetOrCreateInfo((int)val.playerClientId).RemainingRevivesForPlayer = ModConfig.RevivesPerPlayer; } } ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[UltimateRevive] ReviveMode=PerPlayer, RevivesPerPlayer={ModConfig.RevivesPerPlayer}"); } break; } } } public static bool IsNetworkReady() { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.CustomMessagingManager != null) { return NetworkManager.Singleton.IsListening; } return false; } public static float GetReviveValidationDistance(PlayerControllerB reviver) { if ((Object)(object)reviver == (Object)null) { return 0f; } return Mathf.Max(0.1f, ModConfig.MaxReviveDistance); } public static float GetTombReviveValidationDistance(PlayerControllerB reviver) { if ((Object)(object)reviver == (Object)null) { return 0f; } return Mathf.Max(0.1f, ModConfig.MaxReviveDistanceTomb); } public static bool IsReviverCloseEnough(PlayerControllerB reviver, Vector3 targetPosition) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)reviver == (Object)null) { return false; } float reviveValidationDistance = GetReviveValidationDistance(reviver); return IsReviverCloseEnoughInternal(reviver, targetPosition, reviveValidationDistance); } public static bool IsReviverCloseEnoughToTomb(PlayerControllerB reviver, Vector3 targetPosition) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)reviver == (Object)null) { return false; } float tombReviveValidationDistance = GetTombReviveValidationDistance(reviver); return IsReviverCloseEnoughInternal(reviver, targetPosition, tombReviveValidationDistance); } private static bool IsReviverCloseEnoughInternal(PlayerControllerB reviver, Vector3 targetPosition, float maxDistance) { //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_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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; Vector3 val2; if ((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)reviver && (Object)(object)reviver.gameplayCamera != (Object)null) { val2 = ((Component)reviver.gameplayCamera).transform.position; } else { if (!((Object)(object)((Component)reviver).transform != (Object)null)) { return false; } val2 = ((Component)reviver).transform.position + Vector3.up * 1.6f; maxDistance += 1.5f; } return Vector3.Distance(val2, targetPosition) <= maxDistance; } public static MaskedPlayerEnemy GetConvertedMaskedForPlayer(int playerClientId, bool requireDead = false) { if (!ModConfig.EnableMaskedPlayerRevive) { return null; } MaskedPlayerEnemy[] array = Object.FindObjectsOfType(); foreach (MaskedPlayerEnemy val in array) { if (!((Object)(object)val == (Object)null) && (!requireDead || MaskedConversionTracker.IsMaskedDead(val)) && TryResolveConvertedMaskedPlayer(val, out var playerClientId2, out var _) && playerClientId2 == playerClientId) { return val; } } return null; } public static MaskedPlayerEnemy GetConvertedMaskedCorpseForPlayer(int playerClientId) { return GetConvertedMaskedForPlayer(playerClientId, requireDead: true); } public static bool TryResolveConvertedMaskedPlayer(MaskedPlayerEnemy masked, out int playerClientId, out string source) { playerClientId = -1; source = "none"; if ((Object)(object)masked == (Object)null) { return false; } if (ModConfig.EnableMaskedPlayerRevive && MaskedConversionTracker.IsConvertedMasked(masked, out playerClientId)) { source = "InternalTag"; return true; } return false; } public static MaskedPlayerEnemy GetMaskedByNetworkObjectId(ulong networkObjectId) { if (networkObjectId == 0L) { return null; } if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.SpawnManager != null && NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var value) && (Object)(object)value != (Object)null) { MaskedPlayerEnemy component = ((Component)value).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)value).GetComponentInChildren(); if ((Object)(object)component != (Object)null) { return component; } } MaskedPlayerEnemy[] array = Object.FindObjectsOfType(); foreach (MaskedPlayerEnemy val in array) { if (!((Object)(object)val == (Object)null)) { NetworkObject component2 = ((Component)val).GetComponent(); if ((Object)(object)component2 != (Object)null && component2.NetworkObjectId == networkObjectId) { return val; } } } return null; } } public static class MaskedConversionTracker { internal sealed class PendingConversion { public int PlayerClientId; public MaskedOrigin Origin; public Vector3 Position; public float TimeRegistered; } public static readonly Dictionary MaskedByPlayerId = ModState.MaskedByPlayerId; private const float PendingLifetimeSeconds = 20f; private static readonly List PendingConversions = new List(); private static ManualLogSource _log; public static void SetLogSource(ManualLogSource logSource) { _log = logSource; } public static void Reset() { PendingConversions.Clear(); ModState.MaskedByNetworkObjectId.Clear(); } public static int? GetPlayerIdForMasked(MaskedPlayerEnemy masked) { if ((Object)(object)masked == (Object)null) { return null; } if ((Object)(object)masked.mimickingPlayer != (Object)null) { return (int)masked.mimickingPlayer.playerClientId; } return null; } public static bool HasPendingForPlayer(int playerClientId) { CleanupExpiredPending(); return PendingConversions.Any((PendingConversion x) => x.PlayerClientId == playerClientId); } public static MaskedPlayerEnemy GetMaskedForPlayer(int playerClientId) { MaskedPlayerEnemy[] array = Object.FindObjectsOfType(); foreach (MaskedPlayerEnemy val in array) { MaskedOriginInfo component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.FormerPlayerClientId == playerClientId && (component.Origin == MaskedOrigin.ConvertedByMaskItem || component.Origin == MaskedOrigin.ConvertedByMaskedEnemy)) { return val; } } return null; } public static ulong GetNetworkObjectId(MaskedPlayerEnemy masked) { if ((Object)(object)masked == (Object)null) { return 0uL; } if ((Object)(object)((masked != null) ? ((NetworkBehaviour)masked).NetworkObject : null) == (Object)null) { return 0uL; } return ((NetworkBehaviour)masked).NetworkObject.NetworkObjectId; } public static void TagMaskedAsConverted(MaskedPlayerEnemy masked, int playerClientId, MaskedOrigin origin, string reason = null) { //IL_0078: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)masked == (Object)null) && ModConfig.EnableMaskedPlayerRevive && playerClientId >= 0) { if (origin != MaskedOrigin.ConvertedByMaskItem && origin != MaskedOrigin.ConvertedByMaskedEnemy) { origin = MaskedOrigin.ConvertedByMaskedEnemy; } MaskedOriginInfo orAddInfo = GetOrAddInfo(masked); orAddInfo.Origin = origin; orAddInfo.FormerPlayerClientId = playerClientId; orAddInfo.TaggedAt = Time.time; ulong networkObjectId = GetNetworkObjectId(masked); Vector3 val = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : Vector3.zero); ModState.MaskedInfo value = new ModState.MaskedInfo { PlayerClientId = playerClientId, Masked = masked, NetworkObjectId = networkObjectId, LastPosition = val, IsDead = false, LastUpdateTime = Time.time }; MaskedByPlayerId[playerClientId] = value; ModState.MaskedByNetworkObjectId[networkObjectId] = value; PendingConversions.RemoveAll((PendingConversion x) => x.PlayerClientId == playerClientId); string reason2 = (string.IsNullOrWhiteSpace(reason) ? "MaskedTag:direct" : reason); string text = (string.IsNullOrWhiteSpace(reason) ? "direct" : reason); GeneralUtil.SetPlayerDeathState(playerClientId, PlayerDeathType.Masked, networkObjectId, val, reason2); ReviveInteractTriggerManager.ForceRefreshPlayer(playerClientId, reason2); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Tagged Masked {networkObjectId} as {origin} from player {playerClientId} ({text})."); } } } public static void RegisterPending(PlayerControllerB player, MaskedOrigin origin) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.EnableMaskedPlayerRevive && !((Object)(object)player == (Object)null) && (origin == MaskedOrigin.ConvertedByMaskItem || origin == MaskedOrigin.ConvertedByMaskedEnemy)) { int playerClientId = (int)player.playerClientId; Vector3 val = (((Object)(object)((Component)player).transform != (Object)null) ? ((Component)player).transform.position : Vector3.zero); PendingConversions.RemoveAll((PendingConversion x) => x.PlayerClientId == playerClientId); PendingConversions.Add(new PendingConversion { PlayerClientId = playerClientId, Origin = origin, Position = val, TimeRegistered = Time.time }); GeneralUtil.SetPlayerDeathState(playerClientId, PlayerDeathType.Masked, 0uL, val, $"MaskedPending:{origin}"); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Registered pending Masked conversion: player={playerClientId}, origin={origin}."); } } } public static bool TryTagMaskedFromPending(MaskedPlayerEnemy masked) { if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive) { return false; } CleanupExpiredPending(); if (PendingConversions.Count == 0) { return false; } PendingConversion pendingConversion = null; int mimicId = GetMimickingPlayerClientId(masked); if (mimicId >= 0) { pendingConversion = PendingConversions.FirstOrDefault((PendingConversion x) => x.PlayerClientId == mimicId); } if (pendingConversion == null) { return false; } ApplyConvertedTag(masked, GetOrAddInfo(masked), pendingConversion); return true; } public static void TagMaskedOnSpawn(MaskedPlayerEnemy masked) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive) { return; } MaskedOriginInfo orAddInfo = GetOrAddInfo(masked); if (orAddInfo.Origin != MaskedOrigin.ConvertedByMaskItem && orAddInfo.Origin != MaskedOrigin.ConvertedByMaskedEnemy) { int? playerIdForMasked = GetPlayerIdForMasked(masked); if (playerIdForMasked.HasValue && MaskedByPlayerId.TryGetValue(playerIdForMasked.Value, out var value)) { value.Masked = masked; value.NetworkObjectId = GetNetworkObjectId(masked); value.LastPosition = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : Vector3.zero); value.IsDead = false; value.LastUpdateTime = Time.time; } if (!TryTagMaskedFromPending(masked) && orAddInfo.Origin == MaskedOrigin.Unknown) { orAddInfo.Origin = MaskedOrigin.NaturalSpawn; orAddInfo.FormerPlayerClientId = -1; orAddInfo.TaggedAt = Time.time; } } } public static bool IsConvertedMasked(MaskedPlayerEnemy masked, out int playerClientId) { playerClientId = -1; if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive) { return false; } MaskedOriginInfo component = ((Component)masked).GetComponent(); if ((Object)(object)component == (Object)null) { TagMaskedOnSpawn(masked); component = ((Component)masked).GetComponent(); } if ((Object)(object)component == (Object)null) { return false; } if (component.Origin != MaskedOrigin.ConvertedByMaskItem && component.Origin != MaskedOrigin.ConvertedByMaskedEnemy) { return false; } playerClientId = component.FormerPlayerClientId; return playerClientId >= 0; } public static bool IsMaskedDead(MaskedPlayerEnemy masked) { if ((Object)(object)masked != (Object)null) { return ((EnemyAI)masked).isEnemyDead; } return false; } public static void OnMaskedDeath(MaskedPlayerEnemy masked) { //IL_004c: 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_0051: Unknown result type (might be due to invalid IL or missing references) int? playerIdForMasked = GetPlayerIdForMasked(masked); if (playerIdForMasked.HasValue && MaskedByPlayerId.TryGetValue(playerIdForMasked.Value, out var value)) { value.IsDead = true; value.LastPosition = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : value.LastPosition); value.LastUpdateTime = Time.time; ModState.BodyByPlayerId[playerIdForMasked.Value] = null; ulong networkObjectId = GetNetworkObjectId(masked); if (ModState.MaskedByNetworkObjectId.ContainsKey(networkObjectId)) { ModState.MaskedByNetworkObjectId.Remove(networkObjectId); } } } public static void ForgetPlayer(int playerClientId) { PendingConversions.RemoveAll((PendingConversion x) => x.PlayerClientId == playerClientId); } private static void ApplyConvertedTag(MaskedPlayerEnemy masked, MaskedOriginInfo info, PendingConversion pending) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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) info.Origin = pending.Origin; info.FormerPlayerClientId = pending.PlayerClientId; info.TaggedAt = Time.time; ulong networkObjectId = GetNetworkObjectId(masked); Vector3 deathPosition = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : pending.Position); GeneralUtil.SetPlayerDeathState(pending.PlayerClientId, PlayerDeathType.Masked, networkObjectId, deathPosition, "MaskedTagAssigned"); ReviveInteractTriggerManager.ForceRefreshPlayer(pending.PlayerClientId, "MaskedTagAssigned"); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Tagged Masked {GetNetworkObjectId(masked)} as {pending.Origin} from player {pending.PlayerClientId}."); } } private static MaskedOriginInfo GetOrAddInfo(MaskedPlayerEnemy masked) { MaskedOriginInfo maskedOriginInfo = ((Component)masked).GetComponent(); if ((Object)(object)maskedOriginInfo == (Object)null) { maskedOriginInfo = ((Component)masked).gameObject.AddComponent(); } return maskedOriginInfo; } private static int GetMimickingPlayerClientId(MaskedPlayerEnemy masked) { try { PlayerControllerB value = Traverse.Create((object)masked).Field("mimickingPlayer").GetValue(); if ((Object)(object)value != (Object)null) { return (int)value.playerClientId; } } catch { } return -1; } private static void CleanupExpiredPending() { float now = Time.time; PendingConversions.RemoveAll((PendingConversion x) => now - x.TimeRegistered > 20f); } } public enum MaskedOrigin { Unknown, NaturalSpawn, ConvertedByMaskItem, ConvertedByMaskedEnemy } public sealed class MaskedOriginInfo : MonoBehaviour { public MaskedOrigin Origin; public int FormerPlayerClientId = -1; public float TaggedAt; } public static class ModConfig { public enum ReviveMode { Unlimited, PerLevelMultiplier, FixedPerLevel, PerPlayer } public static Color TombMonitorDotColor = new Color(0.15f, 0.75f, 1f, 1f); public const string DefaultDeathTombBundlePath = "chaps-UltimateRevive/sm_tombstone_3_dark_rip.bundle"; public const string DefaultDeathTombPrefabName = "sm_tombstone_3_dark_rip"; public static float ReviveTime; public static bool CanPickUpBodies; public static float DeadBodyWeight; public static bool CanReviveTeleportedBodies; public static int ReviveHealth; public static int ExtraHealthLostPerRevive; public static float RevivesPerLevelMultiplier; public static int FixedRevivesPerLevel; public static int RevivesPerPlayer; public static bool InfiniteReviveTime; public static int TimeUntilCannotBeRevived; public static float MaxReviveDistance; public static float MaxReviveDistanceTomb; public static bool EnableMaskedPlayerRevive; public static bool EnableDeathTombFallback; public static bool EnableDeathTombCollider; public static string DeathTombAssetBundlePath; public static string DeathTombPrefabName; public static float DeathTombGroundOffset; public static float DeathTombDownOffset; public static float DeathTombSpawnHeight; public static bool EnableDebugLogs; public static ReviveMode CurrentReviveMode; public static bool DebugLogging { get; internal set; } public static void Bind(ConfigFile config) { //IL_031d: 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_0327: Unknown result type (might be due to invalid IL or missing references) string value = config.Bind("DeathMarker", "TombMonitorDotColor", "0.15,0.75,1,1", "Color of the tomb radar dot (format: r,g,b,a between 0 and 1).\n Examples: red=1,0,0,1; black=0,0,0,1; blue=0,0,1,1; green=0,1,0,1; yellow=1,1,0,1; white=1,1,1,1; orange=1,0.5,0,1; purple=0.5,0,0.5,1.").Value; ReviveTime = config.Bind("Revive", "ReviveTime", 5f, "How long, in seconds, the revive button must be held.").Value; CanPickUpBodies = config.Bind("Bodies", "CanPickUpBodies", true, "If false, pressing interact on bodies is blocked so the revive prompt is easier to use.").Value; DeadBodyWeight = config.Bind("Bodies", "DeadBodyWeight", 1.35f, "Weight multiplier applied to dead bodies.").Value; CanReviveTeleportedBodies = config.Bind("Rules", "CanReviveTeleported", true, "If false, bodies and tomb teleported back to the ship cannot be revived.").Value; ReviveHealth = config.Bind("Rules", "HealthYouReviveWith", 35, "Base health after revive.").Value; ExtraHealthLostPerRevive = config.Bind("Rules", "ExtraHealthLostPerRevive", 5, "Additional health penalty each time the same player is revived in the same round.").Value; CurrentReviveMode = config.Bind("Rules", "ReviveMode", ReviveMode.PerPlayer, "Revive limit mode: Unlimited, PerLevelMultiplier, FixedPerLevel, PerPlayer").Value; RevivesPerLevelMultiplier = config.Bind("Rules", "RevivesPerLevelMultiplier", 1.25f, "Revives per level = controlled players * this value, unless FixedRevivesPerLevel is above 0 (used if ReviveMode=PerLevelMultiplier).").Value; FixedRevivesPerLevel = config.Bind("Rules", "FixedRevivesPerLevel", 4, "Maximum number of revives allowed per level (used if ReviveMode=FixedPerLevel)").Value; RevivesPerPlayer = config.Bind("Rules", "RevivesPerPlayer", 2, "Maximum number of revives allowed per player per level (used if ReviveMode=PerPlayer)").Value; InfiniteReviveTime = config.Bind("Rules", "InfiniteReviveTime", false, "If true, players can be revived no matter how long they have been dead.").Value; TimeUntilCannotBeRevived = config.Bind("Rules", "TimeUntilCannotBeRevived", 120, "How long someone can be dead and still be revived, in seconds.").Value; MaxReviveDistance = config.Bind("Revive", "MaxReviveDistance", 3f, "Maximum distance from reviver camera to body.").Value; MaxReviveDistanceTomb = config.Bind("Revive", "MaxReviveDistanceTomb", 3f, "Maximum distance from reviver camera to tomb revive source.").Value; EnableMaskedPlayerRevive = config.Bind("Masked", "EnableMaskedPlayerRevive", true, "If true, a player converted into a Masked by a mask item or by a Masked enemy can be revived after the converted Masked is killed.").Value; EnableDeathTombFallback = config.Bind("DeathMarker", "EnableDeathTombFallback", true, "If true, spawn a 3D tomb marker at the last death position when the latest revive source is missing.").Value; EnableDeathTombCollider = config.Bind("DeathMarker", "EnableDeathTombCollider", true, "If false, the death tomb will have no collision, including colliders already present on bundle prefabs.").Value; DeathTombAssetBundlePath = config.Bind("DeathMarker", "DeathTombAssetBundlePath", "chaps-UltimateRevive/sm_tombstone_3_dark_rip.bundle", "Optional absolute or relative path to an AssetBundle containing your tomb prefab.\n Valid options for this mod:\n -chaps-UltimateRevive/sm_tombstone_3_dark_rip.bundle.\n -chaps-UltimateRevive/sm_tombstone_3_dark_text.bundle.\n -chaps-UltimateRevive/sm_tombstone_3_light_rip.bundle.\n -chaps-UltimateRevive/sm_tombstone_3_light_text.bundle.").Value; DeathTombPrefabName = config.Bind("DeathMarker", "DeathTombPrefabName", "sm_tombstone_3_dark_rip", "Prefab name inside the AssetBundle to instantiate as tomb marker.\n Valid options for this mod:\n -sm_tombstone_3_dark_rip.\n -sm_tombstone_3_dark_text.\n -sm_tombstone_3_light_rip.\n -sm_tombstone_3_light_text.").Value; DeathTombGroundOffset = config.Bind("DeathMarker", "DeathTombGroundOffset", 0.02f, "Vertical offset applied above detected ground.").Value; DeathTombDownOffset = config.Bind("DeathMarker", "DeathTombDownOffset", 2.6f, "How much to move the tomb down after teleport correction (default 2.6).").Value; DeathTombSpawnHeight = config.Bind("DeathMarker", "DeathTombSpawnHeight", 0f, "Height above death position used for downward ground detection and gravity settle.").Value; TombMonitorDotColor = ParseColorConfig(value, new Color(0.15f, 0.75f, 1f, 1f)); EnableDebugLogs = config.Bind("Debug", "EnableDebugLogs", false, "If true, emit local-only debug logs (not synchronized from host).").Value; } public static void ApplySyncedConfig(float reviveTime, bool canPickUpBodies, float deadBodyWeight, bool canReviveTeleportedBodies, int reviveHealth, int extraHealthLostPerRevive, float revivesPerLevelMultiplier, int fixedRevivesPerLevel, bool infiniteReviveTime, int timeUntilCannotBeRevived, float maxReviveDistance, float maxReviveDistanceTomb, bool enableMaskedPlayerRevive, bool enableDeathTombFallback, bool enableDeathTombCollider, string deathTombAssetBundlePath, string deathTombPrefabName, float deathTombGroundOffset, float deathTombDownOffset, float deathTombSpawnHeight, int reviveMode = -1, int revivesPerPlayer = -1) { ReviveTime = reviveTime; CanPickUpBodies = canPickUpBodies; DeadBodyWeight = deadBodyWeight; CanReviveTeleportedBodies = canReviveTeleportedBodies; ReviveHealth = reviveHealth; ExtraHealthLostPerRevive = extraHealthLostPerRevive; RevivesPerLevelMultiplier = revivesPerLevelMultiplier; FixedRevivesPerLevel = fixedRevivesPerLevel; InfiniteReviveTime = infiniteReviveTime; TimeUntilCannotBeRevived = timeUntilCannotBeRevived; MaxReviveDistance = maxReviveDistance; MaxReviveDistanceTomb = maxReviveDistanceTomb; EnableMaskedPlayerRevive = enableMaskedPlayerRevive; EnableDeathTombFallback = enableDeathTombFallback; EnableDeathTombCollider = enableDeathTombCollider; DeathTombAssetBundlePath = deathTombAssetBundlePath; DeathTombPrefabName = deathTombPrefabName; DeathTombGroundOffset = deathTombGroundOffset; DeathTombDownOffset = deathTombDownOffset; DeathTombSpawnHeight = deathTombSpawnHeight; if (reviveMode >= 0) { CurrentReviveMode = (ReviveMode)reviveMode; } if (revivesPerPlayer >= 0) { RevivesPerPlayer = revivesPerPlayer; } DeathMarkerManager.InvalidateBundleCache(); } private static Color ParseColorConfig(string value, Color fallback) { //IL_0082: 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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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) if (string.IsNullOrWhiteSpace(value)) { return fallback; } string[] array = value.Split(','); if (array.Length < 3) { return fallback; } try { float num = float.Parse(array[0].Trim(), CultureInfo.InvariantCulture); float num2 = float.Parse(array[1].Trim(), CultureInfo.InvariantCulture); float num3 = float.Parse(array[2].Trim(), CultureInfo.InvariantCulture); float num4 = ((array.Length > 3) ? float.Parse(array[3].Trim(), CultureInfo.InvariantCulture) : 1f); return new Color(num, num2, num3, num4); } catch { return fallback; } } } public static class ModState { public class MaskedInfo { public int PlayerClientId; public MaskedPlayerEnemy Masked; public ulong NetworkObjectId; public Vector3 LastPosition; public bool IsDead; public float LastUpdateTime; } public sealed class TombInfo { public int PlayerClientId; public GameObject TombObject; public DeadBodyInfo DeadBody; public Vector3 LastPosition; public float LastUpdateTime; } public static readonly Dictionary TombByPlayerId = new Dictionary(); public static readonly Dictionary PendingTombPositionByPlayerId = new Dictionary(); public static readonly Dictionary BodyByPlayerId = new Dictionary(); public static readonly Dictionary BodyByNetworkObjectId = new Dictionary(); public static readonly Dictionary MaskedByPlayerId = new Dictionary(); public static readonly Dictionary MaskedByNetworkObjectId = new Dictionary(); public static bool Active; public static int RemainingRevives = int.MaxValue; public static readonly List PlayerInfos = new List(); public static void Reset() { Active = false; RemainingRevives = int.MaxValue; PlayerInfos.Clear(); MaskedByPlayerId.Clear(); MaskedByNetworkObjectId.Clear(); BodyByNetworkObjectId.Clear(); MaskedConversionTracker.Reset(); DeathMarkerManager.Reset(); ReviveInteractTriggerManager.Reset(); BodyByPlayerId.Clear(); TombByPlayerId.Clear(); PendingTombPositionByPlayerId.Clear(); } public static void RegisterTomb(int playerClientId, GameObject tombObject, DeadBodyInfo deadBody, Vector3 position) { //IL_0021: 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) TombByPlayerId[playerClientId] = new TombInfo { PlayerClientId = playerClientId, TombObject = tombObject, DeadBody = deadBody, LastPosition = position, LastUpdateTime = Time.time }; PendingTombPositionByPlayerId.Remove(playerClientId); } public static bool TryGetTombObject(int playerClientId, out GameObject tombObject) { tombObject = null; if (!TombByPlayerId.TryGetValue(playerClientId, out var value)) { return false; } if (value == null || (Object)(object)value.TombObject == (Object)null) { TombByPlayerId.Remove(playerClientId); return false; } tombObject = value.TombObject; return true; } public static void UnregisterTomb(int playerClientId) { TombByPlayerId.Remove(playerClientId); PendingTombPositionByPlayerId.Remove(playerClientId); } } public sealed class PlayerReviveInfo { public int PlayerClientId; public bool HasBeenTeleported; public float TimeDiedAt; public int TimesRevivedThisLevel; public PlayerDeathType LastDeathType; public ulong LastDeathNetworkObjectId; public Vector3 LastDeathPosition; public bool HasLastDeathPosition; public int RemainingRevivesForPlayer; } public enum PlayerDeathType { Unknown, Body, Masked } [BepInPlugin("chaps.UltimateRevive", "UltimateRevive", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { private sealed class RuntimeDriver : MonoBehaviour { } public const string ModGuid = "chaps.UltimateRevive"; public const string ModName = "UltimateRevive"; public const string ModVersion = "1.1.1"; internal static ManualLogSource Log; internal static ReviveInputActions InputActions; private Harmony _harmony; private bool _isQuitting; private static RuntimeDriver _runtimeDriver; public static Plugin Instance { get; private set; } internal static InputAction ReviveAction => InputActions?.Revive; private void Awake() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); InputActions = new ReviveInputActions(); ModConfig.Bind(((BaseUnityPlugin)this).Config); GeneralUtil.SetLogSource(Log); ReviveNetwork.SetLogSource(Log); ReviveLogic.SetLogSource(Log); MaskedConversionTracker.SetLogSource(Log); DeathMarkerManager.SetLogSource(Log); _harmony = new Harmony("chaps.UltimateRevive"); try { _harmony.PatchAll(); VerifyHarmonyHooks(); } catch (Exception arg) { Log.LogError((object)$"Harmony patching failed: {arg}"); } InteractTriggerPatch.TryPatch(_harmony); Log.LogInfo((object)"UltimateRevive 1.1.1 loaded."); } private void OnDestroy() { if (_isQuitting) { ReviveNetwork.UnregisterHandlers(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } private void OnApplicationQuit() { _isQuitting = true; } internal static void DebugLog(string message) { if (ModConfig.EnableDebugLogs) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } } internal static string GetReviveBindingLabel() { //IL_0043: 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_0030: 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) InputAction reviveAction = ReviveAction; if (reviveAction == null) { return "key"; } int num = -1; if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.localPlayerUsingController) { num = InputActionRebindingExtensions.GetBindingIndex(reviveAction, InputBinding.MaskByGroup("Gamepad")); } else { num = InputActionRebindingExtensions.GetBindingIndex(reviveAction, InputBinding.MaskByGroup("KeyboardAndMouse")); if (num == -1) { num = InputActionRebindingExtensions.GetBindingIndex(reviveAction, InputBinding.MaskByGroup("Keyboard&Mouse")); } if (num == -1) { num = InputActionRebindingExtensions.GetBindingIndex(reviveAction, InputBinding.MaskByGroup("Keyboard")); } } if (num == -1) { return "key"; } return InputActionRebindingExtensions.GetBindingDisplayString(reviveAction, num, (DisplayStringOptions)0); } private void VerifyHarmonyHooks() { VerifyPatched(typeof(PlayerControllerB), "Update"); VerifyPatched(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger"); VerifyPatched(typeof(PlayerControllerB), "KillPlayerClientRpc"); VerifyPatched(typeof(StartOfRound), "Start"); VerifyPatched(typeof(StartOfRound), "openingDoorsSequence"); } private void VerifyPatched(Type type, string methodName) { MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { Log.LogWarning((object)("Harmony target missing: " + type.Name + "." + methodName)); return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo != null && ((patchInfo.Prefixes != null && patchInfo.Prefixes.Any((Patch p) => p.owner == "chaps.UltimateRevive")) || (patchInfo.Postfixes != null && patchInfo.Postfixes.Any((Patch p) => p.owner == "chaps.UltimateRevive")) || (patchInfo.Transpilers != null && patchInfo.Transpilers.Any((Patch p) => p.owner == "chaps.UltimateRevive")) || (patchInfo.Finalizers != null && patchInfo.Finalizers.Any((Patch p) => p.owner == "chaps.UltimateRevive")))) { Log.LogInfo((object)("Harmony hook active: " + type.Name + "." + methodName)); } else { Log.LogWarning((object)("Harmony hook NOT active: " + type.Name + "." + methodName)); } } private static void EnsureRuntimeDriver() { //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_0030: Expected O, but got Unknown if (!((Object)(object)_runtimeDriver != (Object)null) || !((Object)(object)((Component)_runtimeDriver).gameObject != (Object)null)) { GameObject val = new GameObject("UltimateRevive.RuntimeDriver"); Object.DontDestroyOnLoad((Object)val); _runtimeDriver = val.AddComponent(); } } internal static void RunCoroutineSafe(IEnumerator routine) { if (routine == null) { return; } try { if ((Object)(object)Instance != (Object)null && (Object)(object)((Component)Instance).gameObject != (Object)null && ((Behaviour)Instance).isActiveAndEnabled) { ((MonoBehaviour)Instance).StartCoroutine(routine); return; } if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)((Component)StartOfRound.Instance).gameObject != (Object)null && ((Behaviour)StartOfRound.Instance).isActiveAndEnabled) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(routine); return; } EnsureRuntimeDriver(); if ((Object)(object)_runtimeDriver != (Object)null && (Object)(object)((Component)_runtimeDriver).gameObject != (Object)null && ((Behaviour)_runtimeDriver).isActiveAndEnabled) { ((MonoBehaviour)_runtimeDriver).StartCoroutine(routine); return; } ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[UltimateRevive] No active MonoBehaviour available for coroutine."); } } catch (Exception arg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)$"[UltimateRevive] Failed to start coroutine: {arg}"); } } } } public static class ReviveAPI { public static bool CanRevive(RagdollGrabbableObject body) { if ((Object)(object)body == (Object)null) { return false; } if (!GeneralUtil.TryGetBodyOwner(body, out var targetPlayerClientId, out var _)) { return false; } return CanRevivePlayer(targetPlayerClientId); } public static bool CanRevivePlayer(int targetPlayerClientId) { PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(targetPlayerClientId); if (PassesGlobalRules(targetPlayerClientId, playerByClientId)) { return HasRevivableSource(targetPlayerClientId); } return false; } public static bool CanReviveMasked(MaskedPlayerEnemy masked) { if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive) { return false; } if (!GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out var playerClientId, out var _)) { return false; } return CanRevivePlayer(playerClientId); } public static object GetReviveSourceForPlayer(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return null; } int num = (int)player.playerClientId; switch (GeneralUtil.GetPlayerLastDeathType(num)) { case PlayerDeathType.Body: { RagdollGrabbableObject bodyForPlayer = GeneralUtil.GetBodyForPlayer(num); if ((Object)(object)bodyForPlayer != (Object)null) { return bodyForPlayer; } if (DeathMarkerManager.TombDeadBodiesPublic != null && DeathMarkerManager.TombDeadBodiesPublic.TryGetValue(num, out var value) && (Object)(object)value != (Object)null) { return value; } break; } case PlayerDeathType.Masked: { MaskedPlayerEnemy val = GeneralUtil.GetMaskedByNetworkObjectId(GeneralUtil.GetPlayerLastDeathNetworkObjectId(num)) ?? GeneralUtil.GetConvertedMaskedCorpseForPlayer(num); if ((Object)(object)val != (Object)null) { return val; } break; } } return null; } public static bool CanReviveUniversal(object target) { RagdollGrabbableObject val = (RagdollGrabbableObject)((target is RagdollGrabbableObject) ? target : null); if (val != null) { return CanRevive(val); } MaskedPlayerEnemy val2 = (MaskedPlayerEnemy)((target is MaskedPlayerEnemy) ? target : null); if (val2 != null) { return CanReviveMasked(val2); } if (target is int targetPlayerClientId) { return CanRevivePlayer(targetPlayerClientId); } return false; } public static void RevivePlayer(int targetPlayerClientId) { ReviveNetwork.RequestRevive(targetPlayerClientId); } private static bool PassesGlobalRules(int targetPlayerClientId, PlayerControllerB targetPlayer) { if (!ModState.Active) { return false; } if ((Object)(object)targetPlayer == (Object)null || !targetPlayer.isPlayerDead) { return false; } if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out var _)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { return false; } if (!ModConfig.InfiniteReviveTime && Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId) > (float)ModConfig.TimeUntilCannotBeRevived) { return false; } return true; } private static bool HasRevivableSource(int targetPlayerClientId) { PlayerDeathType playerLastDeathType = GeneralUtil.GetPlayerLastDeathType(targetPlayerClientId); ulong playerLastDeathNetworkObjectId = GeneralUtil.GetPlayerLastDeathNetworkObjectId(targetPlayerClientId); Vector3 position; if (playerLastDeathType == PlayerDeathType.Masked) { if (!ModConfig.EnableMaskedPlayerRevive) { return false; } MaskedPlayerEnemy val = ((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetMaskedByNetworkObjectId(playerLastDeathNetworkObjectId) : null); if ((Object)(object)val == (Object)null) { val = GeneralUtil.GetConvertedMaskedCorpseForPlayer(targetPlayerClientId); } if ((Object)(object)val != (Object)null) { return true; } if (ModConfig.EnableDeathTombFallback) { return GeneralUtil.TryGetPlayerLastDeathPosition(targetPlayerClientId, out position); } return false; } if ((Object)(object)((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetBodyByNetworkObjectId(playerLastDeathNetworkObjectId) : GeneralUtil.GetBodyForPlayer(targetPlayerClientId)) != (Object)null) { return true; } if (ModConfig.EnableDeathTombFallback) { return GeneralUtil.TryGetPlayerLastDeathPosition(targetPlayerClientId, out position); } return false; } } public sealed class ReviveInputActions : LcInputActions { [InputAction(/*Could not decode attribute arguments.*/)] public InputAction Revive { get; set; } } public static class ReviveInteractTriggerManager { private enum ReviveSourceKind { None, Body, Masked, Tomb } private sealed class ReviveTriggerEntry { public int TargetPlayerId; public ReviveSourceKind SourceKind; public ulong SourceObjectId; public GameObject RootObject; public GameObject TriggerHostObject; public InteractTrigger Trigger; public Collider AddedCollider; public bool OwnsTrigger; public bool OwnsTriggerHostObject; public bool ChangedLayer; public int OriginalLayer; public GameObject LayerAdjustedObject; public Transform FollowTransform; public Vector3 StaticPosition; } private const string CustomBodyTriggerHostName = "UltimateReviveBodyTrigger"; private const string CustomTombTriggerHostName = "UltimateReviveTombTrigger"; private const float HoverGraceSeconds = 0.45f; private static readonly Vector3 MaskedTriggerAliveLocalOffset = new Vector3(0f, 0.9f, 0f); private static readonly Vector3 MaskedTriggerDeadLocalOffset = new Vector3(0f, 0.35f, 0f); private const float MaskedTriggerDeadForwardShift = 0.95f; private const float MaskedTriggerDeadVerticalLift = 0.35f; private static readonly Dictionary EntriesByPlayer = new Dictionary(); private static readonly Dictionary TriggerInstanceToPlayer = new Dictionary(); private static readonly string[] PreferredPlayerTriggerFieldNames = new string[4] { "hoveringOverTrigger", "currentInteractTrigger", "currentTriggerInRange", "hoveredOverTrigger" }; private static readonly BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static FieldInfo[] _cachedPlayerTriggerFields; private static PropertyInfo[] _cachedPlayerTriggerProperties; private static int _lastLoggedHoverTarget = -2; private static ReviveSourceKind _lastLoggedHoverKind = ReviveSourceKind.None; private static string _lastLoggedHoverPath = string.Empty; private static bool _lastLoggedHoverCanRevive; private static bool _hadHover; private static int _lastHoveredTargetId = -1; private static float _lastHoveredSeenAt = -999f; private static readonly Dictionary _lastResolveDiagnosticsByPlayer = new Dictionary(); private static readonly Dictionary _lastTickDiagnosticsByPlayer = new Dictionary(); private static readonly Dictionary _lastForceDiagnosticsByPlayer = new Dictionary(); private static readonly Dictionary _lastObservedTriggerDiagnosticsByPlayer = new Dictionary(); public static void DisableVanillaBodyTriggers(RagdollGrabbableObject body) { if ((Object)(object)body == (Object)null) { return; } InteractTrigger[] componentsInChildren = ((Component)body).GetComponentsInChildren(true); foreach (InteractTrigger obj in componentsInChildren) { ((Behaviour)obj).enabled = false; Collider component = ((Component)obj).GetComponent(); if ((Object)(object)component != (Object)null) { component.enabled = false; } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[ReviveTrigger] Disabled vanilla triggers on body " + ((Object)body).name)); } } public static void WarmUpCache() { CachePlayerTriggerMembers(); FieldInfo[] cachedPlayerTriggerFields = _cachedPlayerTriggerFields; Plugin.DebugLog($"[ReviveTrigger] cache warmed: fields={((cachedPlayerTriggerFields != null) ? cachedPlayerTriggerFields.Length : 0)}"); } public static void SetReviveTriggerEnabled(RagdollGrabbableObject body, bool enabled) { if ((Object)(object)body == (Object)null) { return; } InteractTrigger[] componentsInChildren = ((Component)body).GetComponentsInChildren(true); foreach (InteractTrigger obj in componentsInChildren) { ((Behaviour)obj).enabled = enabled; Collider component = ((Component)obj).GetComponent(); if ((Object)(object)component != (Object)null) { component.enabled = enabled; } } } public static void Reset() { foreach (int item in EntriesByPlayer.Keys.ToList()) { RemoveEntry(item); } EntriesByPlayer.Clear(); TriggerInstanceToPlayer.Clear(); _lastResolveDiagnosticsByPlayer.Clear(); _lastTickDiagnosticsByPlayer.Clear(); _lastForceDiagnosticsByPlayer.Clear(); _lastObservedTriggerDiagnosticsByPlayer.Clear(); _lastHoveredTargetId = -1; _lastHoveredSeenAt = -999f; } public static void ForceRefreshPlayer(int playerId, string reason = null) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.EnableDebugLogs) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveTrigger][DEBUG] Enter ForceRefreshPlayer: playerId={playerId} reason={reason}"); } } if (!ModState.Active) { if (ModConfig.EnableDebugLogs) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[ReviveTrigger][DEBUG] Early return: ModState not active for playerId={playerId}"); } } return; } if (!TryResolveCurrentReviveSource(playerId, out var sourceKind, out var sourceObjectId, out var followTransform, out var staticPosition)) { if (ModConfig.EnableDebugLogs) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[ReviveTrigger][DEBUG] Early return: Could not resolve revive source for playerId={playerId}"); } } RemoveEntry(playerId); LogForceDiagnostic(playerId, "result=none reason=" + (string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason)); return; } if (ModConfig.EnableDebugLogs) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[ReviveTrigger][DEBUG] EnsureOrUpdateEntry: playerId={playerId} sourceKind={sourceKind} sourceObjectId={sourceObjectId}"); } } EnsureOrUpdateEntry(playerId, sourceKind, sourceObjectId, followTransform, staticPosition); LogForceDiagnostic(playerId, string.Format("result={0} obj={1} reason={2}", sourceKind, sourceObjectId, string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason)); } public static bool TryGetHoveredReviveTarget(PlayerControllerB reviver, out int targetPlayerId, out string reason, out bool canRevive) { targetPlayerId = -1; reason = "no revive trigger"; canRevive = false; if (!TryGetHoveredEntry(reviver, out var entry)) { if (_hadHover) { _hadHover = false; _lastLoggedHoverTarget = -2; _lastLoggedHoverKind = ReviveSourceKind.None; _lastLoggedHoverPath = string.Empty; _lastLoggedHoverCanRevive = false; Plugin.DebugLog("[ReviveHover] cleared"); } return false; } targetPlayerId = entry.TargetPlayerId; canRevive = EvaluateCanRevive(entry, reviver, out var resolvedTargetId, out reason); if (resolvedTargetId >= 0) { targetPlayerId = resolvedTargetId; } string text = ((entry.SourceKind == ReviveSourceKind.Body) ? "vanilla-body" : "custom"); if (entry.SourceKind != ReviveSourceKind.Body && ((Object)(object)entry.Trigger == (Object)null || !TriggerInstanceToPlayer.ContainsKey(((Object)entry.Trigger).GetInstanceID()))) { text = "unmapped"; } if (!_hadHover || _lastLoggedHoverTarget != targetPlayerId || _lastLoggedHoverKind != entry.SourceKind || _lastLoggedHoverPath != text || _lastLoggedHoverCanRevive != canRevive) { _hadHover = true; _lastLoggedHoverTarget = targetPlayerId; _lastLoggedHoverKind = entry.SourceKind; _lastLoggedHoverPath = text; _lastLoggedHoverCanRevive = canRevive; Plugin.DebugLog($"[ReviveHover] player={targetPlayerId} source={entry.SourceKind} path={text} canRevive={canRevive} reason={reason}"); } return true; } private static void EnsureOrUpdateEntry(int playerId, ReviveSourceKind sourceKind, ulong sourceObjectId, Transform followTransform, Vector3 staticPosition) { //IL_009b: 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) if (!TryResolveSourceHostObject(playerId, sourceKind, sourceObjectId, followTransform, out var hostObject) || (Object)(object)hostObject == (Object)null) { RemoveEntry(playerId); return; } if (!EntriesByPlayer.TryGetValue(playerId, out var value) || value == null || (Object)(object)value.Trigger == (Object)null || (Object)(object)value.RootObject == (Object)null || (Object)(object)value.RootObject != (Object)(object)hostObject || value.SourceKind != sourceKind || value.SourceObjectId != sourceObjectId) { RemoveEntry(playerId); value = CreateEntry(playerId, sourceKind, sourceObjectId, hostObject); if (value == null) { return; } EntriesByPlayer[playerId] = value; } value.FollowTransform = followTransform; value.StaticPosition = staticPosition; if (sourceKind != ReviveSourceKind.Body) { ConfigureTrigger(value.Trigger, playerId, sourceKind); } if (sourceKind == ReviveSourceKind.Masked) { UpdateMaskedTriggerAnchor(value); } } private static ReviveTriggerEntry CreateEntry(int playerId, ReviveSourceKind sourceKind, ulong sourceObjectId, GameObject hostObject) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: 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) if ((Object)(object)hostObject == (Object)null) { return null; } switch (sourceKind) { case ReviveSourceKind.Body: return CreateBodyEntryUsingVanillaTrigger(playerId, sourceObjectId, hostObject); case ReviveSourceKind.Masked: return CreateMaskedEntryUsingChildTrigger(playerId, sourceObjectId, hostObject); case ReviveSourceKind.Tomb: return CreateTombEntryUsingChildTrigger(playerId, sourceObjectId, hostObject); default: { bool flag = false; InteractTrigger obj = hostObject.GetComponent(); bool flag2 = (Object)(object)obj == (Object)null; if (obj == null) { obj = hostObject.AddComponent(); } InteractTrigger val = obj; Collider val2 = null; if (!HasUsableInteractCollider(hostObject)) { SphereCollider obj2 = hostObject.AddComponent(); ((Collider)obj2).isTrigger = true; obj2.radius = 0.8f; obj2.center = new Vector3(0f, 0.9f, 0f); val2 = (Collider)(object)obj2; } int num = LayerMask.NameToLayer("InteractableObject"); bool changedLayer = false; int layer = hostObject.layer; if (num >= 0 && hostObject.layer != num) { hostObject.layer = num; changedLayer = true; } ConfigureTrigger(val, playerId, sourceKind); RegisterTriggerMapping(val, playerId, sourceKind, hostObject); Collider component = hostObject.GetComponent(); bool flag3 = HasUsableInteractCollider(hostObject); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveTrigger] attached source={sourceKind} player={playerId} host={((Object)hostObject).name} triggerHost={((Object)hostObject).name} ownsTrigger={flag2} ownsHost={flag} addedCollider={(Object)(object)val2 != (Object)null} hasCollider={(Object)(object)component != (Object)null} usableCollider={flag3} triggerId={((Object)val).GetInstanceID()} layer={hostObject.layer}"); } return new ReviveTriggerEntry { TargetPlayerId = playerId, SourceKind = sourceKind, SourceObjectId = sourceObjectId, RootObject = hostObject, TriggerHostObject = hostObject, Trigger = val, AddedCollider = val2, OwnsTrigger = flag2, OwnsTriggerHostObject = flag, ChangedLayer = changedLayer, OriginalLayer = layer, LayerAdjustedObject = hostObject, FollowTransform = null, StaticPosition = Vector3.zero }; } } } private static ReviveTriggerEntry CreateMaskedEntryUsingChildTrigger(int playerId, ulong sourceObjectId, GameObject hostObject) { //IL_0032: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hostObject == (Object)null) { return null; } Transform val = hostObject.transform.Find("UltimateReviveMaskedTrigger"); bool flag = (Object)(object)val == (Object)null; GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? ((object)((Component)val).gameObject) : ((object)new GameObject("UltimateReviveMaskedTrigger"))); if (flag) { val2.transform.SetParent(hostObject.transform, false); val2.transform.localPosition = MaskedTriggerAliveLocalOffset; } SphereCollider obj = val2.GetComponent() ?? val2.AddComponent(); ((Collider)obj).isTrigger = true; obj.radius = 0.6f; obj.center = Vector3.zero; InteractTrigger obj2 = val2.GetComponent(); bool ownsTrigger = (Object)(object)obj2 == (Object)null; if (obj2 == null) { obj2 = val2.AddComponent(); } InteractTrigger val3 = obj2; int num = LayerMask.NameToLayer("InteractableObject"); bool changedLayer = false; int layer = val2.layer; if (num >= 0 && val2.layer != num) { val2.layer = num; changedLayer = true; } ConfigureTrigger(val3, playerId, ReviveSourceKind.Masked); RegisterTriggerMapping(val3, playerId, ReviveSourceKind.Masked, val2); bool flag2 = false; MaskedPlayerEnemy val4 = hostObject.GetComponent() ?? hostObject.GetComponentInChildren(true); if ((Object)(object)val4 != (Object)null) { flag2 = MaskedConversionTracker.IsMaskedDead(val4); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveTrigger] maskedChildAttached player={playerId} host={((Object)hostObject).name} child={((Object)val2).name} ownsTriggerHost={flag} triggerId={((Object)val3).GetInstanceID()} layer={val2.layer} maskedDead={flag2}"); } return new ReviveTriggerEntry { TargetPlayerId = playerId, SourceKind = ReviveSourceKind.Masked, SourceObjectId = sourceObjectId, RootObject = hostObject, TriggerHostObject = val2, Trigger = val3, AddedCollider = null, OwnsTrigger = ownsTrigger, OwnsTriggerHostObject = flag, ChangedLayer = changedLayer, OriginalLayer = layer, LayerAdjustedObject = val2, FollowTransform = null, StaticPosition = Vector3.zero }; } private static ReviveTriggerEntry CreateTombEntryUsingChildTrigger(int playerId, ulong sourceObjectId, GameObject hostObject) { //IL_0032: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: 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) if ((Object)(object)hostObject == (Object)null) { return null; } Transform val = hostObject.transform.Find("UltimateReviveTombTrigger"); bool flag = (Object)(object)val == (Object)null; GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? ((object)((Component)val).gameObject) : ((object)new GameObject("UltimateReviveTombTrigger"))); if (flag) { val2.transform.SetParent(hostObject.transform, false); val2.transform.localPosition = new Vector3(0f, 0.8f, 0f); } SphereCollider obj = val2.GetComponent() ?? val2.AddComponent(); ((Collider)obj).isTrigger = true; obj.radius = 0.8f; obj.center = Vector3.zero; InteractTrigger obj2 = val2.GetComponent(); bool ownsTrigger = (Object)(object)obj2 == (Object)null; if (obj2 == null) { obj2 = val2.AddComponent(); } InteractTrigger val3 = obj2; int num = LayerMask.NameToLayer("InteractableObject"); bool changedLayer = false; int layer = val2.layer; if (num >= 0 && val2.layer != num) { val2.layer = num; changedLayer = true; } ConfigureTrigger(val3, playerId, ReviveSourceKind.Tomb); RegisterTriggerMapping(val3, playerId, ReviveSourceKind.Tomb, val2); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveTrigger] tombChildAttached player={playerId} host={((Object)hostObject).name} child={((Object)val2).name} ownsTriggerHost={flag} triggerId={((Object)val3).GetInstanceID()} layer={val2.layer}"); } return new ReviveTriggerEntry { TargetPlayerId = playerId, SourceKind = ReviveSourceKind.Tomb, SourceObjectId = sourceObjectId, RootObject = hostObject, TriggerHostObject = val2, Trigger = val3, AddedCollider = null, OwnsTrigger = ownsTrigger, OwnsTriggerHostObject = flag, ChangedLayer = changedLayer, OriginalLayer = layer, LayerAdjustedObject = val2, FollowTransform = null, StaticPosition = Vector3.zero }; } private static void UpdateMaskedTriggerAnchor(ReviveTriggerEntry entry) { //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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00f2: 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) if (entry == null || entry.SourceKind != ReviveSourceKind.Masked || (Object)(object)entry.TriggerHostObject == (Object)null) { return; } MaskedPlayerEnemy val = (((Object)(object)entry.RootObject != (Object)null) ? entry.RootObject.GetComponent() : null); if ((Object)(object)val == (Object)null) { return; } Transform transform = entry.TriggerHostObject.transform; if ((Object)(object)transform == (Object)null) { return; } bool flag = MaskedConversionTracker.IsMaskedDead(val); ConfigureMaskedTriggerText(entry, flag); if (!flag) { transform.localPosition = MaskedTriggerAliveLocalOffset; return; } Vector3 val2 = Vector3.ProjectOnPlane(((Component)val).transform.forward, Vector3.up); if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f) { ((Vector3)(ref val2)).Normalize(); } else { val2 = ((Component)val).transform.forward; } if (TryGetMaskedDeadAnchorWorld(val, out var anchor)) { transform.position = anchor; } else { transform.position = ((Component)val).transform.position + Vector3.up * MaskedTriggerDeadLocalOffset.y + val2 * 0.95f; } } public static void OnMaskedKilled(MaskedPlayerEnemy masked) { //IL_007e: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)masked == (Object)null)) { if (!GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out var playerClientId, out var source)) { MaskedConversionTracker.TagMaskedOnSpawn(masked); GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out playerClientId, out source); } if (playerClientId >= 0) { ulong networkObjectId = MaskedConversionTracker.GetNetworkObjectId(masked); Vector3 deathPosition = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : Vector3.zero); GeneralUtil.SetPlayerDeathState(playerClientId, PlayerDeathType.Masked, networkObjectId, deathPosition, "Masked.KillEnemy"); ForceRefreshPlayer(playerClientId, "Masked.KillEnemy"); } ReviveTriggerEntry reviveTriggerEntry = EntriesByPlayer.Values.FirstOrDefault((ReviveTriggerEntry e) => e != null && (Object)(object)e.RootObject == (Object)(object)((Component)masked).gameObject); if (reviveTriggerEntry != null) { UpdateMaskedTriggerAnchor(reviveTriggerEntry); } } } private static bool TryGetMaskedDeadAnchorWorld(MaskedPlayerEnemy masked, out Vector3 anchor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0066: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) anchor = Vector3.zero; if ((Object)(object)masked == (Object)null || (Object)(object)((Component)masked).transform == (Object)null) { return false; } Collider[] componentsInChildren = ((Component)masked).GetComponentsInChildren(true); Collider val = null; float num = -1f; Collider[] array = componentsInChildren; Bounds bounds; foreach (Collider val2 in array) { if (!((Object)(object)val2 == (Object)null) && !val2.isTrigger) { bounds = val2.bounds; Vector3 size = ((Bounds)(ref bounds)).size; float num2 = size.x * size.y * size.z; if (num2 > num) { val = val2; num = num2; } } } if ((Object)(object)val == (Object)null) { return false; } Vector3 val3 = Vector3.ProjectOnPlane(((Component)masked).transform.forward, Vector3.up); if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f) { val3 = Vector3.zero; } else { ((Vector3)(ref val3)).Normalize(); } bounds = val.bounds; float y = ((Bounds)(ref bounds)).center.y; bounds = val.bounds; float num3 = Mathf.Min(y, ((Bounds)(ref bounds)).min.y + 0.35f); bounds = val.bounds; float x = ((Bounds)(ref bounds)).center.x; bounds = val.bounds; anchor = new Vector3(x, num3, ((Bounds)(ref bounds)).center.z) + val3 * 0.95f; return true; } private static ReviveTriggerEntry CreateBodyEntryUsingVanillaTrigger(int playerId, ulong sourceObjectId, GameObject hostObject) { //IL_00a8: 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_0138: 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_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.EnableDebugLogs) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[ReviveTrigger][DEBUG] Enter CreateBodyEntryUsingVanillaTrigger: playerId={0} host={1}", playerId, ((Object)(object)hostObject != (Object)null) ? ((Object)hostObject).name : "null")); } } if ((Object)(object)hostObject == (Object)null) { if (ModConfig.EnableDebugLogs) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[ReviveTrigger][DEBUG] Early return: hostObject is null for playerId={playerId}"); } } return null; } RagdollGrabbableObject val = hostObject.GetComponent() ?? hostObject.GetComponentInParent() ?? hostObject.GetComponentInChildren(true); if ((Object)(object)val == (Object)null && (Object)(object)hostObject.transform != (Object)null) { val = GeneralUtil.GetBodyNearPosition(hostObject.transform.position, 8f); } if ((Object)(object)val == (Object)null) { val = GeneralUtil.GetBodyFromDeadBodyReference(GeneralUtil.GetPlayerByClientId(playerId)); } if ((Object)(object)val == (Object)null) { if (ModConfig.EnableDebugLogs) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[ReviveTrigger][DEBUG] Early return: no ragdoll found for playerId={playerId} host={((Object)hostObject).name}"); } } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[ReviveTrigger] bodyVanillaPending player={playerId} host={((Object)hostObject).name} reason=no-ragdoll-yet"); } return null; } Transform transform = ((Component)val).transform; Vector3 position = transform.position; InteractTrigger val2 = SelectBestVanillaBodyTrigger(val); GameObject triggerHostObject = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null); Collider addedCollider = null; bool ownsTrigger = false; bool ownsTriggerHostObject = false; bool changedLayer = false; int originalLayer = 0; GameObject layerAdjustedObject = null; if ((Object)(object)val2 == (Object)null) { if (ModConfig.EnableDebugLogs) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[ReviveTrigger][DEBUG] No vanilla trigger found, creating custom for playerId={playerId} body={((Object)val).name}"); } } val2 = CreateCustomBodyTrigger(val, playerId, out triggerHostObject, out addedCollider, out changedLayer, out originalLayer, out layerAdjustedObject, out ownsTrigger, out ownsTriggerHostObject); if ((Object)(object)val2 == (Object)null) { if (ModConfig.EnableDebugLogs) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[ReviveTrigger][DEBUG] Early return: custom trigger creation failed for playerId={playerId} body={((Object)val).name}"); } } ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)$"[ReviveTrigger] bodyVanillaPending player={playerId} host={((Object)hostObject).name} reason=no-trigger-yet ragdoll={((Object)val).name}"); } return null; } ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[ReviveTrigger] bodyCustomAttached player={playerId} host={((Object)hostObject).name} trigger={((Object)val2).name} triggerId={((Object)val2).GetInstanceID()} triggerObj={((Object)((Component)val2).gameObject).name} ragdoll={((Object)val).name}"); } } else { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)$"[ReviveTrigger] bodyVanillaAttached player={playerId} host={((Object)hostObject).name} trigger={((Object)val2).name} triggerId={((Object)val2).GetInstanceID()} triggerObj={((Object)((Component)val2).gameObject).name} hasRagdoll={(Object)(object)val != (Object)null}"); } } ConfigureTrigger(val2, playerId, ReviveSourceKind.Body); RegisterTriggerMapping(val2, playerId, ReviveSourceKind.Body, triggerHostObject); if (ModConfig.EnableDebugLogs) { ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogInfo((object)$"[ReviveTrigger][DEBUG] Returning new ReviveTriggerEntry for playerId={playerId} body={((Object)val).name}"); } } return new ReviveTriggerEntry { TargetPlayerId = playerId, SourceKind = ReviveSourceKind.Body, SourceObjectId = sourceObjectId, RootObject = ((Component)val).gameObject, TriggerHostObject = triggerHostObject, Trigger = val2, AddedCollider = addedCollider, OwnsTrigger = ownsTrigger, OwnsTriggerHostObject = ownsTriggerHostObject, ChangedLayer = changedLayer, OriginalLayer = originalLayer, LayerAdjustedObject = layerAdjustedObject, FollowTransform = transform, StaticPosition = (((Object)(object)transform != (Object)null) ? transform.position : position) }; } private static InteractTrigger SelectBestVanillaBodyTrigger(RagdollGrabbableObject body) { //IL_0113: 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_011c: 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_0121: 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_01be: 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) if ((Object)(object)body == (Object)null) { return null; } List list = new List(); InteractTrigger component = ((Component)body).GetComponent(); if ((Object)(object)component != (Object)null) { list.Add(component); } InteractTrigger componentInParent = ((Component)body).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { list.Add(componentInParent); } InteractTrigger[] componentsInChildren = ((Component)body).GetComponentsInChildren(true); foreach (InteractTrigger val in componentsInChildren) { if ((Object)(object)val != (Object)null) { list.Add(val); } } if (list.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[ReviveTrigger] bodyRagdollSearch body=" + ((Object)body).name + " candidates=0")); } return null; } Dictionary dictionary = new Dictionary(); foreach (InteractTrigger item in list) { int instanceID = ((Object)item).GetInstanceID(); if (!dictionary.ContainsKey(instanceID)) { dictionary[instanceID] = item; } } Collider primaryRagdollCollider = GetPrimaryRagdollCollider(body); Vector3 val2; if (!((Object)(object)primaryRagdollCollider != (Object)null)) { val2 = ((Component)body).transform.position; } else { Bounds bounds = primaryRagdollCollider.bounds; val2 = ((Bounds)(ref bounds)).center; } Vector3 val3 = val2; string arg = (((Object)(object)primaryRagdollCollider != (Object)null) ? ((Object)primaryRagdollCollider).name : "none"); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[ReviveTrigger] bodyRagdollSearch body={((Object)body).name} candidates={dictionary.Count} primaryCollider={arg}"); } InteractTrigger val4 = null; float num = float.MaxValue; foreach (KeyValuePair item2 in dictionary) { InteractTrigger value = item2.Value; if (!((Object)(object)value == (Object)null) && !((Object)(object)((Component)value).transform == (Object)null)) { float num2 = Vector3.SqrMagnitude(((Component)value).transform.position - val3); if ((Object)(object)primaryRagdollCollider != (Object)null && (Object)(object)((Component)value).gameObject == (Object)(object)((Component)primaryRagdollCollider).gameObject) { num2 -= 1000f; } Collider component2 = ((Component)value).GetComponent(); if ((Object)(object)component2 != (Object)null && component2.enabled) { num2 -= 5f; } string text = (((Object)(object)((Component)value).gameObject != (Object)null) ? ((Object)((Component)value).gameObject).name : "unknown"); string text2 = BuildTransformPath(((Component)value).transform); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[ReviveTrigger] bodyRagdollCandidate id={((Object)value).GetInstanceID()} name={((Object)value).name} obj={text} score={num2:0.000} path={text2}"); } if (num2 < num) { num = num2; val4 = value; } } } if ((Object)(object)val4 != (Object)null) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[ReviveTrigger] bodyRagdollSelected id={((Object)val4).GetInstanceID()} name={((Object)val4).name} score={num:0.000} body={((Object)body).name}"); } } return val4; } public static Collider GetPrimaryRagdollCollider(RagdollGrabbableObject body) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00b9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)body == (Object)null) { return null; } Collider component = ((Component)body).GetComponent(); if ((Object)(object)component != (Object)null && component.enabled && !component.isTrigger) { return component; } Collider[] componentsInChildren = ((Component)body).GetComponentsInChildren(true); Collider val = null; float num = -1f; float num2 = float.MaxValue; Vector3 position = ((Component)body).transform.position; Collider[] array = componentsInChildren; foreach (Collider val2 in array) { if (!((Object)(object)val2 == (Object)null) && val2.enabled && !val2.isTrigger) { Bounds bounds = val2.bounds; Vector3 size = ((Bounds)(ref bounds)).size; float num3 = size.x * size.y * size.z; bounds = val2.bounds; float num4 = Vector3.SqrMagnitude(((Bounds)(ref bounds)).center - position); if (num3 > num || (Mathf.Approximately(num3, num) && num4 < num2)) { val = val2; num = num3; num2 = num4; } } } if ((Object)(object)val != (Object)null) { return val; } return component; } private static InteractTrigger CreateCustomBodyTrigger(RagdollGrabbableObject body, int playerId, out GameObject triggerHostObject, out Collider addedCollider, out bool changedLayer, out int originalLayer, out GameObject layerAdjustedObject, out bool ownsTrigger, out bool ownsTriggerHostObject) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_009d: 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_00c2: 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_00d7: 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_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) triggerHostObject = null; addedCollider = null; changedLayer = false; originalLayer = 0; layerAdjustedObject = null; ownsTrigger = false; ownsTriggerHostObject = false; if ((Object)(object)body == (Object)null || (Object)(object)((Component)body).transform == (Object)null) { return null; } Transform val = ((Component)body).transform.Find("UltimateReviveBodyTrigger"); if ((Object)(object)val != (Object)null) { triggerHostObject = ((Component)val).gameObject; } if ((Object)(object)triggerHostObject == (Object)null) { triggerHostObject = new GameObject("UltimateReviveBodyTrigger"); triggerHostObject.transform.SetParent(((Component)body).transform, false); ownsTriggerHostObject = true; } Collider primaryRagdollCollider = GetPrimaryRagdollCollider(body); Vector3 val2; if (!((Object)(object)primaryRagdollCollider != (Object)null)) { val2 = ((Component)body).transform.position + Vector3.up * 0.9f; } else { Bounds bounds = primaryRagdollCollider.bounds; val2 = ((Bounds)(ref bounds)).center; } Vector3 val3 = val2; triggerHostObject.transform.position = val3; triggerHostObject.transform.rotation = Quaternion.identity; InteractTrigger val4 = triggerHostObject.GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = triggerHostObject.AddComponent(); ownsTrigger = true; } SphereCollider val5 = triggerHostObject.GetComponent(); if ((Object)(object)val5 == (Object)null) { val5 = (SphereCollider)(object)(addedCollider = (Collider)(object)triggerHostObject.AddComponent()); } float num = 0.5f; ((Collider)val5).isTrigger = true; val5.radius = num; val5.center = Vector3.zero; triggerHostObject.layer = 0; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveTrigger] bodyCustomTrigger player={playerId} body={((Object)body).name} host={((Object)triggerHostObject).name} radius={num:0.00} center=({val3.x:0.00},{val3.y:0.00},{val3.z:0.00})"); } return val4; } private static string BuildTransformPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return "none"; } List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } private static bool TryResolveSourceHostObject(int playerId, ReviveSourceKind sourceKind, ulong sourceObjectId, Transform followTransform, out GameObject hostObject) { hostObject = null; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[TryResolveSourceHostObject] playerId={0} sourceKind={1} sourceObjectId={2} followTransform={3}", playerId, sourceKind, sourceObjectId, ((Object)(object)followTransform != (Object)null) ? ((Object)followTransform).name : "null")); } switch (sourceKind) { case ReviveSourceKind.Masked: { if (sourceObjectId != 0L) { if (ModState.MaskedByNetworkObjectId.TryGetValue(sourceObjectId, out var value2) && value2 != null && (Object)(object)value2.Masked != (Object)null) { hostObject = ((Component)value2.Masked).gameObject; ManualLogSource log8 = Plugin.Log; if (log8 != null) { GameObject obj4 = hostObject; log8.LogInfo((object)("[TryResolveSourceHostObject] MASKED: trouvé via ModState.MaskedByNetworkObjectId: " + ((obj4 != null) ? ((Object)obj4).name : null))); } return (Object)(object)hostObject != (Object)null; } MaskedPlayerEnemy maskedByNetworkObjectId = GeneralUtil.GetMaskedByNetworkObjectId(sourceObjectId); if ((Object)(object)maskedByNetworkObjectId != (Object)null) { hostObject = ((Component)maskedByNetworkObjectId).gameObject; ManualLogSource log9 = Plugin.Log; if (log9 != null) { GameObject obj5 = hostObject; log9.LogInfo((object)("[TryResolveSourceHostObject] MASKED: trouvé via GeneralUtil.GetMaskedByNetworkObjectId: " + ((obj5 != null) ? ((Object)obj5).name : null))); } return (Object)(object)hostObject != (Object)null; } ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogInfo((object)"[TryResolveSourceHostObject] MASKED: pas trouvé via NetworkObjectId"); } } if ((Object)(object)followTransform != (Object)null) { hostObject = ((Component)followTransform).gameObject; ManualLogSource log11 = Plugin.Log; if (log11 != null) { GameObject obj6 = hostObject; log11.LogInfo((object)("[TryResolveSourceHostObject] MASKED: trouvé via followTransform: " + ((obj6 != null) ? ((Object)obj6).name : null))); } return (Object)(object)hostObject != (Object)null; } MaskedPlayerEnemy convertedMaskedForPlayer = GeneralUtil.GetConvertedMaskedForPlayer(playerId); if ((Object)(object)convertedMaskedForPlayer != (Object)null) { hostObject = ((Component)convertedMaskedForPlayer).gameObject; ManualLogSource log12 = Plugin.Log; if (log12 != null) { GameObject obj7 = hostObject; log12.LogInfo((object)("[TryResolveSourceHostObject] MASKED: trouvé via GetConvertedMaskedForPlayer: " + ((obj7 != null) ? ((Object)obj7).name : null))); } return (Object)(object)hostObject != (Object)null; } ManualLogSource log13 = Plugin.Log; if (log13 != null) { log13.LogInfo((object)"[TryResolveSourceHostObject] MASKED: échec de résolution"); } return false; } case ReviveSourceKind.Body: { if ((Object)(object)followTransform != (Object)null) { DeadBodyInfo val = ((Component)followTransform).GetComponent() ?? ((Component)followTransform).GetComponentInParent() ?? ((Component)followTransform).GetComponentInChildren(true); hostObject = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : ((Component)followTransform).gameObject); ManualLogSource log3 = Plugin.Log; if (log3 != null) { GameObject obj = hostObject; log3.LogInfo((object)("[TryResolveSourceHostObject] BODY: via followTransform: hostObject=" + ((obj != null) ? ((Object)obj).name : null) + " body=" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "null"))); } return (Object)(object)hostObject != (Object)null; } if (sourceObjectId != 0L) { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.SpawnManager != null && NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(sourceObjectId, out var value) && (Object)(object)value != (Object)null) { DeadBodyInfo val2 = ((Component)value).GetComponent() ?? ((Component)value).GetComponentInParent() ?? ((Component)value).GetComponentInChildren(true); hostObject = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : ((Component)value).gameObject); ManualLogSource log4 = Plugin.Log; if (log4 != null) { GameObject obj2 = hostObject; log4.LogInfo((object)("[TryResolveSourceHostObject] BODY: via SpawnedObjects: hostObject=" + ((obj2 != null) ? ((Object)obj2).name : null) + " body=" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "null"))); } return (Object)(object)hostObject != (Object)null; } DeadBodyInfo[] array = Object.FindObjectsOfType(true); foreach (DeadBodyInfo val3 in array) { if ((Object)(object)val3 == (Object)null) { continue; } NetworkObject val4 = ((Component)val3).GetComponent() ?? ((Component)val3).GetComponentInParent(); if ((Object)(object)val4 != (Object)null && val4.NetworkObjectId == sourceObjectId) { hostObject = ((Component)val3).gameObject; ManualLogSource log5 = Plugin.Log; if (log5 != null) { GameObject obj3 = hostObject; log5.LogInfo((object)("[TryResolveSourceHostObject] BODY: via FindObjectsOfType: hostObject=" + ((obj3 != null) ? ((Object)obj3).name : null))); } return (Object)(object)hostObject != (Object)null; } } ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)"[TryResolveSourceHostObject] BODY: pas trouvé via NetworkObjectId"); } } ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)"[TryResolveSourceHostObject] BODY: échec de résolution"); } return false; } case ReviveSourceKind.Tomb: { bool flag = DeathMarkerManager.TryGetMarkerObject(playerId, out hostObject) && (Object)(object)hostObject != (Object)null; ManualLogSource log14 = Plugin.Log; if (log14 != null) { object arg = flag; GameObject obj8 = hostObject; log14.LogInfo((object)$"[TryResolveSourceHostObject] TOMB: trouvé={arg} hostObject={((obj8 != null) ? ((Object)obj8).name : null)}"); } return flag; } default: { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[TryResolveSourceHostObject] Autre type: échec de résolution"); } return false; } } } private static void ConfigureTrigger(InteractTrigger trigger, int playerId, ReviveSourceKind sourceKind) { if (!((Object)(object)trigger == (Object)null)) { InitializeInteractTriggerFromScratch(trigger); string text = GeneralUtil.GetPlayerByClientId(playerId)?.playerUsername; if (string.IsNullOrWhiteSpace(text)) { text = $"Player {playerId}"; } string text2 = sourceKind switch { ReviveSourceKind.Masked => "masked body", ReviveSourceKind.Tomb => "tomb", _ => "body", }; trigger.interactable = true; trigger.hoverTip = "Revive " + text; trigger.disabledHoverTip = string.Empty; SetFieldOrPropertyIfExists(trigger, "interactTip", "Revive (" + text2 + ")"); } } private static void ConfigureMaskedTriggerText(ReviveTriggerEntry entry, bool maskedDead) { if (entry != null && !((Object)(object)entry.Trigger == (Object)null)) { string text = GeneralUtil.GetPlayerByClientId(entry.TargetPlayerId)?.playerUsername; if (string.IsNullOrWhiteSpace(text)) { text = $"Player {entry.TargetPlayerId}"; } if (!maskedDead) { entry.Trigger.interactable = true; entry.Trigger.hoverTip = "Kill masked to revive " + text; entry.Trigger.disabledHoverTip = "Kill masked to revive " + text; SetFieldOrPropertyIfExists(entry.Trigger, "interactTip", "Kill masked first"); } else { entry.Trigger.interactable = true; entry.Trigger.hoverTip = "Revive " + text; entry.Trigger.disabledHoverTip = string.Empty; SetFieldOrPropertyIfExists(entry.Trigger, "interactTip", "Revive (masked body)"); } } } private static void InitializeInteractTriggerFromScratch(InteractTrigger trigger) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown if (!((Object)(object)trigger == (Object)null)) { trigger.interactable = true; trigger.holdInteraction = false; trigger.timeToHold = 0f; trigger.oneHandedItemAllowed = true; trigger.twoHandedItemAllowed = true; trigger.hoverTip = "Revive"; trigger.disabledHoverTip = ""; SetFieldOrPropertyIfExists(trigger, "specialCharacterAnimation", ""); SetFieldOrPropertyIfExists(trigger, "stopAnimationManually", false); SetFieldOrPropertyIfExists(trigger, "interactAnimation", ""); SetFieldOrPropertyIfExists(trigger, "interactTip", "Revive"); if (trigger.onInteract == null) { trigger.onInteract = new InteractEvent(); } } } private static void SetFieldOrPropertyIfExists(object instance, string memberName, object value) { if (instance == null) { return; } Type type = value?.GetType(); Type type2 = instance.GetType(); FieldInfo field = type2.GetField(memberName, InstanceFlags); if (field != null && (type == null || field.FieldType.IsAssignableFrom(type))) { field.SetValue(instance, value); return; } PropertyInfo property = type2.GetProperty(memberName, InstanceFlags); if (property != null && property.CanWrite && (type == null || property.PropertyType.IsAssignableFrom(type))) { property.SetValue(instance, value); } } private static bool EvaluateCanRevive(ReviveTriggerEntry entry, PlayerControllerB reviver, out int resolvedTargetId, out string reason) { //IL_0195: 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_019a: 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_00fc: 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_0101: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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) resolvedTargetId = entry.TargetPlayerId; reason = "source missing"; if (entry.SourceKind == ReviveSourceKind.Body) { PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(entry.TargetPlayerId); RagdollGrabbableObject val = TryResolveEntryBody(entry); if ((Object)(object)val == (Object)null) { val = ((entry.SourceObjectId != 0L) ? GeneralUtil.GetBodyByNetworkObjectId(entry.SourceObjectId) : GeneralUtil.GetBodyForPlayer(entry.TargetPlayerId)); } if ((Object)(object)val == (Object)null) { val = GeneralUtil.GetBodyFromDeadBodyReference(playerByClientId); } if ((Object)(object)val != (Object)null) { if (ReviveRules.CanRevive(reviver, val, out resolvedTargetId, out reason)) { return true; } if (string.Equals(reason, "no body", StringComparison.Ordinal)) { Vector3 targetPosition = (((Object)(object)((Component)val).transform != (Object)null) ? ((Component)val).transform.position : entry.StaticPosition); if (!GeneralUtil.IsReviverCloseEnoughToTomb(reviver, targetPosition)) { reason = "too far from body"; return false; } resolvedTargetId = entry.TargetPlayerId; return ReviveRules.CanReviveAtTomb(reviver, entry.TargetPlayerId, out reason); } return false; } Vector3 targetPosition2 = (((Object)(object)entry.RootObject != (Object)null) ? entry.RootObject.transform.position : entry.StaticPosition); if (!GeneralUtil.IsReviverCloseEnoughToTomb(reviver, targetPosition2)) { reason = "too far from body"; return false; } resolvedTargetId = entry.TargetPlayerId; return ReviveRules.CanReviveAtTomb(reviver, entry.TargetPlayerId, out reason); } if (entry.SourceKind == ReviveSourceKind.Masked) { MaskedPlayerEnemy val2 = ((entry.SourceObjectId != 0L) ? GeneralUtil.GetMaskedByNetworkObjectId(entry.SourceObjectId) : GeneralUtil.GetConvertedMaskedForPlayer(entry.TargetPlayerId)); if ((Object)(object)val2 == (Object)null) { reason = "masked missing"; return false; } return ReviveRules.CanReviveMasked(reviver, val2, out resolvedTargetId, out reason); } Vector3 targetPosition3 = (((Object)(object)entry.RootObject != (Object)null) ? entry.RootObject.transform.position : entry.StaticPosition); if (!GeneralUtil.IsReviverCloseEnoughToTomb(reviver, targetPosition3)) { reason = "too far from body"; return false; } return ReviveRules.CanReviveAtTomb(reviver, entry.TargetPlayerId, out reason); } private static RagdollGrabbableObject TryResolveEntryBody(ReviveTriggerEntry entry) { if (entry == null) { return null; } GameObject rootObject = entry.RootObject; if ((Object)(object)rootObject != (Object)null) { RagdollGrabbableObject val = rootObject.GetComponent() ?? rootObject.GetComponentInParent() ?? rootObject.GetComponentInChildren(true); if ((Object)(object)val != (Object)null) { return val; } } Transform followTransform = entry.FollowTransform; if ((Object)(object)followTransform == (Object)null) { return null; } return ((Component)followTransform).GetComponent() ?? ((Component)followTransform).GetComponentInParent() ?? ((Component)followTransform).GetComponentInChildren(true); } private static bool TryResolveCurrentReviveSource(int playerId, out ReviveSourceKind sourceKind, out ulong sourceObjectId, out Transform followTransform, out Vector3 staticPosition) { //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_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_0426: 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_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_029c: 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_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) sourceKind = ReviveSourceKind.None; sourceObjectId = 0uL; followTransform = null; staticPosition = Vector3.zero; PlayerDeathType playerLastDeathType = GeneralUtil.GetPlayerLastDeathType(playerId); ulong playerLastDeathNetworkObjectId = GeneralUtil.GetPlayerLastDeathNetworkObjectId(playerId); if (playerLastDeathType == PlayerDeathType.Masked && ModConfig.EnableMaskedPlayerRevive) { MaskedPlayerEnemy val = null; string arg; string source; if (playerLastDeathNetworkObjectId != 0L) { MaskedPlayerEnemy maskedByNetworkObjectId = GeneralUtil.GetMaskedByNetworkObjectId(playerLastDeathNetworkObjectId); int playerClientId; if ((Object)(object)maskedByNetworkObjectId == (Object)null) { arg = "id-missing"; } else if (!GeneralUtil.TryResolveConvertedMaskedPlayer(maskedByNetworkObjectId, out playerClientId, out source)) { arg = "id-not-converted"; } else if (playerClientId != playerId) { arg = $"id-owner-mismatch:{playerClientId}"; } else { val = maskedByNetworkObjectId; arg = "id-match"; } } else { arg = "no-latest-id"; } string text = "not-checked"; if ((Object)(object)val == (Object)null) { MaskedPlayerEnemy convertedMaskedForPlayer = GeneralUtil.GetConvertedMaskedForPlayer(playerId); int playerClientId2; if ((Object)(object)convertedMaskedForPlayer == (Object)null) { text = "owner-none"; } else if (!GeneralUtil.TryResolveConvertedMaskedPlayer(convertedMaskedForPlayer, out playerClientId2, out source)) { text = "owner-not-converted"; } else if (playerClientId2 != playerId) { text = $"owner-mismatch:{playerClientId2}"; } else { val = convertedMaskedForPlayer; text = "owner-match"; } } else { text = "skipped-id-match"; } if ((Object)(object)val != (Object)null) { sourceKind = ReviveSourceKind.Masked; sourceObjectId = MaskedConversionTracker.GetNetworkObjectId(val); followTransform = ((Component)val).transform; LogResolveDiagnostic(playerId, $"resolve=masked sourceObj={sourceObjectId} latestObj={playerLastDeathNetworkObjectId} follow={(Object)(object)followTransform != (Object)null}"); return true; } LogResolveDiagnostic(playerId, $"resolve=masked strict-miss latestObj={playerLastDeathNetworkObjectId} byId={arg} ownerFallback={text}"); } if (DeathMarkerManager.TryGetMarkerObject(playerId, out var markerObject) && (Object)(object)markerObject != (Object)null && (Object)(object)markerObject.transform != (Object)null) { sourceKind = ReviveSourceKind.Tomb; sourceObjectId = 0uL; followTransform = markerObject.transform; staticPosition = markerObject.transform.position; LogResolveDiagnostic(playerId, "resolve=tomb marker sourceObj=0 follow=true"); return true; } if (playerLastDeathType == PlayerDeathType.Body || playerLastDeathType == PlayerDeathType.Unknown) { PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(playerId); RagdollGrabbableObject val2 = ((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetBodyByNetworkObjectId(playerLastDeathNetworkObjectId) : GeneralUtil.GetBodyForPlayer(playerId)); if ((Object)(object)val2 == (Object)null) { val2 = GeneralUtil.GetBodyFromDeadBodyReference(playerByClientId); } if ((Object)(object)val2 == (Object)null && GeneralUtil.TryGetPlayerLastDeathPosition(playerId, out var position)) { val2 = GeneralUtil.GetBodyNearPosition(position); } if ((Object)(object)val2 != (Object)null && DeathMarkerManager.IsTombRagdollGrabbable(val2)) { val2 = null; } if ((Object)(object)val2 != (Object)null) { sourceKind = ReviveSourceKind.Body; sourceObjectId = (((Object)(object)((NetworkBehaviour)val2).NetworkObject != (Object)null) ? ((NetworkBehaviour)val2).NetworkObject.NetworkObjectId : 0); followTransform = ((Component)val2).transform; LogResolveDiagnostic(playerId, $"resolve=body ragdoll sourceObj={sourceObjectId} latestObj={playerLastDeathNetworkObjectId} follow={(Object)(object)followTransform != (Object)null}"); return true; } if (GeneralUtil.TryGetPlayerLastDeathPosition(playerId, out var position2)) { DeadBodyInfo deadBodyNearPosition = GeneralUtil.GetDeadBodyNearPosition(position2); if ((Object)(object)deadBodyNearPosition != (Object)null && (Object)(object)((Component)deadBodyNearPosition).transform != (Object)null && IsDeadBodyOwnedByPlayer(deadBodyNearPosition, playerId, playerByClientId)) { sourceKind = ReviveSourceKind.Body; sourceObjectId = 0uL; followTransform = ((Component)deadBodyNearPosition).transform; staticPosition = ((Component)deadBodyNearPosition).transform.position; LogResolveDiagnostic(playerId, "resolve=body deadBodyNearPosition sourceObj=0 follow=true"); return true; } if ((Object)(object)deadBodyNearPosition != (Object)null) { LogResolveDiagnostic(playerId, "resolve=body deadBodyNearPosition skipped-owner-mismatch"); } } if ((Object)(object)playerByClientId?.deadBody != (Object)null && (Object)(object)((Component)playerByClientId.deadBody).transform != (Object)null && !DeathMarkerManager.IsTombDeadBody(playerId, playerByClientId.deadBody)) { sourceKind = ReviveSourceKind.Body; sourceObjectId = 0uL; followTransform = ((Component)playerByClientId.deadBody).transform; staticPosition = ((Component)playerByClientId.deadBody).transform.position; LogResolveDiagnostic(playerId, "resolve=body player.deadBody sourceObj=0 follow=true"); return true; } } if (GeneralUtil.TryGetPlayerLastDeathPosition(playerId, out var position3) && DeathMarkerManager.TryGetMarkerObject(playerId, out var markerObject2) && (Object)(object)markerObject2 != (Object)null) { sourceKind = ReviveSourceKind.Tomb; sourceObjectId = 0uL; followTransform = markerObject2.transform; staticPosition = position3; LogResolveDiagnostic(playerId, $"resolve=tomb forced sourceObj=0 pos=({position3.x:0.00},{position3.y:0.00},{position3.z:0.00})"); return true; } PlayerControllerB playerByClientId2 = GeneralUtil.GetPlayerByClientId(playerId); Vector3 position4; string text2 = (GeneralUtil.TryGetPlayerLastDeathPosition(playerId, out position4) ? $"({position4.x:0.00},{position4.y:0.00},{position4.z:0.00})" : "none"); LogResolveDiagnostic(playerId, $"resolve=none deathType={playerLastDeathType} latestObj={playerLastDeathNetworkObjectId} hasPlayer={(Object)(object)playerByClientId2 != (Object)null} hasDeadBodyRef={(Object)(object)playerByClientId2?.deadBody != (Object)null} deathPos={text2}"); return false; } private static bool IsDeadBodyOwnedByPlayer(DeadBodyInfo deadBody, int playerId, PlayerControllerB expectedPlayer) { if ((Object)(object)deadBody == (Object)null) { return false; } if (DeathMarkerManager.IsTombDeadBody(playerId, deadBody)) { return true; } if (DeathMarkerManager.TryGetTombOwnerPlayerId(deadBody, out var playerClientId)) { return playerClientId == playerId; } PlayerControllerB playerScript = deadBody.playerScript; if ((Object)(object)playerScript != (Object)null) { return (int)playerScript.playerClientId == playerId; } if ((Object)(object)expectedPlayer != (Object)null && (Object)(object)expectedPlayer.deadBody == (Object)(object)deadBody) { return true; } if (StartOfRound.Instance?.allPlayerScripts != null) { PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.deadBody != (Object)(object)deadBody)) { return (int)val.playerClientId == playerId; } } } return false; } private static void RemoveEntry(int playerId) { if (!EntriesByPlayer.TryGetValue(playerId, out var value) || value == null) { return; } if (value.OwnsTriggerHostObject && (Object)(object)value.TriggerHostObject != (Object)null) { if ((Object)(object)value.Trigger != (Object)null) { TriggerInstanceToPlayer.Remove(((Object)value.Trigger).GetInstanceID()); } Object.Destroy((Object)(object)value.TriggerHostObject); value.Trigger = null; } if ((Object)(object)value.Trigger != (Object)null) { TriggerInstanceToPlayer.Remove(((Object)value.Trigger).GetInstanceID()); if (value.OwnsTrigger) { Object.Destroy((Object)(object)value.Trigger); } } if ((Object)(object)value.AddedCollider != (Object)null) { Object.Destroy((Object)(object)value.AddedCollider); } if (value.ChangedLayer && (Object)(object)value.LayerAdjustedObject != (Object)null) { value.LayerAdjustedObject.layer = value.OriginalLayer; } Plugin.DebugLog($"[ReviveTrigger] removed source={value.SourceKind} player={playerId}"); EntriesByPlayer.Remove(playerId); _lastResolveDiagnosticsByPlayer.Remove(playerId); _lastTickDiagnosticsByPlayer.Remove(playerId); _lastForceDiagnosticsByPlayer.Remove(playerId); } private static void RegisterTriggerMapping(InteractTrigger trigger, int playerId, ReviveSourceKind sourceKind, GameObject hostObject) { if ((Object)(object)trigger == (Object)null) { return; } int instanceID = ((Object)trigger).GetInstanceID(); if (TriggerInstanceToPlayer.TryGetValue(instanceID, out var value) && value != playerId) { string text = "unknown"; if (EntriesByPlayer.TryGetValue(value, out var value2)) { object obj; if (value2 == null) { obj = null; } else { GameObject triggerHostObject = value2.TriggerHostObject; obj = ((triggerHostObject != null) ? triggerHostObject.transform : null); } if ((Object)obj != (Object)null) { text = BuildTransformPath(value2.TriggerHostObject.transform); } } string text2 = (((Object)(object)((hostObject != null) ? hostObject.transform : null) != (Object)null) ? BuildTransformPath(hostObject.transform) : "unknown"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"[ReviveTrigger] id-collision triggerId={instanceID} oldPlayer={value} newPlayer={playerId} source={sourceKind} oldPath={text} newPath={text2}"); } } TriggerInstanceToPlayer[instanceID] = playerId; } private static void LogResolveDiagnostic(int playerId, string message) { if (!_lastResolveDiagnosticsByPlayer.TryGetValue(playerId, out var value) || !string.Equals(value, message, StringComparison.Ordinal)) { _lastResolveDiagnosticsByPlayer[playerId] = message; Plugin.DebugLog($"[ReviveResolve] player={playerId} {message}"); } } private static void LogForceDiagnostic(int playerId, string message) { if (!_lastForceDiagnosticsByPlayer.TryGetValue(playerId, out var value) || !string.Equals(value, message, StringComparison.Ordinal)) { _lastForceDiagnosticsByPlayer[playerId] = message; Plugin.DebugLog($"[ReviveForce] player={playerId} {message}"); } } private static bool TryGetHoveredEntry(PlayerControllerB player, out ReviveTriggerEntry entry) { entry = null; if ((Object)(object)player == (Object)null) { return false; } if (TryGetCurrentInteractTrigger(player, out var trigger) && (Object)(object)trigger != (Object)null) { LogObservedTriggerDiagnostic(player, trigger); int instanceID = ((Object)trigger).GetInstanceID(); ReviveTriggerEntry reviveTriggerEntry = null; ReviveTriggerEntry value2; if (!TriggerInstanceToPlayer.TryGetValue(instanceID, out var value)) { if (TryCreateEntryFromVanillaBodyTrigger(trigger, out var entry2)) { reviveTriggerEntry = entry2; } } else if (EntriesByPlayer.TryGetValue(value, out value2) && value2 != null && (Object)(object)value2.Trigger == (Object)(object)trigger) { reviveTriggerEntry = value2; } if (reviveTriggerEntry != null) { RagdollGrabbableObject val = TryResolveEntryBody(reviveTriggerEntry); if ((Object)(object)val != (Object)null && ((GrabbableObject)val).isHeld) { return false; } entry = reviveTriggerEntry; RememberHoveredEntry(entry); return true; } } else { LogObservedTriggerDiagnostic(player, null); } if (TryGetHoveredBodyEntryBySight(player, out var entry3)) { entry = entry3; RememberHoveredEntry(entry); return true; } if (TryGetGraceHoveredEntry(player, out var entry4)) { entry = entry4; return true; } return false; } private static void RememberHoveredEntry(ReviveTriggerEntry entry) { if (entry != null) { _lastHoveredTargetId = entry.TargetPlayerId; _lastHoveredSeenAt = Time.time; } } private static bool TryGetGraceHoveredEntry(PlayerControllerB player, out ReviveTriggerEntry entry) { //IL_0065: 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_006a: 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_0075: Unknown result type (might be due to invalid IL or missing references) entry = null; if ((Object)(object)player == (Object)null) { return false; } if (_lastHoveredTargetId < 0) { return false; } if (Time.time - _lastHoveredSeenAt > 0.45f) { return false; } if (!EntriesByPlayer.TryGetValue(_lastHoveredTargetId, out var value) || value == null) { return false; } Vector3 targetPosition = (((Object)(object)value.RootObject != (Object)null) ? value.RootObject.transform.position : value.StaticPosition); if (!((value.SourceKind == ReviveSourceKind.Tomb) ? GeneralUtil.IsReviverCloseEnoughToTomb(player, targetPosition) : GeneralUtil.IsReviverCloseEnough(player, targetPosition))) { return false; } entry = value; return true; } private static bool TryGetHoveredBodyEntryBySight(PlayerControllerB player, out ReviveTriggerEntry entry) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) entry = null; if ((Object)(object)player == (Object)null) { return false; } Transform val = (((Object)(object)player.gameplayCamera != (Object)null) ? ((Component)player.gameplayCamera).transform : ((Component)player).transform); if ((Object)(object)val == (Object)null) { return false; } float reviveValidationDistance = GeneralUtil.GetReviveValidationDistance(player); float num = Mathf.Max(player.grabDistance, reviveValidationDistance); RaycastHit[] array = Physics.RaycastAll(new Ray(val.position, val.forward), num, -1, (QueryTriggerInteraction)2); if (array == null || array.Length == 0) { return false; } Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val2 = array2[i]; if (!(((RaycastHit)(ref val2)).distance > reviveValidationDistance)) { Collider collider = ((RaycastHit)(ref val2)).collider; if (!((Object)(object)collider == (Object)null) && TryGetBodyEntryForCollider(collider, out entry)) { return true; } } } return false; } private static bool TryGetBodyEntryForCollider(Collider collider, out ReviveTriggerEntry entry) { entry = null; if ((Object)(object)collider == (Object)null) { return false; } Transform transform = ((Component)collider).transform; foreach (ReviveTriggerEntry value in EntriesByPlayer.Values) { if (value == null) { continue; } RagdollGrabbableObject val = TryResolveEntryBody(value); if ((Object)(object)val != (Object)null && ((GrabbableObject)val).isHeld) { continue; } if ((Object)(object)value.TriggerHostObject != (Object)null) { Transform transform2 = value.TriggerHostObject.transform; if ((Object)(object)transform == (Object)(object)transform2 || transform.IsChildOf(transform2) || transform2.IsChildOf(transform)) { entry = value; return true; } } Transform val2 = (((Object)(object)val != (Object)null) ? ((Component)val).transform : value.FollowTransform); if ((Object)(object)val2 != (Object)null && ((Object)(object)transform == (Object)(object)val2 || transform.IsChildOf(val2) || val2.IsChildOf(transform))) { entry = value; return true; } if (value.SourceKind == ReviveSourceKind.Body) { PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(value.TargetPlayerId); Transform val3 = (((Object)(object)playerByClientId?.deadBody != (Object)null) ? ((Component)playerByClientId.deadBody).transform : null); if ((Object)(object)val3 != (Object)null && ((Object)(object)transform == (Object)(object)val3 || transform.IsChildOf(val3))) { entry = value; return true; } } } return false; } private static void LogObservedTriggerDiagnostic(PlayerControllerB player, InteractTrigger trigger) { if (!((Object)(object)player == (Object)null)) { int num = (int)player.playerClientId; string text; if ((Object)(object)trigger == (Object)null) { text = "observed=none"; } else { string text2 = (((Object)(object)((Component)trigger).gameObject != (Object)null) ? ((Object)((Component)trigger).gameObject).name : "none"); string text3 = (((Object)(object)((Component)trigger).transform != (Object)null) ? BuildTransformPath(((Component)trigger).transform) : "none"); int num2 = (((Object)(object)((Component)trigger).gameObject != (Object)null) ? ((Component)trigger).gameObject.layer : (-1)); bool enabled = ((Behaviour)trigger).enabled; Collider component = ((Component)trigger).GetComponent(); string text4 = (((Object)(object)component == (Object)null) ? "none" : $"{((object)component).GetType().Name}:{component.enabled}:trigger={component.isTrigger}"); RagdollGrabbableObject componentInParent = ((Component)trigger).GetComponentInParent(); string text5 = (((Object)(object)componentInParent != (Object)null) ? ((Object)componentInParent).name : "none"); ulong num3 = (((Object)(object)((componentInParent != null) ? ((NetworkBehaviour)componentInParent).NetworkObject : null) != (Object)null) ? ((NetworkBehaviour)componentInParent).NetworkObject.NetworkObjectId : 0); DeadBodyInfo componentInParent2 = ((Component)trigger).GetComponentInParent(); string text6 = (((Object)(object)componentInParent2 != (Object)null) ? ((Object)componentInParent2).name : "none"); int value; string text7 = (TriggerInstanceToPlayer.TryGetValue(((Object)trigger).GetInstanceID(), out value) ? value.ToString() : "none"); text = $"observed=id={((Object)trigger).GetInstanceID()} name={((Object)trigger).name} obj={text2} enabled={enabled} interactable={trigger.interactable} layer={num2} collider={text4} mappedPlayer={text7} body={text5}/obj:{num3} deadBody={text6} path={text3}"; } if (!_lastObservedTriggerDiagnosticsByPlayer.TryGetValue(num, out var value2) || !string.Equals(value2, text, StringComparison.Ordinal)) { _lastObservedTriggerDiagnosticsByPlayer[num] = text; Plugin.DebugLog($"[ReviveLook] viewer={num} {text}"); } } } private static bool TryCreateEntryFromVanillaBodyTrigger(InteractTrigger trigger, out ReviveTriggerEntry entry) { //IL_00c3: 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_00c8: Unknown result type (might be due to invalid IL or missing references) entry = null; if ((Object)(object)trigger == (Object)null) { return false; } RagdollGrabbableObject val = ((Component)trigger).GetComponentInParent(); if ((Object)(object)val == (Object)null) { val = ((Component)trigger).GetComponent(); } if ((Object)(object)val == (Object)null) { return false; } if (!GeneralUtil.TryGetBodyOwner(val, out var targetPlayerClientId, out var targetPlayer) || (Object)(object)targetPlayer == (Object)null || !targetPlayer.isPlayerDead) { return false; } ulong sourceObjectId = (((Object)(object)((NetworkBehaviour)val).NetworkObject != (Object)null) ? ((NetworkBehaviour)val).NetworkObject.NetworkObjectId : 0); entry = new ReviveTriggerEntry { TargetPlayerId = targetPlayerClientId, SourceKind = ReviveSourceKind.Body, SourceObjectId = sourceObjectId, RootObject = ((Component)val).gameObject, Trigger = trigger, FollowTransform = ((Component)val).transform, StaticPosition = (((Object)(object)((Component)val).transform != (Object)null) ? ((Component)val).transform.position : Vector3.zero) }; return true; } private static bool TryGetCurrentInteractTrigger(PlayerControllerB player, out InteractTrigger trigger) { trigger = null; if ((Object)(object)player == (Object)null) { return false; } CachePlayerTriggerMembers(); string[] preferredPlayerTriggerFieldNames = PreferredPlayerTriggerFieldNames; foreach (string fieldName in preferredPlayerTriggerFieldNames) { FieldInfo fieldInfo = _cachedPlayerTriggerFields.FirstOrDefault((FieldInfo f) => f.Name.Equals(fieldName, StringComparison.Ordinal)); if (!(fieldInfo == null)) { object? value = fieldInfo.GetValue(player); InteractTrigger val = (InteractTrigger)((value is InteractTrigger) ? value : null); if ((Object)(object)val != (Object)null) { trigger = val; return true; } } } FieldInfo[] cachedPlayerTriggerFields = _cachedPlayerTriggerFields; for (int i = 0; i < cachedPlayerTriggerFields.Length; i++) { object? value2 = cachedPlayerTriggerFields[i].GetValue(player); InteractTrigger val2 = (InteractTrigger)((value2 is InteractTrigger) ? value2 : null); if ((Object)(object)val2 != (Object)null) { trigger = val2; return true; } } PropertyInfo[] cachedPlayerTriggerProperties = _cachedPlayerTriggerProperties; foreach (PropertyInfo propertyInfo in cachedPlayerTriggerProperties) { try { object? value3 = propertyInfo.GetValue(player); InteractTrigger val3 = (InteractTrigger)((value3 is InteractTrigger) ? value3 : null); if ((Object)(object)val3 != (Object)null) { trigger = val3; return true; } } catch { } } return false; } private static void CachePlayerTriggerMembers() { if (_cachedPlayerTriggerFields == null || _cachedPlayerTriggerProperties == null) { _cachedPlayerTriggerFields = (from f in typeof(PlayerControllerB).GetFields(InstanceFlags) where typeof(InteractTrigger).IsAssignableFrom(f.FieldType) select f).ToArray(); _cachedPlayerTriggerProperties = (from p in typeof(PlayerControllerB).GetProperties(InstanceFlags) where p.GetIndexParameters().Length == 0 && p.CanRead && typeof(InteractTrigger).IsAssignableFrom(p.PropertyType) select p).ToArray(); } } private static bool HasUsableInteractCollider(GameObject hostObject) { if ((Object)(object)hostObject == (Object)null) { return false; } Collider[] components = hostObject.GetComponents(); foreach (Collider val in components) { if ((Object)(object)val != (Object)null && val.enabled) { return true; } } return false; } } public static class ReviveLogic { [CompilerGenerated] private sealed class d__9 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: { <>1__state = -1; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || !val.isPlayerDead || (Object)(object)HUDManager.Instance == (Object)null) { return false; } HUDManager.Instance.UpdateBoxesSpectateUI(); return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static ManualLogSource _log; public static void SetLogSource(ManualLogSource logSource) { _log = logSource; } public static void ApplyRevive(int playerClientId, int health, int remainingRevives, Vector3 revivePosition, bool isInsideFactory, ulong maskedNetworkObjectId = 0uL) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(playerClientId); if ((Object)(object)playerByClientId == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"Cannot revive player {playerClientId}: player not found."); } return; } bool isPlayerDead = playerByClientId.isPlayerDead; ModState.RemainingRevives = remainingRevives; if (!isPlayerDead) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"Ignoring revive for {playerClientId}: already alive."); } return; } PlayerDeathType playerLastDeathType = GeneralUtil.GetPlayerLastDeathType(playerClientId); ulong playerLastDeathNetworkObjectId = GeneralUtil.GetPlayerLastDeathNetworkObjectId(playerClientId); RagdollGrabbableObject val = null; MaskedPlayerEnemy val2 = null; switch (playerLastDeathType) { case PlayerDeathType.Body: val = GeneralUtil.GetBodyByNetworkObjectId(playerLastDeathNetworkObjectId); if ((Object)(object)val == (Object)null) { val = GeneralUtil.GetBodyForPlayer(playerClientId); } break; case PlayerDeathType.Masked: val2 = GeneralUtil.GetMaskedByNetworkObjectId(playerLastDeathNetworkObjectId); if ((Object)(object)val2 == (Object)null) { val2 = GeneralUtil.GetConvertedMaskedCorpseForPlayer(playerClientId); } break; } try { RevivePlayerState(playerByClientId, health, revivePosition, isInsideFactory); } catch (Exception arg) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogError((object)$"[ApplyRevive] RevivePlayerState exception for player {playerClientId}: {arg}"); } } try { RemoveBody(val); if ((Object)(object)val2 != (Object)null) { MethodInfo method = typeof(ReviveInteractTriggerManager).GetMethod("RemoveEntry", BindingFlags.Static | BindingFlags.NonPublic); if (method != null) { method.Invoke(null, new object[1] { playerClientId }); } Object.Destroy((Object)(object)((Component)val2).gameObject); } TombDeadBodySetup.UnregisterActiveTomb(playerByClientId); DeathMarkerManager.ClearMarker(playerClientId); GeneralUtil.ClearPlayerDeathState(playerClientId); MaskedConversionTracker.ForgetPlayer(playerClientId); } catch (Exception arg2) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)$"[ApplyRevive] Cleanup exception for player {playerClientId}: {arg2}"); } } try { ShowReviveTip(playerByClientId, remainingRevives); RefreshSpectatorListForDeadPlayers(); } catch (Exception arg3) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogError((object)$"[ApplyRevive] UI exception for player {playerClientId}: {arg3}"); } } } private static void RevivePlayerState(PlayerControllerB player, int health, Vector3 revivePosition, bool isInsideFactory) { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) try { int num = (int)player.playerClientId; player.ResetPlayerBloodObjects(player.isPlayerDead); player.isClimbingLadder = false; player.ResetZAndXRotation(); if ((Object)(object)player.thisController != (Object)null) { ((Collider)player.thisController).enabled = true; } player.health = health; player.disableLookInput = false; player.isPlayerDead = false; player.isPlayerControlled = true; player.isInElevator = !isInsideFactory; player.isInHangarShipRoom = !isInsideFactory; player.isInsideFactory = isInsideFactory; player.setPositionOfDeadPlayer = false; player.deadBody = null; player.redirectToEnemy = null; if ((Object)(object)StartOfRound.Instance != (Object)null) { StartOfRound instance = StartOfRound.Instance; instance.livingPlayers++; StartOfRound.Instance.allPlayersDead = false; StartOfRound.Instance.UpdatePlayerVoiceEffects(); if (StartOfRound.Instance.allPlayerObjects != null && num >= 0 && num < StartOfRound.Instance.allPlayerObjects.Length) { player.DisablePlayerModel(StartOfRound.Instance.allPlayerObjects[num], true, true); } } player.TeleportPlayer(revivePosition, false, 0f, false, true); if ((Object)(object)player.helmetLight != (Object)null) { ((Behaviour)player.helmetLight).enabled = false; } player.Crouch(false); player.criticallyInjured = false; if ((Object)(object)player.playerBodyAnimator != (Object)null) { player.playerBodyAnimator.SetBool("Limp", false); } player.bleedingHeavily = false; player.activatingItem = false; player.twoHanded = false; player.inSpecialInteractAnimation = false; player.disableSyncInAnimation = false; player.inAnimationWithEnemy = null; player.holdingWalkieTalkie = false; player.speakingToWalkieTalkie = false; player.isSinking = false; player.isUnderwater = false; player.sinkingValue = 0f; player.voiceMuffledByEnemy = false; player.hasBegunSpectating = false; player.hinderedMultiplier = 1f; player.isMovementHindered = 0; player.sourcesCausingSinking = 0; if ((Object)(object)player.statusEffectAudio != (Object)null) { player.statusEffectAudio.Stop(); } player.DisableJetpackControlsLocally(); if ((Object)(object)player.mapRadarDotAnimator != (Object)null) { player.mapRadarDotAnimator.SetBool("dead", false); } if ((Object)(object)StartOfRound.Instance != (Object)null) { player.reverbPreset = StartOfRound.Instance.shipReverb; } FixVoiceState(player, num); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)string.Format("[RevivePlayerState] Exception for player {0}: {1}", ((Object)(object)player != (Object)null) ? player.playerClientId.ToString() : "null", arg)); } } try { FixLocalPlayerUiIfNeeded(player, health); } catch (Exception arg2) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)string.Format("[RevivePlayerState] UI exception for player {0}: {1}", ((Object)(object)player != (Object)null) ? player.playerClientId.ToString() : "null", arg2)); } } } private static void FixVoiceState(PlayerControllerB player, int playerIndex) { try { if ((Object)(object)SoundManager.Instance != (Object)null) { SoundManager.Instance.earsRingingTimer = 0f; if (SoundManager.Instance.playerVoicePitchTargets != null && playerIndex >= 0 && playerIndex < SoundManager.Instance.playerVoicePitchTargets.Length) { SoundManager.Instance.playerVoicePitchTargets[playerIndex] = 1f; SoundManager.Instance.SetPlayerPitch(1f, playerIndex); } } if ((Object)(object)player.currentVoiceChatIngameSettings == (Object)null && (Object)(object)StartOfRound.Instance != (Object)null) { StartOfRound.Instance.RefreshPlayerVoicePlaybackObjects(); } if (!((Object)(object)player.currentVoiceChatIngameSettings != (Object)null)) { return; } if ((Object)(object)player.currentVoiceChatIngameSettings.voiceAudio == (Object)null) { player.currentVoiceChatIngameSettings.InitializeComponents(); } if ((Object)(object)player.currentVoiceChatIngameSettings.voiceAudio != (Object)null) { OccludeAudio component = ((Component)player.currentVoiceChatIngameSettings.voiceAudio).GetComponent(); if ((Object)(object)component != (Object)null) { component.overridingLowPass = false; } } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Voice reset skipped: " + ex.Message)); } } } private static void FixLocalPlayerUiIfNeeded(PlayerControllerB revivedPlayer, int health) { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || val.playerClientId != revivedPlayer.playerClientId) { return; } val.bleedingHeavily = false; val.criticallyInjured = false; val.isPlayerDead = false; val.isPlayerControlled = true; val.disableLookInput = false; val.hasBegunSpectating = false; val.health = health; val.spectatedPlayerScript = null; if ((Object)(object)val.playerBodyAnimator != (Object)null) { val.playerBodyAnimator.SetBool("Limp", false); } if ((Object)(object)HUDManager.Instance != (Object)null) { if ((Object)(object)HUDManager.Instance.audioListenerLowPass != (Object)null) { ((Behaviour)HUDManager.Instance.audioListenerLowPass).enabled = false; } HUDManager.Instance.UpdateHealthUI(health, true); HUDManager.Instance.RemoveSpectateUI(); if ((Object)(object)HUDManager.Instance.gasHelmetAnimator != (Object)null) { HUDManager.Instance.gasHelmetAnimator.SetBool("gasEmitting", false); } if ((Object)(object)HUDManager.Instance.gameOverAnimator != (Object)null) { HUDManager.Instance.gameOverAnimator.SetTrigger("revive"); } } if ((Object)(object)StartOfRound.Instance != (Object)null) { StartOfRound.Instance.SetSpectateCameraToGameOverMode(false, val); StartOfRound.Instance.SetPlayerObjectExtrapolate(false); } } private static void RemoveBody(RagdollGrabbableObject body) { //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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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) if ((Object)(object)body == (Object)null) { return; } try { if ((Object)(object)body != (Object)null && ((GrabbableObject)body).isHeld && (Object)(object)((GrabbableObject)body).playerHeldBy != (Object)null) { ((GrabbableObject)body).playerHeldBy.DropAllHeldItems(true, false, false, false, default(Vector3), default(Vector3), default(Vector3), default(Vector3), default(Vector3)); } NetworkObject networkObject = ((NetworkBehaviour)body).NetworkObject; if ((Object)(object)networkObject != (Object)null && networkObject.IsSpawned) { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { networkObject.Despawn(true); } return; } if ((Object)(object)body.ragdoll != (Object)null) { Object.Destroy((Object)(object)((Component)body.ragdoll).gameObject); } if ((Object)(object)((Component)body).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)body).gameObject); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Body removal skipped: " + ex.Message)); } } } private static void ShowReviveTip(PlayerControllerB player, int remainingRevives) { if (!((Object)(object)HUDManager.Instance == (Object)null)) { string text = ((ModConfig.CurrentReviveMode != 0 && remainingRevives != int.MaxValue) ? $"{remainingRevives} revives left." : "Unlimited revives."); HUDManager.Instance.DisplayTip("Revive", player.playerUsername + " was revived. " + text, false, false, "LC_Tip1"); PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && val.isPlayerDead) { HUDManager.Instance.UpdateBoxesSpectateUI(); } } } private static void RefreshSpectatorListForDeadPlayers() { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val == (Object)null) && val.isPlayerDead && !((Object)(object)HUDManager.Instance == (Object)null)) { HUDManager.Instance.UpdateBoxesSpectateUI(); if ((Object)(object)StartOfRound.Instance != (Object)null) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(RefreshSpectatorListDelayed()); } } } [IteratorStateMachine(typeof(d__9))] private static IEnumerator RefreshSpectatorListDelayed() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__9(0); } } public static class ReviveNetwork { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnServerReviveMessage; public static HandleNamedMessageDelegate <1>__OnClientReviveMessage; public static HandleNamedMessageDelegate <2>__OnRequestRevive; public static HandleNamedMessageDelegate <3>__OnPerformRevive; public static HandleNamedMessageDelegate <4>__OnRejectRevive; public static HandleNamedMessageDelegate <5>__OnSyncConfig; } private const string RequestReviveMessage = "UltimateRevive.RequestRevive.v2"; private const string PerformReviveMessage = "UltimateRevive.PerformRevive.v2"; private const string RejectReviveMessage = "UltimateRevive.RejectRevive.v2"; private const string SyncConfigMessage = "UltimateRevive.SyncConfig.v1"; private const int ConfigPayloadWriterSize = 2048; private static ManualLogSource _log; private static bool _registered; private static readonly HashSet _configSyncedClientIds = new HashSet(); public static void SetLogSource(ManualLogSource logSource) { _log = logSource; } public static void SendReviveNetworkMessage(bool toServer, ulong targetClientId, string payload) { //IL_006e: 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) if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(payload, false); if (toServer) { NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UR_SV_Revive", 0uL, val, (NetworkDelivery)2); Plugin.DebugLog("[ReviveTrigger] Sent custom message to server: " + payload); } else { NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UR_CL_Revive", targetClientId, val, (NetworkDelivery)2); Plugin.DebugLog($"[ReviveTrigger] Sent custom message to client {targetClientId}: {payload}"); } } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void RegisterNetworkHandlers() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //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_0050: Expected O, but got Unknown if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.CustomMessagingManager == null) { return; } if (NetworkManager.Singleton.IsServer) { CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; object obj = <>O.<0>__OnServerReviveMessage; if (obj == null) { HandleNamedMessageDelegate val = OnServerReviveMessage; <>O.<0>__OnServerReviveMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("UR_SV_Revive", (HandleNamedMessageDelegate)obj); } else { CustomMessagingManager customMessagingManager2 = NetworkManager.Singleton.CustomMessagingManager; object obj2 = <>O.<1>__OnClientReviveMessage; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnClientReviveMessage; <>O.<1>__OnClientReviveMessage = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("UR_CL_Revive", (HandleNamedMessageDelegate)obj2); } Plugin.DebugLog("[ReviveTrigger] Network handlers registered"); } public static void UnregisterNetworkHandlers() { if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.CustomMessagingManager != null) { NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("UR_SV_Revive"); NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("UR_CL_Revive"); Plugin.DebugLog("[ReviveTrigger] Network handlers unregistered"); } } private static void OnServerReviveMessage(ulong senderId, FastBufferReader reader) { try { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); Plugin.DebugLog($"[ReviveTrigger] Server received custom message from {senderId}: {text}"); if (text == "ping") { SendReviveNetworkMessage(toServer: false, senderId, "pong"); Plugin.DebugLog($"[ReviveTrigger] Pong renvoyé à {senderId}"); } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[OnServerReviveMessage] Exception: {arg}"); } } } private static void OnClientReviveMessage(ulong senderId, FastBufferReader reader) { try { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); Plugin.DebugLog("[ReviveTrigger] Client received custom message from server: " + text); if (text == "pong") { ShowLocalTip("Ping/Pong", "Réponse pong reçue du serveur !", warning: false); } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[OnClientReviveMessage] Exception: {arg}"); } } } public static void TryRegisterHandlers() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //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_0098: Expected O, but got Unknown //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_00bd: Expected O, but got Unknown if (!_registered && !((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.CustomMessagingManager != null) { CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; object obj = <>O.<2>__OnRequestRevive; if (obj == null) { HandleNamedMessageDelegate val = OnRequestRevive; <>O.<2>__OnRequestRevive = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("UltimateRevive.RequestRevive.v2", (HandleNamedMessageDelegate)obj); object obj2 = <>O.<3>__OnPerformRevive; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnPerformRevive; <>O.<3>__OnPerformRevive = val2; obj2 = (object)val2; } customMessagingManager.RegisterNamedMessageHandler("UltimateRevive.PerformRevive.v2", (HandleNamedMessageDelegate)obj2); object obj3 = <>O.<4>__OnRejectRevive; if (obj3 == null) { HandleNamedMessageDelegate val3 = OnRejectRevive; <>O.<4>__OnRejectRevive = val3; obj3 = (object)val3; } customMessagingManager.RegisterNamedMessageHandler("UltimateRevive.RejectRevive.v2", (HandleNamedMessageDelegate)obj3); object obj4 = <>O.<5>__OnSyncConfig; if (obj4 == null) { HandleNamedMessageDelegate val4 = OnSyncConfig; <>O.<5>__OnSyncConfig = val4; obj4 = (object)val4; } customMessagingManager.RegisterNamedMessageHandler("UltimateRevive.SyncConfig.v1", (HandleNamedMessageDelegate)obj4); ReviveNetworkSync.RegisterHandlers(); _registered = true; ModState.Active = true; _configSyncedClientIds.Clear(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"Custom networking registered."); } } } public static void UnregisterHandlers() { if (_registered && !((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.CustomMessagingManager != null) { CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; customMessagingManager.UnregisterNamedMessageHandler("UltimateRevive.RequestRevive.v2"); customMessagingManager.UnregisterNamedMessageHandler("UltimateRevive.PerformRevive.v2"); customMessagingManager.UnregisterNamedMessageHandler("UltimateRevive.RejectRevive.v2"); customMessagingManager.UnregisterNamedMessageHandler("UltimateRevive.SyncConfig.v1"); ReviveNetworkSync.UnregisterHandlers(); _registered = false; ModState.Active = false; _configSyncedClientIds.Clear(); } } public static void SendConfigToClients() { //IL_0022: 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) if (!GeneralUtil.IsNetworkReady() || !NetworkManager.Singleton.IsServer) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(2048, (Allocator)2, -1); try { WriteConfigPayload(val); List list = new List(); foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds) { if (connectedClientsId != 0L) { list.Add(connectedClientsId); } } if (list.Count <= 0) { return; } NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UltimateRevive.SyncConfig.v1", (IReadOnlyList)list, val, (NetworkDelivery)3); foreach (ulong item in list) { _configSyncedClientIds.Add(item); } ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Config synchronized to {list.Count} client(s)."); } } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void SyncConfigForNewClients() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) if (!GeneralUtil.IsNetworkReady() || !NetworkManager.Singleton.IsServer || NetworkManager.Singleton.CustomMessagingManager == null) { return; } HashSet connectedClientIds = new HashSet(); foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds) { connectedClientIds.Add(connectedClientsId); } _configSyncedClientIds.RemoveWhere((ulong id) => !connectedClientIds.Contains(id)); List list = new List(); foreach (ulong connectedClientsId2 in NetworkManager.Singleton.ConnectedClientsIds) { if (connectedClientsId2 != 0L && !_configSyncedClientIds.Contains(connectedClientsId2)) { list.Add(connectedClientsId2); } } if (list.Count == 0) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(2048, (Allocator)2, -1); try { WriteConfigPayload(val); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UltimateRevive.SyncConfig.v1", (IReadOnlyList)list, val, (NetworkDelivery)3); foreach (ulong item in list) { _configSyncedClientIds.Add(item); } ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Config synchronized to {list.Count} new client(s)."); } } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void RequestRevive(int targetPlayerClientId) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (!GeneralUtil.IsNetworkReady()) { ShowLocalTip("Revive failed", "Network is not ready.", warning: true); return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return; } int reviverPlayerClientId = (int)val.playerClientId; if (NetworkManager.Singleton.IsServer) { ProcessReviveRequest(reviverPlayerClientId, targetPlayerClientId, NetworkManager.Singleton.LocalClientId); return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(8, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteValueSafe(ref reviverPlayerClientId, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref targetPlayerClientId, default(ForPrimitives)); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UltimateRevive.RequestRevive.v2", 0uL, val2, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val2)).Dispose(); } } private static void OnRequestRevive(ulong senderClientId, FastBufferReader reader) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_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) try { if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer) { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); if (!TryResolveReviverPlayerIdFromSender(senderClientId, num, out var reviverPlayerId)) { Plugin.DebugLog($"[ReviveNetwork] reject request sender={senderClientId} claimedReviver={num} target={num2} reason=reviver invalid"); SendReject(senderClientId, "reviver invalid"); return; } Plugin.DebugLog($"[ReviveNetwork] request sender={senderClientId} claimedReviver={num} resolvedReviver={reviverPlayerId} target={num2}"); ProcessReviveRequest(reviverPlayerId, num2, senderClientId); } } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[OnRequestRevive] Exception: {arg}"); } } } private static bool TryResolveReviverPlayerIdFromSender(ulong senderClientId, int claimedReviverPlayerId, out int reviverPlayerId) { reviverPlayerId = -1; PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts; if (array == null) { return false; } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val == (Object)null)) { NetworkObject networkObject = ((NetworkBehaviour)val).NetworkObject; if (!((Object)(object)networkObject == (Object)null) && networkObject.OwnerClientId == senderClientId) { reviverPlayerId = (int)val.playerClientId; return true; } } } PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(claimedReviverPlayerId); if ((Object)(object)playerByClientId != (Object)null && (Object)(object)((NetworkBehaviour)playerByClientId).NetworkObject != (Object)null && ((NetworkBehaviour)playerByClientId).NetworkObject.OwnerClientId == senderClientId) { reviverPlayerId = claimedReviverPlayerId; return true; } return false; } private static void ProcessReviveRequest(int reviverPlayerClientId, int targetPlayerClientId, ulong requestingNetworkClientId) { //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: 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_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0337: 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_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Unknown result type (might be due to invalid IL or missing references) //IL_03fa: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(reviverPlayerClientId); if ((Object)(object)playerByClientId == (Object)null) { Plugin.DebugLog($"[ReviveNetwork] reject target={targetPlayerClientId} reviver={reviverPlayerClientId} reason=reviver invalid"); SendReject(requestingNetworkClientId, "reviver invalid"); return; } GeneralUtil.GetPlayerByClientId(targetPlayerClientId); PlayerDeathType playerLastDeathType = GeneralUtil.GetPlayerLastDeathType(targetPlayerClientId); ulong playerLastDeathNetworkObjectId = GeneralUtil.GetPlayerLastDeathNetworkObjectId(targetPlayerClientId); RagdollGrabbableObject val = null; MaskedPlayerEnemy val2 = null; if (ModState.BodyByPlayerId.TryGetValue(targetPlayerClientId, out var value) && (Object)(object)value != (Object)null) { val = value; } bool flag; int targetPlayerClientId2; string reason2; if (playerLastDeathType == PlayerDeathType.Masked) { if (!ModConfig.EnableMaskedPlayerRevive) { SendReject(requestingNetworkClientId, "masked revive disabled"); return; } val2 = ((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetMaskedByNetworkObjectId(playerLastDeathNetworkObjectId) : null); if ((Object)(object)val2 == (Object)null) { val2 = GeneralUtil.GetConvertedMaskedCorpseForPlayer(targetPlayerClientId); } if ((Object)(object)val2 == (Object)null) { string reason = null; if (!TryResolveTombValidationPosition(targetPlayerClientId, out var position, out var method) || !ReviveRules.CanReviveAtTomb(playerByClientId, targetPlayerClientId, out reason)) { Plugin.DebugLog(string.Format("[ReviveMethod] tombValidation player={0} death=Masked method={1} result=reject reason={2}", targetPlayerClientId, method ?? "none", string.IsNullOrEmpty(reason) ? "monitor target source missing" : reason)); SendReject(requestingNetworkClientId, string.IsNullOrEmpty(reason) ? "monitor target source missing" : reason); } else { Plugin.DebugLog($"[ReviveMethod] tombValidation player={targetPlayerClientId} death=Masked method={method} result=ok"); CompleteTombRevive(targetPlayerClientId, position); } return; } if (!GeneralUtil.TryResolveConvertedMaskedPlayer(val2, out var playerClientId, out var _) || playerClientId != targetPlayerClientId) { SendReject(requestingNetworkClientId, "monitor target source mismatch"); return; } flag = ReviveRules.CanReviveMasked(playerByClientId, val2, out targetPlayerClientId2, out reason2); } else { val = ((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetBodyByNetworkObjectId(playerLastDeathNetworkObjectId) : GeneralUtil.GetBodyForPlayer(targetPlayerClientId)); if ((Object)(object)val == (Object)null) { string reason3 = null; if (!TryResolveTombValidationPosition(targetPlayerClientId, out var position2, out var method2) || !ReviveRules.CanReviveAtTomb(playerByClientId, targetPlayerClientId, out reason3)) { Plugin.DebugLog(string.Format("[ReviveMethod] tombValidation player={0} death=Body method={1} result=reject reason={2}", targetPlayerClientId, method2 ?? "none", string.IsNullOrEmpty(reason3) ? "monitor target source missing" : reason3)); SendReject(requestingNetworkClientId, string.IsNullOrEmpty(reason3) ? "monitor target source missing" : reason3); } else { Plugin.DebugLog($"[ReviveMethod] tombValidation player={targetPlayerClientId} death=Body method={method2} result=ok"); CompleteTombRevive(targetPlayerClientId, position2); } return; } if (!GeneralUtil.TryGetBodyOwner(val, out var targetPlayerClientId3, out var _) || targetPlayerClientId3 != targetPlayerClientId) { string reason4 = null; if (!DeathMarkerManager.TryGetMarkerPosition(targetPlayerClientId, out var position3)) { Plugin.DebugLog($"[ReviveMethod] tombValidation player={targetPlayerClientId} death=BodyOwnerMismatch method=marker-only result=reject reason=tomb marker missing"); SendReject(requestingNetworkClientId, "tomb marker missing"); } else if (!ReviveRules.CanReviveAtTomb(playerByClientId, targetPlayerClientId, out reason4)) { Plugin.DebugLog(string.Format("[ReviveMethod] tombValidation player={0} death=BodyOwnerMismatch method=marker-only result=reject reason={1}", targetPlayerClientId, string.IsNullOrEmpty(reason4) ? "monitor target source mismatch" : reason4)); SendReject(requestingNetworkClientId, string.IsNullOrEmpty(reason4) ? "monitor target source mismatch" : reason4); } else { Plugin.DebugLog($"[ReviveMethod] tombValidation player={targetPlayerClientId} death=BodyOwnerMismatch method=marker-only result=ok"); CompleteTombRevive(targetPlayerClientId, position3); } return; } flag = ReviveRules.CanRevive(playerByClientId, val, out targetPlayerClientId2, out reason2); } if (!flag) { SendReject(requestingNetworkClientId, reason2); return; } PlayerControllerB playerByClientId2 = GeneralUtil.GetPlayerByClientId(targetPlayerClientId2); if ((Object)(object)playerByClientId2 == (Object)null) { SendReject(requestingNetworkClientId, "target missing"); return; } Vector3 val3 = (((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform != (Object)null) ? ((Component)val).transform.position : ((Component)val2).transform.position); bool isInsideFactory = playerByClientId2.isInsideFactory; ulong maskedNetworkObjectId = (((Object)(object)val2 != (Object)null) ? MaskedConversionTracker.GetNetworkObjectId(val2) : 0); PlayerControllerB closestAlivePlayer = GeneralUtil.GetClosestAlivePlayer(val3); if ((Object)(object)closestAlivePlayer != (Object)null) { isInsideFactory = closestAlivePlayer.isInsideFactory; val3 = ((Component)closestAlivePlayer).transform.position; } int health = GeneralUtil.IncrementReviveCountAndGetHealth(targetPlayerClientId2); if ((Object)(object)val2 != (Object)null) { ulong networkObjectId = MaskedConversionTracker.GetNetworkObjectId(val2); Vector3 deathPosition = (((Object)(object)((Component)val2).transform != (Object)null) ? ((Component)val2).transform.position : val3); GeneralUtil.SetPlayerDeathState(targetPlayerClientId2, PlayerDeathType.Masked, networkObjectId, deathPosition, "ReviveNetwork:FinalizeSourceMasked"); ModState.MaskedByPlayerId.Remove(targetPlayerClientId2); } else if ((Object)(object)val != (Object)null) { ulong networkObjectId2 = (((Object)(object)((NetworkBehaviour)val).NetworkObject != (Object)null) ? ((NetworkBehaviour)val).NetworkObject.NetworkObjectId : 0); Vector3 deathPosition2 = (((Object)(object)((Component)val).transform != (Object)null) ? ((Component)val).transform.position : val3); GeneralUtil.SetPlayerDeathState(targetPlayerClientId2, PlayerDeathType.Body, networkObjectId2, deathPosition2, "ReviveNetwork:FinalizeSourceBody"); ModState.BodyByPlayerId.Remove(targetPlayerClientId2); } int revivesLeft; if (ModConfig.CurrentReviveMode == ModConfig.ReviveMode.PerPlayer) { GeneralUtil.TryConsumePlayerRevive(targetPlayerClientId2, out revivesLeft); } else if (ModConfig.CurrentReviveMode == ModConfig.ReviveMode.FixedPerLevel || ModConfig.CurrentReviveMode == ModConfig.ReviveMode.PerLevelMultiplier) { if (ModState.RemainingRevives > 0) { ModState.RemainingRevives--; } revivesLeft = ModState.RemainingRevives; } else { revivesLeft = ModState.RemainingRevives; } BroadcastRevive(targetPlayerClientId2, health, revivesLeft, val3, isInsideFactory, maskedNetworkObjectId); } private static void CompleteTombRevive(int targetPlayerClientId, Vector3 tombPos) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(targetPlayerClientId); if ((Object)(object)playerByClientId == (Object)null) { return; } int health = GeneralUtil.IncrementReviveCountAndGetHealth(targetPlayerClientId); bool isInsideFactory = playerByClientId.isInsideFactory; PlayerControllerB closestAlivePlayer = GeneralUtil.GetClosestAlivePlayer(tombPos); if ((Object)(object)closestAlivePlayer != (Object)null) { isInsideFactory = closestAlivePlayer.isInsideFactory; tombPos = ((Component)closestAlivePlayer).transform.position; } int revivesLeft; if (ModConfig.CurrentReviveMode == ModConfig.ReviveMode.PerPlayer) { GeneralUtil.TryConsumePlayerRevive(targetPlayerClientId, out revivesLeft); } else if (ModConfig.CurrentReviveMode == ModConfig.ReviveMode.FixedPerLevel || ModConfig.CurrentReviveMode == ModConfig.ReviveMode.PerLevelMultiplier) { if (ModState.RemainingRevives > 0) { ModState.RemainingRevives--; } revivesLeft = ModState.RemainingRevives; } else { revivesLeft = ModState.RemainingRevives; } BroadcastRevive(targetPlayerClientId, health, revivesLeft, tombPos, isInsideFactory, 0uL); } private static void BroadcastRevive(int targetPlayerClientId, int health, int remainingRevives, Vector3 position, bool insideFactory, ulong maskedNetworkObjectId) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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) if (!GeneralUtil.IsNetworkReady()) { return; } if (NetworkManager.Singleton.IsServer) { ReviveLogic.ApplyRevive(targetPlayerClientId, health, remainingRevives, position, insideFactory, maskedNetworkObjectId); } List list = new List(); foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds) { if (connectedClientsId != 0L) { list.Add(connectedClientsId); } } if (list.Count == 0) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(96, (Allocator)2, -1); try { WriteRevivePayload(val, targetPlayerClientId, health, remainingRevives, position, insideFactory, maskedNetworkObjectId); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UltimateRevive.PerformRevive.v2", (IReadOnlyList)list, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void OnPerformRevive(ulong senderClientId, FastBufferReader reader) { //IL_0000: 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) try { ReadRevivePayload(reader, out var targetPlayerClientId, out var health, out var remainingRevives, out var position, out var insideFactory, out var maskedNetworkObjectId); ReviveLogic.ApplyRevive(targetPlayerClientId, health, remainingRevives, position, insideFactory, maskedNetworkObjectId); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[OnPerformRevive] Exception: {arg}"); } } } private static void SendReject(ulong networkClientId, string reason) { //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_003e: 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_005a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, -1); try { FixedString128Bytes val2 = new FixedString128Bytes(reason ?? "unknown reason"); ((FastBufferWriter)(ref val)).WriteValueSafe(ref val2, default(ForFixedStrings)); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("UltimateRevive.RejectRevive.v2", networkClientId, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void OnRejectRevive(ulong senderClientId, FastBufferReader reader) { //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) try { FixedString128Bytes val = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForFixedStrings)); ShowLocalTip("Cannot revive", ((object)(FixedString128Bytes)(ref val)).ToString(), warning: true); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)$"[OnRejectRevive] Exception: {arg}"); } } } private static void WriteRevivePayload(FastBufferWriter writer, int targetPlayerClientId, int health, int remainingRevives, Vector3 position, bool insideFactory, ulong maskedNetworkObjectId) { //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_0018: 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_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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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) ((FastBufferWriter)(ref writer)).WriteValueSafe(ref targetPlayerClientId, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref health, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref remainingRevives, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref position.x, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref position.y, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref position.z, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref insideFactory, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref maskedNetworkObjectId, default(ForPrimitives)); } private static void ReadRevivePayload(FastBufferReader reader, out int targetPlayerClientId, out int health, out int remainingRevives, out Vector3 position, out bool insideFactory, out ulong maskedNetworkObjectId) { //IL_0005: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_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) ((FastBufferReader)(ref reader)).ReadValueSafe(ref targetPlayerClientId, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref health, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref remainingRevives, default(ForPrimitives)); float num = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num2, default(ForPrimitives)); float num3 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num3, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref insideFactory, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref maskedNetworkObjectId, default(ForPrimitives)); position = new Vector3(num, num2, num3); } private static void ShowLocalTip(string title, string body, bool warning) { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(title, body, warning, false, "LC_Tip1"); } } private static void WriteConfigPayload(FastBufferWriter writer) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0087: 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_009c: 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_00b1: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_00e1: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012f: 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_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_015c: 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_0178: 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_0182: 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_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: 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_01be: 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_01d3: 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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0202: 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_021c: 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_0236: 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_0250: 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) ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.ReviveTime, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.CanPickUpBodies, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.DeadBodyWeight, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.CanReviveTeleportedBodies, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.ReviveHealth, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.ExtraHealthLostPerRevive, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.RevivesPerLevelMultiplier, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.FixedRevivesPerLevel, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.InfiniteReviveTime, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.TimeUntilCannotBeRevived, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.MaxReviveDistance, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.MaxReviveDistanceTomb, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.EnableMaskedPlayerRevive, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.EnableDeathTombFallback, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.EnableDeathTombCollider, default(ForPrimitives)); int currentReviveMode = (int)ModConfig.CurrentReviveMode; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref currentReviveMode, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.RevivesPerPlayer, default(ForPrimitives)); FixedString512Bytes val = new FixedString512Bytes(ModConfig.DeathTombAssetBundlePath ?? string.Empty); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref val, default(ForFixedStrings)); FixedString128Bytes val2 = new FixedString128Bytes(ModConfig.DeathTombPrefabName ?? string.Empty); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref val2, default(ForFixedStrings)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.DeathTombGroundOffset, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.DeathTombSpawnHeight, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.DeathTombDownOffset, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.TombMonitorDotColor.r, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.TombMonitorDotColor.g, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.TombMonitorDotColor.b, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(ref ModConfig.TombMonitorDotColor.a, default(ForPrimitives)); } private static void ReadConfigPayload(FastBufferReader reader, out float reviveTime, out bool canPickUpBodies, out float deadBodyWeight, out bool canReviveTeleportedBodies, out int reviveHealth, out int extraHealthLostPerRevive, out float revivesPerLevelMultiplier, out int fixedRevivesPerLevel, out bool infiniteReviveTime, out int timeUntilCannotBeRevived, out float maxReviveDistance, out float maxReviveDistanceTomb, out bool enableMaskedPlayerRevive, out bool enableDeathTombFallback, out bool enableDeathTombCollider, out int reviveMode, out int revivesPerPlayer, out string deathTombAssetBundlePath, out string deathTombPrefabName, out float deathTombGroundOffset, out float deathTombSpawnHeight, out float deathTombDownOffset, out float tombDotR, out float tombDotG, out float tombDotB, out float tombDotA) { //IL_0005: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00a5: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00db: 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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0179: 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_018b: 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_019d: 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_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: 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_01c7: 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_01d9: 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_01eb: Unknown result type (might be due to invalid IL or missing references) ((FastBufferReader)(ref reader)).ReadValueSafe(ref reviveTime, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref canPickUpBodies, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref deadBodyWeight, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref canReviveTeleportedBodies, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref reviveHealth, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref extraHealthLostPerRevive, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref revivesPerLevelMultiplier, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref fixedRevivesPerLevel, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref infiniteReviveTime, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref timeUntilCannotBeRevived, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref maxReviveDistance, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref maxReviveDistanceTomb, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref enableMaskedPlayerRevive, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref enableDeathTombFallback, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref enableDeathTombCollider, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref reviveMode, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref revivesPerPlayer, default(ForPrimitives)); FixedString512Bytes val = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForFixedStrings)); deathTombAssetBundlePath = ((object)(FixedString512Bytes)(ref val)).ToString(); FixedString128Bytes val2 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val2, default(ForFixedStrings)); deathTombPrefabName = ((object)(FixedString128Bytes)(ref val2)).ToString(); ((FastBufferReader)(ref reader)).ReadValueSafe(ref deathTombGroundOffset, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref deathTombSpawnHeight, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref deathTombDownOffset, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref tombDotR, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref tombDotG, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref tombDotB, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref tombDotA, default(ForPrimitives)); } private static void OnSyncConfig(ulong senderClientId, FastBufferReader reader) { //IL_0019: 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) try { if (!NetworkManager.Singleton.IsServer && senderClientId == 0L) { ReadConfigPayload(reader, out var reviveTime, out var canPickUpBodies, out var deadBodyWeight, out var canReviveTeleportedBodies, out var reviveHealth, out var extraHealthLostPerRevive, out var revivesPerLevelMultiplier, out var fixedRevivesPerLevel, out var infiniteReviveTime, out var timeUntilCannotBeRevived, out var maxReviveDistance, out var maxReviveDistanceTomb, out var enableMaskedPlayerRevive, out var enableDeathTombFallback, out var enableDeathTombCollider, out var reviveMode, out var revivesPerPlayer, out var deathTombAssetBundlePath, out var deathTombPrefabName, out var deathTombGroundOffset, out var deathTombSpawnHeight, out var deathTombDownOffset, out var tombDotR, out var tombDotG, out var tombDotB, out var tombDotA); ModConfig.ApplySyncedConfig(reviveTime, canPickUpBodies, deadBodyWeight, canReviveTeleportedBodies, reviveHealth, extraHealthLostPerRevive, revivesPerLevelMultiplier, fixedRevivesPerLevel, infiniteReviveTime, timeUntilCannotBeRevived, maxReviveDistance, maxReviveDistanceTomb, enableMaskedPlayerRevive, enableDeathTombFallback, enableDeathTombCollider, deathTombAssetBundlePath, deathTombPrefabName, deathTombGroundOffset, deathTombDownOffset, deathTombSpawnHeight, reviveMode, revivesPerPlayer); ModConfig.TombMonitorDotColor = new Color(tombDotR, tombDotG, tombDotB, tombDotA); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"Received config synchronization from host."); } } } catch (Exception arg) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)$"[OnSyncConfig] Exception: {arg}"); } } } private static bool TryResolveTombValidationPosition(int playerClientId, out Vector3 position, out string method) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (DeathMarkerManager.TryGetMarkerPosition(playerClientId, out position)) { method = "active-marker"; return true; } if (!ModConfig.EnableDeathTombFallback && GeneralUtil.TryGetPlayerLastDeathPosition(playerClientId, out position)) { method = "death-position-exception"; return true; } method = "none"; position = default(Vector3); return false; } } public struct PlayerDeathStateSyncData : INetworkSerializable { public int PlayerClientId; public bool HasBeenTeleported; public float TimeDiedAt; public int TimesRevivedThisLevel; public PlayerDeathType LastDeathType; public ulong LastDeathNetworkObjectId; public Vector3 LastDeathPosition; public bool HasLastDeathPosition; public int RemainingRevivesForPlayer; public bool HasMaskedInfo; public ulong MaskedNetworkObjectId; public Vector3 MaskedLastPosition; public bool MaskedIsDead; public float MaskedLastUpdateTime; public unsafe void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { //IL_000a: 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_0020: 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_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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0078: 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_009b: 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_00b1: 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_00c8: 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_00df: 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_0103: 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_011a: 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) ((BufferSerializer*)(&serializer))->SerializeValue(ref PlayerClientId, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref HasBeenTeleported, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref TimeDiedAt, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref TimesRevivedThisLevel, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref LastDeathType, default(ForEnums)); ((BufferSerializer*)(&serializer))->SerializeValue(ref LastDeathNetworkObjectId, default(ForPrimitives)); serializer.SerializeValue(ref LastDeathPosition); ((BufferSerializer*)(&serializer))->SerializeValue(ref HasLastDeathPosition, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref RemainingRevivesForPlayer, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref HasMaskedInfo, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref MaskedNetworkObjectId, default(ForPrimitives)); serializer.SerializeValue(ref MaskedLastPosition); ((BufferSerializer*)(&serializer))->SerializeValue(ref MaskedIsDead, default(ForPrimitives)); ((BufferSerializer*)(&serializer))->SerializeValue(ref MaskedLastUpdateTime, default(ForPrimitives)); } } public static class ReviveNetworkSync { private sealed class TombSyncCoroutineRunner : MonoBehaviour { } [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnReceiveDeathState; public static HandleNamedMessageDelegate <1>__OnReceiveTombPosition; } [CompilerGenerated] private sealed class d__23 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public int playerId; private WaitForSeconds 5__2; private int 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__23(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = new WaitForSeconds(0.25f); 5__3 = 0; break; case 1: <>1__state = -1; 5__3++; break; } if (5__3 < 12 && ModState.PendingTombPositionByPlayerId.TryGetValue(playerId, out var value) && !TryApplyPendingTombPosition(playerId, stopRetryOnSuccess: false)) { TryCreateLocalTombForPlayer(playerId, value, applyPendingAfterSpawn: false); if (!TryApplyPendingTombPosition(playerId, stopRetryOnSuccess: false)) { <>2__current = 5__2; <>1__state = 1; return true; } } PendingTombRetryCoroutines.Remove(playerId); if (ModState.PendingTombPositionByPlayerId.ContainsKey(playerId)) { Plugin.DebugLog($"[TombSync][CLIENT] could not apply pending tomb position after retry window player={playerId}"); ModState.PendingTombPositionByPlayerId.Remove(playerId); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string DeathStateMsg = "UltimateRevive.PlayerDeathState"; private const string TombPositionMessage = "UltimateRevive.TombPositionSync"; private const int TombPositionRetryMaxAttempts = 12; private const float TombPositionRetryDelaySeconds = 0.25f; private static bool _registered; private static readonly Dictionary PendingTombRetryCoroutines = new Dictionary(); private static TombSyncCoroutineRunner _runner; public static void RegisterHandlers() { //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_0046: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && !_registered) { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnReceiveDeathState; if (obj == null) { HandleNamedMessageDelegate val = OnReceiveDeathState; <>O.<0>__OnReceiveDeathState = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("UltimateRevive.PlayerDeathState", (HandleNamedMessageDelegate)obj); CustomMessagingManager customMessagingManager2 = singleton.CustomMessagingManager; object obj2 = <>O.<1>__OnReceiveTombPosition; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnReceiveTombPosition; <>O.<1>__OnReceiveTombPosition = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("UltimateRevive.TombPositionSync", (HandleNamedMessageDelegate)obj2); _registered = true; Debug.Log((object)"[UltimateRevive] Network sync handler registered."); } } public static void UnregisterHandlers() { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && _registered) { singleton.CustomMessagingManager.UnregisterNamedMessageHandler("UltimateRevive.PlayerDeathState"); singleton.CustomMessagingManager.UnregisterNamedMessageHandler("UltimateRevive.TombPositionSync"); _registered = false; ClearAllPendingTombPositions(); Debug.Log((object)"[UltimateRevive] Network sync handler unregistered."); } } public static void SendPlayerDeathStateToClients(int playerClientId) { //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_00df: 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_00e4: 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) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || !singleton.IsServer) { return; } PlayerReviveInfo orCreateInfo = GeneralUtil.GetOrCreateInfo(playerClientId); ModState.MaskedByPlayerId.TryGetValue(playerClientId, out var value); PlayerDeathStateSyncData playerDeathStateSyncData = default(PlayerDeathStateSyncData); playerDeathStateSyncData.PlayerClientId = orCreateInfo.PlayerClientId; playerDeathStateSyncData.HasBeenTeleported = orCreateInfo.HasBeenTeleported; playerDeathStateSyncData.TimeDiedAt = orCreateInfo.TimeDiedAt; playerDeathStateSyncData.TimesRevivedThisLevel = orCreateInfo.TimesRevivedThisLevel; playerDeathStateSyncData.LastDeathType = orCreateInfo.LastDeathType; playerDeathStateSyncData.LastDeathNetworkObjectId = orCreateInfo.LastDeathNetworkObjectId; playerDeathStateSyncData.LastDeathPosition = orCreateInfo.LastDeathPosition; playerDeathStateSyncData.HasLastDeathPosition = orCreateInfo.HasLastDeathPosition; playerDeathStateSyncData.RemainingRevivesForPlayer = orCreateInfo.RemainingRevivesForPlayer; playerDeathStateSyncData.HasMaskedInfo = value != null; playerDeathStateSyncData.MaskedNetworkObjectId = value?.NetworkObjectId ?? 0; playerDeathStateSyncData.MaskedLastPosition = value?.LastPosition ?? Vector3.zero; playerDeathStateSyncData.MaskedIsDead = value?.IsDead ?? false; playerDeathStateSyncData.MaskedLastUpdateTime = value?.LastUpdateTime ?? 0f; PlayerDeathStateSyncData playerDeathStateSyncData2 = playerDeathStateSyncData; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteNetworkSerializable(ref playerDeathStateSyncData2); singleton.CustomMessagingManager.SendNamedMessageToAll("UltimateRevive.PlayerDeathState", val, (NetworkDelivery)3); Debug.Log((object)$"[UltimateRevive] Sent death state sync for player {playerClientId}"); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void OnReceiveDeathState(ulong senderClientId, FastBufferReader reader) { //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_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) try { if ((Object)(object)NetworkManager.Singleton == (Object)null || senderClientId != 0L) { return; } PlayerDeathStateSyncData playerDeathStateSyncData = default(PlayerDeathStateSyncData); ((FastBufferReader)(ref reader)).ReadNetworkSerializable(ref playerDeathStateSyncData); PlayerReviveInfo orCreateInfo = GeneralUtil.GetOrCreateInfo(playerDeathStateSyncData.PlayerClientId); orCreateInfo.HasBeenTeleported = playerDeathStateSyncData.HasBeenTeleported; orCreateInfo.TimeDiedAt = playerDeathStateSyncData.TimeDiedAt; orCreateInfo.TimesRevivedThisLevel = playerDeathStateSyncData.TimesRevivedThisLevel; orCreateInfo.LastDeathType = playerDeathStateSyncData.LastDeathType; orCreateInfo.LastDeathNetworkObjectId = playerDeathStateSyncData.LastDeathNetworkObjectId; orCreateInfo.LastDeathPosition = playerDeathStateSyncData.LastDeathPosition; orCreateInfo.HasLastDeathPosition = playerDeathStateSyncData.HasLastDeathPosition; orCreateInfo.RemainingRevivesForPlayer = playerDeathStateSyncData.RemainingRevivesForPlayer; if (playerDeathStateSyncData.HasMaskedInfo) { ModState.MaskedInfo value = new ModState.MaskedInfo { PlayerClientId = playerDeathStateSyncData.PlayerClientId, Masked = null, NetworkObjectId = playerDeathStateSyncData.MaskedNetworkObjectId, LastPosition = playerDeathStateSyncData.MaskedLastPosition, IsDead = playerDeathStateSyncData.MaskedIsDead, LastUpdateTime = playerDeathStateSyncData.MaskedLastUpdateTime }; ModState.MaskedByPlayerId[playerDeathStateSyncData.PlayerClientId] = value; ModState.MaskedByNetworkObjectId[playerDeathStateSyncData.MaskedNetworkObjectId] = value; TryResolveMaskedReference(playerDeathStateSyncData.PlayerClientId); } else { if (ModState.MaskedByPlayerId.TryGetValue(playerDeathStateSyncData.PlayerClientId, out var value2)) { ModState.MaskedByNetworkObjectId.Remove(value2.NetworkObjectId); } ModState.MaskedByPlayerId.Remove(playerDeathStateSyncData.PlayerClientId); } Debug.Log((object)$"[UltimateRevive] Received death state sync for player {playerDeathStateSyncData.PlayerClientId}"); } catch (Exception arg) { Debug.LogError((object)$"[UltimateRevive] Error in OnReceiveDeathState: {arg}"); } } private static void TryResolveMaskedReference(int playerClientId) { if (!ModState.MaskedByPlayerId.TryGetValue(playerClientId, out var value) || value.NetworkObjectId == 0L) { return; } NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.SpawnManager != null && singleton.SpawnManager.SpawnedObjects.TryGetValue(value.NetworkObjectId, out var value2)) { MaskedPlayerEnemy component = ((Component)value2).GetComponent(); if (!((Object)(object)component == (Object)null)) { value.Masked = component; ModState.MaskedByPlayerId[playerClientId] = value; Debug.Log((object)$"[UltimateRevive] Resolved MaskedPlayerEnemy for player {playerClientId}"); } } } public static void SendTombPositionToClients(int playerClientId, Vector3 position) { //IL_0077: 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_0099: 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) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || !singleton.IsServer) { return; } List list = new List(); foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != 0L) { list.Add(connectedClientsId); } } if (list.Count == 0) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(ref playerClientId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); singleton.CustomMessagingManager.SendNamedMessage("UltimateRevive.TombPositionSync", (IReadOnlyList)list, val, (NetworkDelivery)3); Plugin.DebugLog($"[TombSync][SERVER] sent tomb player={playerClientId} pos={position} clients={list.Count}"); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void OnReceiveTombPosition(ulong senderClientId, FastBufferReader reader) { //IL_002c: 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_0047: 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_0073: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.IsServer || senderClientId != 0L) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); ModState.PendingTombPositionByPlayerId[num] = val; if (!TryApplyPendingTombPosition(num)) { Plugin.DebugLog($"[TombSync][CLIENT] pending tomb player={num} pos={val}"); TryCreateLocalTombForPlayer(num, val); if (!TryApplyPendingTombPosition(num)) { StartPendingTombRetry(num); } } } catch (Exception arg) { Plugin.DebugLog($"[TombSync][CLIENT] error: {arg}"); } } public static bool TryApplyPendingTombPosition(int playerId) { return TryApplyPendingTombPosition(playerId, stopRetryOnSuccess: true); } private static bool TryApplyPendingTombPosition(int playerId, bool stopRetryOnSuccess) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) try { if (!ModState.PendingTombPositionByPlayerId.TryGetValue(playerId, out var value)) { return true; } if (!ModState.TryGetTombObject(playerId, out var tombObject) || (Object)(object)tombObject == (Object)null) { return false; } ApplySyncedTombPosition(tombObject, value); ReviveInteractTriggerManager.ForceRefreshPlayer(playerId, "TombSync:client:positionApplied"); if (ModState.TombByPlayerId.TryGetValue(playerId, out var value2)) { value2.LastPosition = value; value2.LastUpdateTime = Time.time; } ModState.PendingTombPositionByPlayerId.Remove(playerId); if (stopRetryOnSuccess) { StopPendingTombRetry(playerId); } Plugin.DebugLog($"[TombSync][CLIENT] applied tomb player={playerId} pos={value}"); return true; } catch (Exception arg) { Plugin.DebugLog($"[TombSync][CLIENT] apply pending error player={playerId}: {arg}"); return false; } } private static bool TryCreateLocalTombForPlayer(int playerId, Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return TryCreateLocalTombForPlayer(playerId, position, applyPendingAfterSpawn: true); } private static bool TryCreateLocalTombForPlayer(int playerId, Vector3 position, bool applyPendingAfterSpawn) { //IL_0049: 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) if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return false; } if (playerId < 0 || playerId >= StartOfRound.Instance.allPlayerScripts.Length) { return false; } PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerId]; if ((Object)(object)val == (Object)null) { return false; } if (!DeathMarkerManager.TrySpawnTombForPlayer(val, position, "TombSync:client")) { return false; } if (applyPendingAfterSpawn) { TryApplyPendingTombPosition(playerId); } ReviveInteractTriggerManager.ForceRefreshPlayer(playerId, "TombSync:client:tombSpawned"); Plugin.DebugLog($"[TombSync][CLIENT] created local tomb player={playerId} pos={position}"); return true; } public static void FlushPendingTombPositions() { //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_0040: Unknown result type (might be due to invalid IL or missing references) if (ModState.PendingTombPositionByPlayerId.Count == 0) { return; } foreach (KeyValuePair item in new List>(ModState.PendingTombPositionByPlayerId)) { int key = item.Key; Vector3 value = item.Value; if (!TryApplyPendingTombPosition(key)) { TryCreateLocalTombForPlayer(key, value); if (!TryApplyPendingTombPosition(key)) { StartPendingTombRetry(key); } } } } public static void ClearPendingTombPosition(int playerId) { ModState.PendingTombPositionByPlayerId.Remove(playerId); StopPendingTombRetry(playerId); } public static void ClearAllPendingTombPositions() { foreach (KeyValuePair pendingTombRetryCoroutine in PendingTombRetryCoroutines) { if ((Object)(object)_runner != (Object)null && pendingTombRetryCoroutine.Value != null) { ((MonoBehaviour)_runner).StopCoroutine(pendingTombRetryCoroutine.Value); } } PendingTombRetryCoroutines.Clear(); ModState.PendingTombPositionByPlayerId.Clear(); if ((Object)(object)_runner != (Object)null) { Object.Destroy((Object)(object)((Component)_runner).gameObject); _runner = null; } } private static void StartPendingTombRetry(int playerId) { if (ModState.PendingTombPositionByPlayerId.ContainsKey(playerId) && !PendingTombRetryCoroutines.ContainsKey(playerId)) { TombSyncCoroutineRunner tombSyncCoroutineRunner = EnsureCoroutineRunner(); if (!((Object)(object)tombSyncCoroutineRunner == (Object)null)) { PendingTombRetryCoroutines[playerId] = ((MonoBehaviour)tombSyncCoroutineRunner).StartCoroutine(ApplyPendingTombWhenReady(playerId)); Plugin.DebugLog($"[TombSync][CLIENT] started pending retry player={playerId}"); } } } [IteratorStateMachine(typeof(d__23))] private static IEnumerator ApplyPendingTombWhenReady(int playerId) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__23(0) { playerId = playerId }; } private static void StopPendingTombRetry(int playerId) { if (PendingTombRetryCoroutines.TryGetValue(playerId, out var value)) { if ((Object)(object)_runner != (Object)null && value != null) { ((MonoBehaviour)_runner).StopCoroutine(value); } PendingTombRetryCoroutines.Remove(playerId); } } private static TombSyncCoroutineRunner EnsureCoroutineRunner() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_runner != (Object)null) { return _runner; } GameObject val = new GameObject("UltimateRevive_TombSyncCoroutineRunner"); Object.DontDestroyOnLoad((Object)val); _runner = val.AddComponent(); return _runner; } private static void ApplySyncedTombPosition(GameObject tombObject, Vector3 position) { //IL_003d: 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) if (!((Object)(object)tombObject == (Object)null)) { TombDeadBodySetup.UltimateReviveTombTeleportOffset ultimateReviveTombTeleportOffset = tombObject.GetComponent() ?? tombObject.GetComponentInChildren(true) ?? tombObject.GetComponentInParent(); if ((Object)(object)ultimateReviveTombTeleportOffset != (Object)null) { ultimateReviveTombTeleportOffset.ApplySyncedTombPosition(position); } else { tombObject.transform.position = position; } } } } public static class ReviveRules { public static bool CanRevive(PlayerControllerB reviver, RagdollGrabbableObject body, out int targetPlayerClientId, out string reason) { targetPlayerClientId = -1; reason = string.Empty; if (!ModState.Active) { return false; } if ((Object)(object)reviver == (Object)null || reviver.isPlayerDead) { return false; } PlayerReviveInfo playerReviveInfo = null; if ((Object)(object)reviver != (Object)null) { playerReviveInfo = ModState.PlayerInfos.Find((PlayerReviveInfo p) => p.PlayerClientId != (int)reviver.playerClientId && p.LastDeathType != PlayerDeathType.Unknown); } if (playerReviveInfo != null && playerReviveInfo.LastDeathType == PlayerDeathType.Masked && ModState.MaskedByPlayerId.TryGetValue(playerReviveInfo.PlayerClientId, out var value) && value != null && (Object)(object)value.Masked != (Object)null) { if (!value.IsDead) { reason = "kill masked first"; return false; } targetPlayerClientId = playerReviveInfo.PlayerClientId; if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out reason)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { reason = "body was teleported"; return false; } if (!ModConfig.InfiniteReviveTime && Time.time - playerReviveInfo.TimeDiedAt > (float)ModConfig.TimeUntilCannotBeRevived) { reason = "time expired"; return false; } return true; } if ((Object)(object)body == (Object)null && playerReviveInfo != null && playerReviveInfo.LastDeathType == PlayerDeathType.Body && playerReviveInfo.HasLastDeathPosition) { targetPlayerClientId = playerReviveInfo.PlayerClientId; if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out reason)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { reason = "body was teleported"; return false; } if (!ModConfig.InfiniteReviveTime && Time.time - playerReviveInfo.TimeDiedAt > (float)ModConfig.TimeUntilCannotBeRevived) { reason = "time expired"; return false; } return true; } if ((Object)(object)body != (Object)null && (Object)(object)((NetworkBehaviour)body).NetworkObject != (Object)null) { foreach (KeyValuePair item in ModState.BodyByPlayerId) { if ((Object)(object)item.Value != (Object)null && (Object)(object)((NetworkBehaviour)item.Value).NetworkObject != (Object)null && ((NetworkBehaviour)item.Value).NetworkObject.NetworkObjectId == ((NetworkBehaviour)body).NetworkObject.NetworkObjectId) { body = item.Value; break; } } } else if ((Object)(object)body == (Object)null && (Object)(object)reviver != (Object)null) { foreach (KeyValuePair item2 in ModState.BodyByPlayerId) { if ((Object)(object)item2.Value != (Object)null && item2.Key != (int)reviver.playerClientId) { _ = item2.Key; body = item2.Value; break; } } } if (!GeneralUtil.TryGetBodyOwner(body, out targetPlayerClientId, out var targetPlayer)) { return false; } if (!targetPlayer.isPlayerDead) { return false; } if ((int)reviver.playerClientId == targetPlayerClientId) { return false; } if ((Object)(object)body == (Object)null || (Object)(object)((Component)body).transform == (Object)null) { return false; } if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out reason)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { reason = "body was teleported"; return false; } if (!ModConfig.InfiniteReviveTime && Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId) > (float)ModConfig.TimeUntilCannotBeRevived) { reason = "time expired"; return false; } return true; } public static float SecondsLeft(int targetPlayerClientId) { if (ModConfig.InfiniteReviveTime) { return float.PositiveInfinity; } float num = Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId); return Mathf.Max(0f, (float)ModConfig.TimeUntilCannotBeRevived - num); } public static bool CanReviveMasked(PlayerControllerB reviver, MaskedPlayerEnemy masked, out int targetPlayerClientId, out string reason) { targetPlayerClientId = -1; reason = string.Empty; if (!ModState.Active) { return false; } if (!ModConfig.EnableMaskedPlayerRevive) { return false; } if ((Object)(object)reviver == (Object)null || reviver.isPlayerDead) { return false; } if ((Object)(object)masked == (Object)null) { return false; } if ((Object)(object)masked != (Object)null) { int? playerIdForMasked = MaskedConversionTracker.GetPlayerIdForMasked(masked); if (playerIdForMasked.HasValue && ModState.MaskedByPlayerId.TryGetValue(playerIdForMasked.Value, out var value) && value != null) { if (!value.IsDead) { reason = "kill masked first"; targetPlayerClientId = playerIdForMasked.Value; return false; } targetPlayerClientId = playerIdForMasked.Value; if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out reason)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { reason = "body was teleported"; return false; } if (!ModConfig.InfiniteReviveTime && Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId) > (float)ModConfig.TimeUntilCannotBeRevived) { reason = "time expired"; return false; } return true; } } if (!MaskedConversionTracker.IsMaskedDead(masked)) { reason = "kill masked first"; return false; } if (!GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out targetPlayerClientId, out var _)) { return false; } PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(targetPlayerClientId); if ((Object)(object)playerByClientId == (Object)null) { return false; } if (!playerByClientId.isPlayerDead) { return false; } if ((int)reviver.playerClientId == targetPlayerClientId) { return false; } if ((Object)(object)masked == (Object)null || (Object)(object)((Component)masked).transform == (Object)null) { return false; } if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out reason)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { reason = "body was teleported"; return false; } if (!ModConfig.InfiniteReviveTime && Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId) > (float)ModConfig.TimeUntilCannotBeRevived) { reason = "time expired"; return false; } return true; } public static bool CanReviveAtTomb(PlayerControllerB reviver, int targetPlayerClientId, out string reason) { reason = string.Empty; if (!ModState.Active) { return false; } if ((Object)(object)reviver == (Object)null || reviver.isPlayerDead) { reason = "reviver invalid"; return false; } PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(targetPlayerClientId); if ((Object)(object)playerByClientId == (Object)null || !playerByClientId.isPlayerDead) { return false; } if ((int)reviver.playerClientId == targetPlayerClientId) { return false; } if (!GeneralUtil.CanRevivePlayer(targetPlayerClientId, out reason)) { return false; } if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId)) { reason = "body was teleported"; return false; } if (!ModConfig.InfiniteReviveTime && Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId) > (float)ModConfig.TimeUntilCannotBeRevived) { reason = "time expired"; return false; } return true; } } public static class TombDeadBodySetup { public sealed class UltimateReviveTombMarker : MonoBehaviour { public int playerId; } public sealed class UltimateReviveTombTeleportOffset : MonoBehaviour { private DeadBodyInfo body; private PlayerControllerB player; private bool hasLastPosition; private Vector3 lastCorrectedPosition; private const float TeleportMoveThreshold = 1.5f; private float DownOffset => ModConfig.DeathTombDownOffset; public void Init(DeadBodyInfo body, PlayerControllerB player) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) this.body = body; this.player = player; hasLastPosition = true; lastCorrectedPosition = ((Component)this).transform.position; } private void LateUpdate() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_010b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)body == (Object)null) { body = ((Component)this).GetComponent(); } if ((Object)(object)body == (Object)null) { return; } if (!hasLastPosition) { hasLastPosition = true; lastCorrectedPosition = ((Component)this).transform.position; } else { if (Vector3.Distance(((Component)this).transform.position, lastCorrectedPosition) < 1.5f) { return; } Vector3 val = ((Component)this).transform.position + Vector3.down * DownOffset; ApplyFullTombPosition(val); if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer && (Object)(object)player != (Object)null) { ReviveNetworkSync.SendTombPositionToClients((int)player.playerClientId, val); } if ((Object)(object)player != (Object)null) { int num = (int)player.playerClientId; PlayerReviveInfo orCreateInfo = GeneralUtil.GetOrCreateInfo(num); orCreateInfo.LastDeathPosition = val; orCreateInfo.HasLastDeathPosition = true; if (ModState.MaskedByPlayerId.TryGetValue(num, out var value) && value != null) { value.LastPosition = val; value.LastUpdateTime = Time.time; } } lastCorrectedPosition = val; Debug.LogWarning((object)("[UltimateRevive][TombTeleportOffset] corrected tomb position " + $"down={DownOffset} newPos={val}")); } } public void ApplySyncedTombPosition(Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) ApplyFullTombPosition(position); hasLastPosition = true; lastCorrectedPosition = position; } private void ApplyFullTombPosition(Vector3 position) { //IL_0006: 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_003b: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) ((Component)this).transform.position = position; if ((Object)(object)body != (Object)null) { if ((Object)(object)body.radarDot != (Object)null) { body.radarDot.position = position; ((Component)body.radarDot).gameObject.SetActive(true); } if ((Object)(object)body.nightVisionRadar != (Object)null) { ((Component)body.nightVisionRadar).transform.position = position; } if (body.bodyParts != null) { for (int i = 0; i < body.bodyParts.Length; i++) { Rigidbody val = body.bodyParts[i]; if (!((Object)(object)val == (Object)null)) { ((Component)val).transform.position = position; val.velocity = Vector3.zero; val.angularVelocity = Vector3.zero; } } } } if ((Object)(object)player != (Object)null) { ((Component)player).transform.position = position; } } } private static readonly Dictionary ActiveTombBodies = new Dictionary(); private static readonly Dictionary NextRadarCheck = new Dictionary(); public static DeadBodyInfo SetupTombAsDeadBody(PlayerControllerB player, GameObject tombObject, Vector3 position) { //IL_0021: 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_01c2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)tombObject == (Object)null) { return null; } int playerIndex = GetPlayerIndex(player); tombObject.transform.position = position; DeadBodyInfo val = tombObject.GetComponent(); if ((Object)(object)val == (Object)null) { val = tombObject.AddComponent(); } val.playerObjectId = playerIndex; val.playerScript = player; val.setMaterialToPlayerSuit = false; val.bodyBleedingHeavily = false; val.detachedHead = false; val.overrideSpawnPosition = true; val.causeOfDeath = (CauseOfDeath)0; val.attachedLimb = null; val.attachedTo = null; val.secondaryAttachedLimb = null; val.secondaryAttachedTo = null; val.grabBodyObject = null; val.bloodSplashParticle = null; val.beamUpParticle = EnsureDummyParticle(tombObject.transform, "TombBeamUpParticle"); val.beamOutParticle = EnsureDummyParticle(tombObject.transform, "TombBeamOutParticle"); if ((Object)(object)val.bodyAudio == (Object)null) { val.bodyAudio = EnsureBodyAudio(tombObject.transform); } val.bodyBloodDecals = Array.Empty(); val.canBeGrabbedBackByPlayers = false; val.deactivated = false; val.radarDot = EnsureRadarDot(tombObject.transform); val.nightVisionRadar = EnsureNightVisionRadar(tombObject.transform); EnsureTombTeleportOffset(tombObject, val, player); EnsureTombScanNode(tombObject.transform, player); EnsureTombMonitorDot(tombObject.transform, player); SanitizeTombPhysics(tombObject); val.bodyParts = EnsureBodyParts(tombObject.transform, player); player.isPlayerDead = true; player.redirectToEnemy = null; player.deadBody = val; UltimateReviveTombMarker ultimateReviveTombMarker = tombObject.GetComponent(); if ((Object)(object)ultimateReviveTombMarker == (Object)null) { ultimateReviveTombMarker = tombObject.AddComponent(); } ultimateReviveTombMarker.playerId = playerIndex; RegisterActiveTomb(player, val); SyncDeadPlayerToTomb(player, val); EnsureTombTeleportOffset(tombObject, val, player); if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { ReviveNetworkSync.SendTombPositionToClients(playerIndex, tombObject.transform.position); } Debug.LogWarning((object)("[UltimateRevive][TombSetupOK] " + $"player={player.playerClientId} " + "body=" + ((Object)val).name + " " + $"deadBodyAssigned={(Object)(object)player.deadBody == (Object)(object)val} " + "radarDot=" + (((Object)(object)val.radarDot == (Object)null) ? "NULL" : ((object)val.radarDot).GetType().Name) + " nightVisionRadar=" + (((Object)(object)val.nightVisionRadar == (Object)null) ? "NULL" : "ok") + " parts=" + ((val.bodyParts == null) ? "NULL" : val.bodyParts.Length.ToString()))); return val; } private static void EnsureTombTeleportOffset(GameObject tombObject, DeadBodyInfo body, PlayerControllerB player) { if (!((Object)(object)tombObject == (Object)null)) { UltimateReviveTombTeleportOffset ultimateReviveTombTeleportOffset = tombObject.GetComponent(); if ((Object)(object)ultimateReviveTombTeleportOffset == (Object)null) { ultimateReviveTombTeleportOffset = tombObject.AddComponent(); } ultimateReviveTombTeleportOffset.Init(body, player); } } public static void RegisterActiveTomb(PlayerControllerB player, DeadBodyInfo body) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)body == (Object)null)) { int playerIndex = GetPlayerIndex(player); ActiveTombBodies[playerIndex] = body; NextRadarCheck[playerIndex] = 0f; player.isPlayerDead = true; player.deadBody = body; ModState.RegisterTomb(playerIndex, ((Component)body).gameObject, body, ((Component)body).transform.position); SyncDeadPlayerToTomb(player, body); ReviveNetworkSync.TryApplyPendingTombPosition(playerIndex); } } private static ParticleSystem EnsureDummyParticle(Transform root, string name) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_003f: 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_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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { return null; } Transform val = root.Find(name); GameObject val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).gameObject; } else { val2 = new GameObject(name); val2.transform.SetParent(root, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; } ParticleSystem val3 = val2.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = val2.AddComponent(); } MainModule main = val3.main; ((MainModule)(ref main)).loop = false; ((MainModule)(ref main)).playOnAwake = false; ((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(0.01f); ((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(0f); ((MainModule)(ref main)).startSize = MinMaxCurve.op_Implicit(0f); ((MainModule)(ref main)).maxParticles = 1; EmissionModule emission = val3.emission; ((EmissionModule)(ref emission)).enabled = false; val2.SetActive(true); return val3; } private static AudioSource EnsureBodyAudio(Transform root) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { return null; } Transform val = root.Find("TombBodyAudio"); GameObject val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).gameObject; } else { val2 = new GameObject("TombBodyAudio"); val2.transform.SetParent(root, false); val2.transform.localPosition = Vector3.zero; } AudioSource val3 = val2.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = val2.AddComponent(); } val3.playOnAwake = false; val3.spatialBlend = 1f; val3.volume = 0f; return val3; } public static void UnregisterActiveTomb(PlayerControllerB player) { if (!((Object)(object)player == (Object)null)) { int playerIndex = GetPlayerIndex(player); if (ActiveTombBodies.ContainsKey(playerIndex)) { ActiveTombBodies.Remove(playerIndex); NextRadarCheck.Remove(playerIndex); ModState.UnregisterTomb(playerIndex); ReviveNetworkSync.ClearPendingTombPosition(playerIndex); RemoveTombObjectRadarTarget(player); } } } private static GameObject EnsureTombMonitorDot(Transform tombRoot, PlayerControllerB player) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_011f: 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) if ((Object)(object)tombRoot == (Object)null) { return null; } Transform val = tombRoot.Find("TombMonitorDot"); GameObject val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).gameObject; } else { GameObject val3 = null; if ((Object)(object)player != (Object)null && (Object)(object)player.mapRadarDotAnimator != (Object)null) { val3 = ((Component)player.mapRadarDotAnimator).gameObject; } if ((Object)(object)val3 != (Object)null) { val2 = Object.Instantiate(val3, tombRoot); ((Object)val2).name = "TombMonitorDot"; Animator[] componentsInChildren = val2.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Behaviour)componentsInChildren[i]).enabled = false; } } else { val2 = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)val2).name = "TombMonitorDot"; val2.transform.SetParent(tombRoot, false); Collider component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } } val2.transform.SetParent(tombRoot, false); val2.transform.localPosition = new Vector3(0f, 0.25f, 0f); val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = new Vector3(0.35f, 0.35f, 0.35f); val2.SetActive(true); int bestRadarDotLayer = GetBestRadarDotLayer(player); if (bestRadarDotLayer >= 0) { SetLayerRecursively(val2, bestRadarDotLayer); } ApplyTombMonitorDotColor(val2, ModConfig.TombMonitorDotColor); Plugin.DebugLog("[UltimateRevive][TombMonitorDot] ensured monitor dot " + $"player={(((Object)(object)player == (Object)null) ? (-1) : ((int)player.playerClientId))} " + $"layer={val2.layer} root={((Object)tombRoot).name}"); return val2; } private static int GetBestRadarDotLayer(PlayerControllerB player) { if ((Object)(object)player != (Object)null && (Object)(object)player.mapRadarDotAnimator != (Object)null) { return ((Component)player.mapRadarDotAnimator).gameObject.layer; } int num = LayerMask.NameToLayer("MapRadar"); if (num >= 0) { return num; } int num2 = LayerMask.NameToLayer("Radar"); if (num2 >= 0) { return num2; } return -1; } private static void SetLayerRecursively(GameObject obj, int layer) { if (!((Object)(object)obj == (Object)null) && layer >= 0) { obj.layer = layer; Transform transform = obj.transform; for (int i = 0; i < transform.childCount; i++) { SetLayerRecursively(((Component)transform.GetChild(i)).gameObject, layer); } } } private static void ApplyTombMonitorDotColor(GameObject obj, Color color) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)obj == (Object)null) { return; } Renderer[] componentsInChildren = obj.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } val.enabled = true; Material material = val.material; if (!((Object)(object)material == (Object)null)) { if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", color); } if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } if (material.HasProperty("_EmissionColor")) { material.SetColor("_EmissionColor", color); } } } } private static void RemoveTombObjectRadarTarget(PlayerControllerB player) { if ((Object)(object)player == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return; } ManualCameraRenderer mapScreen = StartOfRound.Instance.mapScreen; if ((Object)(object)mapScreen == (Object)null || mapScreen.radarTargets == null) { return; } int playerIndex = GetPlayerIndex(player); for (int num = mapScreen.radarTargets.Count - 1; num >= 0; num--) { TransformAndName val = mapScreen.radarTargets[num]; if (!((Object)(object)val.transform == (Object)null)) { UltimateReviveTombMarker ultimateReviveTombMarker = ((Component)val.transform).GetComponent(); if ((Object)(object)ultimateReviveTombMarker == (Object)null) { ultimateReviveTombMarker = ((Component)val.transform).GetComponentInParent(); } if (!((Object)(object)ultimateReviveTombMarker == (Object)null) && ultimateReviveTombMarker.playerId == playerIndex) { mapScreen.radarTargets.RemoveAt(num); Plugin.DebugLog("[UltimateRevive][TombTerminal] removed tomb object target " + $"player={player.playerClientId} index={num}"); } } } } private static void SyncDeadPlayerToTomb(PlayerControllerB player, DeadBodyInfo body) { //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_0025: 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_006f: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)body == (Object)null) { return; } Vector3 position = ((Component)body).transform.position; ((Component)player).transform.position = position; if ((Object)(object)body.radarDot != (Object)null) { body.radarDot.position = position; ((Component)body.radarDot).gameObject.SetActive(true); } if ((Object)(object)body.nightVisionRadar != (Object)null) { ((Component)body.nightVisionRadar).transform.position = position; } if (body.bodyParts != null) { for (int i = 0; i < body.bodyParts.Length; i++) { Rigidbody val = body.bodyParts[i]; if (!((Object)(object)val == (Object)null)) { ((Component)val).transform.position = position; if (!val.isKinematic) { val.velocity = Vector3.zero; val.angularVelocity = Vector3.zero; } } } } EnsureTombMonitorDot(((Component)body).transform, player); } private static void SanitizeTombPhysics(GameObject tombObject) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) Rigidbody component = tombObject.GetComponent(); if ((Object)(object)component != (Object)null) { if (!component.isKinematic) { component.velocity = Vector3.zero; component.angularVelocity = Vector3.zero; } component.useGravity = false; component.isKinematic = true; } MeshCollider[] componentsInChildren = tombObject.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Collider)componentsInChildren[i]).enabled = false; } Transform val = tombObject.transform.Find("TombCollision"); if ((Object)(object)val == (Object)null) { GameObject val2 = new GameObject("TombCollision"); val2.transform.SetParent(tombObject.transform, false); val = val2.transform; } BoxCollider val3 = ((Component)val).GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)val).gameObject.AddComponent(); } val3.size = new Vector3(0.6f, 1.35f, 0.35f); val3.center = new Vector3(0f, 0.675f, 0f); ((Collider)val3).isTrigger = !ModConfig.EnableDeathTombCollider; ((Collider)val3).enabled = true; } private static ScanNodeProperties EnsureTombScanNode(Transform tombRoot, PlayerControllerB player) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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) if ((Object)(object)tombRoot == (Object)null) { return null; } Transform val = tombRoot.Find("ScanNode"); GameObject val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).gameObject; } else { val2 = new GameObject("ScanNode"); val2.transform.SetParent(tombRoot, false); val2.transform.localPosition = new Vector3(0f, 0.75f, 0f); val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; } int num = LayerMask.NameToLayer("ScanNode"); if (num >= 0) { val2.layer = num; } ScanNodeProperties val3 = val2.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = val2.AddComponent(); } string text = (((Object)(object)player != (Object)null && !string.IsNullOrEmpty(player.playerUsername)) ? player.playerUsername : "Player"); val3.headerText = text + "'s tomb"; val3.subText = "Revive marker"; val3.minRange = 1; val3.maxRange = 13; val3.requiresLineOfSight = false; val3.nodeType = 0; val3.creatureScanID = -1; SphereCollider val4 = val2.GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = val2.AddComponent(); } ((Collider)val4).isTrigger = true; val4.radius = 0.45f; val4.center = Vector3.zero; ((Collider)val4).enabled = true; Plugin.DebugLog("[UltimateRevive][TombScanNode] created scan node " + $"player={(((Object)(object)player == (Object)null) ? (-1) : ((int)player.playerClientId))} " + $"text='{val3.headerText}' layer={val2.layer}"); return val3; } private static Light EnsureNightVisionRadar(Transform root) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_003c: 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) Transform val = root.Find("NightVisionRadar"); GameObject val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).gameObject; } else { val2 = new GameObject("NightVisionRadar"); val2.transform.SetParent(root, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; } Light val3 = val2.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = val2.AddComponent(); } val3.type = (LightType)2; val3.range = 2f; val3.intensity = 0f; ((Behaviour)val3).enabled = false; return val3; } private static Transform EnsureRadarDot(Transform root) { //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_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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00c1: 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) Transform val = root.Find("RadarDot_World"); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(true); val.SetParent(root, false); val.localPosition = Vector3.zero; val.localRotation = Quaternion.identity; return val; } Transform val2 = root.Find("RadarDot"); if ((Object)(object)val2 != (Object)null && !(val2 is RectTransform)) { ((Component)val2).gameObject.SetActive(true); val2.SetParent(root, false); val2.localPosition = Vector3.zero; val2.localRotation = Quaternion.identity; return val2; } GameObject val3 = new GameObject("RadarDot_World"); val3.transform.SetParent(root, false); val3.transform.localPosition = Vector3.zero; val3.transform.localRotation = Quaternion.identity; val3.SetActive(true); return val3.transform; } private static Rigidbody[] EnsureBodyParts(Transform root, PlayerControllerB player) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0081: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) int num = 0; if ((Object)(object)player != (Object)null && player.bodyParts != null) { num = player.bodyParts.Length; } int num2 = Mathf.Max(num, 6); Rigidbody[] array = (Rigidbody[])(object)new Rigidbody[num2]; for (int i = 0; i < num2; i++) { string text = $"TombBodyPart_{i}"; Transform val = root.Find(text); GameObject val2; if ((Object)(object)val != (Object)null) { val2 = ((Component)val).gameObject; } else { val2 = new GameObject(text); val2.transform.SetParent(root, false); } val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; Rigidbody val3 = val2.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = val2.AddComponent(); } BoxCollider val4 = val2.GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = val2.AddComponent(); } val4.size = new Vector3(0.2f, 0.2f, 0.2f); val4.center = Vector3.zero; ((Collider)val4).isTrigger = !ModConfig.EnableDeathTombCollider; ((Collider)val4).enabled = true; val3.isKinematic = false; val3.useGravity = false; val3.constraints = (RigidbodyConstraints)126; val3.detectCollisions = true; val3.velocity = Vector3.zero; val3.angularVelocity = Vector3.zero; array[i] = val3; } return array; } private static int GetPlayerIndex(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return -1; } if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.allPlayerScripts != null) { for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++) { if ((Object)(object)StartOfRound.Instance.allPlayerScripts[i] == (Object)(object)player) { return i; } } } return (int)player.playerClientId; } } } namespace UltimateRevive.Patches { internal static class InteractTriggerPatch { private static void AwakePostfix(InteractTrigger __instance) { } private static void StartPostfix(InteractTrigger __instance) { SafeRefresh(__instance, "Start"); } private static void OnEnablePostfix(InteractTrigger __instance) { SafeRefresh(__instance, "OnEnable"); } private static void OnDestroyPostfix(InteractTrigger __instance) { } private static void SafeRefresh(InteractTrigger trigger, string reason) { try { if (!((Object)(object)trigger == (Object)null) && IsReviveRelatedTrigger(trigger)) { TryRefreshBodyOwner(trigger); } } catch (Exception arg) { Plugin.DebugLog($"[InteractTriggerPatch] skipped trigger refresh reason={reason} error={arg}"); } } private static bool IsReviveRelatedTrigger(InteractTrigger trigger) { if ((Object)(object)trigger == (Object)null) { return false; } GameObject gameObject = ((Component)trigger).gameObject; if ((Object)(object)gameObject == (Object)null) { return false; } if ((((Object)gameObject).name ?? string.Empty).StartsWith("UltimateRevive")) { return true; } Transform transform = ((Component)trigger).transform; if ((Object)(object)transform == (Object)null) { return false; } Transform root = transform.root; if ((Object)(object)root != (Object)null && ((Object)root).name.StartsWith("UltimateReviveTomb_")) { return true; } if ((Object)(object)((Component)trigger).GetComponentInParent() != (Object)null) { return true; } if ((Object)(object)((Component)trigger).GetComponentInParent() != (Object)null) { return true; } return false; } private static void TryRefreshBodyOwner(InteractTrigger trigger) { //IL_01e9: 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_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0366: 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_036b: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) try { if (!ModState.Active || (Object)(object)trigger == (Object)null) { return; } RagdollGrabbableObject val = null; MaskedPlayerEnemy val2 = null; ulong num = 0uL; NetworkObject val3 = ((Component)trigger).GetComponent() ?? ((Component)trigger).GetComponentInParent(); if ((Object)(object)val3 != (Object)null) { num = val3.NetworkObjectId; if (ModState.BodyByNetworkObjectId.TryGetValue(num, out var value) && (Object)(object)value != (Object)null) { val = value; } if (ModState.MaskedByNetworkObjectId.TryGetValue(num, out var value2) && value2 != null && (Object)(object)value2.Masked != (Object)null) { val2 = value2.Masked; } } if ((Object)(object)val == (Object)null) { val = ((Component)trigger).GetComponent() ?? ((Component)trigger).GetComponentInParent(); } if ((Object)(object)val2 == (Object)null) { val2 = ((Component)trigger).GetComponentInParent(); } if (((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null) || ((Object)(object)val != (Object)null && DeathMarkerManager.IsTombRagdollGrabbable(val))) { return; } int? num2 = null; if ((Object)(object)val2 != (Object)null) { num2 = MaskedConversionTracker.GetPlayerIdForMasked(val2); if (num2.HasValue && ModState.MaskedByPlayerId.TryGetValue(num2.Value, out var value3) && value3 != null) { val = null; } } if (!num2.HasValue) { DeadBodyInfo componentInParent = ((Component)trigger).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { num2 = componentInParent.playerObjectId; } } if ((Object)(object)val != (Object)null && (Object)(object)((NetworkBehaviour)val).NetworkObject != (Object)null) { _ = ((NetworkBehaviour)val).NetworkObject.NetworkObjectId; if (num2.HasValue && (!ModState.BodyByPlayerId.ContainsKey(num2.Value) || (Object)(object)ModState.BodyByPlayerId[num2.Value] != (Object)(object)val)) { ModState.BodyByPlayerId[num2.Value] = val; } } ulong networkObjectId = (((Object)(object)val != (Object)null && (Object)(object)((NetworkBehaviour)val).NetworkObject != (Object)null) ? ((NetworkBehaviour)val).NetworkObject.NetworkObjectId : 0); Vector3 deathPosition = (((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform != (Object)null) ? ((Component)val).transform.position : Vector3.zero); DeadBodyInfo componentInParent2 = ((Component)trigger).GetComponentInParent(); if ((Object)(object)componentInParent2 != (Object)null) { int playerObjectId = componentInParent2.playerObjectId; PlayerControllerB val4 = ((StartOfRound.Instance?.allPlayerScripts != null && playerObjectId >= 0 && playerObjectId < StartOfRound.Instance.allPlayerScripts.Length) ? StartOfRound.Instance.allPlayerScripts[playerObjectId] : null); if ((Object)(object)val4 != (Object)null && val4.isPlayerDead) { GeneralUtil.SetPlayerDeathState(playerObjectId, PlayerDeathType.Body, networkObjectId, deathPosition, "Trigger.Active:DeadBodyInfo"); ReviveInteractTriggerManager.ForceRefreshPlayer(playerObjectId, "Trigger.Active:DeadBodyInfo"); return; } } if (!((Object)(object)val2 != (Object)null)) { return; } int? playerIdForMasked = MaskedConversionTracker.GetPlayerIdForMasked(val2); if (playerIdForMasked.HasValue && ModState.MaskedByPlayerId.TryGetValue(playerIdForMasked.Value, out var value4) && value4 != null && (Object)(object)value4.Masked != (Object)null) { PlayerControllerB val5 = ((StartOfRound.Instance?.allPlayerScripts != null && playerIdForMasked.Value >= 0 && playerIdForMasked.Value < StartOfRound.Instance.allPlayerScripts.Length) ? StartOfRound.Instance.allPlayerScripts[playerIdForMasked.Value] : null); if ((Object)(object)val5 != (Object)null && val5.isPlayerDead) { ulong networkObjectId2 = value4.NetworkObjectId; Vector3 deathPosition2 = (((Object)(object)((Component)value4.Masked).transform != (Object)null) ? ((Component)value4.Masked).transform.position : value4.LastPosition); GeneralUtil.SetPlayerDeathState(playerIdForMasked.Value, PlayerDeathType.Masked, networkObjectId2, deathPosition2, "Trigger.Active:MaskedPlayerEnemy:MaskedInfo"); ReviveInteractTriggerManager.ForceRefreshPlayer(playerIdForMasked.Value, "Trigger.Active:MaskedPlayerEnemy:MaskedInfo"); } } } catch (Exception arg) { Plugin.DebugLog($"[InteractTriggerPatch.TryRefreshBodyOwner] error={arg}"); } } public static void TryPatch(Harmony harmony) { Type typeFromHandle = typeof(InteractTriggerPatch); Type typeFromHandle2 = typeof(InteractTrigger); TryPatchMethod(harmony, typeFromHandle2, "Awake", typeFromHandle.GetMethod("AwakePostfix", BindingFlags.Static | BindingFlags.NonPublic)); TryPatchMethod(harmony, typeFromHandle2, "Start", typeFromHandle.GetMethod("StartPostfix", BindingFlags.Static | BindingFlags.NonPublic)); TryPatchMethod(harmony, typeFromHandle2, "OnEnable", typeFromHandle.GetMethod("OnEnablePostfix", BindingFlags.Static | BindingFlags.NonPublic)); TryPatchMethod(harmony, typeFromHandle2, "OnDestroy", typeFromHandle.GetMethod("OnDestroyPostfix", BindingFlags.Static | BindingFlags.NonPublic)); } private static void TryPatchMethod(Harmony harmony, Type targetType, string methodName, MethodInfo postfix) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown if (postfix == null) { return; } MethodInfo method = targetType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { return; } try { harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(postfix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception) { } } } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] internal static class KillPlayerPatch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PlayerControllerB player; public Vector3 deathPosition; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0044: 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_014e: 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_00b2: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; try { if (TryResolveNoBodyDeath(player, deathPosition, allowTomb: false, "KillPlayer:noBody:nextFrame")) { return false; } } catch (Exception arg3) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)string.Format("[KillPlayerPatch.ResolveNoBodyDeathCoroutine] Exception for player {0}: {1}", ((Object)(object)player != (Object)null) ? player.playerClientId.ToString() : "null", arg3)); } } <>2__current = (object)new WaitForSeconds(0.35f); <>1__state = 2; return true; case 2: <>1__state = -1; try { if (TryResolveNoBodyDeath(player, deathPosition, allowTomb: false, "KillPlayer:noBody:0.35s")) { return false; } } catch (Exception arg2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)string.Format("[KillPlayerPatch.ResolveNoBodyDeathCoroutine] Exception for player {0}: {1}", ((Object)(object)player != (Object)null) ? player.playerClientId.ToString() : "null", arg2)); } } <>2__current = (object)new WaitForSeconds(1.15f); <>1__state = 3; return true; case 3: <>1__state = -1; try { TryResolveNoBodyDeath(player, deathPosition, allowTomb: true, "KillPlayer:noBody:fallbackTomb"); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)string.Format("[KillPlayerPatch.ResolveNoBodyDeathCoroutine] Exception for player {0}: {1}", ((Object)(object)player != (Object)null) ? player.playerClientId.ToString() : "null", arg)); } } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static void Postfix(PlayerControllerB __instance, Vector3 bodyVelocity, bool spawnBody, CauseOfDeath causeOfDeath, int deathAnimation, Vector3 positionOffset, bool setOverrideDropItems) { //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_0038: 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_003d: 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_006c: Unknown result type (might be due to invalid IL or missing references) if (!ModState.Active || (Object)(object)__instance == (Object)null) { return; } int playerClientId = (int)__instance.playerClientId; Vector3 deathPosition = (((Object)(object)((Component)__instance).transform != (Object)null) ? (((Component)__instance).transform.position + positionOffset) : positionOffset); if (spawnBody) { if (!ModConfig.CanPickUpBodies) { RagdollGrabbableObject bodyForPlayer = GeneralUtil.GetBodyForPlayer(playerClientId); if ((Object)(object)bodyForPlayer != (Object)null) { ReviveInteractTriggerManager.DisableVanillaBodyTriggers(bodyForPlayer); } } } else { if (GeneralUtil.GetPlayerLastDeathType(playerClientId) != PlayerDeathType.Masked) { GeneralUtil.SetPlayerDeathState(playerClientId, PlayerDeathType.Body, 0uL, deathPosition, "KillPlayer:noBody"); ReviveNetworkSync.SendPlayerDeathStateToClients(playerClientId); } Plugin.RunCoroutineSafe(ResolveNoBodyDeathCoroutine(__instance, deathPosition)); } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator ResolveNoBodyDeathCoroutine(PlayerControllerB player, Vector3 deathPosition) { //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) //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0) { player = player, deathPosition = deathPosition }; } private static bool TryResolveNoBodyDeath(PlayerControllerB player, Vector3 deathPosition, bool allowTomb, string reason) { //IL_01fa: 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_006f: 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_0206: 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_0074: 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_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: 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_0105: 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_00da: 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_01d0: Unknown result type (might be due to invalid IL or missing references) if (!ModState.Active || (Object)(object)player == (Object)null || !player.isPlayerDead) { return true; } int num = (int)player.playerClientId; if (GeneralUtil.GetPlayerLastDeathType(num) == PlayerDeathType.Masked && ModConfig.EnableMaskedPlayerRevive) { MaskedPlayerEnemy maskedForPlayer = MaskedConversionTracker.GetMaskedForPlayer(num); if ((Object)(object)maskedForPlayer != (Object)null) { ulong networkObjectId = MaskedConversionTracker.GetNetworkObjectId(maskedForPlayer); Vector3 val = (((Object)(object)((Component)maskedForPlayer).transform != (Object)null) ? ((Component)maskedForPlayer).transform.position : deathPosition); if (!ModState.MaskedByNetworkObjectId.TryGetValue(networkObjectId, out var value) || value == null) { value = new ModState.MaskedInfo { PlayerClientId = num, Masked = maskedForPlayer, NetworkObjectId = networkObjectId, LastPosition = val, IsDead = false, LastUpdateTime = Time.time }; } else { value.Masked = maskedForPlayer; value.NetworkObjectId = networkObjectId; value.LastPosition = val; value.IsDead = false; value.LastUpdateTime = Time.time; } ModState.MaskedByNetworkObjectId[networkObjectId] = value; GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Masked, networkObjectId, val, reason + ":maskedResolved"); ReviveInteractTriggerManager.ForceRefreshPlayer(num, reason + ":maskedResolved"); return true; } if (!allowTomb && MaskedConversionTracker.HasPendingForPlayer(num)) { return false; } } if ((Object)(object)player.deadBody != (Object)null && !DeathMarkerManager.IsTombDeadBody(num, player.deadBody)) { ReviveInteractTriggerManager.ForceRefreshPlayer(num, reason + ":deadBodyResolved"); return true; } RagdollGrabbableObject bodyForPlayer = GeneralUtil.GetBodyForPlayer(num); if ((Object)(object)bodyForPlayer != (Object)null && !DeathMarkerManager.IsTombRagdollGrabbable(bodyForPlayer)) { ulong num2 = (((Object)(object)((NetworkBehaviour)bodyForPlayer).NetworkObject != (Object)null) ? ((NetworkBehaviour)bodyForPlayer).NetworkObject.NetworkObjectId : 0); Vector3 deathPosition2 = (((Object)(object)((Component)bodyForPlayer).transform != (Object)null) ? ((Component)bodyForPlayer).transform.position : deathPosition); if (num2 != 0L) { ModState.BodyByNetworkObjectId[num2] = bodyForPlayer; } GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Body, num2, deathPosition2, reason + ":bodyResolved"); ReviveInteractTriggerManager.ForceRefreshPlayer(num, reason + ":bodyResolved"); return true; } if (!allowTomb) { return false; } Vector3 position = deathPosition; if (GeneralUtil.TryGetPlayerLastDeathPosition(num, out var position2)) { position = position2; } else if ((Object)(object)((Component)player).transform != (Object)null) { position = ((Component)player).transform.position; } if (DeathMarkerManager.TrySpawnTombForPlayer(player, position, reason)) { ReviveInteractTriggerManager.ForceRefreshPlayer(num, reason + ":tombSpawned"); return true; } return false; } } [HarmonyPatch(typeof(MaskedPlayerEnemy), "CreateMimicClientRpc")] internal static class MaskedCreateMimicPatch { private static void Postfix(MaskedPlayerEnemy __instance, NetworkObjectReference netObjectRef, bool inFactory, int playerKilled) { NetworkObject val = default(NetworkObject); if (!ModState.Active || !ModConfig.EnableMaskedPlayerRevive || !((NetworkObjectReference)(ref netObjectRef)).TryGet(ref val, (NetworkManager)null) || (Object)(object)val == (Object)null) { return; } MaskedPlayerEnemy val2 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInChildren(true); if (!((Object)(object)val2 == (Object)null)) { if (playerKilled >= 0) { MaskedConversionTracker.TagMaskedAsConverted(val2, playerKilled, MaskedOrigin.ConvertedByMaskedEnemy, "CreateMimicClientRpc:playerKilled"); } else { MaskedConversionTracker.TagMaskedOnSpawn(val2); } } } } [HarmonyPatch(typeof(CadaverBloomAI), "BurstForth")] public static class CadaverBloom_BurstForth_Patch { private static void Prefix(CadaverBloomAI __instance, out bool __state) { __state = !__instance.hasBurst; } private static void Postfix(CadaverBloomAI __instance, PlayerControllerB player, bool kill, bool __state) { if (__state && !((Object)(object)player == (Object)null) && __instance.hasBurst) { int num = (int)player.playerClientId; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[CadaverBloom] Player {num} transformed into Cadaver Bloom"); } CadaverBloomTracker.PlayerBlooms[num] = __instance; CadaverBloomTracker.PendingBodyCheck.Add(num); } } } [HarmonyPatch(typeof(CadaverBloomAI), "KillEnemy")] public static class CadaverBloom_KillEnemy_Patch { private struct KillEnemyState { public PlayerControllerB Player; public CadaverGrowthAI Growth; public int BloomSlot; } [CompilerGenerated] private sealed class d__4 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CadaverBloomAI bloom; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__4(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: { <>1__state = -1; if ((Object)(object)bloom == (Object)null) { return false; } NetworkObject networkObject = ((NetworkBehaviour)bloom).NetworkObject; if ((Object)(object)networkObject != (Object)null && networkObject.IsSpawned) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[CadaverBloom] Despawning dead CadaverBloomAI object"); } networkObject.Despawn(true); } else if ((Object)(object)((Component)bloom).gameObject != (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[CadaverBloom] Destroying dead CadaverBloomAI GameObject fallback"); } Object.Destroy((Object)(object)((Component)bloom).gameObject); } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static void Prefix(CadaverBloomAI __instance, out KillEnemyState __state) { __state = new KillEnemyState { Player = __instance.burstPlayer, Growth = Object.FindObjectOfType(), BloomSlot = -1 }; if ((Object)(object)__state.Growth == (Object)null || __state.Growth.bloomEnemies == null) { return; } try { for (int i = 0; i < __state.Growth.bloomEnemies.Length; i++) { if (__state.Growth.bloomEnemies[i] == __instance) { __state.BloomSlot = i; break; } } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[CadaverBloom_KillEnemy_Patch.Prefix] Exception while searching for bloom slot: {arg}"); } } } private static void Postfix(CadaverBloomAI __instance, KillEnemyState __state) { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) bool flag = false; if ((Object)(object)__state.Growth != (Object)null && __state.Growth.bloomEnemies != null && __state.BloomSlot >= 0 && __state.BloomSlot < __state.Growth.bloomEnemies.Length && __state.Growth.bloomEnemies[__state.BloomSlot] == __instance) { __state.Growth.bloomEnemies[__state.BloomSlot] = null; flag = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[CadaverBloom] Freed bloom slot {__state.BloomSlot}"); } } if (!flag) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[CadaverBloom] KillEnemy: no matching bloom slot found, old Bloom will not be despawned by this patch"); } } else { TryDespawnDeadBloomNextFrame(__instance); } if ((Object)(object)__state.Player == (Object)null) { return; } int num = (int)__state.Player.playerClientId; DeadBodyInfo deadBody = __state.Player.deadBody; CadaverBloomTracker.PlayerBlooms.Remove(num); if ((Object)(object)deadBody != (Object)null) { CadaverBloomTracker.PlayerBodies[num] = deadBody; RagdollGrabbableObject component = ((Component)deadBody).GetComponent(); if ((Object)(object)component != (Object)null) { ModState.BodyByPlayerId[num] = component; Vector3 deathPosition = (((Object)(object)((Component)component).transform != (Object)null) ? ((Component)component).transform.position : ((Component)deadBody).transform.position); GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Body, ((Object)(object)((NetworkBehaviour)component).NetworkObject != (Object)null) ? ((NetworkBehaviour)component).NetworkObject.NetworkObjectId : 0, deathPosition, "CadaverBloom:bodySpawned"); ReviveNetworkSync.SendPlayerDeathStateToClients(num); } } else if (!CadaverBloomTracker.PendingBodyCheck.Contains(num)) { CadaverBloomTracker.PendingBodyCheck.Add(num); } ReviveInteractTriggerManager.ForceRefreshPlayer(num, "CadaverBloom"); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[CadaverBloom] KillEnemy: body refresh for player {num}"); } } private static void TryDespawnDeadBloomNextFrame(CadaverBloomAI bloom) { if (!((Object)(object)bloom == (Object)null) && ((NetworkBehaviour)bloom).IsServer) { if ((Object)(object)StartOfRound.Instance != (Object)null) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(DespawnDeadBloomCoroutine(bloom)); } else { ((MonoBehaviour)bloom).StartCoroutine(DespawnDeadBloomCoroutine(bloom)); } } } [IteratorStateMachine(typeof(d__4))] private static IEnumerator DespawnDeadBloomCoroutine(CadaverBloomAI bloom) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__4(0) { bloom = bloom }; } } [HarmonyPatch] internal static class MaskedConversionPatch { [HarmonyPatch(typeof(MaskedPlayerEnemy), "Start")] [HarmonyPostfix] private static void MaskedStartPostfix(MaskedPlayerEnemy __instance) { if (ModState.Active && ModConfig.EnableMaskedPlayerRevive) { MaskedConversionTracker.TagMaskedOnSpawn(__instance); } } [HarmonyPatch(typeof(HauntedMaskItem), "CreateMimicServerRpc")] [HarmonyPrefix] private static void CreateMimicServerRpcPrefix(HauntedMaskItem __instance) { if (ModState.Active && ModConfig.EnableMaskedPlayerRevive && !((Object)(object)__instance == (Object)null)) { if ((Object)(object)((GrabbableObject)(__instance?)).playerHeldBy != (Object)null) { MaskedConversionTracker.RegisterPending(((GrabbableObject)__instance).playerHeldBy, MaskedOrigin.ConvertedByMaskItem); } } } [HarmonyPatch(typeof(HauntedMaskItem), "FinishAttaching")] [HarmonyPostfix] private static void FinishAttachingPostfix(HauntedMaskItem __instance) { if (ModState.Active && ModConfig.EnableMaskedPlayerRevive && !((Object)(object)__instance == (Object)null)) { PlayerControllerB val = null; try { val = Traverse.Create((object)__instance).Field("previousPlayerHeldBy").GetValue(); } catch { } if ((Object)(object)val != (Object)null && val.isPlayerDead) { MaskedConversionTracker.RegisterPending(val, MaskedOrigin.ConvertedByMaskItem); } } } [HarmonyPatch(typeof(MaskedPlayerEnemy), "killAnimation")] [HarmonyPostfix] private static void KillAnimationPostfix(MaskedPlayerEnemy __instance) { if (!ModState.Active || !ModConfig.EnableMaskedPlayerRevive || (Object)(object)__instance == (Object)null) { return; } PlayerControllerB val = ((EnemyAI)(__instance?)).inSpecialAnimationWithPlayer; if (!((Object)(object)val == (Object)null)) { int num = (int)val.playerClientId; MaskedConversionTracker.RegisterPending(val, MaskedOrigin.ConvertedByMaskedEnemy); if ((Object)(object)MaskedConversionTracker.GetMaskedForPlayer(num) != (Object)null) { ReviveInteractTriggerManager.ForceRefreshPlayer(num, "Masked.killAnimation"); } } } [HarmonyPatch(typeof(MaskedPlayerEnemy), "FinishKillAnimation")] [HarmonyPostfix] private static void FinishKillAnimationPostfix(MaskedPlayerEnemy __instance) { if (!ModState.Active || !ModConfig.EnableMaskedPlayerRevive || (Object)(object)__instance == (Object)null) { return; } PlayerControllerB val = null; try { val = Traverse.Create((object)__instance).Field("lastPlayerKilled").GetValue(); } catch { } if (!((Object)(object)val == (Object)null)) { int num = (int)val.playerClientId; MaskedConversionTracker.RegisterPending(val, MaskedOrigin.ConvertedByMaskedEnemy); if ((Object)(object)MaskedConversionTracker.GetMaskedForPlayer(num) != (Object)null) { ReviveInteractTriggerManager.ForceRefreshPlayer(num, "Masked.FinishKillAnimation"); } } } [HarmonyPatch(typeof(MaskedPlayerEnemy), "KillEnemy")] [HarmonyPostfix] private static void KillEnemyPostfix(MaskedPlayerEnemy __instance) { if (!ModState.Active || !ModConfig.EnableMaskedPlayerRevive || (Object)(object)__instance == (Object)null) { return; } ReviveInteractTriggerManager.OnMaskedKilled(__instance); MaskedConversionTracker.OnMaskedDeath(__instance); try { int? playerIdForMasked = MaskedConversionTracker.GetPlayerIdForMasked(__instance); if (playerIdForMasked.HasValue) { ReviveNetworkSync.SendPlayerDeathStateToClients(playerIdForMasked.Value); } } catch (Exception arg) { Debug.LogError((object)$"[MaskedConversionPatch.KillEnemyPostfix] Error during player stats sync: {arg}"); } } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class PlayerControllerBPatch { private const string ReviveOverlayPrefix = "Revive:"; private static float _startedReviveAt; private static bool _startedRevive; private static int _revivingPlayerId = -1; [HarmonyPatch("KillPlayerClientRpc")] [HarmonyPrefix] private static void KillPlayerClientRpcPrefix(PlayerControllerB __instance, ref int playerId) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_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_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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) try { Vector3 val = (((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).transform != (Object)null) ? ((Component)__instance).transform.position : Vector3.zero); if (ModConfig.EnableMaskedPlayerRevive) { EnemyAI obj = (((Object)(object)__instance != (Object)null) ? __instance.redirectToEnemy : null); MaskedPlayerEnemy val2 = (MaskedPlayerEnemy)(object)((obj is MaskedPlayerEnemy) ? obj : null); if ((Object)(object)val2 != (Object)null) { ulong networkObjectId = MaskedConversionTracker.GetNetworkObjectId(val2); Vector3 deathPosition = (((Object)(object)((Component)val2).transform != (Object)null) ? ((Component)val2).transform.position : val); GeneralUtil.SetPlayerDeathState(playerId, PlayerDeathType.Masked, networkObjectId, deathPosition, "KillPlayerClientRpc:redirectToEnemy"); ReviveInteractTriggerManager.ForceRefreshPlayer(playerId, "KillPlayerClientRpc:redirectToEnemy"); return; } if (GeneralUtil.GetPlayerLastDeathType(playerId) == PlayerDeathType.Masked) { ulong playerLastDeathNetworkObjectId = GeneralUtil.GetPlayerLastDeathNetworkObjectId(playerId); MaskedPlayerEnemy val3 = ((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetMaskedByNetworkObjectId(playerLastDeathNetworkObjectId) : GeneralUtil.GetConvertedMaskedCorpseForPlayer(playerId)); if ((Object)(object)val3 != (Object)null) { ulong networkObjectId2 = MaskedConversionTracker.GetNetworkObjectId(val3); Vector3 deathPosition2 = (((Object)(object)((Component)val3).transform != (Object)null) ? ((Component)val3).transform.position : val); GeneralUtil.SetPlayerDeathState(playerId, PlayerDeathType.Masked, networkObjectId2, deathPosition2, "KillPlayerClientRpc:preserveMasked"); ReviveInteractTriggerManager.ForceRefreshPlayer(playerId, "KillPlayerClientRpc:preserveMasked"); return; } } } GeneralUtil.SetPlayerDeathState(playerId, PlayerDeathType.Body, 0uL, val, "KillPlayerClientRpc"); ReviveInteractTriggerManager.ForceRefreshPlayer(playerId, "KillPlayerClientRpc:body"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("KillPlayerClientRpcPrefix fallback after exception: " + ex.Message + ex.GetType().FullName + ": " + ex.Message + "\n" + ex.StackTrace)); } GeneralUtil.SetPlayerDeathState(playerId, PlayerDeathType.Body, 0uL, Vector3.zero, "KillPlayerClientRpc:exceptionFallback"); ReviveInteractTriggerManager.ForceRefreshPlayer(playerId, "KillPlayerClientRpc:exceptionFallback"); } } [HarmonyPatch("SetHoverTipAndCurrentInteractTrigger")] [HarmonyPostfix] private static void SetHoverTipAndCurrentInteractTriggerPostfix(PlayerControllerB __instance) { try { HandleReviveUiAndInput(__instance); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.SetHoverTipAndCurrentInteractTriggerPostfix] {arg}"); } } } [HarmonyPatch("Update")] [HarmonyPostfix] private static void UpdatePostfix(PlayerControllerB __instance) { try { HandleReviveUiAndInput(__instance); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.UpdatePostfix] {arg}"); } } } private static void HandleReviveUiAndInput(PlayerControllerB player) { try { if ((Object)(object)player == (Object)null) { return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { ResetProgress(); } else { if ((uint)player.playerClientId != (uint)val.playerClientId) { return; } if (player.isPlayerDead) { ResetProgress(); return; } TMP_Text cursorTip = (TMP_Text)(object)player.cursorTip; if ((Object)(object)cursorTip == (Object)null) { ResetProgress(); return; } if (!TryGetReviveTarget(player, out var targetPlayerId, out var reason, out var canRevive)) { ResetProgress(); return; } if (!canRevive) { if (reason == "kill masked first" && ModState.RemainingRevives > 0) { SetReviveOverlay(cursorTip, "Revive: Kill the masked to revive your teammate"); } else if (!string.IsNullOrWhiteSpace(reason) && reason != "too far from body" && reason != "source missing") { SetReviveOverlay(cursorTip, BuildBlockedTip(reason)); } ResetProgress(); return; } InputAction reviveAction = Plugin.ReviveAction; if (reviveAction == null) { SetReviveOverlay(cursorTip, "Revive: key unavailable"); ResetProgress(); return; } if (!reviveAction.IsPressed()) { SetReviveOverlay(cursorTip, BuildIdleTip(targetPlayerId)); ResetProgress(); return; } if (!_startedRevive || _revivingPlayerId != targetPlayerId) { _startedRevive = true; _revivingPlayerId = targetPlayerId; _startedReviveAt = Time.time; } float num = Time.time - _startedReviveAt; if (num >= ModConfig.ReviveTime) { if (ModConfig.EnableDebugLogs) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveDebug] RequestRevive called for player={targetPlayerId} elapsed={num:0.00}/{ModConfig.ReviveTime:0.00}"); } } ReviveNetwork.RequestRevive(targetPlayerId); SetReviveOverlay(cursorTip, "Revive: requested"); ResetProgress(); } else { SetReviveOverlay(cursorTip, string.Format("{0} {1}/{2:0}s", "Revive:", Mathf.CeilToInt(num), ModConfig.ReviveTime)); } } } catch (Exception arg) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[PlayerControllerBPatch.HandleReviveUiAndInput] {arg}"); } ResetProgress(); } } private static bool TryGetReviveTarget(PlayerControllerB reviver, out int targetPlayerId, out string reason, out bool canRevive) { try { targetPlayerId = -1; reason = string.Empty; canRevive = false; if (ReviveInteractTriggerManager.TryGetHoveredReviveTarget(reviver, out targetPlayerId, out reason, out canRevive)) { return true; } reason = "no revive trigger"; return false; } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.TryGetReviveTarget] {arg}"); } targetPlayerId = -1; reason = "error"; canRevive = false; return false; } } private static bool IsLocalAlivePlayer(PlayerControllerB player) { try { if ((Object)(object)player == (Object)null || player.isPlayerDead) { return false; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return false; } return (uint)player.playerClientId == (uint)val.playerClientId; } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.IsLocalAlivePlayer] {arg}"); } return false; } } private static string BuildIdleTip(int targetPlayerId) { try { string reviveBindingLabel = Plugin.GetReviveBindingLabel(); if (ModConfig.InfiniteReviveTime) { return "Revive: hold " + reviveBindingLabel; } float num = ReviveRules.SecondsLeft(targetPlayerId); if (num <= 0f) { return "Revive: 0s left"; } return string.Format("{0} hold {1} ({2}s)", "Revive:", reviveBindingLabel, Mathf.CeilToInt(num)); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.BuildIdleTip] {arg}"); } return "Revive: error"; } } private static string BuildBlockedTip(string reason) { try { if (string.IsNullOrWhiteSpace(reason)) { return string.Empty; } string text = reason.Trim().ToLowerInvariant(); if (text == "source missing" || text == "too far from body") { return string.Empty; } if (text.Contains("outside") || text.Contains("line of sight")) { return "Revive: keep target in sight"; } return "Revive: " + reason; } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.BuildBlockedTip] {arg}"); } return "Revive: error"; } } private static void SetReviveOverlay(TMP_Text cursorTip, string reviveLine) { try { if (!((Object)(object)cursorTip == (Object)null)) { string text = StripReviveOverlay(cursorTip.text); if (string.IsNullOrWhiteSpace(text)) { cursorTip.text = reviveLine; } else { cursorTip.text = reviveLine + "\n" + text; } } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.SetReviveOverlay] {arg}"); } } } private static string StripReviveOverlay(string text) { try { if (string.IsNullOrWhiteSpace(text)) { return string.Empty; } string[] array = text.Split('\n'); StringBuilder stringBuilder = new StringBuilder(); string[] array2 = array; foreach (string text2 in array2) { string text3 = text2?.Trim() ?? string.Empty; if (!string.IsNullOrWhiteSpace(text3) && !text3.StartsWith("Revive:", StringComparison.OrdinalIgnoreCase)) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(text2); } } return stringBuilder.ToString(); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.StripReviveOverlay] {arg}"); } return string.Empty; } } private static void ResetProgress() { try { _startedRevive = false; _revivingPlayerId = -1; _startedReviveAt = 0f; } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[PlayerControllerBPatch.ResetProgress] {arg}"); } } } } [HarmonyPatch(typeof(RagdollGrabbableObject))] internal static class RagdollGrabbableObjectPatch { [CompilerGenerated] private sealed class d__3 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public RagdollGrabbableObject body; private float[] 5__2; private int 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__3(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = new float[5] { 0.2f, 0.5f, 1f, 2f, 4f }; 5__3 = 0; break; case 1: <>1__state = -1; try { if (!ModState.Active || (Object)(object)body == (Object)null) { return false; } TryRefreshBodySource(body, $"Ragdoll.Delayed:{5__2[5__3]:0.0}s"); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[RagdollGrabbableObjectPatch.LateRefreshCoroutine] {arg}"); } } 5__3++; break; } if (5__3 < 5__2.Length) { <>2__current = (object)new WaitForSeconds(5__2[5__3]); <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__2 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public RagdollGrabbableObject body; private bool 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = (Object)(object)body != (Object)null && ((GrabbableObject)body).isHeld; ReviveInteractTriggerManager.SetReviveTriggerEnabled(body, !5__2); break; case 1: <>1__state = -1; break; } if ((Object)(object)body != (Object)null) { bool isHeld = ((GrabbableObject)body).isHeld; if (isHeld != 5__2) { ReviveInteractTriggerManager.SetReviveTriggerEnabled(body, !isHeld); 5__2 = isHeld; } <>2__current = null; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly Dictionary LastRefreshSignatureByBody = new Dictionary(); [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(RagdollGrabbableObject __instance) { try { if (!ModState.Active || (Object)(object)__instance == (Object)null) { return; } if (!ModConfig.CanPickUpBodies && (Object)(object)__instance != (Object)null) { Collider primaryRagdollCollider = ReviveInteractTriggerManager.GetPrimaryRagdollCollider(__instance); if ((Object)(object)primaryRagdollCollider != (Object)null) { primaryRagdollCollider.enabled = false; } } if ((Object)(object)((GrabbableObject)(__instance?)).itemProperties != (Object)null) { ((GrabbableObject)__instance).itemProperties.weight = ModConfig.DeadBodyWeight; } TryRefreshBodySource(__instance, "Ragdoll.Start"); ((MonoBehaviour)__instance).StartCoroutine(LateRefreshCoroutine(__instance)); ((MonoBehaviour)__instance).StartCoroutine(MonitorIsHeldCoroutine(__instance)); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[RagdollGrabbableObjectPatch.StartPostfix] {arg}"); } } } [IteratorStateMachine(typeof(d__2))] private static IEnumerator MonitorIsHeldCoroutine(RagdollGrabbableObject body) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__2(0) { body = body }; } [IteratorStateMachine(typeof(d__3))] private static IEnumerator LateRefreshCoroutine(RagdollGrabbableObject body) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__3(0) { body = body }; } private static void TryRefreshBodySource(RagdollGrabbableObject body, string reason) { //IL_0037: 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_003c: 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_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: 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_0211: 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_0105: Unknown result type (might be due to invalid IL or missing references) try { if (!ModState.Active || (Object)(object)body == (Object)null) { return; } int instanceID = ((Object)body).GetInstanceID(); Vector3 val = (((Object)(object)((Component)body).transform != (Object)null) ? ((Component)body).transform.position : Vector3.zero); ulong num = (((Object)(object)((NetworkBehaviour)body).NetworkObject != (Object)null) ? ((NetworkBehaviour)body).NetworkObject.NetworkObjectId : 0); if (num == 0L) { return; } DeadBodyInfo componentInParent = ((Component)body).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { int playerObjectId = componentInParent.playerObjectId; PlayerControllerB val2 = ((StartOfRound.Instance?.allPlayerScripts != null && playerObjectId >= 0 && playerObjectId < StartOfRound.Instance.allPlayerScripts.Length) ? StartOfRound.Instance.allPlayerScripts[playerObjectId] : null); if ((Object)(object)val2 != (Object)null && val2.isPlayerDead && GeneralUtil.GetPlayerLastDeathType(playerObjectId) != PlayerDeathType.Masked) { string signature = $"deadBodyInfo:{playerObjectId}:{num}"; if (ShouldApplyRefresh(instanceID, signature)) { GeneralUtil.SetPlayerDeathState(playerObjectId, PlayerDeathType.Body, num, val, hasPosition: true, reason + ":DeadBodyInfo", skipDeathTimeUpdate: true); ReviveNetworkSync.SendPlayerDeathStateToClients(playerObjectId); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[ReviveTrigger][DIAG] Try to ForceRefreshPlayer: playerId={playerObjectId} bodyId={num} reason={reason}"); } ReviveInteractTriggerManager.ForceRefreshPlayer(playerObjectId, reason + ":DeadBodyInfo"); } return; } } if (GeneralUtil.TryGetBodyOwner(body, out var targetPlayerClientId, out var _) && GeneralUtil.GetPlayerLastDeathType(targetPlayerClientId) != PlayerDeathType.Masked) { string signature2 = $"owner:{targetPlayerClientId}:{num}"; if (ShouldApplyRefresh(instanceID, signature2)) { GeneralUtil.SetPlayerDeathState(targetPlayerClientId, PlayerDeathType.Body, num, val, hasPosition: true, reason + ":ownerResolved", skipDeathTimeUpdate: true); ReviveNetworkSync.SendPlayerDeathStateToClients(targetPlayerClientId); ReviveInteractTriggerManager.ForceRefreshPlayer(targetPlayerClientId, reason + ":ownerResolved"); } return; } PlayerReviveInfo playerReviveInfo = null; float num2 = 49f; foreach (PlayerReviveInfo playerInfo in ModState.PlayerInfos) { if (playerInfo != null && playerInfo.LastDeathType == PlayerDeathType.Body && playerInfo.HasLastDeathPosition) { Vector3 val3 = playerInfo.LastDeathPosition - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; playerReviveInfo = playerInfo; } } } if (playerReviveInfo != null) { string signature3 = $"deathPos:{playerReviveInfo.PlayerClientId}:{num}"; if (ShouldApplyRefresh(instanceID, signature3)) { GeneralUtil.SetPlayerDeathState(playerReviveInfo.PlayerClientId, PlayerDeathType.Body, num, val, hasPosition: true, reason + ":matchedByDeathPos", skipDeathTimeUpdate: true); ReviveNetworkSync.SendPlayerDeathStateToClients(playerReviveInfo.PlayerClientId); ReviveInteractTriggerManager.ForceRefreshPlayer(playerReviveInfo.PlayerClientId, reason + ":matchedByDeathPos"); } } } catch (Exception arg) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[RagdollGrabbableObjectPatch.TryRefreshBodySource] {arg}"); } } } private static bool ShouldApplyRefresh(int bodyInstanceId, string signature) { try { if (string.IsNullOrWhiteSpace(signature)) { return true; } if (LastRefreshSignatureByBody.TryGetValue(bodyInstanceId, out var value) && value == signature) { return false; } LastRefreshSignatureByBody[bodyInstanceId] = signature; return true; } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[RagdollGrabbableObjectPatch.ShouldApplyRefresh] {arg}"); } return false; } } [HarmonyPatch("OnDestroy")] [HarmonyPostfix] private static void OnDestroyPostfix(RagdollGrabbableObject __instance) { try { if (!((Object)(object)__instance == (Object)null)) { LastRefreshSignatureByBody.Remove(((Object)__instance).GetInstanceID()); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[RagdollGrabbableObjectPatch.OnDestroyPostfix] {arg}"); } } } } [HarmonyPatch(typeof(ShipTeleporter))] public static class ShipTeleporterPatch { [HarmonyPostfix] [HarmonyPatch("beamUpPlayer")] private static void Postfix_beamUpPlayer(ShipTeleporter __instance) { try { if (!__instance.isInverseTeleporter) { PlayerControllerB val = StartOfRound.Instance?.mapScreen?.targetedPlayer; if ((Object)(object)val != (Object)null && val.isPlayerDead) { GeneralUtil.TryMarkTeleportedIfDead((int)val.playerClientId); } } } catch (Exception arg) { Debug.LogError((object)$"[UltimateRevive] ShipTeleporterPatch exception: {arg}"); } } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatch { [CompilerGenerated] private sealed class d__5 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; case 1: <>1__state = -1; try { ReviveNetwork.TryRegisterHandlers(); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[StartOfRoundPatch.RegisterNetworkDelayed] {arg}"); } } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(StartOfRound __instance) { try { ModState.Active = true; ReviveNetwork.RegisterNetworkHandlers(); ReviveNetwork.TryRegisterHandlers(); ReviveNetwork.SendConfigToClients(); ReviveInteractTriggerManager.WarmUpCache(); if ((Object)(object)__instance != (Object)null) { ((MonoBehaviour)__instance).StartCoroutine(RegisterNetworkDelayed()); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[StartOfRoundPatch.StartPostfix] {arg}"); } } } [HarmonyPatch("OnClientConnect")] [HarmonyPostfix] private static void OnClientConnectPostfix(StartOfRound __instance) { try { ModState.Active = true; if ((Object)(object)__instance != (Object)null) { ((MonoBehaviour)__instance).StartCoroutine(RegisterNetworkDelayed()); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[StartOfRoundPatch.OnClientConnectPostfix] {arg}"); } } } [HarmonyPatch("OnLocalDisconnect")] [HarmonyPostfix] private static void OnLocalDisconnectPostfix() { try { ReviveNetwork.UnregisterNetworkHandlers(); ReviveNetwork.UnregisterHandlers(); ModState.Reset(); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[StartOfRoundPatch.OnLocalDisconnectPostfix] {arg}"); } } } [HarmonyPatch("openingDoorsSequence")] [HarmonyPrefix] private static void OpeningDoorsSequencePrefix() { try { if (ModState.Active) { GeneralUtil.ResetAllPlayerInfos(); MaskedConversionTracker.Reset(); DeathMarkerManager.Reset(); ReviveInteractTriggerManager.Reset(); GeneralUtil.InitializeReviveLimits(); ReviveNetwork.SyncConfigForNewClients(); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[StartOfRoundPatch.OpeningDoorsSequencePrefix] {arg}"); } } } [HarmonyPatch("EndOfGame")] [HarmonyPrefix] private static void EndOfGamePrefix() { try { if (ModState.Active) { GeneralUtil.ResetAllPlayerInfos(); MaskedConversionTracker.Reset(); DeathMarkerManager.Reset(); ReviveInteractTriggerManager.Reset(); } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[StartOfRoundPatch.EndOfGamePrefix] {arg}"); } } } [IteratorStateMachine(typeof(d__5))] private static IEnumerator RegisterNetworkDelayed() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__5(0); } } }