Decompiled source of Better First Person v1.1.2

BetterFirstPerson.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using On.EntityStates;
using On.RoR2;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace BetterFirstPerson;

internal static class EmotesSupport
{
	private const string EmotesGuid = "com.weliveinasociety.CustomEmotesAPI";

	private const BindingFlags Static = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	private const BindingFlags Instance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	private static bool looked;

	private static MemberInfo localMapper;

	private static MemberInfo clipName;

	private static Type clipOwner;

	public static bool IsLocalEmotePlaying()
	{
		if (!looked)
		{
			Look();
		}
		if (localMapper == null)
		{
			return false;
		}
		try
		{
			object obj = Read(localMapper, null);
			if (obj != null)
			{
				Object val = (Object)((obj is Object) ? obj : null);
				if (val == null || Object.op_Implicit(val))
				{
					Type type = obj.GetType();
					if (clipName == null || clipOwner != type)
					{
						clipOwner = type;
						clipName = Member(type, "currentClipName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (clipName == null)
						{
							return false;
						}
					}
					string text = Read(clipName, obj) as string;
					return !string.IsNullOrEmpty(text) && text != "none";
				}
			}
			return false;
		}
		catch (Exception)
		{
			return false;
		}
	}

	private static void Look()
	{
		looked = true;
		try
		{
			if (Chainloader.PluginInfos.TryGetValue("com.weliveinasociety.CustomEmotesAPI", out var value) && Object.op_Implicit((Object)(object)value.Instance))
			{
				Type type = ((object)value.Instance).GetType().Assembly.GetType("EmotesAPI.CustomEmotesAPI", throwOnError: false);
				if (type != null)
				{
					localMapper = Member(type, "localMapper", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("CustomEmotesAPI found but not readable: " + ex.Message));
		}
	}

	private static MemberInfo Member(Type type, string name, BindingFlags flags)
	{
		FieldInfo field = type.GetField(name, flags);
		if (field != null)
		{
			return field;
		}
		return type.GetProperty(name, flags);
	}

	private static object Read(MemberInfo member, object target)
	{
		if (member is FieldInfo fieldInfo)
		{
			return fieldInfo.GetValue(target);
		}
		if (member is PropertyInfo propertyInfo)
		{
			return propertyInfo.GetValue(target, null);
		}
		return null;
	}
}
internal static class FieldOfView
{
	private const float MaxWidening = 1.41f;

	private static float TanHalf(float fov)
	{
		return Mathf.Tan(fov * 0.5f * ((float)Math.PI / 180f));
	}

	public static float VerticalToHorizontal(float verticalFov, float aspect)
	{
		return 2f * Mathf.Atan(TanHalf(verticalFov) * aspect) * 57.29578f;
	}

	public static float Compute(CameraRigController rig, float gameFov, float aspect, out float baseFov)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		baseFov = (Object.op_Implicit((Object)(object)rig) ? rig.baseFov : 60f);
		CameraTargetParams val = (Object.op_Implicit((Object)(object)rig) ? rig.targetParams : null);
		if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.cameraParams))
		{
			CharacterCameraParamsData basic = CharacterCameraParamsData.basic;
			CharacterCameraParamsData.Blend(ref val.cameraParams.data, ref basic, 1f);
			if (basic.fov.value > 1f)
			{
				baseFov = basic.fov.value;
			}
		}
		float num = TanHalf(Mathf.Clamp(Settings.FieldOfView.Value, 30f, 170f)) / Mathf.Max(aspect, 0.01f);
		if (gameFov < 1f || baseFov < 1f)
		{
			return 2f * Mathf.Atan(num) * 57.29578f;
		}
		float num2 = TanHalf(gameFov) / TanHalf(baseFov);
		if (num2 > 1f)
		{
			float num3 = Mathf.Clamp01(Settings.SprintFovEffect.Value);
			num2 = 1f + (Mathf.Min(num2, 1.41f) - 1f) * num3;
		}
		return Mathf.Clamp(2f * Mathf.Atan(num * num2) * 57.29578f, 1f, 170f);
	}
}
internal static class FirstPersonController
{
	private static bool wanted;

	private static CharacterBody lastBody;

	private static CharacterBody failedBody;

	private static float lastSteerTime = -1f;

	public static FirstPersonSession Session { get; private set; }

	public static void Install()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		PlayerCharacterMasterController.Update += new hook_Update(PlayerUpdate);
		CameraRigController.SetCameraState += new hook_SetCameraState(SetCameraState);
		GenericCharacterMain.HandleMovements += new hook_HandleMovements(HandleMovements);
		CharacterModel.UpdateMaterials += new hook_UpdateMaterials(UpdateMaterials);
		CharacterModel.UpdateOverlays += new hook_UpdateOverlays(UpdateOverlays);
		GenericSkill.OnExecute += new hook_OnExecute(SkillExecuted);
		Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(PreCull));
		Run.onRunDestroyGlobal += delegate
		{
			EndSession();
		};
		Application.quitting += FirstPersonVisuals.OnQuit;
	}

	private static void PlayerUpdate(orig_Update orig, PlayerCharacterMasterController self)
	{
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		orig.Invoke(self);
		NetworkUser networkUser = self.networkUser;
		LocalUser val = (Object.op_Implicit((Object)(object)networkUser) ? networkUser.localUser : null);
		if (val == null || val != LocalUserManager.GetFirstLocalUser())
		{
			return;
		}
		CameraRigController cameraRigController = networkUser.cameraRigController;
		if (!Object.op_Implicit((Object)(object)cameraRigController))
		{
			return;
		}
		CharacterMaster master = self.master;
		CharacterBody val2 = (Object.op_Implicit((Object)(object)master) ? master.GetBody() : null);
		if (Object.op_Implicit((Object)(object)val2) && val2 != lastBody)
		{
			if (lastBody != null && !Settings.RememberFirstPerson.Value)
			{
				wanted = false;
			}
			lastBody = val2;
			failedBody = null;
		}
		if (ToggleKeyPressed() && !PauseManager.isPaused && !IsTyping(val))
		{
			wanted = ((Object.op_Implicit((Object)(object)val2) && CanRun(val2, cameraRigController)) ? (Session == null) : (!wanted));
			failedBody = null;
		}
		bool flag = wanted && Object.op_Implicit((Object)(object)val2) && val2 != failedBody && CanRun(val2, cameraRigController);
		FirstPersonSession firstPersonSession = Session;
		if (firstPersonSession != null && (!flag || firstPersonSession.Body != val2 || firstPersonSession.Rig != cameraRigController))
		{
			EndSession();
			firstPersonSession = null;
		}
		if (firstPersonSession == null && flag)
		{
			firstPersonSession = StartSession(val2, cameraRigController);
			if (firstPersonSession == null)
			{
				failedBody = val2;
			}
		}
		if (firstPersonSession != null)
		{
			InputBankTest inputBank = val2.inputBank;
			if (Object.op_Implicit((Object)(object)inputBank))
			{
				inputBank.aimDirection = firstPersonSession.ViewForward;
			}
			if (Settings.BodyRotation.Value == BodyTurning.Smart)
			{
				val2.SetAimTimer(0.2f);
			}
		}
	}

	private static bool CanRun(CharacterBody body, CameraRigController rig)
	{
		HealthComponent healthComponent = body.healthComponent;
		if (!Object.op_Implicit((Object)(object)healthComponent) || !healthComponent.alive)
		{
			return false;
		}
		if (rig.target != ((Component)body).gameObject || rig.hasOverride)
		{
			return false;
		}
		VehicleSeat currentVehicle = body.currentVehicle;
		if (Object.op_Implicit((Object)(object)currentVehicle) && (currentVehicle.hidePassenger || currentVehicle.isSurvivorPod))
		{
			return false;
		}
		if (Settings.ThirdPersonWhileEmoting.Value && EmotesSupport.IsLocalEmotePlaying())
		{
			return false;
		}
		return true;
	}

	private static bool ToggleKeyPressed()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		KeyboardShortcut value = Settings.ToggleKey.Value;
		KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey;
		if ((int)mainKey == 0 || !UnityInput.Current.GetKeyDown(mainKey))
		{
			return false;
		}
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
		{
			if (!UnityInput.Current.GetKey(modifier))
			{
				return false;
			}
		}
		return true;
	}

	private static bool IsTyping(LocalUser localUser)
	{
		EventSystem eventSystem = (EventSystem)(object)localUser.eventSystem;
		GameObject val = (Object.op_Implicit((Object)(object)eventSystem) ? eventSystem.currentSelectedGameObject : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return false;
		}
		TMP_InputField component = val.GetComponent<TMP_InputField>();
		if (Object.op_Implicit((Object)(object)component) && component.isFocused)
		{
			return true;
		}
		InputField component2 = val.GetComponent<InputField>();
		if (Object.op_Implicit((Object)(object)component2))
		{
			return component2.isFocused;
		}
		return false;
	}

	private static FirstPersonSession StartSession(CharacterBody body, CameraRigController rig)
	{
		try
		{
			Session = FirstPersonSession.Start(body, rig);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("First person could not start: " + ex));
			Session = null;
		}
		return Session;
	}

	public static void EndSession()
	{
		FirstPersonSession session = Session;
		Session = null;
		if (session == null)
		{
			return;
		}
		try
		{
			session.End();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Leaving first person failed: " + ex));
		}
	}

	private static void Fail(Exception exception)
	{
		Plugin.Log.LogError((object)("First person stopped after an error, press the key to try again: " + exception));
		failedBody = ((Session != null) ? Session.Body : null);
		EndSession();
	}

	private static void SetCameraState(orig_SetCameraState orig, CameraRigController self, CameraState cameraState)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		orig.Invoke(self, cameraState);
		FirstPersonSession session = Session;
		if (session == null || session.Rig != self)
		{
			return;
		}
		try
		{
			session.AfterRigCameraState(cameraState.fov);
		}
		catch (Exception exception)
		{
			Fail(exception);
		}
	}

	private static void PreCull(Camera camera)
	{
		FirstPersonSession session = Session;
		if (session == null || session.Camera != camera)
		{
			return;
		}
		try
		{
			session.OnPreCull();
		}
		catch (Exception exception)
		{
			Fail(exception);
		}
	}

	private static void UpdateMaterials(orig_UpdateMaterials orig, CharacterModel self)
	{
		orig.Invoke(self);
		if (Session != null)
		{
			FirstPersonVisuals.AfterModelMaterialsUpdated(self);
		}
	}

	private static void UpdateOverlays(orig_UpdateOverlays orig, CharacterModel self)
	{
		orig.Invoke(self);
		FirstPersonSession session = Session;
		if (session != null && self == session.CharacterModel && Settings.HideOwnBursts.Value)
		{
			FirstPersonVisuals.StripOverlays(self);
		}
	}

	private static void SkillExecuted(orig_OnExecute orig, GenericSkill self)
	{
		orig.Invoke(self);
		FirstPersonSession session = Session;
		if (session != null && self.characterBody == session.Body)
		{
			FirstPersonVisuals.OnSkillExecuted(self);
		}
	}

	private static void HandleMovements(orig_HandleMovements orig, GenericCharacterMain self)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		orig.Invoke(self);
		FirstPersonSession session = Session;
		if (session == null || Settings.BodyRotation.Value != BodyTurning.FollowCamera || !session.OwnsStateMachine(((EntityState)self).outer))
		{
			return;
		}
		CharacterBody body = session.Body;
		CharacterDirection val = (Object.op_Implicit((Object)(object)body) ? body.characterDirection : null);
		InputBankTest val2 = (Object.op_Implicit((Object)(object)body) ? body.inputBank : null);
		if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2))
		{
			Vector3 aimDirection = val2.aimDirection;
			aimDirection.y = 0f;
			if (!(((Vector3)(ref aimDirection)).sqrMagnitude < 0.0001f))
			{
				val.moveVector = ((Vector3)(ref aimDirection)).normalized;
				lastSteerTime = Time.fixedTime;
			}
		}
	}

	internal static bool SnapBodyYaw(FirstPersonSession session, float yaw)
	{
		if (Settings.BodyRotation.Value != BodyTurning.FollowCamera)
		{
			return false;
		}
		if (Time.fixedTime - lastSteerTime > Time.fixedDeltaTime * 2f)
		{
			return false;
		}
		CharacterDirection val = (Object.op_Implicit((Object)(object)session.Body) ? session.Body.characterDirection : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return false;
		}
		val.yaw = yaw;
		return true;
	}
}
internal sealed class FirstPersonSession
{
	private static readonly HashSet<int> logged = new HashSet<int>();

