Decompiled source of DeathBodyOutline v0.2.0

DeathBodyOutline.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppTMPro;
using Kp0hyc.Labyrinthine.Shared;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(DeathBodyOutline), "Death Body Outline", "0.2.0", "kp0hyc", null)]
[assembly: MelonGame("Valko Game Studios", "Labyrinthine")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("DeathBodyOutline")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DeathBodyOutline")]
[assembly: AssemblyTitle("DeathBodyOutline")]
[assembly: AssemblyVersion("1.0.0.0")]
public class DeathBodyOutline : MelonMod
{
	private sealed class BodyCloneResult
	{
		public GameObject Body { get; init; }

		public GameObject Nameplate { get; init; }

		public string Source { get; init; }

		public string LocalHeadCompletion { get; init; }
	}

	private readonly struct RendererMaterialCopyResult
	{
		public readonly int SourceRenderers;

		public readonly int TargetRenderers;

		public readonly int RenderersCopied;

		public readonly int MaterialsCopied;

		public RendererMaterialCopyResult(int sourceRenderers, int targetRenderers, int renderersCopied, int materialsCopied)
		{
			SourceRenderers = sourceRenderers;
			TargetRenderers = targetRenderers;
			RenderersCopied = renderersCopied;
			MaterialsCopied = materialsCopied;
		}

		public override string ToString()
		{
			return "sourceRenderers=" + SourceRenderers.ToString(CultureInfo.InvariantCulture) + " targetRenderers=" + TargetRenderers.ToString(CultureInfo.InvariantCulture) + " renderersCopied=" + RenderersCopied.ToString(CultureInfo.InvariantCulture) + " materialsCopied=" + MaterialsCopied.ToString(CultureInfo.InvariantCulture);
		}
	}

	private const string HarmonyId = "kp0hyc.deathbodyoutline";

	private const float DuplicateGuardSeconds = 2f;

	private const int VisualModeDisabled = 0;

	private const int VisualModeFinalDeathOnly = 1;

	private const int VisualModeEveryDeath = 2;

	private const string DeadNameSuffix = " ☠";

	private const float NameplateHeightAboveBodyRoot = 1.08f;

	private const float NameplateHeightAboveHead = 0.42f;

	private const float MaxNameplateBoundsHorizontalOffset = 1.35f;

	private static readonly Color DeadNameColor = new Color(1f, 0.18f, 0.14f, 1f);

	private static readonly Dictionary<string, float> s_lastOutlineTimeByKey = new Dictionary<string, float>(StringComparer.Ordinal);

	private static readonly List<GameObject> s_outlines = new List<GameObject>();

	private static MelonPreferences_Entry<bool> s_verboseLogs;

	private static MelonPreferences_Entry<int> s_visualMode;

	private static Harmony s_harmony;

	private static GameObject s_root;

	private static void CreateBodyOutline(string label, object player, GameObject sourceBody, string sourceContext)
	{
		BodyCloneResult bodyCloneResult = CreateFrozenBodyClone(label, player, sourceBody);
		if (!((Object)(object)bodyCloneResult.Body == (Object)null))
		{
			if ((Object)(object)bodyCloneResult.Nameplate != (Object)null)
			{
				s_outlines.Add(bodyCloneResult.Nameplate);
			}
			s_outlines.Add(bodyCloneResult.Body);
			LogVerbose("body clone spawned label=" + label + " sourceContext=" + sourceContext + " source=" + bodyCloneResult.Source + " body=" + DescribeGameObject(bodyCloneResult.Body) + " nameplate=" + DescribeGameObject(bodyCloneResult.Nameplate));
		}
	}

	private static BodyCloneResult CreateFrozenBodyClone(string label, object player, GameObject sourceBody)
	{
		//IL_000d: 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)
		GameObject root = GetRoot();
		GameObject val = Object.Instantiate<GameObject>(sourceBody, sourceBody.transform.position, sourceBody.transform.rotation);
		((Object)val).name = "DeathBodyOutline_Body_" + SanitizeName(label) + "_" + s_outlines.Count.ToString(CultureInfo.InvariantCulture);
		val.transform.SetParent(root.transform, true);
		val.SetActive(true);
		int num = FreezeBodyClone(val);
		string text = TryCompleteLocalHeadlessBody(label, player, sourceBody, val);
		GameObject val2 = TryCloneNameplate(player, label, val);
		LogVerbose("body clone created label=" + label + " stripped=" + num.ToString(CultureInfo.InvariantCulture) + " localHeadCompletion=" + text + " source=" + DescribeGameObject(sourceBody) + " clone=" + DescribeGameObject(val) + " nameplate=" + DescribeGameObject(val2));
		return new BodyCloneResult
		{
			Body = val,
			Nameplate = val2,
			Source = DescribeGameObject(sourceBody),
			LocalHeadCompletion = text
		};
	}

	private static void SetLayerRecursive(GameObject gameObject, int layer)
	{
		if ((Object)(object)gameObject == (Object)null)
		{
			return;
		}
		foreach (Transform componentsInChild in gameObject.GetComponentsInChildren<Transform>(true))
		{
			if ((Object)(object)componentsInChild != (Object)null)
			{
				((Component)componentsInChild).gameObject.layer = layer;
			}
		}
	}

	private static int FreezeBodyClone(GameObject clone)
	{
		int num = 0;
		foreach (Animator componentsInChild in clone.GetComponentsInChildren<Animator>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null))
			{
				componentsInChild.keepAnimatorStateOnDisable = true;
				componentsInChild.speed = 0f;
				((Behaviour)componentsInChild).enabled = false;
				num++;
			}
		}
		foreach (Renderer componentsInChild2 in clone.GetComponentsInChildren<Renderer>(true))
		{
			if ((Object)(object)componentsInChild2 != (Object)null)
			{
				componentsInChild2.enabled = true;
			}
		}
		foreach (Collider componentsInChild3 in clone.GetComponentsInChildren<Collider>(true))
		{
			if (!((Object)(object)componentsInChild3 == (Object)null))
			{
				componentsInChild3.enabled = false;
				num++;
			}
		}
		foreach (Rigidbody componentsInChild4 in clone.GetComponentsInChildren<Rigidbody>(true))
		{
			if (!((Object)(object)componentsInChild4 == (Object)null))
			{
				componentsInChild4.isKinematic = true;
				componentsInChild4.detectCollisions = false;
				num++;
			}
		}
		foreach (Light componentsInChild5 in clone.GetComponentsInChildren<Light>(true))
		{
			if (!((Object)(object)componentsInChild5 == (Object)null))
			{
				((Behaviour)componentsInChild5).enabled = false;
				num++;
			}
		}
		foreach (Behaviour componentsInChild6 in clone.GetComponentsInChildren<Behaviour>(true))
		{
			if ((Object)(object)componentsInChild6 == (Object)null || componentsInChild6 is Animator || componentsInChild6 is Light)
			{
				continue;
			}
			string text = ((object)componentsInChild6).GetType().FullName ?? ((object)componentsInChild6).GetType().Name;
			if (text.IndexOf("Renderer", StringComparison.OrdinalIgnoreCase) < 0)
			{
				try
				{
					componentsInChild6.enabled = false;
					num++;
				}
				catch (Exception ex)
				{
					LogVerbose("body behaviour disable failed type=" + text + " error=" + ex.GetType().Name);
				}
			}
		}
		return num;
	}

	private static GameObject ResolveCurrentVisualBodyObject(object player, string context)
	{
		object value = GetFieldOrProp(player, "CurrentBody") ?? GetFieldOrProp(player, "_CurrentBody_k__BackingField");
		bool? boolValue = GetBoolValue(GetFieldOrProp(player, "IsCurrentBodyHeadless") ?? GetFieldOrProp(player, "_IsCurrentBodyHeadless_k__BackingField"));
		object obj = GetFieldOrProp(player, "AnimHandler") ?? GetFieldOrProp(player, "animHandler");
		object fieldOrProp = GetFieldOrProp(obj, "VisualObj");
		GameObject val = (GameObject)(((fieldOrProp is GameObject) ? fieldOrProp : null) ?? ((object)/*isinst with value type is only supported in some contexts*/));
		object value2 = GetFieldOrProp(obj, "CurrentState") ?? GetFieldOrProp(obj, "_currentState");
		bool flag = HasRenderer(val);
		LogVerbose("body source context=" + context + " source=AnimationHandler.VisualObj currentBody=" + FormatObjectValue(value) + " isCurrentHeadless=" + FormatObjectValue(boolValue) + " animState=" + FormatObjectValue(value2) + " hasRenderer=" + flag.ToString(CultureInfo.InvariantCulture) + " visual=" + DescribeGameObject(val));
		if (!flag)
		{
			return null;
		}
		return val;
	}

	private static bool LooksHeadless(GameObject gameObject)
	{
		if ((Object)(object)gameObject == (Object)null)
		{
			return false;
		}
		Transform val = gameObject.transform;
		while ((Object)(object)val != (Object)null)
		{
			if (((Object)val).name.IndexOf("Headless", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static Vector3 ResolveHeadCenter(GameObject gameObject)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: 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_009e: 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_00ad: 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_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_00e2: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)gameObject == (Object)null)
		{
			return Vector3.zero;
		}
		foreach (Transform componentsInChild in gameObject.GetComponentsInChildren<Transform>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null))
			{
				string text = ((Object)componentsInChild).name.ToLowerInvariant();
				if (!text.Contains("headless") && (text == "head" || text.EndsWith(":head", StringComparison.Ordinal) || text.EndsWith("_head", StringComparison.Ordinal)))
				{
					return componentsInChild.position;
				}
			}
		}
		if (!TryComputeRendererBounds(gameObject, out var bounds))
		{
			return gameObject.transform.position + Vector3.up * 1.6f;
		}
		return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y, ((Bounds)(ref bounds)).center.z);
	}

	public override void OnInitializeMelon()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		MelonPreferences_Category obj = MelonPreferences.CreateCategory("DeathBodyOutline");
		s_visualMode = obj.CreateEntry<int>("VisualMode", 1, "Visual Mode", (string)null, false, false, (ValueValidator)null, (string)null);
		s_verboseLogs = obj.CreateEntry<bool>("VerboseLogs", false, "Verbose Logs", (string)null, false, false, (ValueValidator)null, (string)null);
		s_harmony = new Harmony("kp0hyc.deathbodyoutline");
		PatchFinalDeathRpc();
		((MelonBase)this).LoggerInstance.Msg("DeathBodyOutline initialized.");
	}

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		ClearOutlines("scene loaded " + sceneName);
	}

	private void PatchFinalDeathRpc()
	{
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Expected O, but got Unknown
		Type type = FindType("Death") ?? FindType("ValkoGames.Labyrinthine.Players.Death");
		if (type == null)
		{
			((MelonBase)this).LoggerInstance.Warning("Death type was not found; outlines will not spawn.");
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(type, "UserCode_RpcOnBleedOutTimeout__SByte__Boolean", new Type[2]
		{
			typeof(sbyte),
			typeof(bool)
		}, (Type[])null);
		if (methodInfo == null)
		{
			((MelonBase)this).LoggerInstance.Warning("Final-death RPC method was not found; outlines will not spawn.");
			return;
		}
		MethodInfo methodInfo2 = AccessTools.Method(typeof(DeathBodyOutline), "OnFinalDeathRpcPrefix", (Type[])null, (Type[])null);
		s_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		LogVerbose("patched " + type.FullName + "." + methodInfo.Name);
	}

	private static void OnFinalDeathRpcPrefix(object __instance, sbyte livesLeft, bool respawn)
	{
		try
		{
			SpawnOutlineForDeath(__instance, livesLeft, respawn);
		}
		catch (Exception ex)
		{
			MelonLogger.Warning("[Death Body Outline] final death outline failed: " + ex.GetType().Name + " " + ex.Message);
		}
	}

	private static void SpawnOutlineForDeath(object deathInstance, sbyte livesLeft, bool respawn)
	{
		int visualMode = GetVisualMode();
		object fieldOrProp = GetFieldOrProp(deathInstance, "playerNetworkSync");
		if (fieldOrProp == null)
		{
			LogVerbose("final death ignored: playerNetworkSync missing");
			return;
		}
		string playerKey = GetPlayerKey(fieldOrProp);
		switch (visualMode)
		{
		case 0:
			LogVerbose("final death ignored: visual mode disabled key=" + playerKey + " livesLeft=" + livesLeft + " respawn=" + respawn);
			return;
		case 1:
			if (!IsOutOfLivesDeath(livesLeft, respawn))
			{
				LogVerbose("final death ignored by visual mode key=" + playerKey + " mode=" + visualMode.ToString(CultureInfo.InvariantCulture) + " livesLeft=" + livesLeft + " respawn=" + respawn);
				return;
			}
			break;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (s_lastOutlineTimeByKey.TryGetValue(playerKey, out var value) && realtimeSinceStartup - value < 2f)
		{
			LogVerbose("duplicate final death ignored key=" + playerKey + " age=" + (realtimeSinceStartup - value).ToString("0.00", CultureInfo.InvariantCulture));
			return;
		}
		s_lastOutlineTimeByKey[playerKey] = realtimeSinceStartup;
		string playerLabel = GetPlayerLabel(fieldOrProp);
		LogVerbose("final death source decision start key=" + playerKey + " label=" + playerLabel + " mode=" + visualMode.ToString(CultureInfo.InvariantCulture) + " livesLeft=" + livesLeft + " respawn=" + respawn);
		GameObject val = ResolveCurrentVisualBodyObject(fieldOrProp, "final-time");
		if ((Object)(object)val != (Object)null)
		{
			LogVerbose("final death source selected key=" + playerKey + " label=" + playerLabel + " sourceMode=final-time body=" + DescribeGameObject(val));
			CreateBodyOutline(playerLabel, fieldOrProp, val, "final-time");
			LogVerbose("outline spawned key=" + playerKey + " label=" + playerLabel + " livesLeft=" + livesLeft + " respawn=" + respawn + " source=" + DescribeGameObject(val));
		}
		else
		{
			LogVerbose("final death source selected key=" + playerKey + " label=" + playerLabel + " sourceMode=none player=" + DescribeObject(fieldOrProp));
		}
	}

	private static bool IsOutOfLivesDeath(sbyte livesLeft, bool respawn)
	{
		if (livesLeft > 0)
		{
			return !respawn;
		}
		return true;
	}

	private static int GetVisualMode()
	{
		int num = ((s_visualMode == null) ? 1 : s_visualMode.Value);
		if (num < 0)
		{
			return 0;
		}
		if (num > 2)
		{
			return 2;
		}
		return num;
	}

	private static string TryCompleteLocalHeadlessBody(string label, object player, GameObject sourceBody, GameObject clone)
	{
		if (!LooksHeadless(sourceBody))
		{
			return "source-headed";
		}
		if (!IsLocalPlayer(player))
		{
			LogVerbose("local head completion skipped: non-local selected source is headless label=" + label + " player=" + DescribeObject(player) + " source=" + DescribeGameObject(sourceBody) + " clone=" + DescribeGameObject(clone));
			return "source-headless local=False";
		}
		GameObject val = ResolveLocalHeadSource(player, sourceBody);
		if ((Object)(object)val == (Object)null)
		{
			LogVerbose("local head completion skipped: explicit head source missing label=" + label + " player=" + DescribeObject(player) + " source=" + DescribeGameObject(sourceBody) + " clone=" + DescribeGameObject(clone));
			return "source-headless local=True headSource=none";
		}
		int num = CompleteHeadlessBodyRendererFromSource(val, clone);
		int num2 = CopyLocalHairChildrenFromSource(val, clone);
		return "source-headless local=True headSource=" + ((Object)val).name + " completedBody=" + num.ToString(CultureInfo.InvariantCulture) + " copiedHair=" + num2.ToString(CultureInfo.InvariantCulture) + " label=" + label;
	}

	private static bool IsLocalPlayer(object player)
	{
		if (TryConvertBool(GetFieldOrProp(player, "isOwned") ?? GetFieldOrProp(player, "IsOwned") ?? GetFieldOrProp(player, "_isOwned_k__BackingField") ?? GetFieldOrProp(player, "_IsOwned_k__BackingField"), out var result))
		{
			return result;
		}
		Type type = FindType("PlayerNetworkSync");
		object obj = GetStaticFieldOrProp(type, "instance") ?? GetStaticFieldOrProp(type, "Instance");
		object fieldOrProp = GetFieldOrProp(player, "gameObject");
		GameObject val = (GameObject)((fieldOrProp is GameObject) ? fieldOrProp : null);
		object fieldOrProp2 = GetFieldOrProp(obj, "gameObject");
		GameObject val2 = (GameObject)((fieldOrProp2 is GameObject) ? fieldOrProp2 : null);
		bool result2 = obj == player || ((Object)(object)val != (Object)null && val == val2);
		LogVerbose("local player ownership field missing; instance match=" + result2.ToString(CultureInfo.InvariantCulture) + " player=" + DescribeObject(player) + " instance=" + DescribeObject(obj));
		return result2;
	}

	private static GameObject ResolveLocalHeadSource(object player, GameObject sourceBody)
	{
		object value = GetFieldOrProp(player, "CurrentBody") ?? GetFieldOrProp(player, "_CurrentBody_k__BackingField");
		if (!TryConvertLong(value, out var result) || result < 0 || result > int.MaxValue)
		{
			LogVerbose("local head source missing: currentBody invalid value=" + FormatObjectValue(value));
			return null;
		}
		object indexedObject = GetIndexedObject(GetFieldOrProp(player, "playerBodies"), (int)result);
		object fieldOrProp = GetFieldOrProp(indexedObject, "remote");
		GameObject val = (GameObject)((fieldOrProp is GameObject) ? fieldOrProp : null);
		LogVerbose("local head source lookup currentBody=" + result.ToString(CultureInfo.InvariantCulture) + " container=" + DescribeObject(indexedObject) + " remote=" + DescribeGameObject(val));
		if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)sourceBody || LooksHeadless(val) || !HasRenderer(val))
		{
			return null;
		}
		return val;
	}

	private static object GetIndexedObject(object values, int index)
	{
		if (values == null || index < 0)
		{
			return null;
		}
		if (values is Array array && index < array.Length)
		{
			return array.GetValue(index);
		}
		if (!TryConvertLong(GetFieldOrProp(values, "Count"), out var result))
		{
			TryConvertLong(GetFieldOrProp(values, "Length"), out result);
		}
		if (index >= result)
		{
			return null;
		}
		MethodInfo method = values.GetType().GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (method == null)
		{
			return null;
		}
		try
		{
			return method.Invoke(values, new object[1] { index });
		}
		catch
		{
			return null;
		}
	}

	private static int CompleteHeadlessBodyRendererFromSource(GameObject headSource, GameObject clone)
	{
		//IL_0128: 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_0156: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		foreach (Renderer item in ((IEnumerable<Renderer>)headSource.GetComponentsInChildren<Renderer>(true)).OrderBy((Renderer renderer) => GetDepth(((Component)renderer).transform)))
		{
			if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).transform == (Object)null || (Object)(object)((Component)item).transform == (Object)(object)headSource.transform || IsHairTransform(headSource.transform, ((Component)item).transform))
			{
				continue;
			}
			string relativePath = GetRelativePath(headSource.transform, ((Component)item).transform);
			Transform val = FindChildByPath(clone.transform, relativePath);
			if ((Object)(object)val == (Object)null || !HasRenderer(((Component)val).gameObject))
			{
				continue;
			}
			Transform val2 = val.parent ?? clone.transform;
			GameObject val3 = Object.Instantiate<GameObject>(((Component)((Component)item).transform).gameObject);
			((Object)val3).name = ((Object)((Component)((Component)item).transform).gameObject).name + "_DeathCompletedBody";
			val3.transform.SetParent(val2, false);
			val3.transform.localPosition = ((Component)item).transform.localPosition;
			val3.transform.localRotation = ((Component)item).transform.localRotation;
			val3.transform.localScale = ((Component)item).transform.localScale;
			SetLayerRecursive(val3, clone.layer);
			string text = RemapSkinnedMeshBones(val3, headSource.transform, clone.transform);
			RendererMaterialCopyResult rendererMaterialCopyResult = CopyRendererMaterialsFromClone(((Component)val).gameObject, val3);
			if (rendererMaterialCopyResult.MaterialsCopied <= 0)
			{
				Object.Destroy((Object)(object)val3);
				LogVerbose("local body completion skipped path=" + relativePath + " reason=no-live-materials source=" + DescribeGameObject(headSource) + " cloneTarget=" + DescribeGameObject(((Component)val).gameObject));
				continue;
			}
			foreach (Renderer componentsInChild in val3.GetComponentsInChildren<Renderer>(true))
			{
				if ((Object)(object)componentsInChild != (Object)null)
				{
					componentsInChild.enabled = true;
				}
			}
			foreach (Renderer componentsInChild2 in ((Component)val).gameObject.GetComponentsInChildren<Renderer>(true))
			{
				if ((Object)(object)componentsInChild2 != (Object)null)
				{
					componentsInChild2.enabled = false;
				}
			}
			num++;
			LogVerbose("local body completion copied path=" + relativePath + " disabledClone=" + GetRelativePath(clone.transform, val) + " parent=" + GetRelativePath(clone.transform, val2) + " materials=" + rendererMaterialCopyResult.ToString() + " " + text);
		}
		LogVerbose("local body completion summary source=" + DescribeGameObject(headSource) + " clone=" + DescribeGameObject(clone) + " completed=" + num.ToString(CultureInfo.InvariantCulture));
		return num;
	}

	private static int CopyLocalHairChildrenFromSource(GameObject headSource, GameObject clone)
	{
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		List<Transform> list = new List<Transform>();
		foreach (Renderer item in ((IEnumerable<Renderer>)headSource.GetComponentsInChildren<Renderer>(true)).OrderBy((Renderer renderer) => GetDepth(((Component)renderer).transform)))
		{
			Transform transform = (((Object)(object)item == (Object)null) ? null : ((Component)item).transform);
			if (!((Object)(object)transform == (Object)null) && !((Object)(object)transform == (Object)(object)headSource.transform) && IsHairTransform(headSource.transform, transform) && !list.Any((Transform existing) => IsChildOf(transform, existing)))
			{
				list.Add(transform);
			}
		}
		int num = 0;
		foreach (Transform item2 in list)
		{
			Transform val = FindMatchingCloneParent(headSource.transform, clone.transform, item2.parent) ?? FindHeadAttachParent(clone.transform);
			if ((Object)(object)val == (Object)null)
			{
				LogVerbose("local hair copy skipped: target parent missing source=" + GetRelativePath(headSource.transform, item2));
				continue;
			}
			GameObject obj = Object.Instantiate<GameObject>(((Component)item2).gameObject);
			((Object)obj).name = ((Object)((Component)item2).gameObject).name + "_DeathHair";
			obj.transform.SetParent(val, false);
			obj.transform.localPosition = item2.localPosition;
			obj.transform.localRotation = item2.localRotation;
			obj.transform.localScale = item2.localScale;
			SetLayerRecursive(obj, clone.layer);
			string text = RemapSkinnedMeshBones(obj, headSource.transform, clone.transform);
			foreach (Renderer componentsInChild in obj.GetComponentsInChildren<Renderer>(true))
			{
				if ((Object)(object)componentsInChild != (Object)null)
				{
					componentsInChild.enabled = true;
				}
			}
			num++;
			LogVerbose("local hair copied path=" + GetRelativePath(headSource.transform, item2) + " parent=" + GetRelativePath(clone.transform, val) + " " + text);
		}
		LogVerbose("local hair copy summary source=" + DescribeGameObject(headSource) + " clone=" + DescribeGameObject(clone) + " selected=" + list.Count.ToString(CultureInfo.InvariantCulture) + " copied=" + num.ToString(CultureInfo.InvariantCulture));
		return num;
	}

	private static RendererMaterialCopyResult CopyRendererMaterialsFromClone(GameObject source, GameObject target)
	{
		Il2CppArrayBase<Renderer> componentsInChildren = source.GetComponentsInChildren<Renderer>(true);
		Il2CppArrayBase<Renderer> componentsInChildren2 = target.GetComponentsInChildren<Renderer>(true);
		int num = 0;
		int num2 = 0;
		int num3 = Math.Min(componentsInChildren.Length, componentsInChildren2.Length);
		for (int i = 0; i < num3; i++)
		{
			Renderer val = componentsInChildren[i];
			Renderer val2 = componentsInChildren2[i];
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
			{
				Il2CppReferenceArray<Material> sharedMaterials = val.sharedMaterials;
				if (sharedMaterials != null && ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length != 0)
				{
					val2.sharedMaterials = sharedMaterials;
					num++;
					num2 += ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length;
				}
			}
		}
		return new RendererMaterialCopyResult(componentsInChildren.Length, componentsInChildren2.Length, num, num2);
	}

	private static bool IsHairTransform(Transform root, Transform transform)
	{
		if ((Object)(object)transform == (Object)null || IsUnderLimbTransform(transform))
		{
			return false;
		}
		if (!GetRelativePath(root, transform).ToLowerInvariant().Contains("hair"))
		{
			return false;
		}
		return true;
	}

	private static bool IsUnderLimbTransform(Transform transform)
	{
		Transform val = transform;
		while ((Object)(object)val != (Object)null)
		{
			string text = ((Object)val).name.ToLowerInvariant();
			if (text.Contains("hand") || text.Contains("forearm") || text.Contains("leftarm") || text.Contains("rightarm"))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static GameObject TryCloneNameplate(object player, string label, GameObject bodyClone)
	{
		//IL_0044: 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)
		object fieldOrProp = GetFieldOrProp(player, "nameplateCanvas");
		object canvas = ((fieldOrProp is Canvas) ? fieldOrProp : null);
		object fieldOrProp2 = GetFieldOrProp(player, "namePlate");
		Component namePlate = (Component)((fieldOrProp2 is Component) ? fieldOrProp2 : null);
		GameObject val = ResolveNameplateSource((Canvas)canvas, namePlate);
		if ((Object)(object)val == (Object)null)
		{
			LogVerbose("nameplate clone skipped: source missing");
			return null;
		}
		GameObject val2 = Object.Instantiate<GameObject>(val, val.transform.position, val.transform.rotation);
		((Object)val2).name = "DeathBodyOutline_Nameplate_" + SanitizeName(label);
		val2.transform.SetParent(bodyClone.transform, true);
		val2.SetActive(true);
		int num = PrepareNameplateClone(val2, label);
		RepositionNameplateClone(val2, bodyClone);
		LogVerbose("nameplate cloned changed=" + num.ToString(CultureInfo.InvariantCulture) + " source=" + DescribeGameObject(val) + " clone=" + DescribeGameObject(val2));
		return val2;
	}

	private static GameObject ResolveNameplateSource(Canvas canvas, Component namePlate)
	{
		if ((Object)(object)namePlate != (Object)null)
		{
			Canvas componentInParent = namePlate.GetComponentInParent<Canvas>(true);
			if ((Object)(object)componentInParent != (Object)null && HasTmpText(((Component)componentInParent).gameObject))
			{
				return ((Component)componentInParent).gameObject;
			}
			if (HasTmpText(namePlate.gameObject))
			{
				return namePlate.gameObject;
			}
		}
		if ((Object)(object)canvas != (Object)null && HasTmpText(((Component)canvas).gameObject))
		{
			return ((Component)canvas).gameObject;
		}
		if (!((Object)(object)namePlate != (Object)null))
		{
			if (!((Object)(object)canvas != (Object)null))
			{
				return null;
			}
			return ((Component)canvas).gameObject;
		}
		return namePlate.gameObject;
	}

	private static void RepositionNameplateClone(GameObject nameplate, GameObject bodyClone)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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_0042: 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_0127: 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_006b: 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_0078: 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_008e: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)nameplate == (Object)null || (Object)(object)bodyClone == (Object)null)
		{
			return;
		}
		Vector3 position = bodyClone.transform.position;
		Vector3 val = ResolveHeadCenter(bodyClone);
		bool flag = IsSaneNameplateHeadAnchor(bodyClone, val);
		bool flag2 = false;
		string text = "{none}";
		Bounds bounds;
		if (flag)
		{
			((Vector3)(ref position))..ctor(val.x, position.y, val.z);
		}
		else if (TryComputeRendererBounds(bodyClone, out bounds))
		{
			float num = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).center.z));
			text = "center=" + FormatVector(((Bounds)(ref bounds)).center) + " size=" + FormatVector(((Bounds)(ref bounds)).size) + " horizontalOffset=" + num.ToString("0.00", CultureInfo.InvariantCulture);
			if (num <= 1.35f)
			{
				((Vector3)(ref position))..ctor(((Bounds)(ref bounds)).center.x, position.y, ((Bounds)(ref bounds)).center.z);
				flag2 = true;
			}
		}
		float num2 = bodyClone.transform.position.y + 1.08f;
		float val2 = (flag ? (val.y + 0.42f) : num2);
		Vector3 val3 = default(Vector3);
		((Vector3)(ref val3))..ctor(position.x, Math.Max(num2, val2), position.z);
		Vector3 position2 = nameplate.transform.position;
		nameplate.transform.position = val3;
		LogVerbose("nameplate reposition before=" + FormatVector(position2) + " after=" + FormatVector(val3) + " root=" + FormatVector(bodyClone.transform.position) + " head=" + FormatVector(val) + " usedHeadCenter=" + flag + " usedBoundsCenter=" + flag2 + " heightAboveRoot=" + 1.08f.ToString("0.00", CultureInfo.InvariantCulture) + " heightAboveHead=" + 0.42f.ToString("0.00", CultureInfo.InvariantCulture) + " bounds=" + text);
	}

	private static bool IsSaneNameplateHeadAnchor(GameObject bodyClone, Vector3 headCenter)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)bodyClone == (Object)null || headCenter == Vector3.zero)
		{
			return false;
		}
		return Vector2.Distance(new Vector2(bodyClone.transform.position.x, bodyClone.transform.position.z), new Vector2(headCenter.x, headCenter.z)) <= 1.35f;
	}

	private static int PrepareNameplateClone(GameObject clone, string label)
	{
		int num = 0;
		foreach (Transform componentsInChild in clone.GetComponentsInChildren<Transform>(true))
		{
			if ((Object)(object)componentsInChild != (Object)null)
			{
				((Component)componentsInChild).gameObject.SetActive(true);
			}
		}
		foreach (Canvas componentsInChild2 in clone.GetComponentsInChildren<Canvas>(true))
		{
			if (!((Object)(object)componentsInChild2 == (Object)null))
			{
				((Behaviour)componentsInChild2).enabled = true;
				num++;
			}
		}
		foreach (CanvasGroup componentsInChild3 in clone.GetComponentsInChildren<CanvasGroup>(true))
		{
			if (!((Object)(object)componentsInChild3 == (Object)null))
			{
				componentsInChild3.alpha = 1f;
				componentsInChild3.interactable = false;
				componentsInChild3.blocksRaycasts = false;
				num++;
			}
		}
		foreach (TMP_Text componentsInChild4 in clone.GetComponentsInChildren<TMP_Text>(true))
		{
			if (!((Object)(object)componentsInChild4 == (Object)null))
			{
				try
				{
					ApplyDeadNameText(componentsInChild4, label);
					((Component)componentsInChild4).gameObject.SetActive(true);
					componentsInChild4.ForceMeshUpdate(false, false);
					num++;
				}
				catch (Exception ex)
				{
					LogVerbose("nameplate TMP update failed type=" + ((object)componentsInChild4).GetType().FullName + " error=" + ex.GetType().Name);
				}
			}
		}
		int num2 = 0;
		foreach (Graphic componentsInChild5 in clone.GetComponentsInChildren<Graphic>(true))
		{
			if (!((Object)(object)componentsInChild5 == (Object)null) && !((Object)(object)((Component)componentsInChild5).GetComponent<TMP_Text>() != (Object)null))
			{
				((Behaviour)componentsInChild5).enabled = false;
				((Component)componentsInChild5).gameObject.SetActive(false);
				num2++;
			}
		}
		LogVerbose("nameplate prepare label=" + label + " canvases=" + clone.GetComponentsInChildren<Canvas>(true).Length.ToString(CultureInfo.InvariantCulture) + " canvasGroups=" + clone.GetComponentsInChildren<CanvasGroup>(true).Length.ToString(CultureInfo.InvariantCulture) + " tmpTexts=" + clone.GetComponentsInChildren<TMP_Text>(true).Length.ToString(CultureInfo.InvariantCulture) + " disabledGraphics=" + num2.ToString(CultureInfo.InvariantCulture) + " runtimeBehavioursKept=True");
		return num;
	}

	private static void UpdateNameplateText(GameObject nameplate, string label)
	{
		if ((Object)(object)nameplate == (Object)null)
		{
			return;
		}
		foreach (TMP_Text componentsInChild in nameplate.GetComponentsInChildren<TMP_Text>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null))
			{
				try
				{
					ApplyDeadNameText(componentsInChild, label);
					((Component)componentsInChild).gameObject.SetActive(true);
					componentsInChild.ForceMeshUpdate(false, false);
				}
				catch (Exception ex)
				{
					LogVerbose("nameplate text reveal update failed type=" + ((object)componentsInChild).GetType().FullName + " error=" + ex.GetType().Name);
				}
			}
		}
	}

	private static bool HasTmpText(GameObject gameObject)
	{
		if ((Object)(object)gameObject != (Object)null)
		{
			return ((IEnumerable<TMP_Text>)gameObject.GetComponentsInChildren<TMP_Text>(true)).Any((TMP_Text text) => (Object)(object)text != (Object)null);
		}
		return false;
	}

	private static void ApplyDeadNameText(TMP_Text text, string label)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		text.text = label + " ☠";
		((Graphic)text).color = DeadNameColor;
	}

	private static void EnableVisibleRenderers(GameObject gameObject, bool enabled)
	{
		if ((Object)(object)gameObject == (Object)null)
		{
			return;
		}
		foreach (Renderer componentsInChild in gameObject.GetComponentsInChildren<Renderer>(true))
		{
			if ((Object)(object)componentsInChild != (Object)null)
			{
				componentsInChild.enabled = enabled;
			}
		}
	}

	private static bool HasRenderer(GameObject gameObject)
	{
		if ((Object)(object)gameObject != (Object)null)
		{
			return ((IEnumerable<Renderer>)gameObject.GetComponentsInChildren<Renderer>(true)).Any((Renderer renderer) => (Object)(object)renderer != (Object)null);
		}
		return false;
	}

	private static bool TryComputeRendererBounds(GameObject gameObject, out Bounds bounds)
	{
		return TryComputeRendererBounds(gameObject, out bounds, includeDeathAppliedAssets: false);
	}

	private static bool TryComputeRendererBounds(GameObject gameObject, out Bounds bounds, bool includeDeathAppliedAssets)
	{
		//IL_0001: 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_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		bounds = default(Bounds);
		if ((Object)(object)gameObject == (Object)null)
		{
			return false;
		}
		bool flag = false;
		foreach (Renderer componentsInChild in gameObject.GetComponentsInChildren<Renderer>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null) && (includeDeathAppliedAssets || !IsDeathAppliedAssetRenderer(componentsInChild)))
			{
				if (!flag)
				{
					bounds = componentsInChild.bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(componentsInChild.bounds);
				}
			}
		}
		return flag;
	}

	private static bool IsDeathAppliedAssetRenderer(Renderer renderer)
	{
		Transform val = (((Object)(object)renderer == (Object)null) ? null : ((Component)renderer).transform);
		while ((Object)(object)val != (Object)null)
		{
			if (!string.IsNullOrWhiteSpace(((Object)val).name) && ((Object)val).name.IndexOf("_DeathAppliedAsset", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static GameObject GetRoot()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		if ((Object)(object)s_root != (Object)null)
		{
			return s_root;
		}
		s_root = new GameObject("DeathBodyOutline_Root");
		return s_root;
	}

	private static void ClearOutlines(string reason)
	{
		foreach (GameObject s_outline in s_outlines)
		{
			if ((Object)(object)s_outline != (Object)null)
			{
				Object.Destroy((Object)(object)s_outline);
			}
		}
		s_outlines.Clear();
		s_lastOutlineTimeByKey.Clear();
		if ((Object)(object)s_root != (Object)null)
		{
			Object.Destroy((Object)(object)s_root);
			s_root = null;
		}
		LogVerbose("outlines cleared: " + reason);
	}

	private static Type FindType(string fullName)
	{
		return Il2CppReflection.FindType(fullName);
	}

	private static string GetPlayerKey(object player)
	{
		if (TryConvertLong(GetFieldOrProp(player, "netId"), out var result) && result > 0)
		{
			return "net:" + result.ToString(CultureInfo.InvariantCulture);
		}
		object clientData = GetClientData(player);
		if (TryConvertLong(GetFieldOrProp(clientData, "SteamID") ?? GetFieldOrProp(clientData, "SteamId") ?? GetFieldOrProp(clientData, "steamID") ?? GetFieldOrProp(clientData, "ID"), out var result2) && result2 > 0)
		{
			return "steam:" + result2.ToString(CultureInfo.InvariantCulture);
		}
		return "obj:" + RuntimeHelpersHash(player).ToString(CultureInfo.InvariantCulture);
	}

	private static string GetPlayerLabel(object player)
	{
		object clientData = GetClientData(player);
		string text = GetStringValue(GetFieldOrProp(clientData, "Name")) ?? GetStringValue(GetFieldOrProp(clientData, "_Name_k__BackingField")) ?? GetStringValue(GetFieldOrProp(GetFieldOrProp(player, "namePlate"), "text")) ?? GetStringValue(GetFieldOrProp(player, "name")) ?? GetStringValue(GetFieldOrProp(GetFieldOrProp(player, "gameObject"), "name"));
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text.Trim();
		}
		return "player";
	}

	private static object GetClientData(object player)
	{
		return GetFieldOrProp(player, "ClientData") ?? GetFieldOrProp(player, "NetworkclientData") ?? GetFieldOrProp(player, "clientData") ?? GetFieldOrProp(player, "_ClientData_k__BackingField") ?? GetFieldOrProp(player, "_NetworkclientData_k__BackingField");
	}

	private static object GetFieldOrProp(object obj, string name)
	{
		return Il2CppReflection.GetFieldOrProp(obj, name);
	}

	private static object GetStaticFieldOrProp(Type type, string name)
	{
		return Il2CppReflection.GetStaticFieldOrProp(type, name);
	}

	private static object FindFirstObjectOfType(Type type)
	{
		return Il2CppReflection.FindFirstObjectOfType(type);
	}

	private static string GetStringValue(object value)
	{
		return ValueConversion.GetStringValue(value);
	}

	private static bool? GetBoolValue(object value)
	{
		return ValueConversion.GetBoolValue(value);
	}

	private static bool TryConvertBool(object value, out bool result)
	{
		return ValueConversion.TryConvertBool(value, out result);
	}

	private static string FormatObjectValue(object value)
	{
		if (value == null)
		{
			return "{none}";
		}
		if (value is bool)
		{
			if (!(bool)value)
			{
				return "False";
			}
			return "True";
		}
		return value.ToString();
	}

	private static bool TryConvertLong(object value, out long result)
	{
		return ValueConversion.TryConvertLong(value, out result);
	}

	private static int RuntimeHelpersHash(object obj)
	{
		if (obj != null)
		{
			return RuntimeHelpers.GetHashCode(obj);
		}
		return 0;
	}

	private static string SanitizeName(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return "player";
		}
		return new string(value.Select((char ch) => (!char.IsLetterOrDigit(ch)) ? '_' : ch).ToArray()).Trim('_');
	}

	private static string FormatVector(Vector3 vector)
	{
		return vector.x.ToString("0.00", CultureInfo.InvariantCulture) + "," + vector.y.ToString("0.00", CultureInfo.InvariantCulture) + "," + vector.z.ToString("0.00", CultureInfo.InvariantCulture);
	}

	private static string DescribeObject(object obj)
	{
		if (obj == null)
		{
			return "{none}";
		}
		GameObject val = (GameObject)((obj is GameObject) ? obj : null);
		if (val != null)
		{
			return DescribeGameObject(val);
		}
		Component val2 = (Component)((obj is Component) ? obj : null);
		if (val2 != null)
		{
			return ((object)val2).GetType().FullName + " go=" + DescribeGameObject(val2.gameObject);
		}
		return obj.GetType().FullName + "#" + RuntimeHelpersHash(obj).ToString(CultureInfo.InvariantCulture);
	}

	private static string DescribeGameObject(GameObject gameObject)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)gameObject == (Object)null)
		{
			return "{none}";
		}
		return ((Object)gameObject).name + " activeSelf=" + gameObject.activeSelf + " activeHierarchy=" + gameObject.activeInHierarchy + " pos=" + FormatVector(gameObject.transform.position) + " renderers=" + gameObject.GetComponentsInChildren<Renderer>(true).Length.ToString(CultureInfo.InvariantCulture);
	}

	private static void LogVerbose(string message)
	{
		if (s_verboseLogs != null && s_verboseLogs.Value)
		{
			MelonLogger.Msg("[Death Body Outline] [Debug] " + message);
		}
	}

	private static string RemapSkinnedMeshBones(GameObject copy, Transform donorRoot, Transform cloneRoot)
	{
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		int num6 = 0;
		foreach (SkinnedMeshRenderer componentsInChild in copy.GetComponentsInChildren<SkinnedMeshRenderer>(true))
		{
			if ((Object)(object)componentsInChild == (Object)null)
			{
				continue;
			}
			num++;
			Il2CppReferenceArray<Transform> bones = componentsInChild.bones;
			if (bones != null && ((Il2CppArrayBase<Transform>)(object)bones).Length > 0)
			{
				Transform[] array = (Transform[])(object)new Transform[((Il2CppArrayBase<Transform>)(object)bones).Length];
				for (int i = 0; i < ((Il2CppArrayBase<Transform>)(object)bones).Length; i++)
				{
					Transform val = ((Il2CppArrayBase<Transform>)(object)bones)[i];
					if ((Object)(object)val == (Object)null)
					{
						array[i] = copy.transform;
						num4++;
						continue;
					}
					if (IsChildOfOrSelf(val, copy.transform))
					{
						array[i] = val;
						num3++;
						continue;
					}
					Transform val2 = MapDonorTransformToClone(donorRoot, cloneRoot, val);
					if ((Object)(object)val2 != (Object)null)
					{
						array[i] = val2;
						num2++;
					}
					else
					{
						array[i] = copy.transform;
						num4++;
					}
				}
				componentsInChild.bones = Il2CppReferenceArray<Transform>.op_Implicit(array);
			}
			Transform rootBone = componentsInChild.rootBone;
			if (!((Object)(object)rootBone == (Object)null) && !IsChildOfOrSelf(rootBone, copy.transform))
			{
				Transform val3 = MapDonorTransformToClone(donorRoot, cloneRoot, rootBone);
				if ((Object)(object)val3 != (Object)null)
				{
					componentsInChild.rootBone = val3;
					num5++;
				}
				else if (IsChildOfOrSelf(rootBone, donorRoot))
				{
					componentsInChild.rootBone = cloneRoot;
					num6++;
				}
			}
		}
		return "skinnedRenderers=" + num.ToString(CultureInfo.InvariantCulture) + " mappedBones=" + num2.ToString(CultureInfo.InvariantCulture) + " keptLocalBones=" + num3.ToString(CultureInfo.InvariantCulture) + " detachedBones=" + num4.ToString(CultureInfo.InvariantCulture) + " mappedRootBones=" + num5.ToString(CultureInfo.InvariantCulture) + " detachedRootBones=" + num6.ToString(CultureInfo.InvariantCulture);
	}

	private static int GetDepth(Transform transform)
	{
		int num = 0;
		Transform val = transform;
		while ((Object)(object)val != (Object)null)
		{
			num++;
			val = val.parent;
		}
		return num;
	}

	private static bool IsChildOf(Transform child, Transform possibleParent)
	{
		Transform val = (((Object)(object)child == (Object)null) ? null : child.parent);
		while ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)val == (Object)(object)possibleParent)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static bool IsChildOfOrSelf(Transform child, Transform possibleParent)
	{
		Transform val = child;
		while ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)val == (Object)(object)possibleParent)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static Transform MapDonorTransformToClone(Transform donorRoot, Transform cloneRoot, Transform donorTransform)
	{
		if ((Object)(object)donorRoot == (Object)null || (Object)(object)cloneRoot == (Object)null || (Object)(object)donorTransform == (Object)null)
		{
			return null;
		}
		if ((Object)(object)donorTransform == (Object)(object)donorRoot)
		{
			return cloneRoot;
		}
		if (!IsChildOfOrSelf(donorTransform, donorRoot))
		{
			return null;
		}
		Transform val = FindChildByPath(cloneRoot, GetRelativePath(donorRoot, donorTransform));
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		return FindDescendantByName(cloneRoot, ((Object)donorTransform).name);
	}

	private static Transform FindMatchingCloneParent(Transform donorRoot, Transform cloneRoot, Transform donorParent)
	{
		if ((Object)(object)donorRoot == (Object)null || (Object)(object)cloneRoot == (Object)null || (Object)(object)donorParent == (Object)null || (Object)(object)donorParent == (Object)(object)donorRoot)
		{
			return cloneRoot;
		}
		Transform val = FindChildByPath(cloneRoot, GetRelativePath(donorRoot, donorParent));
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		return FindDescendantByName(cloneRoot, ((Object)donorParent).name);
	}

	private static Transform FindHeadAttachParent(Transform cloneRoot)
	{
		if ((Object)(object)cloneRoot == (Object)null)
		{
			return null;
		}
		foreach (Transform componentsInChild in ((Component)cloneRoot).GetComponentsInChildren<Transform>(true))
		{
			if (!((Object)(object)componentsInChild == (Object)null))
			{
				string text = ((Object)componentsInChild).name.ToLowerInvariant();
				if (text == "head" || text.EndsWith(":head", StringComparison.Ordinal) || text.EndsWith("_head", StringComparison.Ordinal))
				{
					return componentsInChild;
				}
			}
		}
		return cloneRoot;
	}

	private static Transform FindChildByPath(Transform root, string path)
	{
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		if (string.IsNullOrWhiteSpace(path))
		{
			return root;
		}
		Transform val = root;
		string[] array = path.Split('/');
		foreach (string text in array)
		{
			if (!string.IsNullOrWhiteSpace(text))
			{
				val = FindDirectChildByName(val, text);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
			}
		}
		return val;
	}

	private static Transform FindDirectChildByName(Transform root, string name)
	{
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		for (int i = 0; i < root.childCount; i++)
		{
			Transform child = root.GetChild(i);
			if ((Object)(object)child != (Object)null && string.Equals(((Object)child).name, name, StringComparison.Ordinal))
			{
				return child;
			}
		}
		return null;
	}

	private static Transform FindDescendantByName(Transform root, string name)
	{
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		foreach (Transform componentsInChild in ((Component)root).GetComponentsInChildren<Transform>(true))
		{
			if ((Object)(object)componentsInChild != (Object)null && string.Equals(((Object)componentsInChild).name, name, StringComparison.Ordinal))
			{
				return componentsInChild;
			}
		}
		return null;
	}

	private static string GetRelativePath(Transform root, Transform target)
	{
		if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)null)
		{
			return "{none}";
		}
		if ((Object)(object)root == (Object)(object)target)
		{
			return string.Empty;
		}
		Stack<string> stack = new Stack<string>();
		Transform val = target;
		while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
		{
			stack.Push(((Object)val).name);
			val = val.parent;
		}
		return string.Join("/", stack);
	}
}
namespace Kp0hyc.Labyrinthine.Shared;

