Decompiled source of BenWarning v1.0.0

BepInEx/plugins/ZombePlayerMod.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ZombePlayerMod
{
	[BepInPlugin("BigPickle.ZombePlayerMod", "BenWarning", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("Content Warning.exe")]
	public class ZombePlayerMod : BaseUnityPlugin
	{
		public const string MOD_GUID = "BigPickle.ZombePlayerMod";

		public const string MOD_NAME = "BenWarning";

		public const string MOD_VERSION = "1.0.0";

		private Harmony _harmony;

		internal static ZombePlayerMod Instance { get; private set; }

		internal ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> EnableModelReplacement { get; private set; }

		internal static ConfigEntry<float> ModelScale { get; private set; }

		internal static ConfigEntry<float> ModelFacingYaw { get; private set; }

		internal static ConfigEntry<float> ModelPitch { get; private set; }

		internal static ConfigEntry<float> ModelForwardOffset { get; private set; }

		internal static ConfigEntry<float> HandRaise { get; private set; }

		internal static ConfigEntry<bool> ShowOwnModel { get; private set; }

		private void Awake()
		{
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			EnableModelReplacement = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableModelReplacement", true, "Replace the player model with the Zombe monster.");
			ModelScale = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ModelScale", 1f, "Extra scale multiplier applied to the Zombe model (1.0 = same size as a player).");
			ModelFacingYaw = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ModelFacingYaw", 0f, "Rotates the Zombe around its Y axis (degrees) so it faces the same way the player does. Try 0 or 180.");
			ModelPitch = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ModelPitch", 0f, "Pitches the Zombe's head up/down (degrees) around its sideways axis. Try -90 or 90 if it is lying down.");
			ModelForwardOffset = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ModelForwardOffset", 0f, "Moves the model forward along its facing (meters). Optional cosmetic nudge; the camera rides the Zombe's head so alignment is exact by default.");
			HandRaise = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HandRaise", 0.1f, "Raises the Zombe's hands (world meters) above the player's real hands so held items are gripped properly.");
			ShowOwnModel = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowOwnModel", true, "Whether the local player (you) wears the Zombe model. Turn off to keep the default first-person body and visor.");
			_harmony = new Harmony("BigPickle.ZombePlayerMod");
			_harmony.PatchAll();
			ZombeNetworkHandler.Init();
			Log.LogInfo((object)"BenWarning v1.0.0 loaded! Big Pickle reporting for duty.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "BigPickle.ZombePlayerMod";

		public const string PLUGIN_NAME = "BenWarning";

		public const string PLUGIN_VERSION = "1.0.0";
	}
	public static class ZombeModelCache
	{
		private static Player _zombePlayer;

		private static SkinnedMeshRenderer[] _zombeRenderers;

		private static Transform _rootBone;

		private static Dictionary<string, Transform> _sourceBones;

		private static bool _searchStarted;

		private static bool _loadAllTried;

		private static float _lastLoadAllTime = -999f;

		private static bool _usedFallbackMonster;

		private static int _fallbackRejects;

		private static bool _found;

		private static readonly string[] _keywords = new string[6] { "zombe", "snail", "zombie", "zomby", "sluggie", "slug" };

		public static Player ZombePlayer => _zombePlayer;

		public static SkinnedMeshRenderer[] ZombeRenderers => _zombeRenderers;

		public static Transform RootBone => _rootBone;

		public static Dictionary<string, Transform> SourceBones => _sourceBones;

		public static bool Found => _found;

		public static void Init()
		{
			if (!_searchStarted)
			{
				_searchStarted = true;
				TryFindNow();
			}
		}

		public static void TryFindNow()
		{
			if (!_found)
			{
				SearchResources();
			}
		}

		public static void ResetForRetry()
		{
			if (_usedFallbackMonster)
			{
				_fallbackRejects++;
			}
			_usedFallbackMonster = false;
			_found = false;
			_zombePlayer = null;
			_zombeRenderers = null;
			_rootBone = null;
			_sourceBones = null;
		}

		private static void SearchResources()
		{
			try
			{
				Player[] array = Resources.FindObjectsOfTypeAll<Player>();
				if (array != null)
				{
					Player[] array2 = array;
					foreach (Player val in array2)
					{
						if (!((Object)(object)val == (Object)null) && val.ai && LooksLikeZombe(val))
						{
							_fallbackRejects = 0;
							if (ExtractFromPlayer(val))
							{
								return;
							}
						}
					}
					Player val2 = null;
					if (_fallbackRejects < 3)
					{
						Player[] array3 = array;
						foreach (Player val3 in array3)
						{
							if (!((Object)(object)val3 == (Object)null) && val3.ai && !LooksLikeZombe(val3) && HasUsableMesh(val3))
							{
								val2 = val3;
								break;
							}
						}
					}
					if ((Object)(object)val2 != (Object)null)
					{
						_usedFallbackMonster = true;
						ZombePlayerMod instance = ZombePlayerMod.Instance;
						if (instance != null)
						{
							ManualLogSource log = instance.Log;
							if (log != null)
							{
								log.LogWarning((object)("No keyword-matched Zombe; falling back to monster '" + ((Object)val2).name + "'. Model quality may vary until a real Zombe is found."));
							}
						}
						if (ExtractFromPlayer(val2))
						{
							return;
						}
					}
				}
				GameObject[] array4 = Resources.FindObjectsOfTypeAll<GameObject>();
				if (array4 != null)
				{
					GameObject[] array5 = array4;
					foreach (GameObject val4 in array5)
					{
						if (!((Object)(object)val4 == (Object)null) && !((Object)(object)val4.GetComponentInParent<Player>() != (Object)null) && NameMatches(((Object)val4).name) && ExtractFromGameObject(val4))
						{
							return;
						}
					}
				}
				if (!_found)
				{
					MaybeLoadAll();
				}
			}
			catch (Exception arg)
			{
				ZombePlayerMod instance2 = ZombePlayerMod.Instance;
				if (instance2 != null)
				{
					ManualLogSource log2 = instance2.Log;
					if (log2 != null)
					{
						log2.LogError((object)$"Zombe model search failed: {arg}");
					}
				}
			}
		}

		private static void MaybeLoadAll()
		{
			if (_loadAllTried || Time.realtimeSinceStartup - _lastLoadAllTime < 30f)
			{
				return;
			}
			_lastLoadAllTime = Time.realtimeSinceStartup;
			_loadAllTried = true;
			try
			{
				GameObject[] array = Resources.LoadAll<GameObject>("");
				if (array == null)
				{
					return;
				}
				GameObject[] array2 = array;
				foreach (GameObject val in array2)
				{
					if ((Object)(object)val == (Object)null || !NameMatches(((Object)val).name))
					{
						continue;
					}
					ZombePlayerMod instance = ZombePlayerMod.Instance;
					if (instance != null)
					{
						ManualLogSource log = instance.Log;
						if (log != null)
						{
							log.LogInfo((object)("ResourceLoad matched '" + ((Object)val).name + "' via Resources.LoadAll."));
						}
					}
					if (ExtractFromGameObject(val))
					{
						break;
					}
				}
			}
			catch (Exception ex)
			{
				ZombePlayerMod instance2 = ZombePlayerMod.Instance;
				if (instance2 != null)
				{
					ManualLogSource log2 = instance2.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Resources.LoadAll scan failed: " + ex.Message));
					}
				}
			}
		}

		private static bool HasUsableMesh(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			SkinnedMeshRenderer[] array = (from r in ((Component)player).GetComponentsInChildren<SkinnedMeshRenderer>(true)
				where (Object)(object)r != (Object)null && (Object)(object)r.sharedMesh != (Object)null && r.bones != null && r.bones.Length != 0
				select r).ToArray();
			return array.Length != 0;
		}

		private static bool LooksLikeZombe(Player player)
		{
			try
			{
				PlayerRefs refs = player.refs;
				if (refs != null)
				{
					if ((Object)(object)refs.bodyMeshRenderer != (Object)null && NameMatches(((Object)refs.bodyMeshRenderer).name))
					{
						return true;
					}
					SkinnedMeshRenderer bodyMeshRenderer = refs.bodyMeshRenderer;
					if ((Object)(object)((bodyMeshRenderer != null) ? bodyMeshRenderer.sharedMesh : null) != (Object)null && NameMatches(((Object)refs.bodyMeshRenderer.sharedMesh).name))
					{
						return true;
					}
					if ((Object)(object)refs.rigRoot != (Object)null && NameMatches(((Object)refs.rigRoot).name))
					{
						return true;
					}
				}
				if (NameMatches(((Object)player).name))
				{
					return true;
				}
				SkinnedMeshRenderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<SkinnedMeshRenderer>(true);
				if (componentsInChildren != null)
				{
					SkinnedMeshRenderer[] array = componentsInChildren;
					foreach (SkinnedMeshRenderer val in array)
					{
						if (!((Object)(object)val == (Object)null))
						{
							if (NameMatches(((Object)val).name))
							{
								return true;
							}
							if ((Object)(object)val.sharedMesh != (Object)null && NameMatches(((Object)val.sharedMesh).name))
							{
								return true;
							}
						}
					}
				}
			}
			catch
			{
				return false;
			}
			return false;
		}

		private static bool ExtractFromPlayer(Player player)
		{
			SkinnedMeshRenderer[] array = (from r in ((Component)player).GetComponentsInChildren<SkinnedMeshRenderer>(true)
				where (Object)(object)r != (Object)null && (Object)(object)r.sharedMesh != (Object)null && r.bones != null && r.bones.Length != 0
				select r).ToArray();
			if (array.Length == 0)
			{
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						log.LogWarning((object)("Player '" + ((Object)player).name + "' has no usable skinned mesh renderers; skipping."));
					}
				}
				return false;
			}
			_zombePlayer = player;
			_zombeRenderers = array;
			_rootBone = FindRootBone(player);
			_sourceBones = CollectSourceBones(player, _rootBone);
			_found = true;
			ZombePlayerMod instance2 = ZombePlayerMod.Instance;
			if (instance2 != null)
			{
				ManualLogSource log2 = instance2.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)string.Format("Zombe model cached: {0} renderer(s) / {1} / {2} bones from '{3}'.", array.Length, ((Object)(object)_rootBone != (Object)null) ? ("root=" + ((Object)_rootBone).name) : "no root", _sourceBones?.Count ?? 0, ((Object)player).name));
				}
			}
			return true;
		}

		private static bool ExtractFromGameObject(GameObject go)
		{
			SkinnedMeshRenderer[] array = (from r in go.GetComponentsInChildren<SkinnedMeshRenderer>(true)
				where (Object)(object)r != (Object)null && (Object)(object)r.sharedMesh != (Object)null && r.bones != null && r.bones.Length != 0
				select r).ToArray();
			if (array.Length == 0)
			{
				return false;
			}
			_zombePlayer = null;
			_zombeRenderers = array;
			_rootBone = FindRootBoneFromRenderers(array);
			_sourceBones = CollectSourceBonesFromRenderers(array, _rootBone);
			_found = true;
			ZombePlayerMod instance = ZombePlayerMod.Instance;
			if (instance != null)
			{
				ManualLogSource log = instance.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Zombe model cached from GameObject '{((Object)go).name}': {array.Length} renderer(s), {_sourceBones?.Count ?? 0} bones.");
				}
			}
			return true;
		}

		private static Transform FindRootBone(Player player)
		{
			try
			{
				if ((Object)(object)player.refs?.rigRoot != (Object)null)
				{
					return player.refs.rigRoot.transform;
				}
			}
			catch
			{
			}
			return FindRootBoneFromRenderers(_zombeRenderers);
		}

		private static Transform FindRootBoneFromRenderers(SkinnedMeshRenderer[] renderers)
		{
			if (renderers == null)
			{
				return null;
			}
			foreach (SkinnedMeshRenderer val in renderers)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.rootBone != (Object)null)
				{
					return val.rootBone;
				}
			}
			if (renderers.Length != 0 && (Object)(object)renderers[0] != (Object)null && renderers[0].bones != null && renderers[0].bones.Length != 0)
			{
				return renderers[0].bones[0];
			}
			return null;
		}

		private static Dictionary<string, Transform> CollectSourceBones(Player player, Transform root)
		{
			Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			if ((Object)(object)root != (Object)null)
			{
				AddBoneTree(root, dictionary);
			}
			if ((Object)(object)player != (Object)null)
			{
				Transform[] componentsInChildren = ((Component)player).GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !dictionary.ContainsKey(((Object)val).name))
					{
						dictionary[((Object)val).name] = val;
					}
				}
			}
			return dictionary;
		}

		private static Dictionary<string, Transform> CollectSourceBonesFromRenderers(SkinnedMeshRenderer[] renderers, Transform root)
		{
			Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			if ((Object)(object)root != (Object)null)
			{
				AddBoneTree(root, dictionary);
			}
			if (renderers != null)
			{
				foreach (SkinnedMeshRenderer val in renderers)
				{
					if ((Object)(object)val == (Object)null || val.bones == null)
					{
						continue;
					}
					Transform[] bones = val.bones;
					foreach (Transform val2 in bones)
					{
						if ((Object)(object)val2 == (Object)null)
						{
							continue;
						}
						Transform val3 = val2;
						while ((Object)(object)val3 != (Object)null && dictionary.Count < 100000)
						{
							if (!dictionary.ContainsKey(((Object)val3).name))
							{
								dictionary[((Object)val3).name] = val3;
							}
							val3 = val3.parent;
						}
					}
				}
			}
			return dictionary;
		}

		private static void AddBoneTree(Transform t, Dictionary<string, Transform> map)
		{
			if ((Object)(object)t == (Object)null)
			{
				return;
			}
			if (!map.ContainsKey(((Object)t).name))
			{
				map[((Object)t).name] = t;
			}
			for (int i = 0; i < t.childCount; i++)
			{
				Transform child = t.GetChild(i);
				if ((Object)(object)child != (Object)null)
				{
					AddBoneTree(child, map);
				}
			}
		}

		private static bool NameMatches(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			string text = name.ToLowerInvariant();
			string[] keywords = _keywords;
			foreach (string value in keywords)
			{
				if (text.Contains(value))
				{
					return true;
				}
			}
			return false;
		}
	}
	public class ZombeModelController : MonoBehaviour
	{
		private class BoneSync
		{
			public Transform ZombeBone;

			public Transform PlayerBone;

			public Quaternion BasePlayerLocalRot;

			public Quaternion BaseZombeLocalRot;

			public Quaternion BoneAlign = Quaternion.identity;
		}

		private Player _player;

		private PlayerRagdoll _playerRagdoll;

		private GameObject _visualModel;

		private Dictionary<string, Transform> _zombeBones;

		private Dictionary<string, Transform> _playerBones;

		private Dictionary<string, Transform> _sourceBones;

		private SkinnedMeshRenderer[] _playerRenderers;

		private List<BoneSync> _boneSyncs;

		private List<Transform> _sourceFollowBones;

		private bool _built;

		private bool _ragdolling;

		private Coroutine _ragdollRoutine;

		private SkinnedMeshRenderer[] _clonedRenderers;

		private int _boneFallbackCount;

		private int _diagFrames;

		private bool _diagDone;

		private bool _diagPostDone;

		private float _facingYaw;

		private float _pitch;

		private float _forwardOffset;

		private Dictionary<string, Quaternion> _bindCorrectRot;

		private bool _handPinLogged;

		private bool _footDiagLogged;

		private Transform _playerHip;

		private Transform _playerHead;

		private Vector3 _zombeUp = Vector3.up;

		private Vector3 _zombeForward = Vector3.forward;

		private float _hipToFeetY;

		private float _footLocalY;

		private Vector3 _lastTrackPos;

		private int _trackCount;

		private static readonly string[] _footNames = new string[4] { "leftfoot", "rightfoot", "lefttoebase", "righttoebase" };

		private static readonly string[] _handNames = new string[2] { "Hand_L", "Hand_R" };

		private void Start()
		{
			_player = ((Component)this).GetComponent<Player>();
			if ((Object)(object)_player == (Object)null || _player.ai)
			{
				Object.Destroy((Object)(object)this);
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(WaitForRefs());
			}
		}

		private IEnumerator WaitForRefs()
		{
			for (int i = 0; i < 120; i++)
			{
				if (_player.refs != null && (Object)(object)_player.refs.rigRoot != (Object)null && (Object)(object)_player.refs.ragdoll != (Object)null)
				{
					_playerRagdoll = _player.refs.ragdoll;
					((MonoBehaviour)this).StartCoroutine(WaitForZombeThenBuild());
					yield break;
				}
				yield return null;
			}
			ZombePlayerMod instance = ZombePlayerMod.Instance;
			if (instance != null)
			{
				ManualLogSource log = instance.Log;
				if (log != null)
				{
					log.LogWarning((object)("Player refs never became ready on '" + ((Object)((Component)this).gameObject).name + "'; cancelling model."));
				}
			}
			Object.Destroy((Object)(object)this);
		}

		private IEnumerator WaitForZombeThenBuild()
		{
			int tries = 0;
			while (!_built)
			{
				if ((Object)(object)_player == (Object)null)
				{
					Object.Destroy((Object)(object)this);
					break;
				}
				ZombeModelCache.TryFindNow();
				if (ZombeModelCache.Found)
				{
					int attempts = 0;
					while (attempts < 3 && !_built)
					{
						try
						{
							if (BuildModel())
							{
								_built = true;
								ZombePlayerMod instance = ZombePlayerMod.Instance;
								if (instance != null)
								{
									ManualLogSource log = instance.Log;
									if (log != null)
									{
										log.LogInfo((object)("Zombe model applied to '" + ((Object)((Component)this).gameObject).name + "'."));
									}
								}
								yield break;
							}
						}
						catch (Exception ex)
						{
							ZombePlayerMod instance2 = ZombePlayerMod.Instance;
							if (instance2 != null)
							{
								ManualLogSource log2 = instance2.Log;
								if (log2 != null)
								{
									log2.LogError((object)$"Zombe model build error for {((Object)((Component)this).gameObject).name} (attempt {attempts + 1}): {ex.Message}");
								}
							}
						}
						attempts++;
						yield return null;
					}
					ZombeModelCache.ResetForRetry();
				}
				else
				{
					int num = tries + 1;
					tries = num;
					if (num % 20 == 0)
					{
						ZombePlayerMod instance3 = ZombePlayerMod.Instance;
						if (instance3 != null)
						{
							ManualLogSource log3 = instance3.Log;
							if (log3 != null)
							{
								log3.LogDebug((object)$"Still waiting for the Zombe to load in for '{((Object)((Component)this).gameObject).name}' (tries={tries}).");
							}
						}
					}
				}
				yield return (object)new WaitForSeconds(1f);
			}
		}

		private bool BuildModel()
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			_playerBones = CollectBoneMap(_player.refs?.rigRoot);
			_sourceBones = ZombeModelCache.SourceBones ?? new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			_playerRenderers = (from r in ((Component)_player).GetComponentsInChildren<SkinnedMeshRenderer>(true)
				where (Object)(object)r != (Object)null
				select r).ToArray();
			_visualModel = new GameObject("ZombeModel_" + ((Object)((Component)this).gameObject).name);
			_visualModel.layer = ((Component)this).gameObject.layer;
			_visualModel.transform.SetParent(((Component)this).transform, false);
			_zombeBones = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			if ((Object)(object)ZombeModelCache.RootBone != (Object)null)
			{
				CloneHierarchy(ZombeModelCache.RootBone, _visualModel.transform, _zombeBones);
			}
			int num = CloneMeshes();
			CacheBindCorrections();
			if (num <= 0 || _zombeBones.Count == 0)
			{
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						log.LogWarning((object)$"Zombe clone for '{((Object)((Component)this).gameObject).name}' produced no usable renderers ({num}) or bones ({_zombeBones.Count}); skipping.");
					}
				}
				Object.Destroy((Object)(object)_visualModel);
				_visualModel = null;
				return false;
			}
			FitAndAlign();
			HideOriginalBody();
			BuildBoneSync();
			return true;
		}

		private static Dictionary<string, Transform> CollectBoneMap(GameObject root)
		{
			Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			if ((Object)(object)root == (Object)null)
			{
				return dictionary;
			}
			Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !dictionary.ContainsKey(((Object)val).name))
				{
					dictionary[((Object)val).name] = val;
				}
			}
			return dictionary;
		}

		private void CloneHierarchy(Transform source, Transform parent, Dictionary<string, Transform> boneMap)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0044: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			if ((Object)(object)source == (Object)null)
			{
				return;
			}
			GameObject val = new GameObject(((Object)source).name);
			val.layer = ((Component)source).gameObject.layer;
			val.transform.SetParent(parent, false);
			val.transform.localPosition = source.localPosition;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = source.localScale;
			if (!boneMap.ContainsKey(((Object)source).name))
			{
				boneMap[((Object)source).name] = val.transform;
			}
			foreach (Transform item in source)
			{
				Transform val2 = item;
				if ((Object)(object)val2 != (Object)null)
				{
					CloneHierarchy(val2, val.transform, boneMap);
				}
			}
		}

		private int CloneMeshes()
		{
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			List<SkinnedMeshRenderer> list = new List<SkinnedMeshRenderer>();
			SkinnedMeshRenderer[] zombeRenderers = ZombeModelCache.ZombeRenderers;
			foreach (SkinnedMeshRenderer val in zombeRenderers)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val.sharedMesh == (Object)null || val.bones == null)
				{
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, _visualModel.transform, false);
				((Object)val2).name = ((Object)val).name + "_ZombeMesh";
				val2.layer = ((Component)val).gameObject.layer;
				SkinnedMeshRenderer component = val2.GetComponent<SkinnedMeshRenderer>();
				if ((Object)(object)component == (Object)null)
				{
					Object.Destroy((Object)(object)val2);
					continue;
				}
				_boneFallbackCount = 0;
				component.rootBone = ResolveBone(val.rootBone, _visualModel.transform);
				if (val.bones.Length != 0)
				{
					Transform[] array = (Transform[])(object)new Transform[val.bones.Length];
					for (int j = 0; j < val.bones.Length; j++)
					{
						array[j] = ResolveBone(val.bones[j], _visualModel.transform);
					}
					component.bones = array;
				}
				component.sharedMesh = val.sharedMesh;
				((Renderer)component).sharedMaterials = ((Renderer)val).sharedMaterials;
				((Renderer)component).shadowCastingMode = ((Renderer)val).shadowCastingMode;
				((Renderer)component).receiveShadows = ((Renderer)val).receiveShadows;
				component.updateWhenOffscreen = true;
				((Renderer)component).enabled = true;
				list.Add(component);
				num++;
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						object[] obj = new object[10]
						{
							((Object)val).name,
							((Object)val.sharedMesh).name,
							null,
							null,
							null,
							null,
							null,
							null,
							null,
							null
						};
						Bounds bounds = val.sharedMesh.bounds;
						obj[2] = ((Bounds)(ref bounds)).center;
						bounds = val.sharedMesh.bounds;
						obj[3] = ((Bounds)(ref bounds)).size;
						Matrix4x4[] bindposes = val.sharedMesh.bindposes;
						obj[4] = ((bindposes != null) ? bindposes.Length : 0);
						obj[5] = component.bones.Length;
						obj[6] = val.sharedMesh.vertexCount;
						obj[7] = val2.layer;
						obj[8] = _boneFallbackCount;
						obj[9] = (((Object)(object)component.rootBone != (Object)null) ? ((Object)component.rootBone).name : "null");
						log.LogInfo((object)string.Format("Clone diagnostic: '{0}' mesh='{1}' localCenter={2:F2} localSize={3:F2} bindposes={4} bones={5} vertexCount={6} layer={7} boneFallbacks={8} rootBone='{9}'.", obj));
					}
				}
				_boneFallbackCount = 0;
			}
			_clonedRenderers = list.ToArray();
			LogRenderContext();
			return num;
		}

		private void LogRenderContext()
		{
			try
			{
				Camera[] allCameras = Camera.allCameras;
				if (allCameras == null || allCameras.Length == 0)
				{
					ZombePlayerMod instance = ZombePlayerMod.Instance;
					if (instance != null)
					{
						ManualLogSource log = instance.Log;
						if (log != null)
						{
							log.LogInfo((object)"Render context: no cameras yet.");
						}
					}
					return;
				}
				Camera[] array = allCameras;
				foreach (Camera val in array)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					ZombePlayerMod instance2 = ZombePlayerMod.Instance;
					if (instance2 != null)
					{
						ManualLogSource log2 = instance2.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)$"Render context: camera '{((Object)val).name}' enabled={((Behaviour)val).enabled} cullingMask=0x{val.cullingMask:x8} near={val.nearClipPlane:0.00} far={val.farClipPlane:0.00}");
						}
					}
				}
			}
			catch (Exception ex)
			{
				ZombePlayerMod instance3 = ZombePlayerMod.Instance;
				if (instance3 != null)
				{
					ManualLogSource log3 = instance3.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)("Render context diagnostic failed: " + ex.Message));
					}
				}
			}
		}

		private Transform ResolveBone(Transform original, Transform fallbackRoot)
		{
			if ((Object)(object)original == (Object)null)
			{
				return fallbackRoot;
			}
			if (_zombeBones.TryGetValue(((Object)original).name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Transform parent = original.parent;
			while ((Object)(object)parent != (Object)null)
			{
				if (_zombeBones.TryGetValue(((Object)parent).name, out value) && (Object)(object)value != (Object)null)
				{
					return value;
				}
				parent = parent.parent;
			}
			_boneFallbackCount++;
			return fallbackRoot;
		}

		private void FitAndAlign()
		{
			//IL_0073: 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_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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: 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_01c6: 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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			float value = ZombePlayerMod.ModelScale.Value;
			_facingYaw = ZombePlayerMod.ModelFacingYaw.Value;
			_pitch = ZombePlayerMod.ModelPitch.Value;
			_forwardOffset = ZombePlayerMod.ModelForwardOffset.Value;
			SkinnedMeshRenderer[] renderers = (from r in _visualModel.GetComponentsInChildren<SkinnedMeshRenderer>(true)
				where (Object)(object)r != (Object)null && (Object)(object)r.sharedMesh != (Object)null
				select r).ToArray();
			Bounds val = CombineBounds(renderers);
			Bounds val2 = CombineBounds(_playerRenderers);
			if (((Bounds)(ref val2)).size.y > 0.01f && ((Bounds)(ref val)).size.y > 0.01f)
			{
				float num = value * (((Bounds)(ref val2)).size.y / ((Bounds)(ref val)).size.y);
				_visualModel.transform.localScale = new Vector3(num, num, num);
			}
			else
			{
				_visualModel.transform.localScale = new Vector3(value, value, value);
			}
			Bounds val3 = CombineBounds(renderers);
			_footLocalY = ((Bounds)(ref val3)).min.y - _visualModel.transform.position.y;
			_playerHip = null;
			_playerHead = null;
			if (_playerBones != null)
			{
				_playerBones.TryGetValue("hip", out _playerHip);
				_playerBones.TryGetValue("head", out _playerHead);
			}
			if ((Object)(object)_playerHip != (Object)null)
			{
				_hipToFeetY = ((Bounds)(ref val2)).min.y - _playerHip.position.y;
			}
			_zombeUp = Vector3.up;
			try
			{
				SkinnedMeshRenderer val4 = ((ZombeModelCache.ZombeRenderers != null && ZombeModelCache.ZombeRenderers.Length != 0) ? ZombeModelCache.ZombeRenderers[0] : null);
				if ((Object)(object)val4 != (Object)null && (Object)(object)val4.sharedMesh != (Object)null)
				{
					_zombeForward = Vector3.forward;
					ZombePlayerMod instance = ZombePlayerMod.Instance;
					if (instance != null)
					{
						ManualLogSource log = instance.Log;
						if (log != null)
						{
							log.LogInfo((object)$"Zombe bind frame: up={_zombeUp:F2} forward={_zombeForward:F2}");
						}
					}
				}
			}
			catch (Exception ex)
			{
				ZombePlayerMod instance2 = ZombePlayerMod.Instance;
				if (instance2 != null)
				{
					ManualLogSource log2 = instance2.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Zombe bind-frame read failed: " + ex.Message));
					}
				}
			}
			ZombePlayerMod instance3 = ZombePlayerMod.Instance;
			if (instance3 != null)
			{
				ManualLogSource log3 = instance3.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"Fit: scale={_visualModel.transform.localScale.x:F3} footLocalY={_footLocalY:F2} hipToFeetY={_hipToFeetY:F2} playerHeight={((Bounds)(ref val2)).size.y:F2} zombeHeight={((Bounds)(ref val)).size.y:F2} zombeUp={_zombeUp:F2} facingYaw={_facingYaw:F0} pitch={_pitch:F0}");
				}
			}
		}

		private void AnchorToPlayer()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_00e6: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: 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_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_visualModel == (Object)null)
			{
				return;
			}
			if ((Object)(object)_playerHip == (Object)null || (Object)(object)_playerHead == (Object)null)
			{
				_visualModel.transform.rotation = Quaternion.Euler(0f, _facingYaw, 0f) * Quaternion.Euler(_pitch, 0f, 0f);
				_visualModel.transform.position = ((Component)this).transform.position;
				return;
			}
			Transform val = ((_player.refs != null && (Object)(object)_player.refs.rigRoot != (Object)null) ? _player.refs.rigRoot.transform : ((Component)this).transform);
			Quaternion rotation = val.rotation;
			Quaternion val2 = rotation * Quaternion.Euler(_pitch, 0f, 0f);
			_visualModel.transform.rotation = val2;
			_visualModel.transform.position = _playerHip.position;
			Transform cloneHead = GetCloneHead();
			if ((Object)(object)_playerHead != (Object)null && (Object)(object)cloneHead != (Object)null)
			{
				Transform transform = _visualModel.transform;
				transform.position += _playerHead.position - cloneHead.position;
			}
			Transform transform2 = _visualModel.transform;
			transform2.position += new Vector3(0f, FootGroundSnapY(), 0f);
			Transform transform3 = _visualModel.transform;
			transform3.position += val2 * Vector3.forward * _forwardOffset;
		}

		private static Bounds CombineBounds(SkinnedMeshRenderer[] renderers)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			Bounds result = default(Bounds);
			if (renderers == null)
			{
				return result;
			}
			foreach (SkinnedMeshRenderer val in renderers)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null))
				{
					if (!flag)
					{
						result = ((Renderer)val).bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(((Renderer)val).bounds);
					}
				}
			}
			return result;
		}

		private void HideOriginalBody()
		{
			Renderer[] componentsInChildren = ((Component)_player).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && (!((Object)(object)_visualModel != (Object)null) || !((Component)val).transform.IsChildOf(_visualModel.transform)))
				{
					val.enabled = false;
				}
			}
		}

		private void ForceCloneRenderers()
		{
			if (_clonedRenderers == null)
			{
				return;
			}
			for (int i = 0; i < _clonedRenderers.Length; i++)
			{
				SkinnedMeshRenderer val = _clonedRenderers[i];
				if (!((Object)(object)val == (Object)null))
				{
					if (!((Component)val).gameObject.activeSelf)
					{
						((Component)val).gameObject.SetActive(true);
					}
					if (!((Renderer)val).enabled)
					{
						((Renderer)val).enabled = true;
					}
				}
			}
		}

		private void DisableCloneRenderers()
		{
			if (_clonedRenderers == null)
			{
				return;
			}
			for (int i = 0; i < _clonedRenderers.Length; i++)
			{
				SkinnedMeshRenderer val = _clonedRenderers[i];
				if (!((Object)(object)val == (Object)null))
				{
					((Renderer)val).enabled = false;
				}
			}
		}

		private Transform GetCloneHead()
		{
			if (_zombeBones != null && _zombeBones.TryGetValue("head", out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			return null;
		}

		private void OnEnable()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnCameraPreCull));
		}

		private void OnDisable()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnCameraPreCull));
		}

		private void OnCameraPreCull(Camera cam)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!_built)
			{
				return;
			}
			if ((Object)(object)_player == (Object)(object)Player.localPlayer && (Object)(object)cam == (Object)(object)Camera.main)
			{
				DisableCloneRenderers();
				AnchorToPlayer();
				Transform cloneHead = GetCloneHead();
				if ((Object)(object)cam != (Object)null && (Object)(object)cloneHead != (Object)null)
				{
					((Component)cam).transform.position = cloneHead.position;
				}
			}
			else
			{
				ForceCloneRenderers();
				AnchorToPlayer();
			}
		}

		private void BuildBoneSync()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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)
			_boneSyncs = new List<BoneSync>();
			_sourceFollowBones = new List<Transform>();
			foreach (KeyValuePair<string, Transform> zombeBone in _zombeBones)
			{
				if (!((Object)(object)zombeBone.Value == (Object)(object)_visualModel.transform))
				{
					Transform value2;
					if (_playerBones.TryGetValue(zombeBone.Key, out var value) && (Object)(object)value != (Object)null)
					{
						BoneSync item = new BoneSync
						{
							ZombeBone = zombeBone.Value,
							PlayerBone = value,
							BasePlayerLocalRot = value.localRotation,
							BaseZombeLocalRot = value.localRotation
						};
						_boneSyncs.Add(item);
					}
					else if (_sourceBones != null && _sourceBones.TryGetValue(zombeBone.Key, out value2) && (Object)(object)value2 != (Object)null)
					{
						_sourceFollowBones.Add(zombeBone.Value);
					}
				}
			}
			ZombePlayerMod instance = ZombePlayerMod.Instance;
			if (instance != null)
			{
				ManualLogSource log = instance.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Bone sync on '{((Object)((Component)this).gameObject).name}': {_boneSyncs.Count} animated bones, {_sourceFollowBones.Count} zombe-only bones.");
				}
			}
			LogHandArmCandidates();
		}

		private void LogHandArmCandidates()
		{
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				foreach (KeyValuePair<string, Transform> playerBone in _playerBones)
				{
					if (playerBone.Key.IndexOf("hand", StringComparison.OrdinalIgnoreCase) >= 0 || playerBone.Key.IndexOf("arm", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						stringBuilder.Append(" P:" + playerBone.Key);
					}
				}
				foreach (KeyValuePair<string, Transform> zombeBone in _zombeBones)
				{
					if (zombeBone.Key.IndexOf("hand", StringComparison.OrdinalIgnoreCase) >= 0 || zombeBone.Key.IndexOf("arm", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						stringBuilder.Append(" Z:" + zombeBone.Key);
					}
				}
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						log.LogInfo((object)$"Hand/arm candidates:{stringBuilder}");
					}
				}
			}
			catch
			{
			}
		}

		private void DriveFromPlayerRig()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0051: 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_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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: 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_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: 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_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: 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_01c3: 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_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: 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)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: 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_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _boneSyncs.Count; i++)
			{
				BoneSync boneSync = _boneSyncs[i];
				if (!((Object)(object)boneSync.PlayerBone == (Object)null) && !((Object)(object)boneSync.ZombeBone == (Object)null))
				{
					Quaternion val = Quaternion.Inverse(boneSync.BasePlayerLocalRot) * boneSync.PlayerBone.localRotation;
					boneSync.ZombeBone.localRotation = boneSync.BoneAlign * (boneSync.BaseZombeLocalRot * val);
				}
			}
			for (int j = 0; j < _boneSyncs.Count; j++)
			{
				BoneSync boneSync2 = _boneSyncs[j];
				if ((Object)(object)boneSync2.PlayerBone == (Object)null || (Object)(object)boneSync2.ZombeBone == (Object)null || !IsFootBone(((Object)boneSync2.ZombeBone).name) || _bindCorrectRot == null || !_bindCorrectRot.TryGetValue(((Object)boneSync2.ZombeBone).name, out var value))
				{
					continue;
				}
				Quaternion val2 = boneSync2.PlayerBone.rotation * Quaternion.Euler(180f, 0f, 0f) * value;
				Transform val3 = (((Object)(object)boneSync2.ZombeBone.parent != (Object)null) ? boneSync2.ZombeBone.parent : _visualModel.transform);
				boneSync2.ZombeBone.localRotation = Quaternion.Inverse(val3.rotation) * val2;
				if (_footDiagLogged)
				{
					continue;
				}
				_footDiagLogged = true;
				Quaternion rotation = boneSync2.PlayerBone.rotation;
				Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles;
				rotation = boneSync2.ZombeBone.rotation;
				Vector3 eulerAngles2 = ((Quaternion)(ref rotation)).eulerAngles;
				Quaternion val4 = Quaternion.Inverse(boneSync2.PlayerBone.rotation) * boneSync2.ZombeBone.rotation;
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						log.LogInfo((object)$"FootDiag '{((Object)boneSync2.ZombeBone).name}': playerWorld={eulerAngles:F0} zombeWorld={eulerAngles2:F0} delta={((Quaternion)(ref val4)).eulerAngles:F0}");
					}
				}
			}
			for (int k = 0; k < _handNames.Length; k++)
			{
				if (_playerBones == null || !_playerBones.TryGetValue(_handNames[k], out var value2) || _zombeBones == null || !_zombeBones.TryGetValue(_handNames[k], out var value3))
				{
					continue;
				}
				if (!_handPinLogged)
				{
					_handPinLogged = true;
					ZombePlayerMod instance2 = ZombePlayerMod.Instance;
					if (instance2 != null)
					{
						ManualLogSource log2 = instance2.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)$"Hand pin matched '{_handNames[k]}' pHand={value2.position:F2} zHand={value3.position:F2} raise={ZombePlayerMod.HandRaise.Value:F2}");
						}
					}
				}
				value3.position = value2.position + Vector3.up * ZombePlayerMod.HandRaise.Value;
			}
			if (_sourceBones == null || _sourceFollowBones == null)
			{
				return;
			}
			for (int l = 0; l < _sourceFollowBones.Count; l++)
			{
				Transform val5 = _sourceFollowBones[l];
				if (!((Object)(object)val5 == (Object)null) && _sourceBones.TryGetValue(((Object)val5).name, out var value4) && (Object)(object)value4 != (Object)null)
				{
					val5.localRotation = value4.localRotation;
				}
			}
		}

		private static bool IsFootBone(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			return name.IndexOf("foot", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("toe", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("shoe", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private void CacheBindCorrections()
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			_bindCorrectRot = null;
			try
			{
				SkinnedMeshRenderer val = ((ZombeModelCache.ZombeRenderers != null && ZombeModelCache.ZombeRenderers.Length != 0) ? ZombeModelCache.ZombeRenderers[0] : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val.sharedMesh == (Object)null || val.bones == null || val.sharedMesh.bindposes == null || val.sharedMesh.bindposes.Length == 0)
				{
					return;
				}
				for (int i = 0; i < val.bones.Length; i++)
				{
					Transform val2 = val.bones[i];
					if ((Object)(object)val2 == (Object)null || i >= val.sharedMesh.bindposes.Length || !IsFootBone(((Object)val2).name))
					{
						continue;
					}
					if (_bindCorrectRot == null)
					{
						_bindCorrectRot = new Dictionary<string, Quaternion>();
					}
					Dictionary<string, Quaternion> bindCorrectRot = _bindCorrectRot;
					string name = ((Object)val2).name;
					Matrix4x4 inverse = ((Matrix4x4)(ref val.sharedMesh.bindposes[i])).inverse;
					bindCorrectRot[name] = ((Matrix4x4)(ref inverse)).rotation;
					ZombePlayerMod instance = ZombePlayerMod.Instance;
					if (instance != null)
					{
						ManualLogSource log = instance.Log;
						if (log != null)
						{
							string name2 = ((Object)val2).name;
							inverse = ((Matrix4x4)(ref val.sharedMesh.bindposes[i])).inverse;
							Quaternion rotation = ((Matrix4x4)(ref inverse)).rotation;
							log.LogInfo((object)$"Bind correction '{name2}' = {((Quaternion)(ref rotation)).eulerAngles:F0}");
						}
					}
				}
			}
			catch (Exception ex)
			{
				ZombePlayerMod instance2 = ZombePlayerMod.Instance;
				if (instance2 != null)
				{
					ManualLogSource log2 = instance2.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("Bind correction cache failed: " + ex.Message));
					}
				}
			}
		}

		private float FootGroundSnapY()
		{
			//IL_0046: 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)
			try
			{
				float num = float.MaxValue;
				float num2 = float.MaxValue;
				for (int i = 0; i < _footNames.Length; i++)
				{
					if (_zombeBones != null && _zombeBones.TryGetValue(_footNames[i], out var value) && (Object)(object)value != (Object)null)
					{
						num = Mathf.Min(num, value.position.y);
					}
					if (_playerBones != null && _playerBones.TryGetValue(_footNames[i], out var value2) && (Object)(object)value2 != (Object)null)
					{
						num2 = Mathf.Min(num2, value2.position.y);
					}
				}
				if (num == float.MaxValue || num2 == float.MaxValue)
				{
					return 0f;
				}
				return num2 - num;
			}
			catch (Exception ex)
			{
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						log.LogDebug((object)("Foot ground-snap skipped: " + ex.Message));
					}
				}
				return 0f;
			}
		}

		private void DriveRagdollOnce()
		{
			//IL_0098: 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)
			if ((Object)(object)_playerRagdoll == (Object)null || _zombeBones == null)
			{
				return;
			}
			Bodypart[] componentsInChildren = ((Component)_playerRagdoll).GetComponentsInChildren<Bodypart>(true);
			Bodypart[] array = componentsInChildren;
			foreach (Bodypart val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Transform transform = ((Component)val).transform;
				if (!((Object)(object)transform == (Object)null))
				{
					Transform value;
					Transform val2 = (_zombeBones.TryGetValue(((Object)transform).name, out value) ? value : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						val2.position = transform.position;
						val2.rotation = transform.rotation;
					}
				}
			}
		}

		private IEnumerator RagdollLoop()
		{
			while ((Object)(object)_player != (Object)null && _player.data != null && _player.data.dead)
			{
				DriveRagdollOnce();
				yield return (object)new WaitForFixedUpdate();
			}
			_ragdolling = false;
			if (!((Object)(object)_player != (Object)null) || _player.data == null || _player.data.dead)
			{
				yield break;
			}
			ZombePlayerMod instance = ZombePlayerMod.Instance;
			if (instance != null)
			{
				ManualLogSource log = instance.Log;
				if (log != null)
				{
					log.LogDebug((object)("Ragdoll ended, revived " + ((Object)((Component)this).gameObject).name + "."));
				}
			}
		}

		public void OnPlayerRagdollActivated()
		{
			if (_built && !_ragdolling)
			{
				_ragdolling = true;
				_ragdollRoutine = ((MonoBehaviour)this).StartCoroutine(RagdollLoop());
			}
		}

		private void LateUpdate()
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: 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)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0434: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_0508: Unknown result type (might be due to invalid IL or missing references)
			if (!_built || (Object)(object)_player == (Object)null || _player.data == null)
			{
				return;
			}
			AnchorToPlayer();
			_diagFrames++;
			if (!_diagDone && _diagFrames >= 5 && _clonedRenderers != null)
			{
				_diagDone = true;
				SkinnedMeshRenderer[] clonedRenderers = _clonedRenderers;
				foreach (SkinnedMeshRenderer val in clonedRenderers)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					ZombePlayerMod instance = ZombePlayerMod.Instance;
					if (instance != null)
					{
						ManualLogSource log = instance.Log;
						if (log != null)
						{
							object[] obj = new object[8]
							{
								((Object)val).name,
								((Renderer)val).isVisible,
								((Renderer)val).enabled,
								((Component)val).gameObject.activeSelf,
								null,
								null,
								null,
								null
							};
							Bounds bounds = ((Renderer)val).bounds;
							obj[4] = ((Bounds)(ref bounds)).min;
							bounds = ((Renderer)val).bounds;
							obj[5] = ((Bounds)(ref bounds)).max;
							obj[6] = _visualModel.transform.position;
							obj[7] = ((Component)this).transform.position;
							log.LogInfo((object)string.Format("Render check: '{0}' visible={1} enabled={2} active={3} boundsMin={4:F2} boundsMax={5:F2} modelPos={6:F2} playerPos={7:F2}", obj));
						}
					}
				}
			}
			if (!_diagPostDone && _diagFrames >= 15 && _clonedRenderers != null)
			{
				_diagPostDone = true;
				ForceCloneRenderers();
				SkinnedMeshRenderer[] clonedRenderers2 = _clonedRenderers;
				foreach (SkinnedMeshRenderer val2 in clonedRenderers2)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					ZombePlayerMod instance2 = ZombePlayerMod.Instance;
					if (instance2 != null)
					{
						ManualLogSource log2 = instance2.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)$"Render check (after force-enable): '{((Object)val2).name}' visible={((Renderer)val2).isVisible} enabled={((Renderer)val2).enabled} active={((Component)val2).gameObject.activeSelf}");
						}
					}
				}
			}
			if (_diagFrames == 25)
			{
				ZombePlayerMod instance3 = ZombePlayerMod.Instance;
				if (instance3 != null)
				{
					ManualLogSource log3 = instance3.Log;
					if (log3 != null)
					{
						log3.LogInfo((object)$"Render track: modelPos={_visualModel.transform.position:F2} playerPos={((Component)this).transform.position:F2} hipPos={(((Object)(object)_playerHip != (Object)null) ? _playerHip.position : Vector3.zero):F2} modelOffset={((Component)this).transform.position - _visualModel.transform.position:F2} modelYaw={_visualModel.transform.eulerAngles.y:F1}");
					}
				}
				_lastTrackPos = ((Component)this).transform.position;
			}
			if (_diagFrames >= 25 && _trackCount < 4)
			{
				Vector3 val3 = ((Component)this).transform.position - _lastTrackPos;
				if (((Vector3)(ref val3)).sqrMagnitude > 2.25f)
				{
					_trackCount++;
					_lastTrackPos = ((Component)this).transform.position;
					ZombePlayerMod instance4 = ZombePlayerMod.Instance;
					if (instance4 != null)
					{
						ManualLogSource log4 = instance4.Log;
						if (log4 != null)
						{
							log4.LogInfo((object)$"Render move({_trackCount}): modelPos={_visualModel.transform.position:F2} playerPos={((Component)this).transform.position:F2} hipPos={(((Object)(object)_playerHip != (Object)null) ? _playerHip.position : Vector3.zero):F2}");
						}
					}
				}
			}
			ForceCloneRenderers();
			if (_player.data.dead)
			{
				if (!_ragdolling)
				{
					OnPlayerRagdollActivated();
				}
				return;
			}
			if (_ragdolling)
			{
				_ragdolling = false;
				if (_ragdollRoutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_ragdollRoutine);
				}
			}
			DriveFromPlayerRig();
			if ((Object)(object)_player == (Object)(object)Player.localPlayer)
			{
				Camera main = Camera.main;
				Transform cloneHead = GetCloneHead();
				if ((Object)(object)main != (Object)null && (Object)(object)cloneHead != (Object)null)
				{
					((Component)main).transform.position = cloneHead.position;
				}
			}
		}

		private void Cleanup()
		{
			if ((Object)(object)_visualModel != (Object)null)
			{
				Object.Destroy((Object)(object)_visualModel);
				_visualModel = null;
			}
			if (_playerRenderers == null)
			{
				return;
			}
			SkinnedMeshRenderer[] playerRenderers = _playerRenderers;
			foreach (SkinnedMeshRenderer val in playerRenderers)
			{
				if ((Object)(object)val != (Object)null)
				{
					((Renderer)val).enabled = true;
				}
			}
			if (!((Object)(object)_player != (Object)null))
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)_player).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					val2.enabled = true;
				}
			}
		}

		private void OnDestroy()
		{
			Cleanup();
		}
	}
	public static class ZombeNetworkHandler
	{
		public const uint MOD_ID = 5918018u;

		private static bool _cwApiAvailable;

		private static bool _checked;

		public static bool IsCwApiAvailable => _cwApiAvailable;

		public static void Init()
		{
			if (_checked)
			{
				return;
			}
			_checked = true;
			try
			{
				_cwApiAvailable = Type.GetType("MyceliumNetworking.MyceliumNetwork, MyceliumNetworking", throwOnError: false) != null;
			}
			catch
			{
				_cwApiAvailable = false;
			}
			ZombePlayerMod instance = ZombePlayerMod.Instance;
			if (instance != null)
			{
				ManualLogSource log = instance.Log;
				if (log != null)
				{
					log.LogInfo((object)(_cwApiAvailable ? "Mycelium (CWAPI) detected; Zombe networking available." : "Mycelium (CWAPI) not installed; running in client-side mode (recommended for private lobbies)."));
				}
			}
		}

		public static ulong GetPlayerUserId(Player player)
		{
			try
			{
				if (player?.data == null)
				{
					return 0uL;
				}
				return player.data.userID;
			}
			catch
			{
				return 0uL;
			}
		}
	}
	[HarmonyPatch(typeof(Player))]
	internal static class PlayerPatches
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		internal static void OnPlayerStart(Player __instance)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && !__instance.ai && ZombePlayerMod.EnableModelReplacement.Value && (!((Object)(object)__instance == (Object)(object)Player.localPlayer) || ZombePlayerMod.ShowOwnModel.Value) && (Object)(object)((Component)__instance).GetComponent<ZombeModelController>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<ZombeModelController>();
				}
			}
			catch (Exception arg)
			{
				ZombePlayerMod instance = ZombePlayerMod.Instance;
				if (instance != null)
				{
					ManualLogSource log = instance.Log;
					if (log != null)
					{
						log.LogError((object)$"Player.Start postfix error: {arg}");
					}
				}
			}
		}

		[HarmonyPatch("Die")]
		[HarmonyPostfix]
		internal static void OnPlayerDie(Player __instance)
		{
			ZombeModelController zombeModelController = ((__instance != null) ? ((Component)__instance).GetComponent<ZombeModelController>() : null);
			if ((Object)(object)zombeModelController != (Object)null)
			{
				zombeModelController.OnPlayerRagdollActivated();
			}
		}
	}
	[HarmonyPatch(typeof(PlayerRagdoll))]
	internal static class PlayerRagdollPatches
	{
		[HarmonyPatch("Fall")]
		[HarmonyPostfix]
		internal static void OnRagdollFall(PlayerRagdoll __instance)
		{
			NotifyZombe(__instance);
		}

		[HarmonyPatch("CallFall")]
		[HarmonyPostfix]
		internal static void OnRagdollCallFall(PlayerRagdoll __instance)
		{
			NotifyZombe(__instance);
		}

		[HarmonyPatch("OnEnable")]
		[HarmonyPostfix]
		internal static void OnRagdollEnable(PlayerRagdoll __instance)
		{
			NotifyZombe(__instance);
		}

		private static void NotifyZombe(PlayerRagdoll ragdoll)
		{
			if ((Object)(object)ragdoll == (Object)null)
			{
				return;
			}
			Player componentInParent = ((Component)ragdoll).GetComponentInParent<Player>();
			if (!((Object)(object)componentInParent == (Object)null))
			{
				ZombeModelController component = ((Component)componentInParent).GetComponent<ZombeModelController>();
				if ((Object)(object)component != (Object)null)
				{
					component.OnPlayerRagdollActivated();
				}
			}
		}
	}
}