	public readonly CharacterBody Body;

	public readonly GameObject BodyObject;

	public readonly CameraRigController Rig;

	public readonly Camera Camera;

	public readonly Transform Model;

	public readonly CharacterModel CharacterModel;

	public readonly ChildLocator Locator;

	public readonly HeadAnchor Head;

	public readonly string BodyName;

	public readonly SurvivorSettings Survivor;

	public readonly int HideLayer;

	private readonly EntityStateMachine[] stateMachines;

	private readonly WeaponPose weaponPose;

	private readonly Transform cameraTransform;

	private readonly bool cameraIsChild;

	private readonly Vector3 cameraLocalPosition;

	private readonly Quaternion cameraLocalRotation;

	private readonly float originalNearClip;

	private readonly bool hideLayerWasDrawn;

	private readonly bool originalFading;

	private readonly Transform aimOrigin;

	private readonly Vector3 aimOriginLocalPosition;

	private readonly AimAnimator aimAnimator;

	private bool aimDiagnosticBroken;

	private float lastGameFov;

	private bool fovLogged;

	private Vector3 steadyOffset;

	private bool steadyValid;

	private float steadyTime;

	public Vector3 ViewForward => cameraTransform.forward;

	private FirstPersonSession(CharacterBody body, CameraRigController rig, Camera camera, Transform model, HeadAnchor head, string bodyName, SurvivorSettings survivor)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		Body = body;
		BodyObject = ((Component)body).gameObject;
		Rig = rig;
		Camera = camera;
		Model = model;
		CharacterModel = ((Component)model).GetComponent<CharacterModel>();
		Locator = ((Component)model).GetComponent<ChildLocator>();
		Head = head;
		BodyName = bodyName;
		Survivor = survivor;
		stateMachines = ((Component)body).GetComponents<EntityStateMachine>();
		cameraTransform = ((Component)camera).transform;
		cameraIsChild = (Object)(object)cameraTransform != (Object)(object)((Component)rig).transform;
		cameraLocalPosition = cameraTransform.localPosition;
		cameraLocalRotation = cameraTransform.localRotation;
		originalNearClip = camera.nearClipPlane;
		originalFading = rig.enableFading;
		aimOrigin = body.aimOriginTransform;
		aimOriginLocalPosition = (Object.op_Implicit((Object)(object)aimOrigin) ? aimOrigin.localPosition : Vector3.zero);
		HideLayer = FindHideLayer();
		hideLayerWasDrawn = HideLayer >= 0 && (camera.cullingMask & (1 << HideLayer)) != 0;
		weaponPose = WeaponPose.Build(model, head.Transform, Locator, head.Radius);
		aimAnimator = ((Component)model).GetComponent<AimAnimator>();
	}

	public static FirstPersonSession Start(CharacterBody body, CameraRigController rig)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		Camera sceneCam = rig.sceneCam;
		ModelLocator component = ((Component)body).GetComponent<ModelLocator>();
		Transform val = (Object.op_Implicit((Object)(object)component) ? component.modelTransform : null);
		if (!Object.op_Implicit((Object)(object)sceneCam) || !Object.op_Implicit((Object)(object)val))
		{
			return null;
		}
		string text = BodyCatalog.GetBodyName(body.bodyIndex);
		if (string.IsNullOrEmpty(text))
		{
			text = ((Object)body).name.Replace("(Clone)", "");
		}
		SurvivorSettings survivorSettings = Settings.ForSurvivor(text);
		HeadAnchor headAnchor = HeadFinder.Find(body, val, survivorSettings?.HeadPath.Value);
		if (headAnchor == null)
		{
			Plugin.Log.LogWarning((object)("No head found on " + text + ": set its Head Path in the survivor settings"));
			return null;
		}
		FirstPersonSession firstPersonSession = new FirstPersonSession(body, rig, sceneCam, val, headAnchor, text, survivorSettings);
		firstPersonSession.Begin();
		return firstPersonSession;
	}

	private void Begin()
	{
		Rig.enableFading = false;
		if (hideLayerWasDrawn)
		{
			Camera camera = Camera;
			camera.cullingMask &= ~(1 << HideLayer);
		}
		if (logged.Add(((Object)Body).GetInstanceID()))
		{
			Plugin.Log.LogInfo((object)("First person on " + BodyName + ": eyes at " + Head.Source + ", head size " + Head.Radius.ToString("0.000") + " m, head bone " + (Object.op_Implicit((Object)(object)Head.Bone) ? ("'" + ((Object)Head.Bone).name + "'") : "none") + ", " + Head.Eyes.Length + " eye bones; weapon lowering moves " + weaponPose.Description + ((HideLayer >= 0) ? ("; faded parts go to layer " + HideLayer) : "")));
		}
		FirstPersonVisuals.Begin(this);
	}

	public void End()
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			FirstPersonVisuals.End();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Restoring the weapon and items failed: " + ex.Message));
		}
		try
		{
			weaponPose.Release();
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogWarning((object)("Restoring the arms failed: " + ex2.Message));
		}
		if (Object.op_Implicit((Object)(object)aimOrigin))
		{
			aimOrigin.localPosition = aimOriginLocalPosition;
		}
		if (Object.op_Implicit((Object)(object)Camera))
		{
			Camera.nearClipPlane = originalNearClip;
			if (hideLayerWasDrawn)
			{
				Camera camera = Camera;
				camera.cullingMask |= 1 << HideLayer;
			}
			if (lastGameFov > 1f)
			{
				Camera.fieldOfView = lastGameFov;
			}
			if (cameraIsChild)
			{
				cameraTransform.localPosition = cameraLocalPosition;
				cameraTransform.localRotation = cameraLocalRotation;
			}
		}
		if (Object.op_Implicit((Object)(object)Rig))
		{
			Rig.enableFading = originalFading;
		}
	}

	public bool OwnsStateMachine(EntityStateMachine stateMachine)
	{
		EntityStateMachine[] array = stateMachines;
		for (int i = 0; i < array.Length; i++)
		{
			if (array[i] == stateMachine)
			{
				return true;
			}
		}
		return false;
	}

	public void AfterRigCameraState(float gameFov)
	{
		//IL_0104: 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)
		lastGameFov = gameFov;
		float baseFov;
		float num = FieldOfView.Compute(Rig, gameFov, Camera.aspect, out baseFov);
		Camera.fieldOfView = num;
		Camera.nearClipPlane = Settings.NearClip.Value;
		if (!fovLogged)
		{
			fovLogged = true;
			Plugin.Log.LogInfo((object)("First person FOV: game " + gameFov.ToString("0.0") + " (base " + baseFov.ToString("0.0") + "), aspect " + Camera.aspect.ToString("0.000") + ", applied vertical " + num.ToString("0.0") + " = horizontal " + FieldOfView.VerticalToHorizontal(num, Camera.aspect).ToString("0.0")));
		}
		float y = cameraTransform.eulerAngles.y;
		bool num2 = FirstPersonController.SnapBodyYaw(this, y);
		InputBankTest val = (Object.op_Implicit((Object)(object)Body) ? Body.inputBank : null);
		if (Object.op_Implicit((Object)(object)val))
		{
			val.aimDirection = cameraTransform.forward;
		}
		float sway = (num2 ? Settings.WeaponSway.Value : 0f);
		weaponPose.Apply((Survivor != null) ? Survivor.WeaponLowering.Value : 0f, y, sway);
		RecordAim();
		PlaceCamera();
	}

	private void RecordAim()
	{
		if (aimDiagnosticBroken || !Object.op_Implicit((Object)(object)aimAnimator) || !((Behaviour)aimAnimator).enabled)
		{
			return;
		}
		try
		{
			RecordAimFrom(aimAnimator);
		}
		catch (Exception)
		{
			aimDiagnosticBroken = true;
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private void RecordAimFrom(AimAnimator animator)
	{
		FirstPersonVisuals.RecordAim(animator.currentLocalAngles.yaw, weaponPose.SwayDegrees);
	}

	public void OnPreCull()
	{
		PlaceCamera();
		FirstPersonVisuals.OnPreCull(Camera);
	}

	private void PlaceCamera()
	{
		//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_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: 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_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: 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_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: 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_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)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)Head.Transform))
		{
			return;
		}
		Vector3 eulerAngles = cameraTransform.eulerAngles;
		float y = eulerAngles.y;
		float num = Mathf.DeltaAngle(0f, eulerAngles.x);
		Vector3 center = Head.Center;
		float radius = Head.Radius;
		Vector3 val = Quaternion.Euler(0f, y, 0f) * Vector3.forward;
		float num2 = Mathf.Max(0f, Settings.LookDownPush.Value) * Mathf.Clamp01((num - 15f) / 55f);
		float num3 = Mathf.Max(0f, Settings.ForwardOffset.Value);
		float num4 = radius * num3;
		if (num3 > 0f && Head.Eyes.Length != 0)
		{
			num4 = Mathf.Max(num4, Head.EyesAhead(center, val) + 0.03f);
		}
		float num5 = num4 + radius * num2;
		float num6 = ((Survivor != null) ? (Survivor.CameraHeight.Value * 0.01f) : 0f);
		Vector3 val2 = Steady(center, y, radius) + val * num5 + Vector3.up * num6;
		Vector3 val3 = val2 - center;
		float magnitude = ((Vector3)(ref val3)).magnitude;
		if (magnitude > 0.0001f)
		{
			float num7 = Camera.nearClipPlane + 0.02f;
			Vector3 val4 = val3 / magnitude;
			float num8 = magnitude + num7;
			LayerIndex world = LayerIndex.world;
			RaycastHit val5 = default(RaycastHit);
			if (Physics.Raycast(center, val4, ref val5, num8, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1))
			{
				val2 = center + val4 * Mathf.Max(0f, ((RaycastHit)(ref val5)).distance - num7);
			}
		}
		cameraTransform.position = val2;
		if (Object.op_Implicit((Object)(object)aimOrigin) && Body.aimOriginTransform == aimOrigin)
		{
			aimOrigin.position = val2;
		}
	}

	private Vector3 Steady(Vector3 center, float yaw, float radius)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//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_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)Model))
		{
			steadyValid = false;
			return center;
		}
		Vector3 position = Model.position;
		Quaternion val = Quaternion.Euler(0f, yaw, 0f);
		Vector3 val2 = Quaternion.Inverse(val) * (center - position);
		float num = Mathf.Clamp01(Settings.Stabilization.Value);
		float time = Time.time;
		float num2 = (steadyValid ? (time - steadyTime) : 0f);
		if (!steadyValid || num <= 0f)
		{
			steadyOffset = val2;
		}
		else
		{
			if (num2 > 0f)
			{
				steadyOffset = Vector3.Lerp(steadyOffset, val2, 1f - Mathf.Exp((0f - num2) / (0.3f * num)));
			}
			Vector3 val3 = steadyOffset - val2;
			float num3 = radius * num;
			val3.x = Mathf.Clamp(val3.x, -0.8f * num3, 0.8f * num3);
			val3.y = Mathf.Clamp(val3.y, -0.4f * num3, 0.8f * num3);
			val3.z = Mathf.Clamp(val3.z, -0.25f * num3, 1.25f * num3);
			steadyOffset = val2 + val3;
		}
		steadyValid = true;
		steadyTime = time;
		if (num2 > 0f)
		{
			FirstPersonVisuals.RecordSway(val2, steadyOffset, num2);
		}
		return position + val * steadyOffset;
	}

	private static int FindHideLayer()
	{
		int intVal = LayerIndex.noDraw.intVal;
		if (intVal >= 0 && intVal < 32)
		{
			return intVal;
		}
		for (int num = 31; num >= 8; num--)
		{
			if (string.IsNullOrEmpty(LayerMask.LayerToName(num)))
			{
				return num;
			}
		}
		return -1;
	}
}
internal static class FirstPersonVisuals
{
	private sealed class FadeRenderer
	{
		public Renderer renderer;