internal static class Il2CppReflection
{
	public static Type FindType(string fullName)
	{
		if (string.IsNullOrWhiteSpace(fullName))
		{
			return null;
		}
		string il2cppName = (fullName.StartsWith("Il2Cpp.", StringComparison.Ordinal) ? fullName : ("Il2Cpp." + fullName));
		string il2cppNamespaceName = (fullName.StartsWith("Il2Cpp", StringComparison.Ordinal) ? fullName : ("Il2Cpp" + fullName));
		string shortName = (fullName.Contains(".") ? fullName.Substring(fullName.LastIndexOf('.') + 1) : fullName);
		Type type = Type.GetType(fullName) ?? Type.GetType(il2cppName) ?? Type.GetType(il2cppNamespaceName);
		if (type != null)
		{
			return type;
		}
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			Type type2 = assembly.GetType(fullName, throwOnError: false) ?? assembly.GetType(il2cppName, throwOnError: false) ?? assembly.GetType(il2cppNamespaceName, throwOnError: false);
			if (type2 != null)
			{
				return type2;
			}
		}
		assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly2 in assemblies)
		{
			Type[] source;
			try
			{
				source = assembly2.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				source = ex.Types.Where((Type t) => t != null).ToArray();
			}
			catch
			{
				continue;
			}
			Type type3 = source.FirstOrDefault((Type t) => t.Name == shortName || t.FullName == fullName || t.FullName == il2cppName || t.FullName == il2cppNamespaceName);
			if (type3 != null)
			{
				return type3;
			}
		}
		return null;
	}

	public static object GetFieldOrProp(object obj, string name)
	{
		if (obj == null || string.IsNullOrWhiteSpace(name))
		{
			return null;
		}
		Type type = obj.GetType();
		while (type != null)
		{
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return property.GetValue(obj);
				}
				catch
				{
				}
			}
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return field.GetValue(obj);
				}
				catch
				{
				}
			}
			type = type.BaseType;
		}
		return null;
	}

	public static object GetStaticFieldOrProp(Type type, string name)
	{
		if (type == null || string.IsNullOrWhiteSpace(name))
		{
			return null;
		}
		Type type2 = type;
		while (type2 != null)
		{
			PropertyInfo property = type2.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return property.GetValue(null);
				}
				catch
				{
				}
			}
			FieldInfo field = type2.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					return field.GetValue(null);
				}
				catch
				{
				}
			}
			type2 = type2.BaseType;
		}
		return null;
	}

	public static bool SetFieldOrProp(object obj, string name, object value)
	{
		if (obj == null || string.IsNullOrWhiteSpace(name))
		{
			return false;
		}
		Type type = obj.GetType();
		while (type != null)
		{
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0 && property.CanWrite)
			{
				try
				{
					property.SetValue(obj, value);
					return true;
				}
				catch
				{
				}
			}
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				try
				{
					field.SetValue(obj, value);
					return true;
				}
				catch
				{
				}
			}
			type = type.BaseType;
		}
		return false;
	}

	public static object FindFirstObjectOfType(Type type)
	{
		if (type == null)
		{
			return null;
		}
		Array array = InvokeFindObjectsOfType(typeof(Object), "FindObjectsOfType", type) ?? InvokeFindObjectsOfType(typeof(Resources), "FindObjectsOfTypeAll", type);
		if (array == null || array.Length <= 0)
		{
			return null;
		}
		return array.GetValue(0);
	}

	public static void ForEachListItem(object list, Action<object> action)
	{
		if (list == null || action == null)
		{
			return;
		}
		if (list is Array array)
		{
			{
				foreach (object item in array)
				{
					action(item);
				}
				return;
			}
		}
		if (list is IEnumerable enumerable)
		{
			{
				foreach (object item2 in enumerable)
				{
					action(item2);
				}
				return;
			}
		}
		TryGetCollectionCount(list, out var count);
		if (count <= 0)
		{
			return;
		}
		MethodInfo method = list.GetType().GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (method == null)
		{
			return;
		}
		for (int i = 0; i < count; i++)
		{
			try
			{
				action(method.Invoke(list, new object[1] { i }));
			}
			catch
			{
			}
		}
	}

	public static int GetCollectionCount(object list)
	{
		if (list == null)
		{
			return 0;
		}
		if (list is Array array)
		{
			return array.Length;
		}
		if (TryGetCollectionCount(list, out var count))
		{
			return Math.Max(0, count);
		}
		int num = 0;
		if (list is IEnumerable enumerable)
		{
			foreach (object item in enumerable)
			{
				_ = item;
				num++;
			}
		}
		return num;
	}

	public static bool TryGetCollectionCount(object list, out int count)
	{
		count = 0;
		if (list == null)
		{
			return false;
		}
		if (list is Array array)
		{
			count = array.Length;
			return true;
		}
		Type type = list.GetType();
		if (ValueConversion.TryConvertInt(GetFieldOrProp(list, "Count"), out count))
		{
			return true;
		}
		if (ValueConversion.TryConvertInt(GetFieldOrProp(list, "Length"), out count))
		{
			return true;
		}
		MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "get_Count" && m.GetParameters().Length == 0);
		if (methodInfo != null)
		{
			try
			{
				if (ValueConversion.TryConvertInt(methodInfo.Invoke(list, Array.Empty<object>()), out count))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "get_Length" && m.GetParameters().Length == 0);
		if (methodInfo2 != null)
		{
			try
			{
				return ValueConversion.TryConvertInt(methodInfo2.Invoke(list, Array.Empty<object>()), out count);
			}
			catch
			{
			}
		}
		return false;
	}

	public static string DescribeCollectionAccessForLog(object list, bool enabled)
	{
		if (!enabled)
		{
			return "disabled";
		}
		if (list == null)
		{
			return "null";
		}
		try
		{
			Type type = list.GetType();
			object fieldOrProp = GetFieldOrProp(list, "Count");
			object fieldOrProp2 = GetFieldOrProp(list, "Length");
			MethodInfo method = type.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			bool flag = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any((MethodInfo m) => m.Name == "get_Count" && m.GetParameters().Length == 0);
			string empty = string.Empty;
			try
			{
				empty = string.Join(",", (from i in type.GetInterfaces()
					select i.FullName ?? i.Name into n
					where n.IndexOf("IEnumerable", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("IReadOnlyList", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("IList", StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("ICollection", StringComparison.OrdinalIgnoreCase) >= 0
					select n).Take(6));
			}
			catch
			{
				empty = "unavailable";
			}
			return "type=" + SafeLogToken(type.FullName ?? type.Name) + " array=" + (list is Array) + " enumerable=" + (list is IEnumerable) + " count=" + FormatObjectForLog(fieldOrProp) + " length=" + FormatObjectForLog(fieldOrProp2) + " getCount=" + flag + " getItem=" + (method != null) + " helperCount=" + GetCollectionCount(list) + " interfaces=[" + empty + "]";
		}
		catch (Exception ex)
		{
			return "describe-failed:" + ex.GetType().Name + ":" + SafeLogToken(ex.Message);
		}
	}

	public static bool TryGetIndexedItem(object list, object index, out object item)
	{
		item = null;
		if (list == null)
		{
			return false;
		}
		if (list is Array array && index is int num)
		{
			if (num < 0 || num >= array.Length)
			{
				return false;
			}
			item = array.GetValue(num);
			return true;
		}
		try
		{
			MethodInfo method = list.GetType().GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method != null)
			{
				item = method.Invoke(list, new object[1] { index });
				return true;
			}
		}
		catch
		{
		}
		if (index is int num2)
		{
			int num3 = 0;
			if (list is IEnumerable enumerable)
			{
				foreach (object item2 in enumerable)
				{
					if (num3 == num2)
					{
						item = item2;
						return true;
					}
					num3++;
				}
			}
		}
		return false;
	}

	private static Array InvokeFindObjectsOfType(Type ownerType, string methodName, Type searchedType)
	{
		try
		{
			return ownerType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo candidate)
			{
				ParameterInfo[] parameters = candidate.GetParameters();
				return candidate.Name == methodName && parameters.Length == 1 && parameters[0].ParameterType == typeof(Type);
			})?.Invoke(null, new object[1] { searchedType }) as Array;
		}
		catch
		{
			return null;
		}
	}

	private static string FormatObjectForLog(object value)
	{
		if (value == null)
		{
			return "null";
		}
		try
		{
			return SafeLogToken(value.ToString());
		}
		catch
		{
			return "toString-failed";
		}
	}

	private static string SafeLogToken(string value)
	{
		if (!string.IsNullOrWhiteSpace(value))
		{
			return value.Replace("|", "/").Replace("\r", " ").Replace("\n", " ")
				.Trim();
		}
		return string.Empty;
	}
}
internal static class LabelUtilities
{
	public static bool TryReadVectorXZ(object vector, out float x, out float z)
	{
		x = 0f;
		z = 0f;
		if (vector != null && ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(vector, "x"), out x))
		{
			return ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(vector, "z"), out z);
		}
		return false;
	}

	public static bool TryReadVectorXY(object vector, out float x, out float y)
	{
		x = 0f;
		y = 0f;
		if (vector != null && ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(vector, "x"), out x))
		{
			return ValueConversion.TryConvertFloat(Il2CppReflection.GetFieldOrProp(vector, "y"), out y);
		}
		return false;
	}

	public static bool IsActiveInHierarchy(object obj)
	{
		bool result;
		return !ValueConversion.TryConvertBool(Il2CppReflection.GetFieldOrProp(Il2CppReflection.GetFieldOrProp(obj, "gameObject") ?? obj, "activeInHierarchy"), out result) || result;
	}

	public static string GetObjectName(object obj)
	{
		return (Il2CppReflection.GetFieldOrProp(obj, "name") as string) ?? (Il2CppReflection.GetFieldOrProp(Il2CppReflection.GetFieldOrProp(obj, "gameObject"), "name") as string);
	}

	public static string CleanObjectLabel(string name)
	{
		if (string.IsNullOrWhiteSpace(name))
		{
			return name;
		}
		return name.Replace("(Clone)", string.Empty).Trim();
	}

	public static object GetPlayerClientData(object player)
	{
		string source;
		return GetPlayerClientData(player, out source);
	}

	public static object GetPlayerClientData(object player, out string source)
	{
		source = "none";
		if (player == null)
		{
			return null;
		}
		object fieldOrProp = Il2CppReflection.GetFieldOrProp(player, "ClientData");
		if (fieldOrProp != null)
		{
			source = "ClientData";
			return fieldOrProp;
		}
		fieldOrProp = Il2CppReflection.GetFieldOrProp(player, "NetworkclientData");
		if (fieldOrProp != null)
		{
			source = "NetworkclientData";
			return fieldOrProp;
		}
		fieldOrProp = Il2CppReflection.GetFieldOrProp(player, "clientData");
		if (fieldOrProp != null)
		{
			source = "clientData";
			return fieldOrProp;
		}
		fieldOrProp = Il2CppReflection.GetFieldOrProp(player, "_ClientData_k__BackingField");
		if (fieldOrProp != null)
		{
			source = "_ClientData_k__BackingField";
			return fieldOrProp;
		}
		fieldOrProp = Il2CppReflection.GetFieldOrProp(player, "_NetworkclientData_k__BackingField");
		if (fieldOrProp != null)
		{
			source = "_NetworkclientData_k__BackingField";
			return fieldOrProp;
		}
		return null;
	}

	public static string GetClientDataName(object clientData)
	{
		string source;
		return GetClientDataName(clientData, out source);
	}

	public static string GetClientDataName(object clientData, out string source)
	{
		source = "none";
		if (clientData == null)
		{
			return null;
		}
		string text = Il2CppReflection.GetFieldOrProp(clientData, "Name") as string;
		if (!string.IsNullOrWhiteSpace(text))
		{
			source = "Name";
			return text;
		}
		text = Il2CppReflection.GetFieldOrProp(clientData, "_Name_k__BackingField") as string;
		if (!string.IsNullOrWhiteSpace(text))
		{
			source = "_Name_k__BackingField";
			return text;
		}
		return null;
	}

	public static string GetEndMapPlayerName(object ui, int index)
	{
		if (ui == null || index < 0)
		{
			return null;
		}
		if (!Il2CppReflection.TryGetIndexedItem(Il2CppReflection.GetFieldOrProp(ui, "playerNameUIs"), index, out var item) || item == null)
		{
			return null;
		}
		return GetTextValue(Il2CppReflection.GetFieldOrProp(item, "playerNameText"));
	}

	public static string GetTextValue(object textObject)
	{
		string text = Il2CppReflection.GetFieldOrProp(textObject, "text") as string;
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text;
		}
		text = Il2CppReflection.GetFieldOrProp(textObject, "Text") as string;
		if (!string.IsNullOrWhiteSpace(text))
		{
			return text;
		}
		return null;
	}

	public static bool IsUsefulPlayerLabel(string label)
	{
		if (string.IsNullOrWhiteSpace(label))
		{
			return false;
		}
		string text = label.Trim();
		if (!string.Equals(text, "Player", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "Player(Clone)", StringComparison.OrdinalIgnoreCase))
		{
			return !text.StartsWith("PlayerNetworkSync", StringComparison.OrdinalIgnoreCase);
		}
		return false;
	}

	public static string DescribePlayerNameSources(object player, object ui, int index)
	{
		string source;
		string source2;
		string clientDataName = GetClientDataName(GetPlayerClientData(player, out source), out source2);
		string textValue = GetTextValue(Il2CppReflection.GetFieldOrProp(player, "namePlate"));
		string endMapPlayerName = GetEndMapPlayerName(ui, index);
		string text = Il2CppReflection.GetFieldOrProp(Il2CppReflection.GetFieldOrProp(player, "gameObject") ?? player, "name") as string;
		return "player=" + DescribePlayerObject(player) + " clientData=" + source + " clientNameSource=" + source2 + " clientName='" + clientDataName + "' namePlate='" + textValue + "' endMap='" + endMapPlayerName + "' gameObject='" + text + "'";
	}

	public static string DescribePlayerObject(object player)
	{
		if (player == null)
		{
			return "null";
		}
		string text = player.GetType().FullName ?? player.GetType().Name;
		if (ValueConversion.TryConvertLong(Il2CppReflection.GetFieldOrProp(player, "netId"), out var result) && result > 0)
		{
			return text + "#net:" + result;
		}
		if (ValueConversion.TryConvertLong(Il2CppReflection.GetFieldOrProp(player, "ID"), out var result2) && result2 > 0)
		{
			return text + "#id:" + result2;
		}
		return text + "#obj:" + RuntimeHelpers.GetHashCode(player);
	}
}
internal static class ValueConversion
{
	public static string GetStringValue(object value)
	{
		return value as string;
	}

