Decompiled source of FaceCam v1.3.2

plugins/FaceCam.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("FaceCam")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.2.0")]
[assembly: AssemblyInformationalVersion("1.3.2+cee7aad9de0937e60ff1e8c5228ddd0d3d068c1f")]
[assembly: AssemblyProduct("FaceCam")]
[assembly: AssemblyTitle("FaceCam")]
[assembly: AssemblyVersion("1.3.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FaceCam
{
	[BepInPlugin("com.slopgaming.facecam", "FaceCam", "1.3.2")]
	public class FaceCamPlugin : BaseUnityPlugin
	{
		public enum Corner
		{
			TopLeft,
			TopRight,
			BottomLeft,
			BottomRight
		}

		private struct Threat
		{
			public string Name;

			public Vector3 Point;

			public float Distance;
		}

		public const string PluginGuid = "com.slopgaming.facecam";

		public const string PluginName = "FaceCam";

		public const string PluginVersion = "1.3.2";

		private ConfigEntry<KeyboardShortcut> toggleKey;

		private ConfigEntry<bool> enabledOnStart;

		private ConfigEntry<bool> hideWhenNoThreat;

		private ConfigEntry<float> maxTrackDistance;

		private ConfigEntry<bool> trackMass;

		private ConfigEntry<bool> trackFaces;

		private ConfigEntry<bool> trackHunter;

		private ConfigEntry<bool> trackRemains;

		private ConfigEntry<bool> trackRatKing;

		private ConfigEntry<Corner> corner;

		private ConfigEntry<float> viewportSize;

		private ConfigEntry<float> margin;

		private ConfigEntry<float> borderThickness;

		private ConfigEntry<Color> borderColor;

		private ConfigEntry<bool> showName;

		private ConfigEntry<bool> showDistance;

		private ConfigEntry<float> fieldOfView;

		private ConfigEntry<float> aimSmoothing;

		private ConfigEntry<int> resolutionScale;

		private ConfigEntry<float> shakeDistance;

		private ConfigEntry<float> shakeAngle;

		private ConfigEntry<float> shakePixels;

		private ConfigEntry<float> shakeSpeed;

		private bool active;

		private Camera cam;

		private RenderTexture rt;

		private Canvas hudCanvas;

		private RectTransform hudRoot;

		private Image border;

		private RawImage feed;

		private RectTransform tagRoot;

		private TextMeshProUGUI tagText;

		private Threat current;

		private bool hasThreat;

		private Quaternion aimRotation;

		private bool aimInitialized;

		private Vector2 shakeOffset;

		private static readonly FieldInfo FaceIsPrimary = typeof(DEN_Face).GetField("isPrimary", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo GameDead = typeof(CL_GameManager).GetField("dead", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly FieldInfo GameLoading = typeof(CL_GameManager).GetField("loading", BindingFlags.Instance | BindingFlags.NonPublic);

		internal static ManualLogSource Log;

		private static FaceCamPlugin instance;

		private float nextLiveFontCheck;

		private bool liveFontApplied;

		internal static bool Rendering
		{
			get
			{
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.cam != (Object)null)
				{
					return ((Behaviour)instance.cam).enabled;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_001b: 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_0195: Expected O, but got Unknown
			//IL_0209: 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_0298: Expected O, but got Unknown
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Expected O, but got Unknown
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Key that turns the FaceCam viewport on and off.");
			enabledOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnabledOnStart", false, "Whether the FaceCam starts switched on when the game launches.");
			hideWhenNoThreat = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HideWhenNoThreat", true, "Hide the viewport while no tracked threat exists (or all are out of range).");
			maxTrackDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxTrackDistance", 250f, "Threats further away than this (metres) are ignored.");
			trackMass = ((BaseUnityPlugin)this).Config.Bind<bool>("Threats", "TrackMass", true, "Track the Mass (rising goo below you).");
			trackFaces = ((BaseUnityPlugin)this).Config.Bind<bool>("Threats", "TrackFaces", true, "Track Faces. Each Face is tracked separately and labelled.");
			trackHunter = ((BaseUnityPlugin)this).Config.Bind<bool>("Threats", "TrackHunter", true, "Track the Hunter.");
			trackRemains = ((BaseUnityPlugin)this).Config.Bind<bool>("Threats", "TrackRemains", true, "Track active Remains (the climbing bodies).");
			trackRatKing = ((BaseUnityPlugin)this).Config.Bind<bool>("Threats", "TrackRatKing", true, "Track the Rat King.");
			corner = ((BaseUnityPlugin)this).Config.Bind<Corner>("Viewport", "Corner", Corner.BottomRight, "Screen corner the viewport sits in.");
			viewportSize = ((BaseUnityPlugin)this).Config.Bind<float>("Viewport", "Size", 0.28f, new ConfigDescription("Side length of the square viewport as a fraction of screen height.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 0.9f), Array.Empty<object>()));
			margin = ((BaseUnityPlugin)this).Config.Bind<float>("Viewport", "Margin", 24f, "Gap in pixels between the viewport and the screen edges.");
			borderThickness = ((BaseUnityPlugin)this).Config.Bind<float>("Viewport", "BorderThickness", 3f, "Border thickness in pixels.");
			borderColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Viewport", "BorderColor", new Color(0.85f, 0.85f, 0.85f, 0.9f), "Border colour (RGBA).");
			showName = ((BaseUnityPlugin)this).Config.Bind<bool>("Viewport", "ShowName", true, "Show a name tag with the tracked threat's name.");
			showDistance = ((BaseUnityPlugin)this).Config.Bind<bool>("Viewport", "ShowDistance", false, "Add the distance to the threat to the name tag.");
			fieldOfView = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "FieldOfView", 60f, new ConfigDescription("Field of view of the FaceCam.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 120f), Array.Empty<object>()));
			aimSmoothing = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "AimSmoothing", 10f, "How quickly the camera turns towards the threat. Higher is snappier; 0 snaps instantly.");
			resolutionScale = ((BaseUnityPlugin)this).Config.Bind<int>("Camera", "ResolutionScale", 1, new ConfigDescription("Divides the render texture resolution (1 = full, 2 = half). Raise for performance.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>()));
			shakeDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Shake", "ShakeDistance", 10f, "The viewport starts to shake when the threat is closer than this (metres). 0 disables shake.");
			shakeAngle = ((BaseUnityPlugin)this).Config.Bind<float>("Shake", "ShakeAngle", 1.2f, "Maximum camera wobble in degrees at point blank.");
			shakePixels = ((BaseUnityPlugin)this).Config.Bind<float>("Shake", "ShakePixels", 4f, "Maximum viewport jitter in pixels at point blank.");
			shakeSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Shake", "ShakeSpeed", 18f, "Shake frequency.");
			active = enabledOnStart.Value;
			Log = ((BaseUnityPlugin)this).Logger;
			instance = this;
			new Harmony("com.slopgaming.facecam").PatchAll(typeof(RemainsVisibilityPatch));
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Toggle with {2}. Starts {3}.", "FaceCam", "1.3.2", toggleKey.Value, active ? "on" : "off"));
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				active = !active;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("FaceCam " + (active ? "on" : "off")));
			}
			ENT_Player playerObject = ENT_Player.playerObject;
			if (!active || (Object)(object)playerObject == (Object)null || (Object)(object)playerObject.cam == (Object)null || !InPlayableState())
			{
				Hide();
				return;
			}
			CL_GameManager gMan = CL_GameManager.gMan;
			bool flag = (Object)(object)gMan != (Object)null && gMan.isPaused;
			if (!flag)
			{
				Vector3 position = ((Component)playerObject.cam).transform.position;
				hasThreat = FindClosestThreat(playerObject, position, out current);
			}
			if (!hasThreat && hideWhenNoThreat.Value)
			{
				Hide();
				return;
			}
			if (!EnsureHud(playerObject))
			{
				Hide();
				return;
			}
			LayoutHud();
			EnsureCamera(playerObject.cam);
			if (!flag)
			{
				UpdateCameraPose(((Component)playerObject.cam).transform.position);
			}
			UpdateTag();
			((Component)hudRoot).gameObject.SetActive(true);
			((Behaviour)cam).enabled = !flag;
		}

		private static bool InPlayableState()
		{
			CL_GameManager gMan = CL_GameManager.gMan;
			if ((Object)(object)gMan == (Object)null)
			{
				return false;
			}
			if (CL_GameManager.runHasEnded)
			{
				return false;
			}
			if (GameDead != null && (bool)GameDead.GetValue(null))
			{
				return false;
			}
			if (GameLoading != null && (bool)GameLoading.GetValue(gMan))
			{
				return false;
			}
			return true;
		}

		private void Hide()
		{
			aimInitialized = false;
			if ((Object)(object)cam != (Object)null)
			{
				((Behaviour)cam).enabled = false;
			}
			if ((Object)(object)hudRoot != (Object)null)
			{
				((Component)hudRoot).gameObject.SetActive(false);
			}
		}

		private bool FindClosestThreat(ENT_Player player, Vector3 eye, out Threat best)
		{
			//IL_0002: 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)
			//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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: 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_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			Threat closest = default(Threat);
			float bestDist = maxTrackDistance.Value;
			bool found = false;
			if (trackMass.Value && (Object)(object)DEN_DeathFloor.instance != (Object)null && ((Behaviour)DEN_DeathFloor.instance).isActiveAndEnabled)
			{
				float height = DEN_DeathFloor.GetHeight();
				if (height > -10000f)
				{
					Vector3 position = ((Component)player).transform.position;
					Consider("The Mass", new Vector3(position.x, height, position.z));
				}
			}
			if (trackFaces.Value && DEN_Face.faceInstances != null)
			{
				List<DEN_Face> faceInstances = DEN_Face.faceInstances;
				int num = 2;
				for (int i = 0; i < faceInstances.Count; i++)
				{
					DEN_Face val = faceInstances[i];
					if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled)
					{
						Consider((FaceIsPrimary != null && (bool)FaceIsPrimary.GetValue(val)) ? "Face" : $"Face #{num++}", ((Component)val).transform.position);
					}
				}
			}
			if (trackHunter.Value && DEN_Hunter.activeHunters != null)
			{
				List<DEN_Hunter> activeHunters = DEN_Hunter.activeHunters;
				for (int j = 0; j < activeHunters.Count; j++)
				{
					DEN_Hunter val2 = activeHunters[j];
					if (!((Object)(object)val2 == (Object)null) && ((Behaviour)val2).isActiveAndEnabled)
					{
						Transform val3 = (((Object)(object)val2.headObject != (Object)null) ? val2.headObject : ((Component)val2).transform);
						Consider((activeHunters.Count > 1) ? $"Hunter #{j + 1}" : "Hunter", val3.position);
					}
				}
			}
			if (trackRatKing.Value && DEN_RatKing.activeRatKings != null)
			{
				List<DEN_RatKing> activeRatKings = DEN_RatKing.activeRatKings;
				for (int k = 0; k < activeRatKings.Count; k++)
				{
					DEN_RatKing val4 = activeRatKings[k];
					if (!((Object)(object)val4 == (Object)null) && ((Behaviour)val4).isActiveAndEnabled)
					{
						Consider((activeRatKings.Count > 1) ? $"Rat King #{k + 1}" : "Rat King", ((Component)val4).transform.position);
					}
				}
			}
			if (trackRemains.Value && GameEntity.entities != null)
			{
				List<GameEntity> entities = GameEntity.entities;
				for (int l = 0; l < entities.Count; l++)
				{
					GameEntity obj = entities[l];
					DEN_Remains_Active val5 = (DEN_Remains_Active)(object)((obj is DEN_Remains_Active) ? obj : null);
					if (val5 != null && !((Object)(object)val5 == (Object)null) && val5.active && ((Behaviour)val5).isActiveAndEnabled)
					{
						Vector3 val6;
						if (!((Object)(object)val5.capsuleCollider != (Object)null))
						{
							val6 = ((Component)val5).transform.position;
						}
						else
						{
							Bounds bounds = ((Collider)val5.capsuleCollider).bounds;
							val6 = ((Bounds)(ref bounds)).center;
						}
						Vector3 point = val6;
						Consider("Remains", point);
					}
				}
			}
			best = closest;
			return found;
			void Consider(string name, Vector3 val7)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: 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)
				float num2 = Vector3.Distance(eye, val7);
				if (num2 < bestDist)
				{
					bestDist = num2;
					closest = new Threat
					{
						Name = name,
						Point = val7,
						Distance = num2
					};
					found = true;
				}
			}
		}

		private void UpdateCameraPose(Vector3 eye)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			((Component)cam).transform.position = eye;
			cam.fieldOfView = fieldOfView.Value;
			if (hasThreat)
			{
				Vector3 val = current.Point - eye;
				if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
				{
					Vector3 val2 = Vector3.up;
					if (Mathf.Abs(Vector3.Dot(((Vector3)(ref val)).normalized, val2)) > 0.98f)
					{
						val2 = Vector3.forward;
					}
					Quaternion val3 = Quaternion.LookRotation(val, val2);
					if (!aimInitialized || aimSmoothing.Value <= 0f)
					{
						aimRotation = val3;
					}
					else
					{
						aimRotation = Quaternion.Slerp(aimRotation, val3, 1f - Mathf.Exp((0f - aimSmoothing.Value) * Time.unscaledDeltaTime));
					}
					aimInitialized = true;
				}
			}
			float num = 0f;
			if (hasThreat && shakeDistance.Value > 0f && current.Distance < shakeDistance.Value)
			{
				num = 1f - current.Distance / shakeDistance.Value;
			}
			Quaternion val4 = Quaternion.identity;
			shakeOffset = Vector2.zero;
			if (num > 0f)
			{
				float num2 = Time.unscaledTime * shakeSpeed.Value;
				float num3 = Mathf.PerlinNoise(num2, 0.1f) * 2f - 1f;
				float num4 = Mathf.PerlinNoise(0.7f, num2) * 2f - 1f;
				float num5 = Mathf.PerlinNoise(num2, 3.3f) * 2f - 1f;
				float num6 = shakeAngle.Value * num;
				val4 = Quaternion.Euler(num3 * num6, num4 * num6, num5 * num6 * 0.5f);
				shakeOffset = new Vector2(num3, num4) * shakePixels.Value * num;
			}
			((Component)cam).transform.rotation = aimRotation * val4;
		}

		private void EnsureCamera(Camera playerCam)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_0141: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			int num = Mathf.Max(1, resolutionScale.Value);
			int num2 = Mathf.Max(16, Mathf.RoundToInt(SidePixels() / (float)num));
			if ((Object)(object)rt == (Object)null || ((Texture)rt).width != num2)
			{
				if ((Object)(object)rt != (Object)null)
				{
					if ((Object)(object)cam != (Object)null)
					{
						cam.targetTexture = null;
					}
					rt.Release();
					Object.Destroy((Object)(object)rt);
				}
				rt = new RenderTexture(num2, num2, 24, OpaqueFormat())
				{
					name = "FaceCamRT"
				};
				rt.Create();
				if ((Object)(object)feed != (Object)null)
				{
					feed.texture = (Texture)(object)rt;
				}
			}
			if ((Object)(object)cam == (Object)null)
			{
				GameObject val = new GameObject("FaceCam_Camera");
				Object.DontDestroyOnLoad((Object)(object)val);
				cam = val.AddComponent<Camera>();
				cam.CopyFrom(playerCam);
				cam.depth = playerCam.depth - 1f;
				((Behaviour)cam).enabled = false;
			}
			cam.cullingMask = playerCam.cullingMask;
			cam.clearFlags = playerCam.clearFlags;
			cam.backgroundColor = playerCam.backgroundColor;
			cam.nearClipPlane = playerCam.nearClipPlane;
			cam.farClipPlane = playerCam.farClipPlane;
			cam.aspect = 1f;
			cam.targetTexture = rt;
		}

		private static RenderTextureFormat OpaqueFormat()
		{
			if (!SystemInfo.SupportsRenderTextureFormat((RenderTextureFormat)22))
			{
				return (RenderTextureFormat)4;
			}
			return (RenderTextureFormat)22;
		}

		private float SidePixels()
		{
			return (float)Screen.height * viewportSize.Value;
		}

		private bool EnsureHud(ENT_Player player)
		{
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hudRoot != (Object)null && (Object)(object)hudCanvas != (Object)null)
			{
				return true;
			}
			Image val = null;
			if (player.hands != null)
			{
				Hand[] hands = player.hands;
				foreach (Hand val2 in hands)
				{
					if (val2 != null && (Object)(object)val2.uiInteract != (Object)null)
					{
						val = val2.uiInteract;
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null || (Object)(object)((Graphic)val).canvas == (Object)null)
			{
				return false;
			}
			hudCanvas = ((Graphic)val).canvas.rootCanvas;
			Transform transform = ((Component)hudCanvas).transform;
			Transform val3 = ((Component)val).transform;
			while ((Object)(object)val3.parent != (Object)null && (Object)(object)val3.parent != (Object)(object)transform)
			{
				val3 = val3.parent;
			}
			int num = (((Object)(object)val3.parent == (Object)(object)transform) ? val3.GetSiblingIndex() : 0);
			CL_GameManager gMan = CL_GameManager.gMan;
			if ((Object)(object)gMan != (Object)null && (Object)(object)gMan.pauseMenu != (Object)null && gMan.pauseMenu.transform.IsChildOf(transform))
			{
				Transform val4 = gMan.pauseMenu.transform;
				while ((Object)(object)val4.parent != (Object)null && (Object)(object)val4.parent != (Object)(object)transform)
				{
					val4 = val4.parent;
				}
				if ((Object)(object)val4.parent == (Object)(object)transform)
				{
					num = Mathf.Min(num, val4.GetSiblingIndex());
				}
			}
			GameObject val5 = new GameObject("FaceCam_HUD", new Type[1] { typeof(RectTransform) });
			hudRoot = (RectTransform)val5.transform;
			((Transform)hudRoot).SetParent(transform, false);
			((Transform)hudRoot).SetSiblingIndex(num);
			val5.layer = ((Component)val3).gameObject.layer;
			border = NewChild<Image>("Border", (Transform)(object)hudRoot);
			((Graphic)border).raycastTarget = false;
			Stretch(((Graphic)border).rectTransform, 0f);
			feed = NewChild<RawImage>("Feed", (Transform)(object)hudRoot);
			((Graphic)feed).raycastTarget = false;
			feed.texture = (Texture)(object)rt;
			Image val6 = NewChild<Image>("NameTag", (Transform)(object)((Graphic)feed).rectTransform);
			((Graphic)val6).raycastTarget = false;
			((Graphic)val6).color = new Color(0f, 0f, 0f, 0.55f);
			tagRoot = ((Graphic)val6).rectTransform;
			tagRoot.anchorMin = new Vector2(0f, 0f);
			tagRoot.anchorMax = new Vector2(1f, 0f);
			tagRoot.pivot = new Vector2(0.5f, 0f);
			tagRoot.anchoredPosition = Vector2.zero;
			tagText = NewChild<TextMeshProUGUI>("Text", (Transform)(object)tagRoot);
			((Graphic)tagText).raycastTarget = false;
			((TMP_Text)tagText).alignment = (TextAlignmentOptions)514;
			((TMP_Text)tagText).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)tagText).overflowMode = (TextOverflowModes)1;
			((Graphic)tagText).color = Color.white;
			Stretch(((TMP_Text)tagText).rectTransform, 0f);
			ApplySubtitleFont(tagText);
			liveFontApplied = false;
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"FaceCam HUD attached to canvas '{((Object)hudCanvas).name}' at index {num} (before '{((Object)val3).name}').");
			return true;
		}

		private void ApplySubtitleFont(TextMeshProUGUI text)
		{
			CL_UIManager val = CL_UIManager.instance;
			TMP_Text val2 = (((Object)(object)val != (Object)null && (Object)(object)val.subtitleObject != (Object)null) ? val.subtitleObject.text : null);
			if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font != (Object)null)
			{
				CopyFont(val2, (TMP_Text)(object)text, "subtitle template");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Subtitle font not found; the name tag uses the default TMP font.");
			}
		}

		private void TryApplyLiveSubtitleFont()
		{
			if (liveFontApplied || Time.unscaledTime < nextLiveFontCheck)
			{
				return;
			}
			nextLiveFontCheck = Time.unscaledTime + 1f;
			CL_UIManager val = CL_UIManager.instance;
			if ((Object)(object)val == (Object)null || (Object)(object)val.subtitleParent == (Object)null)
			{
				return;
			}
			UI_Subtitle[] componentsInChildren = ((Component)val.subtitleParent).GetComponentsInChildren<UI_Subtitle>(false);
			foreach (UI_Subtitle val2 in componentsInChildren)
			{
				if (!((Object)(object)val2.text == (Object)null) && !((Object)(object)val2.text.font == (Object)null) && !string.IsNullOrEmpty(val2.text.text))
				{
					CopyFont(val2.text, (TMP_Text)(object)tagText, "live subtitle");
					liveFontApplied = true;
					break;
				}
			}
		}

		private void CopyFont(TMP_Text from, TMP_Text to, string source)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			to.font = from.font;
			to.fontSharedMaterial = from.fontSharedMaterial;
			to.fontStyle = from.fontStyle;
			to.characterSpacing = from.characterSpacing;
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			string[] obj = new string[7]
			{
				"Name tag font from ",
				source,
				": '",
				((Object)from.font).name,
				"', material '",
				null,
				null
			};
			Material fontSharedMaterial = from.fontSharedMaterial;
			obj[5] = ((fontSharedMaterial != null) ? ((Object)fontSharedMaterial).name : null);
			obj[6] = "'.";
			logger.LogInfo((object)string.Concat(obj));
		}

		private static T NewChild<T>(string name, Transform parent) where T : Component
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = ((Component)parent).gameObject.layer
			};
			val.transform.SetParent(parent, false);
			return val.AddComponent<T>();
		}

		private static void Stretch(RectTransform r, float inset)
		{
			//IL_0001: 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_0019: 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)
			r.anchorMin = Vector2.zero;
			r.anchorMax = Vector2.one;
			r.offsetMin = new Vector2(inset, inset);
			r.offsetMax = new Vector2(0f - inset, 0f - inset);
		}

		private void LayoutHud()
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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_0112: 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_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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			float num = ((hudCanvas.scaleFactor > 0f) ? hudCanvas.scaleFactor : 1f);
			float num2 = SidePixels() / num;
			float num3 = margin.Value / num;
			float num4 = borderThickness.Value / num;
			bool flag = corner.Value == Corner.TopLeft || corner.Value == Corner.BottomLeft;
			bool flag2 = corner.Value == Corner.TopLeft || corner.Value == Corner.TopRight;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(flag ? 0f : 1f, flag2 ? 1f : 0f);
			hudRoot.anchorMin = val;
			hudRoot.anchorMax = val;
			hudRoot.pivot = val;
			hudRoot.sizeDelta = new Vector2(num2 + 2f * num4, num2 + 2f * num4);
			hudRoot.anchoredPosition = new Vector2(flag ? num3 : (0f - num3), flag2 ? (0f - num3) : num3) + shakeOffset / num;
			((Graphic)border).color = borderColor.Value;
			Stretch(((Graphic)feed).rectTransform, num4);
			float num5 = num2 * 0.12f;
			tagRoot.sizeDelta = new Vector2(0f, num5);
			((TMP_Text)tagText).fontSize = num5 * 0.62f;
		}

		private void UpdateTag()
		{
			TryApplyLiveSubtitleFont();
			bool flag = (showName.Value || showDistance.Value) && hasThreat;
			((Component)tagRoot).gameObject.SetActive(flag);
			if (flag)
			{
				string text = (showName.Value ? current.Name : "");
				if (showDistance.Value)
				{
					text = (showName.Value ? $"{text}  {current.Distance:0}m" : $"{current.Distance:0}m");
				}
				if (((TMP_Text)tagText).text != text)
				{
					((TMP_Text)tagText).text = text;
				}
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)cam != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)cam).gameObject);
			}
			if ((Object)(object)hudRoot != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)hudRoot).gameObject);
			}
			if ((Object)(object)rt != (Object)null)
			{
				rt.Release();
				Object.Destroy((Object)(object)rt);
			}
		}
	}
	[HarmonyPatch(typeof(DEN_Remains_Active), "Update")]
	internal static class RemainsVisibilityPatch
	{
		private static readonly MethodInfo IsVisibleGetter = AccessTools.PropertyGetter(typeof(Renderer), "isVisible");

		private static readonly MethodInfo Replacement = AccessTools.Method(typeof(RemainsVisibilityPatch), "SeenByPlayer", (Type[])null, (Type[])null);

		private static readonly Plane[] Planes = (Plane[])(object)new Plane[6];

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			int replaced = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, IsVisibleGetter))
				{
					replaced++;
					instruction.opcode = OpCodes.Call;
					instruction.operand = Replacement;
				}
				yield return instruction;
			}
			if (replaced == 0)
			{
				FaceCamPlugin.Log.LogWarning((object)"Remains patch: isVisible call not found; Remains may freeze while on FaceCam.");
			}
		}

		private static bool SeenByPlayer(Renderer renderer)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (!renderer.isVisible)
			{
				return false;
			}
			if (!FaceCamPlugin.Rendering)
			{
				return true;
			}
			ENT_Player playerObject = ENT_Player.playerObject;
			Camera val = (((Object)(object)playerObject != (Object)null) ? playerObject.cam : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			GeometryUtility.CalculateFrustumPlanes(val, Planes);
			return GeometryUtility.TestPlanesAABB(Planes, renderer.bounds);
		}
	}
}