		public SkinnedMeshRenderer skinned;

		public MeshFilter meshFilter;

		public bool measurable;

		public bool dither;

		public bool layerSafe;

		public int subMeshCount = 1;

		public bool applied;

		public bool hidden;

		public int originalLayer;
	}

	private sealed class FadeGroup
	{
		public string name;

		public bool isItem;

		public bool headgear;

		public bool fx;

		public bool wasDrawn;

		public bool awaitingMeasure;

		public ItemDisplay itemDisplay;

		public readonly List<FadeRenderer> renderers = new List<FadeRenderer>();

		public float coverage;

		public float boxCoverage;

		public bool measured;

		public float alpha = 1f;

		public float time;

		public float coverageSum;

		public float coverageMax;

		public float fadedTime;

		public readonly float[] histogram = new float[10];
	}

	private sealed class PendingReadback
	{
		public readonly FadeGroup[] mapping = new FadeGroup[8];

		public int tiles;

		public int height;

		public int generation;

		public int frame;

		public bool busy;

		public Action<AsyncGPUReadbackRequest> callback;
	}

	private enum MeasureMode
	{
		Render,
		Box
	}

	private const int TileWidth = 64;

	private const int MaxTiles = 8;

	private const float ItemThresholdScale = 0.5f;

	private const float BodyFadeStart = 0.8f;

	private const float BodyFadeFull = 0.95f;

	private const float FadeOutTime = 0.1f;

	private const float FadeInTime = 0.25f;

	private const string FullBodyLayerName = "FullBody, Override";

	private const float SkillHideOutTime = 0.05f;

	private const float SkillAnimationDelay = 0.5f;

	private static readonly List<FadeGroup> weaponGroups = new List<FadeGroup>();

	private static readonly List<FadeRenderer> followers = new List<FadeRenderer>();

	private static readonly List<FadeRenderer> headgear = new List<FadeRenderer>();

	private static readonly List<FadeGroup> items = new List<FadeGroup>();

	private static readonly Dictionary<ItemDisplay, FadeGroup> itemGroups = new Dictionary<ItemDisplay, FadeGroup>();

	private static readonly Dictionary<SkinnedMeshRenderer, bool> boundsOverrides = new Dictionary<SkinnedMeshRenderer, bool>();

	private static readonly HashSet<int> describedBodies = new HashSet<int>();

	private static readonly List<ItemDisplay> displayBuffer = new List<ItemDisplay>();

	private static readonly MaterialPropertyBlock block = new MaterialPropertyBlock();

	private static readonly int fadeId = Shader.PropertyToID("_Fade");

	private static readonly PendingReadback[] readbacks = CreateReadbacks(6);

	private static readonly Vector3[] corners = (Vector3[])(object)new Vector3[8];

	private static readonly Vector2[] points = (Vector2[])(object)new Vector2[24];

	private static readonly Vector2[] hull = (Vector2[])(object)new Vector2[48];

	private static readonly Vector2[] clipA = (Vector2[])(object)new Vector2[64];

	private static readonly Vector2[] clipB = (Vector2[])(object)new Vector2[64];

	private static FirstPersonSession session;

	private static CharacterBody body;

	private static CharacterModel model;

	private static Transform modelTransform;

	private static Animator animator;

	private static AnimatorCullingMode animatorCulling;

	private static Camera camera;

	private static string bodyName = "";

	private static FadeGroup bodyGroup;

	private static int classifyFrame = -1;

	private static int lastFrame = -1;

	private static float nextItemRefresh;

	private static float nextStatsLog;

	private static int generation;

	private static bool failed;

	private static int hideLayer = -1;

	private static MeasureMode mode = MeasureMode.Render;

	private static bool renderResourcesTried;

	private static bool renderVerified;

	private static int checkVisibleFrames;

	private static Material coverageMaterial;

	private static RenderTexture coverageTexture;

	private static CommandBuffer commandBuffer;

	private static int tileHeight;

	private static int readbackCursor;

	private static int weaponCursor;

	private static int itemCursor;

	private static Vector3 boxCameraPosition;

	private static Quaternion boxToCamera;

	private static float boxTanH;

	private static float boxTanV;

	private static float boxNear;

	private static bool swayValid;

	private static float swayTime;

	private static float swayHead;

	private static float swayCamera;

	private static Vector3 lastHeadOffset;

	private static Vector3 lastCameraOffset;

	private static float modelAlpha = 1f;

	private static int fullBodyLayer = -1;

	private static bool fullBodyPlaying;

	private static bool skillHide;

	private static GenericSkill lastSkill;

	private static float lastSkillTime = -10f;

	private static bool skillStarted;

	private static int skillHides;

	private static float skillHiddenTime;

	private static int burstsHidden;

	private static readonly HashSet<int> strippedOverlays = new HashSet<int>();

	private static bool overlaysBroken;

	private static bool aimValid;

	private static float lastAimYaw;

	private static int aimFrames;

	private static int aimJumps;

	private static float aimTime;

	private static float aimSum;

	private static float aimMax;

	private static float swayMax;