	public static bool? GetBoolValue(object value)
	{
		if (value == null)
		{
			return null;
		}
		if (value is bool)
		{
			return (bool)value;
		}
		if (!bool.TryParse(value.ToString(), out var result))
		{
			return null;
		}
		return result;
	}

	public static bool TryConvertBool(object value, out bool result)
	{
		result = false;
		if (value == null)
		{
			return false;
		}
		if (value is bool flag)
		{
			result = flag;
			return true;
		}
		if (value is string text)
		{
			return bool.TryParse(text.Trim(), out result);
		}
		try
		{
			result = Convert.ToBoolean(value, CultureInfo.InvariantCulture);
			return true;
		}
		catch
		{
			return bool.TryParse(value.ToString(), out result);
		}
	}

	public static bool TryConvertInt(object value, out int result)
	{
		result = 0;
		if (value == null)
		{
			return false;
		}
		if (value is Enum value2)
		{
			result = Convert.ToInt32(value2, CultureInfo.InvariantCulture);
			return true;
		}
		try
		{
			result = Convert.ToInt32(value, CultureInfo.InvariantCulture);
			return true;
		}
		catch
		{
			return int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
		}
	}

	public static bool TryConvertLong(object value, out long result)
	{
		result = 0L;
		if (value == null)
		{
			return false;
		}
		if (value is Enum value2)
		{
			result = Convert.ToInt64(value2, CultureInfo.InvariantCulture);
			return true;
		}
		if (!(value is long num))
		{
			if (!(value is int num2))
			{
				if (!(value is uint num3))
				{
					if (!(value is ulong num4))
					{
						if (value is short num5)
						{
							result = num5;
							return true;
						}
						if (value is ushort num6)
						{
							result = num6;
							return true;
						}
						if (value is byte b)
						{
							result = b;
							return true;
						}
						if (value is sbyte b2)
						{
							result = b2;
							return true;
						}
						if (value is string text)
						{
							return long.TryParse(text.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
						}
					}
					else if (num4 <= long.MaxValue)
					{
						result = (long)num4;
						return true;
					}
					try
					{
						result = Convert.ToInt64(value, CultureInfo.InvariantCulture);
						return true;
					}
					catch
					{
					}
					string[] array = new string[3] { "m_value", "value", "Value" };
					foreach (string name in array)
					{
						object fieldOrProp = Il2CppReflection.GetFieldOrProp(value, name);
						if (fieldOrProp != null && fieldOrProp != value && TryConvertLong(fieldOrProp, out result))
						{
							return true;
						}
					}
					return long.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
				}
				result = num3;
				return true;
			}
			result = num2;
			return true;
		}
		result = num;
		return true;
	}

	public static bool TryConvertFloat(object value, out float result)
	{
		result = 0f;
		if (value == null)
		{
			return false;
		}
		try
		{
			result = Convert.ToSingle(value, CultureInfo.InvariantCulture);
			return true;
		}
		catch
		{
			return float.TryParse(value.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out result);
		}
	}

	public static bool TryConvertByte(object value, out byte result)
	{
		result = 0;
		if (value == null)
		{
			return false;
		}
		try
		{
			result = Convert.ToByte(value, CultureInfo.InvariantCulture);
			return true;
		}
		catch
		{
			return byte.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
		}
	}

	public static int FloorToInt(float value)
	{
		return (int)Math.Floor(value);
	}

	public static float Clamp(float value, float min, float max)
	{
		if (value < min)
		{
			return min;
		}
		if (!(value > max))
		{
			return value;
		}
		return max;
	}

	public static int Clamp(int value, int min, int max)
	{
		if (value < min)
		{
			return min;
		}
		if (value <= max)
		{
			return value;
		}
		return max;
	}

	public static float Clamp01(float value)
	{
		return Clamp(value, 0f, 1f);
	}
}