	public static void Begin(FirstPersonSession newSession)
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		End();
		failed = false;
		session = newSession;
		body = session.Body;
		camera = session.Camera;
		bodyName = session.BodyName;
		hideLayer = session.HideLayer;
		modelTransform = session.Model;
		model = session.CharacterModel;
		if (!Object.op_Implicit((Object)(object)model) || model.baseRendererInfos == null)
		{
			model = null;
			return;
		}
		RendererInfo[] baseRendererInfos = model.baseRendererInfos;
		for (int i = 0; i < baseRendererInfos.Length; i++)
		{
			Renderer renderer = baseRendererInfos[i].renderer;
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if (val != null)
			{
				OverrideBoundsUpdate(val);
			}
		}
		animator = ((Component)modelTransform).GetComponent<Animator>();
		if (Object.op_Implicit((Object)(object)animator))
		{
			animatorCulling = animator.cullingMode;
			animator.cullingMode = (AnimatorCullingMode)0;
			fullBodyLayer = animator.GetLayerIndex("FullBody, Override");
		}
		classifyFrame = Time.frameCount + 1;
		checkVisibleFrames = 0;
	}

	public static void End()
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		LogStats(final: true);
		generation++;
		try
		{
			RestoreAll();
			foreach (KeyValuePair<SkinnedMeshRenderer, bool> boundsOverride in boundsOverrides)
			{
				if (Object.op_Implicit((Object)(object)boundsOverride.Key))
				{
					boundsOverride.Key.updateWhenOffscreen = boundsOverride.Value;
				}
			}
			if (Object.op_Implicit((Object)(object)animator))
			{
				animator.cullingMode = animatorCulling;
			}
			if (Object.op_Implicit((Object)(object)model) && strippedOverlays.Count > 0)
			{
				RefreshOverlays(model);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Restoring first person visuals failed: " + ex.Message));
		}
		boundsOverrides.Clear();
		weaponGroups.Clear();
		followers.Clear();
		headgear.Clear();
		items.Clear();
		itemGroups.Clear();
		bodyGroup = null;
		session = null;
		body = null;
		model = null;
		modelTransform = null;
		animator = null;
		camera = null;
		classifyFrame = -1;
		lastFrame = -1;
		swayValid = false;
		swayTime = 0f;
		swayHead = 0f;
		swayCamera = 0f;
		aimValid = false;
		aimFrames = 0;
		aimJumps = 0;
		aimTime = 0f;
		aimSum = 0f;
		aimMax = 0f;
		swayMax = 0f;
		modelAlpha = 1f;
		fullBodyLayer = -1;
		fullBodyPlaying = false;
		skillHide = false;
		lastSkill = null;
		lastSkillTime = -10f;
		skillStarted = false;
		skillHides = 0;
		skillHiddenTime = 0f;
		burstsHidden = 0;
		strippedOverlays.Clear();
	}

	public static void OnQuit()
	{
		LogStats(final: true);
	}

	private static void RestoreAll()
	{
		foreach (FadeGroup weaponGroup in weaponGroups)
		{
			RestoreGroup(weaponGroup);
		}
		foreach (FadeRenderer follower in followers)
		{
			if (follower.applied && Object.op_Implicit((Object)(object)follower.renderer))
			{
				Restore(follower);
			}
		}
		foreach (FadeRenderer item in headgear)
		{
			if (item.applied && Object.op_Implicit((Object)(object)item.renderer))
			{
				Restore(item);
			}
		}
		if (bodyGroup != null)
		{
			RestoreGroup(bodyGroup);
		}
		foreach (FadeGroup item2 in items)
		{
			RestoreGroup(item2);
		}
	}

	private static void Fail(Exception exception)
	{
		Plugin.Log.LogError((object)("First person visuals stopped until first person is entered again: " + exception));
		failed = true;
		try
		{
			RestoreAll();
		}
		catch
		{
		}
	}

	public static void OnPreCull(Camera cam)
	{
		if (cam != camera || failed || lastFrame == Time.frameCount)
		{
			return;
		}
		lastFrame = Time.frameCount;
		try
		{
			if (!Object.op_Implicit((Object)(object)model))
			{
				return;
			}
			if (classifyFrame >= 0)
			{
				if (Time.frameCount < classifyFrame)
				{
					return;
				}
				classifyFrame = -1;
				Classify();
			}
			UpdateFade(cam);
		}
		catch (Exception exception)
		{
			Fail(exception);
		}
	}

	public static void AfterModelMaterialsUpdated(CharacterModel updated)
	{
		if (updated != model || failed)
		{
			return;
		}
		foreach (FadeGroup weaponGroup in weaponGroups)
		{
			ReapplyFade(weaponGroup);
		}
		if (bodyGroup != null)
		{
			ReapplyFade(bodyGroup);
		}
		foreach (FadeGroup item in items)
		{
			ReapplyFade(item);
		}
		foreach (FadeRenderer item2 in headgear)
		{
			if (item2.applied && item2.dither && Object.op_Implicit((Object)(object)item2.renderer))
			{
				SetFade(item2, 0f);
			}
		}
	}

	public static void RecordSway(Vector3 headOffset, Vector3 cameraOffset, float deltaTime)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		if (swayValid && deltaTime > 0f && deltaTime < 0.25f)
		{
			float num = swayHead;
			Vector3 val = headOffset - lastHeadOffset;
			swayHead = num + ((Vector3)(ref val)).magnitude;
			float num2 = swayCamera;
			val = cameraOffset - lastCameraOffset;
			swayCamera = num2 + ((Vector3)(ref val)).magnitude;
			swayTime += deltaTime;
		}
		lastHeadOffset = headOffset;
		lastCameraOffset = cameraOffset;
		swayValid = true;
	}

	public static void RecordAim(float aimYaw, float sway)
	{
		float deltaTime = Time.deltaTime;
		if (!(deltaTime <= 0f) && !(deltaTime > 0.25f))
		{
			if (aimValid && Mathf.Abs(Mathf.DeltaAngle(lastAimYaw, aimYaw)) > 1f)
			{
				aimJumps++;
			}
			lastAimYaw = aimYaw;
			aimValid = true;
			aimFrames++;
			aimTime += deltaTime;
			aimSum += Mathf.Abs(aimYaw) * deltaTime;
			aimMax = Mathf.Max(aimMax, Mathf.Abs(aimYaw));
			swayMax = Mathf.Max(swayMax, Mathf.Abs(sway));
		}
	}

	private static void UpdateFade(Camera cam)
	{
		float deltaTime = Time.deltaTime;
		if (Time.time >= nextItemRefresh)
		{
			nextItemRefresh = Time.time + 0.5f;
			RefreshItemDisplays();
		}
		bool value = Settings.FadeWeapon.Value;
		if (value && (mode == MeasureMode.Box || !renderVerified))
		{
			PrepareBox(cam);
			foreach (FadeGroup weaponGroup in weaponGroups)
			{
				weaponGroup.boxCoverage = BoxCoverage(weaponGroup);
			}
			if (mode == MeasureMode.Box)
			{
				if (bodyGroup != null)
				{
					bodyGroup.boxCoverage = 0f;
				}
				foreach (FadeGroup item in items)
				{
					item.boxCoverage = BoxCoverage(item);
				}
			}
			else
			{
				SelfCheck();
			}
		}
		UpdateSkillHide(deltaTime);
		float value2 = Settings.FadeStart.Value;
		float num = Mathf.Max(value2 + 0.01f, Settings.FadeFull.Value);
		float num2 = 1f;
		foreach (FadeGroup weaponGroup2 in weaponGroups)
		{
			WatchSwitchOn(weaponGroup2, value);
			float num3 = (weaponGroup2.fx ? 0.5f : 1f);
			UpdateGroup(weaponGroup2, value, value2 * num3, num * num3, deltaTime);
			if (!weaponGroup2.fx)
			{
				num2 = Mathf.Min(num2, weaponGroup2.alpha);
			}
		}
		foreach (FadeRenderer follower in followers)
		{
			if (Object.op_Implicit((Object)(object)follower.renderer))
			{
				Apply(follower, num2);
			}
		}
		foreach (FadeRenderer item2 in headgear)
		{
			if (Object.op_Implicit((Object)(object)item2.renderer))
			{
				Apply(item2, 0f);
			}
		}
		if (bodyGroup != null)
		{
			UpdateGroup(bodyGroup, value && mode == MeasureMode.Render, 0.8f, 0.95f, deltaTime);
		}
		foreach (FadeGroup item3 in items)
		{
			UpdateGroup(item3, value, value2 * 0.5f, num * 0.5f, deltaTime);
		}
		if (value && mode == MeasureMode.Render)
		{
			RequestCoverage(cam);
		}
		if (Time.time >= nextStatsLog)
		{
			nextStatsLog = Time.time + 60f;
			LogStats(final: false);
		}
	}

	private static void UpdateGroup(FadeGroup group, bool enabled, float start, float full, float deltaTime)
	{
		if (group.headgear)
		{
			group.alpha = 0f;
			{
				foreach (FadeRenderer renderer in group.renderers)
				{
					if (Object.op_Implicit((Object)(object)renderer.renderer))
					{
						Apply(renderer, 0f);
					}
				}
				return;
			}
		}
		float num = ((!enabled) ? 0f : ((mode == MeasureMode.Box) ? group.boxCoverage : (group.measured ? group.coverage : 0f)));
		float num2 = ((num <= start) ? 1f : ((num >= full) ? 0f : (1f - (num - start) / (full - start))));
		if (group.awaitingMeasure)
		{
			if (!enabled || mode != MeasureMode.Render || group.measured)
			{
				group.awaitingMeasure = false;
				group.alpha = num2;
			}
			else
			{
				num2 = 0f;
				group.alpha = 0f;
			}
		}
		float num3 = ((num2 < group.alpha) ? 0.1f : 0.25f);
		group.alpha = Mathf.MoveTowards(group.alpha, num2, deltaTime / num3);
		if (enabled)
		{
			group.time += deltaTime;
			group.coverageSum += num * deltaTime;
			if (num > group.coverageMax)
			{
				group.coverageMax = num;
			}
			group.histogram[Mathf.Clamp((int)(num * 10f), 0, 9)] += deltaTime;
			if (group.alpha < 0.99f)
			{
				group.fadedTime += deltaTime;
			}
		}
		foreach (FadeRenderer renderer2 in group.renderers)
		{
			if (Object.op_Implicit((Object)(object)renderer2.renderer))
			{
				Apply(renderer2, group.alpha);
			}
		}
	}

	private static void WatchSwitchOn(FadeGroup group, bool enabled)
	{
		bool flag = HasDrawable(group);
		if (flag && !group.wasDrawn)
		{
			group.fx = IsEffectGroup(group);
			if (group.fx && enabled && mode == MeasureMode.Render)
			{
				group.awaitingMeasure = true;
				group.measured = false;
				group.alpha = 0f;
			}
		}
		group.wasDrawn = flag;
	}

	private static float ModelFade()
	{
		if (!Object.op_Implicit((Object)(object)model))
		{
			return 1f;
		}
		return model.fade;
	}

	private static void Apply(FadeRenderer fr, float alpha)
	{
		alpha *= modelAlpha;
		if (alpha >= 0.999f)
		{
			if (fr.applied)
			{
				Restore(fr);
			}
			return;
		}
		if (fr.dither)
		{
			SetFade(fr, ModelFade() * alpha);
		}
		SetHidden(fr, alpha <= (fr.dither ? 0.02f : 0.5f));
		fr.applied = true;
	}

	private static void Restore(FadeRenderer fr)
	{
		if (fr.dither)
		{
			SetFade(fr, ModelFade());
		}
		SetHidden(fr, hide: false);
		fr.applied = false;
	}

	private static void SetFade(FadeRenderer fr, float fade)
	{
		fr.renderer.GetPropertyBlock(block);
		block.SetFloat(fadeId, fade);
		fr.renderer.SetPropertyBlock(block);
	}

	private static void SetHidden(FadeRenderer fr, bool hide)
	{
		if (hide == fr.hidden)
		{
			return;
		}
		fr.hidden = hide;
		if (fr.measurable && fr.layerSafe && hideLayer >= 0)
		{
			GameObject gameObject = ((Component)fr.renderer).gameObject;
			if (hide)
			{
				fr.originalLayer = gameObject.layer;
				gameObject.layer = hideLayer;
			}
			else if (gameObject.layer == hideLayer)
			{
				gameObject.layer = fr.originalLayer;
			}
		}
		else
		{
			fr.renderer.forceRenderingOff = hide;
		}
	}

	private static void RestoreGroup(FadeGroup group)
	{
		foreach (FadeRenderer renderer in group.renderers)
		{
			if (renderer.applied && Object.op_Implicit((Object)(object)renderer.renderer))
			{
				Restore(renderer);
			}
		}
		group.alpha = 1f;
	}

	private static void ReapplyFade(FadeGroup group)
	{
		foreach (FadeRenderer renderer in group.renderers)
		{
			if (renderer.applied && renderer.dither && Object.op_Implicit((Object)(object)renderer.renderer))
			{
				SetFade(renderer, ModelFade() * group.alpha * modelAlpha);
			}
		}
	}

	private static void UpdateSkillHide(float deltaTime)
	{
		bool flag = FullBodyAnimationPlaying();
		int num;
		if (flag && (!fullBodyPlaying || skillStarted))
		{
			bool flag2 = Object.op_Implicit((Object)(object)lastSkill) && Time.time - lastSkillTime < 0.5f;
			bool flag3 = flag2 && IsPrimary(lastSkill);
			if (!fullBodyPlaying)
			{
				num = ((!flag3) ? 1 : 0);
			}
			else
			{
				if (skillHide)
				{
					num = 1;
					goto IL_006e;
				}
				if (!flag2)
				{
					num = 0;
					goto IL_0081;
				}
				num = ((!flag3) ? 1 : 0);
			}
			if (num != 0)
			{
				goto IL_006e;
			}
			goto IL_0081;
		}
		if (!flag)
		{
			skillHide = false;
		}
		goto IL_0091;
		IL_006e:
		if (!skillHide)
		{
			skillHides++;
		}
		goto IL_0081;
		IL_0081:
		skillHide = (byte)num != 0;
		goto IL_0091;
		IL_0091:
		fullBodyPlaying = flag;
		skillStarted = false;
		float num2 = ((skillHide && Settings.HideBodyDuringSkills.Value) ? 0f : 1f);
		modelAlpha = Mathf.MoveTowards(modelAlpha, num2, deltaTime / ((num2 < modelAlpha) ? 0.05f : 0.25f));
		if (modelAlpha < 0.999f)
		{
			skillHiddenTime += deltaTime;
		}
	}

	private static bool FullBodyAnimationPlaying()
	{
		if (fullBodyLayer < 0 || !Object.op_Implicit((Object)(object)animator) || !((Behaviour)animator).isActiveAndEnabled || fullBodyLayer >= animator.layerCount)
		{
			return false;
		}
		if (animator.GetLayerWeight(fullBodyLayer) < 0.5f)
		{
			return false;
		}
		if (animator.GetCurrentAnimatorClipInfoCount(fullBodyLayer) > 0)
		{
			return true;
		}
		if (animator.IsInTransition(fullBodyLayer))
		{
			return animator.GetNextAnimatorClipInfoCount(fullBodyLayer) > 0;
		}
		return false;
	}

	private static bool IsPrimary(GenericSkill skill)
	{
		SkillLocator val = (Object.op_Implicit((Object)(object)body) ? body.skillLocator : null);
		if (Object.op_Implicit((Object)(object)val))
		{
			return skill == val.primary;
		}
		return false;
	}

	public static void OnSkillExecuted(GenericSkill skill)
	{
		lastSkill = skill;
		lastSkillTime = Time.time;
		skillStarted = true;
	}

	public static void CountBurst()
	{
		burstsHidden++;
	}

	public static void StripOverlays(CharacterModel target)
	{
		if (overlaysBroken)
		{
			return;
		}
		try
		{
			StripOverlaysFrom(target);
		}
		catch (Exception ex)
		{
			overlaysBroken = true;
			Plugin.Log.LogWarning((object)("Leaving flashes out of the own model stopped working: " + ex.Message));
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static void StripOverlaysFrom(CharacterModel target)
	{
		Material[] currentOverlays = target.currentOverlays;
		int num = ((currentOverlays != null) ? Mathf.Min(target.activeOverlayCount, currentOverlays.Length) : 0);
		if (num <= 0)
		{
			return;
		}
		List<TemporaryOverlayInstance> temporaryOverlays = target.temporaryOverlays;
		int num2 = 0;
		for (int i = 0; i < num; i++)
		{
			Material val = currentOverlays[i];
			if (Object.op_Implicit((Object)(object)val) && (val.IsKeywordEnabled("VERTEXOFFSET") || IsTemporary(temporaryOverlays, val)))
			{
				strippedOverlays.Add(((Object)val).GetInstanceID());
			}
			else
			{
				currentOverlays[num2++] = val;
			}
		}
		for (int j = num2; j < num; j++)
		{
			currentOverlays[j] = null;
		}
		target.activeOverlayCount = num2;
	}

	private static bool IsTemporary(List<TemporaryOverlayInstance> temporary, Material material)
	{
		if (temporary == null)
		{
			return false;
		}
		foreach (TemporaryOverlayInstance item in temporary)
		{
			if (item != null && item.materialInstance == material)
			{
				return true;
			}
		}
		return false;
	}

	private static void RefreshOverlays(CharacterModel target)
	{
		if (overlaysBroken)
		{
			return;
		}
		try
		{
			ForceOverlayUpdate(target);
		}
		catch (Exception)
		{
			overlaysBroken = true;
		}
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	private static void ForceOverlayUpdate(CharacterModel target)
	{
		target.forceUpdate = true;
	}

	private static void Classify()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: 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_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: 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_01ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_024d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0252: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Unknown result type (might be due to invalid IL or missing references)
		//IL_025b: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0261: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_0282: Unknown result type (might be due to invalid IL or missing references)
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = session.Head.Transform;
		Vector3 val = (Object.op_Implicit((Object)(object)transform) ? session.Head.Center : modelTransform.position);
		Vector3 val2 = (Object.op_Implicit((Object)(object)body) ? body.corePosition : val);
		List<Renderer> list = new List<Renderer>();
		RendererInfo[] baseRendererInfos = model.baseRendererInfos;
		foreach (RendererInfo val3 in baseRendererInfos)
		{
			if (Object.op_Implicit((Object)(object)val3.renderer) && !list.Contains(val3.renderer))
			{
				list.Add(val3.renderer);
			}
		}
		HashSet<Renderer> hashSet = new HashSet<Renderer>();
		Vector3 val4 = (Object.op_Implicit((Object)(object)body) ? (body.footPosition + Vector3.up * 0.1f) : val2);
		string text = "pose bounds";
		foreach (Renderer item in list)
		{
			if (item is SkinnedMeshRenderer)
			{
				Bounds bounds = item.bounds;
				((Bounds)(ref bounds)).Expand(0.1f);
				if (((Bounds)(ref bounds)).Contains(val) && ((Bounds)(ref bounds)).Contains(val2) && ((Bounds)(ref bounds)).Contains(val4))
				{
					hashSet.Add(item);
				}
			}
		}
		if (hashSet.Count == 0)
		{
			text = "pose bounds without feet";
			foreach (Renderer item2 in list)
			{
				if (item2 is SkinnedMeshRenderer)
				{
					Bounds bounds2 = item2.bounds;
					((Bounds)(ref bounds2)).Expand(0.1f);
					if (((Bounds)(ref bounds2)).Contains(val) && ((Bounds)(ref bounds2)).Contains(val2))
					{
						hashSet.Add(item2);
					}
				}
			}
		}
		float num = Mathf.Max(0.15f, 0.4f * (Object.op_Implicit((Object)(object)body) ? body.radius : 0.5f));
		float num2 = Mathf.Lerp(val2.y, val4.y, 0.5f);
		int num3 = 0;
		foreach (Renderer item3 in list)
		{
			if (!(item3 is SkinnedMeshRenderer) || hashSet.Contains(item3))
			{
				continue;
			}
			Bounds bounds3 = item3.bounds;
			Vector3 val5 = ((Bounds)(ref bounds3)).center - val2;
			val5.y = 0f;
			if (!(((Bounds)(ref bounds3)).size.y < 0.8f * Mathf.Max(((Bounds)(ref bounds3)).size.x, ((Bounds)(ref bounds3)).size.z)))
			{
				((Bounds)(ref bounds3)).Expand(0.1f);
				if (((Bounds)(ref bounds3)).Contains(val2) && !(((Vector3)(ref val5)).magnitude > num) && (!(((Bounds)(ref bounds3)).min.y > num2) || ((Bounds)(ref bounds3)).Contains(val)))
				{
					hashSet.Add(item3);
					num3++;
				}
			}
		}
		if (num3 > 0)
		{
			text = text + " and " + num3 + " worn around the body";
		}
		if (hashSet.Count == 0 && Object.op_Implicit((Object)(object)transform))
		{
			text = "head bone";
			foreach (Renderer item4 in list)
			{
				SkinnedMeshRenderer val6 = (SkinnedMeshRenderer)(object)((item4 is SkinnedMeshRenderer) ? item4 : null);
				if (val6 != null && UsesBone(val6, transform))
				{
					hashSet.Add(item4);
				}
			}
		}
		if (hashSet.Count == 0)
		{
			text = "largest mesh";
			Renderer val7 = null;
			float num4 = 0f;
			foreach (Renderer item5 in list)
			{
				if (item5 is SkinnedMeshRenderer)
				{
					Bounds bounds4 = item5.bounds;
					Vector3 size = ((Bounds)(ref bounds4)).size;
					float num5 = size.x * size.y * size.z;
					if (!Object.op_Implicit((Object)(object)val7) || num5 > num4)
					{
						val7 = item5;
						num4 = num5;
					}
				}
			}
			if (Object.op_Implicit((Object)(object)val7))
			{
				hashSet.Add(val7);
			}
		}
		StringBuilder stringBuilder = new StringBuilder("First person visuals for " + bodyName + ", body by " + text + ":");
		bodyGroup = new FadeGroup
		{
			name = "body"
		};
		foreach (Renderer item6 in list)
		{
			if (hashSet.Contains(item6))
			{
				FadeRenderer fadeRenderer = MakeFadeRenderer(item6);
				bodyGroup.renderers.Add(fadeRenderer);
				stringBuilder.Append(" '").Append(((Object)item6).name).Append('\'')
					.Append(Describe(fadeRenderer));
			}
		}
		if (bodyGroup.renderers.Count == 0)
		{
			bodyGroup = null;
		}
		stringBuilder.Append("; weapon:");
		int num6 = 0;
		Dictionary<Transform, FadeGroup> dictionary = new Dictionary<Transform, FadeGroup>();
		foreach (Renderer item7 in list)
		{
			if (hashSet.Contains(item7))
			{
				continue;
			}
			FadeRenderer fadeRenderer2 = MakeFadeRenderer(item7);
			num6++;
			if (IsHeadgear(item7))
			{
				headgear.Add(fadeRenderer2);
				stringBuilder.Append(" '").Append(((Object)item7).name).Append("' (headgear, hidden)");
				continue;
			}
			if (!fadeRenderer2.measurable)
			{
				followers.Add(fadeRenderer2);
				stringBuilder.Append(" '").Append(((Object)item7).name).Append('\'')
					.Append(Describe(fadeRenderer2));
				continue;
			}
			Transform parent = ((Component)item7).transform.parent;
			bool flag = !Object.op_Implicit((Object)(object)fadeRenderer2.skinned) && Object.op_Implicit((Object)(object)parent) && (Object)(object)parent != (Object)(object)modelTransform;
			if (!flag || !dictionary.TryGetValue(parent, out var value))
			{
				value = new FadeGroup
				{
					name = ((Object)item7).name
				};
				weaponGroups.Add(value);
				if (flag)
				{
					dictionary[parent] = value;
				}
			}
			else if (value.renderers.Count == 1)
			{
				value.name = ((Object)parent).name;
			}
			value.renderers.Add(fadeRenderer2);
		}
		foreach (FadeGroup weaponGroup in weaponGroups)
		{
			weaponGroup.fx = IsEffectGroup(weaponGroup);
			stringBuilder.Append(" '").Append(weaponGroup.name).Append('\'');
			if (weaponGroup.renderers.Count > 1)
			{
				stringBuilder.Append(" [");
				for (int j = 0; j < weaponGroup.renderers.Count; j++)
				{
					stringBuilder.Append((j == 0) ? "" : ", ").Append(((Object)weaponGroup.renderers[j].renderer).name);
				}
				stringBuilder.Append(']');
			}
			stringBuilder.Append(Describe(weaponGroup.renderers[0]));
			if (weaponGroup.fx)
			{
				stringBuilder.Append(" (glow)");
			}
			if (!HasDrawable(weaponGroup))
			{
				stringBuilder.Append(" (off)");
			}
		}
		if (num6 == 0)
		{
			stringBuilder.Append(" none");
		}
		RefreshItemDisplays();
		nextItemRefresh = Time.time + 0.5f;
		nextStatsLog = Time.time + 60f;
		stringBuilder.Append("; coverage by ").Append((mode == MeasureMode.Render) ? "rendering" : "bounding boxes");
		if (hideLayer >= 0)
		{
			stringBuilder.Append(", hidden on layer ").Append(hideLayer);
		}
		if (Object.op_Implicit((Object)(object)body) && describedBodies.Add(((Object)body).GetInstanceID()))
		{
			Plugin.Log.LogInfo((object)stringBuilder.ToString());
		}
	}

	private static bool IsHeadgear(Renderer renderer)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		HeadAnchor headAnchor = ((session != null) ? session.Head : null);
		if (headAnchor == null || !Object.op_Implicit((Object)(object)headAnchor.Transform) || !Object.op_Implicit((Object)(object)renderer))
		{
			return false;
		}
		if (Object.op_Implicit((Object)(object)headAnchor.Bone) && ((Component)renderer).transform.IsChildOf(headAnchor.Bone))
		{
			return true;
		}
		if (!(renderer is SkinnedMeshRenderer) && !(renderer is MeshRenderer))
		{
			return false;
		}
		Bounds bounds = renderer.bounds;
		float radius = headAnchor.Radius;
		Vector3 val = ((Bounds)(ref bounds)).extents;
		if (((Vector3)(ref val)).magnitude < 2.5f * radius)
		{
			val = ((Bounds)(ref bounds)).center - headAnchor.Center;
			return ((Vector3)(ref val)).magnitude < 1.5f * radius;
		}
		return false;
	}

	private static bool IsEffectGroup(FadeGroup group)
	{
		foreach (FadeRenderer renderer in group.renderers)
		{
			if (renderer.dither || !Object.op_Implicit((Object)(object)renderer.renderer))
			{
				return false;
			}
			Material[] sharedMaterials = renderer.renderer.sharedMaterials;
			if (sharedMaterials.Length == 0)
			{
				return false;
			}
			Material[] array = sharedMaterials;
			foreach (Material val in array)
			{
				if (!Object.op_Implicit((Object)(object)val) || val.renderQueue <= 2500)
				{
					return false;
				}
			}
		}
		return group.renderers.Count > 0;
	}

	private static string Describe(FadeRenderer fr)
	{
		if (!fr.measurable)
		{
			return " (follows)";
		}
		string text = (fr.dither ? " (dither" : " (no dither");
		if (!fr.layerSafe)
		{
			return text + ", collider)";
		}
		return text + ")";
	}

	private static bool UsesBone(SkinnedMeshRenderer skinned, Transform head)
	{
		Transform parent = head.parent;
		Transform[] bones = skinned.bones;
		foreach (Transform val in bones)
		{
			if (Object.op_Implicit((Object)(object)val) && ((Object)(object)val == (Object)(object)head || (Object)(object)val == (Object)(object)parent))
			{
				return true;
			}
		}
		return false;
	}

	private static FadeRenderer MakeFadeRenderer(Renderer renderer)
	{
		FadeRenderer fadeRenderer = new FadeRenderer
		{
			renderer = renderer,
			skinned = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null)
		};
		Mesh val = null;
		if (Object.op_Implicit((Object)(object)fadeRenderer.skinned))
		{
			OverrideBoundsUpdate(fadeRenderer.skinned);
			val = fadeRenderer.skinned.sharedMesh;
		}
		else if (renderer is MeshRenderer)
		{
			fadeRenderer.meshFilter = ((Component)renderer).GetComponent<MeshFilter>();
			val = (Object.op_Implicit((Object)(object)fadeRenderer.meshFilter) ? fadeRenderer.meshFilter.sharedMesh : null);
		}
		if (Object.op_Implicit((Object)(object)val))
		{
			fadeRenderer.measurable = true;
			fadeRenderer.subMeshCount = Mathf.Max(1, val.subMeshCount);
			fadeRenderer.dither = MaterialsDither(renderer);
			fadeRenderer.layerSafe = !Object.op_Implicit((Object)(object)((Component)renderer).GetComponent<Collider>());
		}
		return fadeRenderer;
	}

	private static bool MaterialsDither(Renderer renderer)
	{
		Material[] sharedMaterials = renderer.sharedMaterials;
		if (sharedMaterials.Length == 0)
		{
			return false;
		}
		Material[] array = sharedMaterials;
		foreach (Material val in array)
		{
			if (!Object.op_Implicit((Object)(object)val) || !val.HasProperty(fadeId) || !val.IsKeywordEnabled("DITHER"))
			{
				return false;
			}
			if (val.HasProperty("_FadeOn") && val.GetInt("_FadeOn") == 0)
			{
				return false;
			}
		}
		return true;
	}

	private static void OverrideBoundsUpdate(SkinnedMeshRenderer skinned)
	{
		if (Object.op_Implicit((Object)(object)skinned) && !boundsOverrides.ContainsKey(skinned))
		{
			boundsOverrides[skinned] = skinned.updateWhenOffscreen;
			skinned.updateWhenOffscreen = true;
		}
	}

	private static void RefreshItemDisplays()
	{
		//IL_014b: 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_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)model))
		{
			return;
		}
		for (int num = items.Count - 1; num >= 0; num--)
		{
			FadeGroup fadeGroup = items[num];
			if (!Object.op_Implicit((Object)(object)fadeGroup.itemDisplay))
			{
				foreach (FadeRenderer renderer in fadeGroup.renderers)
				{
					if (renderer.skinned != null)
					{
						boundsOverrides.Remove(renderer.skinned);
					}
				}
				itemGroups.Remove(fadeGroup.itemDisplay);
				items.RemoveAt(num);
			}
		}
		displayBuffer.Clear();
		((Component)model).GetComponentsInChildren<ItemDisplay>(true, displayBuffer);
		foreach (ItemDisplay item in displayBuffer)
		{
			if (!Object.op_Implicit((Object)(object)item) || item.rendererInfos == null || itemGroups.ContainsKey(item))
			{
				continue;
			}
			FadeGroup fadeGroup2 = new FadeGroup
			{
				name = ((Object)item).name.Replace("(Clone)", ""),
				isItem = true,
				itemDisplay = item
			};
			bool flag = true;
			RendererInfo[] rendererInfos = item.rendererInfos;
			foreach (RendererInfo val in rendererInfos)
			{
				if (Object.op_Implicit((Object)(object)val.renderer))
				{
					fadeGroup2.renderers.Add(MakeFadeRenderer(val.renderer));
					flag &= IsHeadgear(val.renderer);
				}
			}
			fadeGroup2.headgear = flag && fadeGroup2.renderers.Count > 0;
			itemGroups[item] = fadeGroup2;
			items.Add(fadeGroup2);
		}
		displayBuffer.Clear();
	}

	private static PendingReadback[] CreateReadbacks(int count)
	{
		PendingReadback[] array = new PendingReadback[count];
		for (int i = 0; i < count; i++)
		{
			PendingReadback pending = new PendingReadback();
			pending.callback = delegate(AsyncGPUReadbackRequest request)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				OnReadback(request, pending);
			};
			array[i] = pending;
		}
		return array;
	}

	private static bool IsDrawn(Renderer renderer)
	{
		if (Object.op_Implicit((Object)(object)renderer) && renderer.enabled)
		{
			return ((Component)renderer).gameObject.activeInHierarchy;
		}
		return false;
	}

	private static bool HasDrawable(FadeGroup group)
	{
		foreach (FadeRenderer renderer in group.renderers)
		{
			if (renderer.measurable && IsDrawn(renderer.renderer))
			{
				return true;
			}
		}
		return false;
	}

	private static Shader FindLoadedShader()
	{
		string[] obj = new string[3] { "Hidden/InternalErrorShader", "Hidden/Internal-Colored", "Unlit/Color" };
		Shader[] array = Resources.FindObjectsOfTypeAll<Shader>();
		string[] array2 = obj;
		foreach (string text in array2)
		{
			Shader[] array3 = array;
			foreach (Shader val in array3)
			{
				if (Object.op_Implicit((Object)(object)val) && ((Object)val).name == text && val.isSupported)
				{
					return val;
				}
			}
		}
		return null;
	}

	private static bool EnsureRenderResources(Camera cam)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Expected O, but got Unknown
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Expected O, but got Unknown
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Expected O, but got Unknown
		if (!Object.op_Implicit((Object)(object)coverageMaterial))
		{
			if (renderResourcesTried)
			{
				mode = MeasureMode.Box;
				return false;
			}
			renderResourcesTried = true;
			Shader val = null;
			try
			{
				if (SystemInfo.supportsAsyncGPUReadback)
				{
					val = FindLoadedShader();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Looking up a shader for screen coverage failed: " + ex.Message));
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				mode = MeasureMode.Box;
				Plugin.Log.LogWarning((object)("Rendered screen coverage is not available (async readback " + SystemInfo.supportsAsyncGPUReadback + "), using bounding boxes"));
				return false;
			}
			coverageMaterial = new Material(val)
			{
				name = "BetterFirstPerson coverage",
				hideFlags = (HideFlags)61
			};
			SetIfPresent(coverageMaterial, "_Color", 1f);
			SetIfPresent(coverageMaterial, "_SrcBlend", 1f);
			SetIfPresent(coverageMaterial, "_DstBlend", 0f);
			SetIfPresent(coverageMaterial, "_ZWrite", 1f);
			SetIfPresent(coverageMaterial, "_ZTest", 4f);
			SetIfPresent(coverageMaterial, "_Cull", 2f);
			commandBuffer = new CommandBuffer
			{
				name = "BetterFirstPerson coverage"
			};
			Plugin.Log.LogInfo((object)("Screen coverage is drawn with " + ((Object)val).name));
		}
		int num = Mathf.Clamp(Mathf.RoundToInt(64f / Mathf.Max(0.2f, cam.aspect)), 8, 160);
		if (!Object.op_Implicit((Object)(object)coverageTexture) || !coverageTexture.IsCreated() || num != tileHeight)
		{
			if (Object.op_Implicit((Object)(object)coverageTexture))
			{
				coverageTexture.Release();
				Object.Destroy((Object)(object)coverageTexture);
			}
			tileHeight = num;
			coverageTexture = new RenderTexture(512, tileHeight, 16, (RenderTextureFormat)0, (RenderTextureReadWrite)1)
			{
				name = "BetterFirstPerson coverage",
				hideFlags = (HideFlags)61
			};
			coverageTexture.Create();
		}
		return true;
	}

	private static void SetIfPresent(Material material, string property, float value)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		if (material.HasProperty(property))
		{
			if (property == "_Color")
			{
				material.SetColor(property, Color.white);
			}
			else
			{
				material.SetFloat(property, value);
			}
		}
	}

	private static void AddTile(PendingReadback slot, ref int tiles, FadeGroup group)
	{
		if (tiles < 8 && !group.headgear)
		{
			if (HasDrawable(group))
			{
				slot.mapping[tiles++] = group;
			}
			else
			{
				group.coverage = 0f;
			}
		}
	}

	private static void RequestCoverage(Camera cam)
	{
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0294: Unknown result type (might be due to invalid IL or missing references)
		if (!EnsureRenderResources(cam))
		{
			return;
		}
		PendingReadback pendingReadback = readbacks[readbackCursor];
		if (pendingReadback.busy)
		{
			if (Time.frameCount - pendingReadback.frame < 120)
			{
				return;
			}
			pendingReadback.busy = false;
		}
		int tiles = 0;
		int count = weaponGroups.Count;
		int i;
		for (i = 0; i < count; i++)
		{
			if (tiles >= 7)
			{
				break;
			}
			AddTile(pendingReadback, ref tiles, weaponGroups[(weaponCursor + i) % count]);
		}
		if (count > 0)
		{
			weaponCursor = (weaponCursor + i) % count;
		}
		if (bodyGroup != null)
		{
			AddTile(pendingReadback, ref tiles, bodyGroup);
		}
		int count2 = items.Count;
		for (i = 0; i < count2; i++)
		{
			if (tiles >= 8)
			{
				break;
			}
			AddTile(pendingReadback, ref tiles, items[(itemCursor + i) % count2]);
		}
		if (count2 > 0)
		{
			itemCursor = (itemCursor + i) % count2;
		}
		if (tiles == 0)
		{
			return;
		}
		commandBuffer.Clear();
		commandBuffer.SetRenderTarget(RenderTargetIdentifier.op_Implicit((Texture)(object)coverageTexture));
		commandBuffer.ClearRenderTarget(true, true, Color.clear);
		commandBuffer.SetViewProjectionMatrices(cam.worldToCameraMatrix, cam.projectionMatrix);
		for (int j = 0; j < tiles; j++)
		{
			commandBuffer.SetViewport(new Rect((float)(j * 64), 0f, 64f, (float)tileHeight));
			foreach (FadeRenderer renderer in pendingReadback.mapping[j].renderers)
			{
				if (renderer.measurable && IsDrawn(renderer.renderer))
				{
					for (int k = 0; k < renderer.subMeshCount; k++)
					{
						commandBuffer.DrawRenderer(renderer.renderer, coverageMaterial, k, 0);
					}
					commandBuffer.SetInvertCulling(true);
					for (int l = 0; l < renderer.subMeshCount; l++)
					{
						commandBuffer.DrawRenderer(renderer.renderer, coverageMaterial, l, 0);
					}
					commandBuffer.SetInvertCulling(false);
				}
			}
		}
		RenderTexture active = RenderTexture.active;
		Graphics.ExecuteCommandBuffer(commandBuffer);
		RenderTexture.active = active;
		pendingReadback.tiles = tiles;
		pendingReadback.height = tileHeight;
		pendingReadback.generation = generation;
		pendingReadback.frame = Time.frameCount;
		pendingReadback.busy = true;
		readbackCursor = (readbackCursor + 1) % readbacks.Length;
		AsyncGPUReadback.Request((Texture)(object)coverageTexture, 0, pendingReadback.callback);
	}

	private static void OnReadback(AsyncGPUReadbackRequest request, PendingReadback pending)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		pending.busy = false;
		try
		{
			if (((AsyncGPUReadbackRequest)(ref request)).hasError || pending.generation != generation)
			{
				return;
			}
			NativeArray<uint> data = ((AsyncGPUReadbackRequest)(ref request)).GetData<uint>(0);
			int num = 512;
			int height = pending.height;
			if (data.Length < num * height)
			{
				return;
			}
			float num2 = 64 * height;
			for (int i = 0; i < pending.tiles; i++)
			{
				int num3 = 0;
				for (int j = 0; j < height; j++)
				{
					int k = j * num + i * 64;
					for (int num4 = k + 64; k < num4; k++)
					{
						if (data[k] != 0)
						{
							num3++;
						}
					}
				}
				FadeGroup obj = pending.mapping[i];
				obj.coverage = (float)num3 / num2;
				obj.measured = true;
			}
		}
		catch (Exception ex)
		{
			if (mode == MeasureMode.Render)
			{
				mode = MeasureMode.Box;
				Plugin.Log.LogWarning((object)("Reading rendered screen coverage failed, using bounding boxes: " + ex.Message));
			}
		}
		finally
		{
			Array.Clear(pending.mapping, 0, 8);
		}
	}

	private static void SelfCheck()
	{
		if (renderVerified)
		{
			return;
		}
		bool flag = bodyGroup != null && bodyGroup.measured && bodyGroup.coverage > 0f;
		float num = 0f;
		foreach (FadeGroup weaponGroup in weaponGroups)
		{
			num = Mathf.Max(num, weaponGroup.boxCoverage);
			flag |= weaponGroup.measured && weaponGroup.coverage > 0f;
		}
		foreach (FadeGroup item in items)
		{
			flag |= item.measured && item.coverage > 0f;
		}
		if (flag)
		{
			renderVerified = true;
			Plugin.Log.LogInfo((object)("Rendered screen coverage works (checked against boxes for " + checkVisibleFrames + " frames)"));
		}
		else if (!(num < 0.05f))
		{
			checkVisibleFrames++;
			if (checkVisibleFrames >= 600)
			{
				mode = MeasureMode.Box;
				Plugin.Log.LogWarning((object)"Rendered screen coverage stayed empty while parts were on screen, using bounding boxes instead");
			}
		}
	}

	private static void PrepareBox(Camera cam)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = ((Component)cam).transform;
		boxCameraPosition = transform.position;
		boxToCamera = Quaternion.Inverse(transform.rotation);
		boxTanV = Mathf.Tan(cam.fieldOfView * 0.5f * ((float)Math.PI / 180f));
		boxTanH = boxTanV * cam.aspect;
		boxNear = cam.nearClipPlane;
	}

	private static float BoxCoverage(FadeGroup group)
	{
		//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_003f: Unknown result type (might be due to invalid IL or missing references)
		float num = 0f;
		foreach (FadeRenderer renderer in group.renderers)
		{
			if (renderer.measurable && IsDrawn(renderer.renderer))
			{
				Bounds bounds = renderer.renderer.bounds;
				if (!((Bounds)(ref bounds)).Contains(boxCameraPosition))
				{
					num = Mathf.Max(num, Coverage(renderer));
				}
			}
		}
		return num;
	}

	private static float Coverage(FadeRenderer fr)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: 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_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		if (!FillBox(fr))
		{
			return 0f;
		}
		for (int i = 0; i < 8; i++)
		{
			corners[i] = boxToCamera * (corners[i] - boxCameraPosition);
		}
		int num = 0;
		for (int j = 0; j < 8; j++)
		{
			if (corners[j].z >= boxNear)
			{
				points[num++] = Project(corners[j]);
			}
		}
		for (int k = 0; k < 8; k++)
		{
			for (int num2 = 1; num2 < 8; num2 <<= 1)
			{
				if ((k & num2) == 0)
				{
					int num3 = k | num2;
					float z = corners[k].z;
					float z2 = corners[num3].z;
					if (z >= boxNear != z2 >= boxNear)
					{
						Vector3 point = Vector3.LerpUnclamped(corners[k], corners[num3], (boxNear - z) / (z2 - z));
						point.z = boxNear;
						points[num++] = Project(point);
					}
				}
			}
		}
		if (num < 3)
		{
			return 0f;
		}
		int num4 = ConvexHull(num);
		if (num4 >= 3)
		{
			return ClippedArea(num4);
		}
		return 0f;
	}

	private static bool FillBox(FadeRenderer fr)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: 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_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_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: 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_00eb: 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_0115: 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_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0147: Unknown result type (might be due to invalid IL or missing references)
		Renderer renderer = fr.renderer;
		if (Object.op_Implicit((Object)(object)fr.meshFilter) && Object.op_Implicit((Object)(object)fr.meshFilter.sharedMesh))
		{
			FillCorners(fr.meshFilter.sharedMesh.bounds, ((Component)renderer).transform);
			return true;
		}
		Bounds bounds = renderer.bounds;
		Vector3 size = ((Bounds)(ref bounds)).size;
		if (((Vector3)(ref size)).sqrMagnitude < 1E-06f)
		{
			return false;
		}
		if (Object.op_Implicit((Object)(object)fr.skinned))
		{
			FillCorners(((Renderer)fr.skinned).localBounds, Object.op_Implicit((Object)(object)fr.skinned.rootBone) ? fr.skinned.rootBone : ((Component)renderer).transform);
			Vector3 val = corners[0];
			Vector3 val2 = corners[0];
			for (int i = 1; i < 8; i++)
			{
				val = Vector3.Min(val, corners[i]);
				val2 = Vector3.Max(val2, corners[i]);
			}
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(0.05f, 0.05f, 0.05f);
			if (Vector3.Max(((Bounds)(ref bounds)).min, val - val3) == ((Bounds)(ref bounds)).min && Vector3.Min(((Bounds)(ref bounds)).max, val2 + val3) == ((Bounds)(ref bounds)).max)
			{
				return true;
			}
		}
		FillCorners(bounds, null);
		return true;
	}

	private static void FillCorners(Bounds bounds, Transform transform)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: 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_007e: 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_007a: 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)
		Vector3 center = ((Bounds)(ref bounds)).center;
		Vector3 extents = ((Bounds)(ref bounds)).extents;
		Vector3 val = default(Vector3);
		for (int i = 0; i < 8; i++)
		{
			((Vector3)(ref val))..ctor(center.x + (((i & 1) != 0) ? extents.x : (0f - extents.x)), center.y + (((i & 2) != 0) ? extents.y : (0f - extents.y)), center.z + (((i & 4) != 0) ? extents.z : (0f - extents.z)));
			corners[i] = (Object.op_Implicit((Object)(object)transform) ? transform.TransformPoint(val) : val);
		}
	}

	private static Vector2 Project(Vector3 point)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		return new Vector2(0.5f + 0.5f * point.x / (point.z * boxTanH), 0.5f + 0.5f * point.y / (point.z * boxTanV));
	}

	private static float Cross(Vector2 origin, Vector2 a, Vector2 b)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: 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)
		return (a.x - origin.x) * (b.y - origin.y) - (a.y - origin.y) * (b.x - origin.x);
	}

	private static int ConvexHull(int count)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 1; i < count; i++)
		{
			Vector2 val = points[i];
			int num = i - 1;
			while (num >= 0 && (points[num].x > val.x || (points[num].x == val.x && points[num].y > val.y)))
			{
				points[num + 1] = points[num];
				num--;
			}
			points[num + 1] = val;
		}
		int num2 = 0;
		for (int j = 0; j < count; j++)
		{
			while (num2 >= 2 && Cross(hull[num2 - 2], hull[num2 - 1], points[j]) <= 0f)
			{
				num2--;
			}
			hull[num2++] = points[j];
		}
		int num3 = count - 2;
		int num4 = num2 + 1;
		while (num3 >= 0)
		{
			while (num2 >= num4 && Cross(hull[num2 - 2], hull[num2 - 1], points[num3]) <= 0f)
			{
				num2--;
			}
			hull[num2++] = points[num3];
			num3--;
		}
		return num2 - 1;
	}

	private static float ClippedArea(int count)
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: 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)
		Array.Copy(hull, clipA, count);
		count = ClipEdge(clipA, count, clipB, alongX: true, 0f, keepGreater: true);
		count = ClipEdge(clipB, count, clipA, alongX: true, 1f, keepGreater: false);
		count = ClipEdge(clipA, count, clipB, alongX: false, 0f, keepGreater: true);
		count = ClipEdge(clipB, count, clipA, alongX: false, 1f, keepGreater: false);
		if (count < 3)
		{
			return 0f;
		}
		float num = 0f;
		for (int i = 0; i < count; i++)
		{
			Vector2 val = clipA[i];
			Vector2 val2 = clipA[(i + 1) % count];
			num += val.x * val2.y - val2.x * val.y;
		}
		return Mathf.Abs(num) * 0.5f;
	}

	private static int ClipEdge(Vector2[] input, int count, Vector2[] output, bool alongX, float bound, bool keepGreater)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: 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_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		if (count == 0)
		{
			return 0;
		}
		int result = 0;
		Vector2 val = input[count - 1];
		bool flag = Inside(val, alongX, bound, keepGreater);
		for (int i = 0; i < count; i++)
		{
			Vector2 val2 = input[i];
			bool num = Inside(val2, alongX, bound, keepGreater);
			if (num != flag)
			{
				float num2 = (alongX ? val.x : val.y);
				float num3 = (alongX ? val2.x : val2.y);
				output[result++] = Vector2.LerpUnclamped(val, val2, (bound - num2) / (num3 - num2));
			}
			if (num)
			{
				output[result++] = val2;
			}
			val = val2;
			flag = num;
		}
		return result;
	}

	private static bool Inside(Vector2 point, bool alongX, float bound, bool keepGreater)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		float num = (alongX ? point.x : point.y);
		if (!keepGreater)
		{
			return num <= bound;
		}
		return num >= bound;
	}

	private static void LogStats(bool final)
	{
		if (weaponGroups.Count == 0 && items.Count == 0 && bodyGroup == null && swayTime < 1f && aimTime < 1f && skillHides == 0 && burstsHidden == 0)
		{
			return;
		}
		StringBuilder stringBuilder = new StringBuilder("Screen coverage " + (final ? "summary" : "so far") + " for " + bodyName + " (" + ((mode == MeasureMode.Render) ? "rendered" : "bounding boxes") + ", fade from " + Settings.FadeStart.Value.ToString("0.00") + " to " + Settings.FadeFull.Value.ToString("0.00") + ", items at half, body from " + 0.8f.ToString("0.00") + "):");
		bool flag = false;
		foreach (FadeGroup weaponGroup in weaponGroups)
		{
			flag |= AppendStats(stringBuilder, weaponGroup);
		}
		if (bodyGroup != null)
		{
			flag |= AppendStats(stringBuilder, bodyGroup);
		}
		foreach (FadeGroup item in items)
		{
			if (item.coverageMax >= 0.02f)
			{
				flag |= AppendStats(stringBuilder, item);
			}
		}
		if (swayTime >= 1f)
		{
			stringBuilder.Append("\n  head movement relative to the body ").Append((100f * swayHead / swayTime).ToString("0")).Append(" cm/s, reaching the camera ")
				.Append((100f * swayCamera / swayTime).ToString("0"))
				.Append(" cm/s (stabilization ")
				.Append(Settings.Stabilization.Value.ToString("0.00"))
				.Append(')');
			flag = true;
		}
		if (aimTime >= 1f && aimFrames > 0)
		{
			stringBuilder.Append("\n  arms aimed to the side of the body: avg ").Append((aimSum / aimTime).ToString("0.0")).Append(" deg, max ")
				.Append(aimMax.ToString("0.0"))
				.Append(" deg, jumps over 1 deg in ")
				.Append((100f * (float)aimJumps / (float)aimFrames).ToString("0.0"))
				.Append("% of frames; sway up to ")
				.Append(swayMax.ToString("0.0"))
				.Append(" deg (weapon sway ")
				.Append(Settings.WeaponSway.Value.ToString("0.00"))
				.Append(')');
			flag = true;
		}
		if (skillHides > 0 || burstsHidden > 0 || strippedOverlays.Count > 0)
		{
			stringBuilder.Append("\n  model hidden for skills that move the whole body ").Append(skillHides).Append(" times, ")
				.Append(skillHiddenTime.ToString("0.0"))
				.Append(" s (")
				.Append((fullBodyLayer >= 0) ? ("full body layer " + fullBodyLayer) : "no full body layer")
				.Append("); bursts at the body hidden: ")
				.Append(burstsHidden)
				.Append("; flashes left out of the model: ")
				.Append(strippedOverlays.Count);
			flag = true;
		}
		if (flag)
		{
			Plugin.Log.LogInfo((object)stringBuilder.ToString());
		}
	}

	private static bool AppendStats(StringBuilder sb, FadeGroup group)
	{
		if (group.time < 1f)
		{
			return false;
		}
		sb.Append("\n  ").Append(group.isItem ? "item '" : ((group == bodyGroup) ? "" : "part '")).Append(group.name)
			.Append((group.isItem || group != bodyGroup) ? "'" : "")
			.Append(": avg ")
			.Append((group.coverageSum / group.time).ToString("0.00"))
			.Append(" max ")
			.Append(group.coverageMax.ToString("0.00"))
			.Append(", faded ")
			.Append((100f * group.fadedTime / group.time).ToString("0"))
			.Append("% of ")
			.Append(group.time.ToString("0"))
			.Append(" s")
			.Append(", % of time per tenth of the screen:");
		float[] histogram = group.histogram;
		foreach (float num in histogram)
		{
			sb.Append(' ').Append((100f * num / group.time).ToString("0"));
		}
		return true;
	}
}
internal sealed class HeadAnchor
{
	public Transform Transform;

	public Vector3 LocalCenter;

	public float LocalRadius;

	public float FallbackRadius;

	public string Source;

	public Transform Bone;

	public Transform[] Eyes = (Transform[])(object)new Transform[0];

	public Vector3 Center => Transform.TransformPoint(LocalCenter);

	public float Radius
	{
		get
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid