Decompiled source of LethalFaceTime v1.0.0

LethalFaceTime.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalFaceTime.Bridges;
using LethalFaceTime.Runtime;
using Microsoft.CodeAnalysis;
using OpenBodyCams;
using OpenBodyCams.API;
using Scoops.gameobjects;
using Scoops.misc;
using Scoops.service;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Animations.Rigging;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
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("LethalFaceTime")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Shows a live video feed of whoever you are on the phone with, on the phone screen.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LethalFaceTime")]
[assembly: AssemblyTitle("LethalFaceTime")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalFaceTime
{
	internal sealed class FaceTimeConfig
	{
		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<int> ResolutionWidth;

		public readonly ConfigEntry<int> ResolutionHeight;

		public readonly ConfigEntry<float> Framerate;

		public readonly ConfigEntry<float> ScreenOpacity;

		public readonly ConfigEntry<float> ScreenInset;

		public readonly ConfigEntry<bool> DrawBehindPhoneWidgets;

		public readonly ConfigEntry<int> ScreenLayerOverride;

		public readonly ConfigEntry<bool> PauseFeedWhenPhoneStowed;

		public readonly ConfigEntry<TeardownMode> TeardownBehaviour;

		public readonly ConfigEntry<float> IdleKeepAliveSeconds;

		public readonly ConfigEntry<CameraFacing> DefaultFacing;

		public readonly ConfigEntry<Key> FlipCameraKey;

		public readonly ConfigEntry<float> FrontDistance;

		public readonly ConfigEntry<float> FrontHeightOffset;

		public readonly ConfigEntry<float> FrontLateralOffset;

		public readonly ConfigEntry<float> FrontYawAngle;

		public readonly ConfigEntry<float> FrontPitchAngle;

		public readonly ConfigEntry<float> PhoneFollowStrength;

		public readonly ConfigEntry<float> PhoneLensClearance;

		public readonly ConfigEntry<bool> MountCameraOnPhone;

		public readonly ConfigEntry<float> SelfieFieldOfView;

		public readonly ConfigEntry<float> RearForwardOffset;

		public readonly ConfigEntry<float> SubjectFraming;

		public readonly ConfigEntry<float> SwitchboardScreenOffset;

		public readonly ConfigEntry<bool> EnableSwitchboardMonitor;

		public readonly ConfigEntry<int> FullscreenResolutionWidth;

		public readonly ConfigEntry<int> FullscreenResolutionHeight;

		public readonly ConfigEntry<float> FullscreenFramerate;

		public readonly ConfigEntry<float> MonitorKnobSpacing;

		public readonly ConfigEntry<float> MonitorKnobScale;

		public readonly ConfigEntry<float> MonitorKnobOutwardOffset;

		public readonly ConfigEntry<bool> LogSwitchboardHierarchy;

		public readonly ConfigEntry<bool> HidePhoneWidgetsDuringCall;

		public readonly ConfigEntry<bool> HideFirstPersonPhoneFromCalls;

		public readonly ConfigEntry<bool> LetterboxPhoneVideo;

		public readonly ConfigEntry<Color> ScreenTint;

		public readonly ConfigEntry<float> ScreenTintStrength;

		public readonly ConfigEntry<float> ScreenEffectStrength;

		public readonly ConfigEntry<float> NightVisionStrength;

		public readonly ConfigEntry<float> SwitchboardScreenInset;

		public readonly ConfigEntry<bool> RequireBodyCamShipUpgrade;

		public readonly ConfigEntry<bool> InterferenceDegradesVideo;

		public readonly ConfigEntry<bool> ShowVideoForNonPlayerCallers;

		public readonly ConfigEntry<bool> ReleaseOrphanedRenderTextures;

		public readonly ConfigEntry<float> ReconcileInterval;

		public readonly ConfigEntry<bool> VerboseLogging;

		public Vector2Int Resolution => new Vector2Int(ResolutionWidth.Value, ResolutionHeight.Value);

		public Vector2Int FullscreenResolution
		{
			get
			{
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				int value = FullscreenResolutionHeight.Value;
				int num = FullscreenResolutionWidth.Value;
				if (num <= 0)
				{
					float num2 = ((ResolutionHeight.Value > 0) ? ((float)ResolutionWidth.Value / (float)ResolutionHeight.Value) : 1.3333334f);
					num = Mathf.RoundToInt((float)value * num2);
				}
				return new Vector2Int(Mathf.Clamp(num, 64, 1920), Mathf.Clamp(value, 64, 1080));
			}
		}

		public FaceTimeConfig(ConfigFile file)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected O, but got Unknown
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got Unknown
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Expected O, but got Unknown
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Expected O, but got Unknown
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Expected O, but got Unknown
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Expected O, but got Unknown
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Expected O, but got Unknown
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Expected O, but got Unknown
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Expected O, but got Unknown
			//IL_043a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0444: Expected O, but got Unknown
			//IL_0473: Unknown result type (might be due to invalid IL or missing references)
			//IL_047d: Expected O, but got Unknown
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Expected O, but got Unknown
			//IL_04e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ef: Expected O, but got Unknown
			//IL_051e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Expected O, but got Unknown
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0575: Expected O, but got Unknown
			//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ab: Expected O, but got Unknown
			//IL_05da: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e4: Expected O, but got Unknown
			//IL_0613: Unknown result type (might be due to invalid IL or missing references)
			//IL_061d: Expected O, but got Unknown
			//IL_064c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0656: Expected O, but got Unknown
			//IL_0685: Unknown result type (might be due to invalid IL or missing references)
			//IL_068f: Expected O, but got Unknown
			//IL_071f: Unknown result type (might be due to invalid IL or missing references)
			//IL_075d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0767: Expected O, but got Unknown
			//IL_0796: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a0: Expected O, but got Unknown
			//IL_07cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_07d9: Expected O, but got Unknown
			//IL_0808: Unknown result type (might be due to invalid IL or missing references)
			//IL_0812: Expected O, but got Unknown
			Enabled = file.Bind<bool>("General", "Enabled", true, "Master switch. When false the mod never creates a camera and never touches the phone model.");
			ReconcileInterval = file.Bind<float>("General", "ReconcileInterval", 0.25f, new ConfigDescription("Seconds between safety-net checks of the local phone's call state. LethalPhones exposes no call events, so the mod hooks PlayerPhone.UpdateCallingUI for instant reaction and uses this low-frequency poll to catch anything the hook misses (disconnects, round end, ownership loss).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			VerboseLogging = file.Bind<bool>("General", "VerboseLogging", false, "Log every call/camera state transition. Useful when reporting a bug.");
			ResolutionWidth = file.Bind<int>("Video", "ResolutionWidth", 256, new ConfigDescription("Width of the video feed in pixels. OpenBodyCams' own default is 160x120; a phone screen is small so there is little reason to go high.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 1024), Array.Empty<object>()));
			ResolutionHeight = file.Bind<int>("Video", "ResolutionHeight", 192, new ConfigDescription("Height of the video feed in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 1024), Array.Empty<object>()));
			Framerate = file.Bind<float>("Video", "Framerate", 24f, new ConfigDescription("Frames per second the phone camera renders at. This maps onto OpenBodyCams' own frame limiter, so lowering it is a direct, real performance saving. 0 means 'render every frame'.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
			ScreenOpacity = file.Bind<float>("Screen", "Opacity", 1f, new ConfigDescription("Opacity of the video layer on the phone screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			ScreenInset = file.Bind<float>("Screen", "Inset", 0f, new ConfigDescription("Padding in canvas units between the edge of the phone's screen canvas and the video. Increase if the video overlaps artwork you want to keep visible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
			DrawBehindPhoneWidgets = file.Bind<bool>("Screen", "DrawBehindPhoneWidgets", true, "True: the video is the bottom layer of the phone canvas, so the dialled number, status text and signal icons stay readable on top of it. False: the video covers them.");
			ScreenLayerOverride = file.Bind<int>("Screen", "LayerOverride", -1, new ConfigDescription("-1 keeps the video on the same Unity layer as the phone's screen canvas (recommended). Set to 23 to put it on the layer OpenBodyCams itself uses to hide first-person geometry from body cams - do that only if you actually see a phone screen recursively mirrored inside a body cam feed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 31), Array.Empty<object>()));
			PauseFeedWhenPhoneStowed = file.Bind<bool>("Performance", "PauseFeedWhenPhoneStowed", true, "Stop rendering the camera while the phone is flipped shut. The call stays connected; only the camera sleeps. Strongly recommended.");
			TeardownBehaviour = file.Bind<TeardownMode>("Performance", "TeardownBehaviour", TeardownMode.DisableOnly, "What to do with the OpenBodyCams camera when a call ends.\nDisableOnly - switch the camera off and keep the object for the rest of the session. Default, and recommended: disabling makes OpenBodyCams set Camera.enabled = false, which is a true zero per-frame cost, so the only thing retained is one small render texture. Every call after the first is also instant.\nDisableThenDestroyWhenIdle - as above, then destroy the camera after IdleKeepAliveSeconds.\nDestroyImmediately - free everything the moment the call drops. Lowest idle memory, but the next call pays to rebuild the camera.\nNote on the two destroying modes: OpenBodyCams creates one HDRP/Unlit material per camera and never destroys it, and that field is internal so this mod cannot destroy it either. Each rebuild therefore leaks one small material. Harmless over a few calls, but it is why DisableOnly is the default rather than the tidiest-looking option.");
			IdleKeepAliveSeconds = file.Bind<float>("Performance", "IdleKeepAliveSeconds", 30f, new ConfigDescription("Only used by DisableThenDestroyWhenIdle.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			InterferenceDegradesVideo = file.Bind<bool>("Flavour", "InterferenceDegradesVideo", true, "Fade the video towards black as LethalPhones' own connection interference rises, so a bad line looks as bad as it sounds.");
			DefaultFacing = file.Bind<CameraFacing>("Camera", "DefaultFacing", CameraFacing.Front, "Which way the phone camera points when a call starts.\nFront - the selfie view: the camera floats at arm's length in front of the other person, looking back at them, so it reads as though they are holding the phone up.\nRear  - their point of view, i.e. an ordinary body cam looking out at whatever they are looking at.");
			FlipCameraKey = file.Bind<Key>("Camera", "FlipCameraKey", (Key)36, "Key that flips between the front and rear camera mid-call, like the button on a real phone. Ignored while typing in chat or using the terminal.");
			PhoneFollowStrength = file.Bind<float>("Camera", "PhoneFollowStrength", 0.65f, new ConfigDescription("How much the camera rides the handset rather than the subject's look direction. The handset carries the walk bob and the hand animation, which is what makes the shot feel hand-held; the look direction keeps the face towards the lens even when the arm lags. 0 is a rigid head cam, 1 follows the hand completely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MountCameraOnPhone = file.Bind<bool>("Camera", "MountCameraOnPhone", true, "Bolts the camera to the handset itself, the way a real phone's front camera is bolted to its screen. This is what stops the phone ever getting in front of the lens - while walking, while looking around, and while holding the dial key, which raises the handset in front of the face. Turn it off for the old free-floating camera, which frames wider but can be blocked by the phone.");
			PhoneLensClearance = file.Bind<float>("Camera", "PhoneLensClearance", 0.14f, new ConfigDescription("How far off the handset's screen face the lens sits, in metres, when MountCameraOnPhone is on. Must clear the phone's own geometry: too small and the camera ends up inside the model, which renders as a solid black picture.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 0.5f), Array.Empty<object>()));
			SelfieFieldOfView = file.Bind<float>("Camera", "SelfieFieldOfView", 82f, new ConfigDescription("Vertical field of view for this mod's cameras, in degrees. Wider than OpenBodyCams' own 65 on purpose: with the camera bolted to a handset held near the ear it sits about 30 cm from the face, and field of view is the only way to widen that shot - backing the camera off would just put the phone in front of it again. Raise it for more head and shoulders, lower it for a tighter face.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 120f), Array.Empty<object>()));
			FrontDistance = file.Bind<float>("Camera", "FrontDistance", 0.65f, new ConfigDescription("Front camera only: how far in front of the head the camera sits, in metres. Roughly arm's length. Larger values show more of their body and surroundings.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.15f, 3f), Array.Empty<object>()));
			FrontHeightOffset = file.Bind<float>("Camera", "FrontHeightOffset", 0.05f, new ConfigDescription("Front camera only: vertical offset from head height, in metres. Slightly positive reads as a phone held just above eye line.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			FrontLateralOffset = file.Bind<float>("Camera", "FrontLateralOffset", -0.12f, new ConfigDescription("Front camera only: sideways offset in metres. Negative is towards their left hand, which is the hand LethalPhones puts the phone in - a small negative value is what makes the shot read as hand-held rather than a floating tripod.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			FrontYawAngle = file.Bind<float>("Camera", "FrontYawAngle", -8f, new ConfigDescription("Front camera only: how far round to the side the camera sits, in degrees. Negative puts it on the subject's left, which is the hand LethalPhones puts the phone in - that is what makes the shot read as hand-held rather than as a webcam bolted to their forehead. Positive swings it round to the right instead.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-80f, 80f), Array.Empty<object>()));
			FrontPitchAngle = file.Bind<float>("Camera", "FrontPitchAngle", -6f, new ConfigDescription("Front camera only: vertical angle in degrees. Negative sits below the eye line and looks slightly up, which is how a phone held at chest height sees you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-70f, 70f), Array.Empty<object>()));
			RearForwardOffset = file.Bind<float>("Camera", "RearForwardOffset", 0.15f, new ConfigDescription("Rear camera only: how far ahead of the head the camera sits, so their own body does not clip into frame.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			SubjectFraming = file.Bind<float>("Camera", "SubjectFraming", 1f, new ConfigDescription("Framing multiplier for non-player callers - creatures holding a phone. The camera pulls back by a multiple of the subject's own measured size, so a knee-high lootbug and a man-sized masked enemy both frame sensibly from one number. Raise it to sit further away and show more of the room.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 5f), Array.Empty<object>()));
			SwitchboardScreenOffset = file.Bind<float>("Camera", "SwitchboardScreenOffset", 0f, new ConfigDescription("Extra distance in metres that the switchboard camera stands off from the centre of its screen. The mod already steps far enough out to avoid clipping into the mesh; raise this if the shot still starts inside the cabinet.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			EnableSwitchboardMonitor = file.Bind<bool>("Switchboard", "EnableSwitchboardMonitor", true, "Adds a knob to the ship's switchboard, between its select-next and select-previous knobs, that toggles a view of whoever is currently calling the switchboard on the switchboard's own display. Purely local: it sends no network messages, so nobody else in the lobby needs this mod, and each player toggles their own view.");
			FullscreenResolutionWidth = file.Bind<int>("Switchboard", "FullscreenResolutionWidth", 0, new ConfigDescription("Width of the caller feed shown on the switchboard's display, or 0 to derive it from the framing (recommended). OpenBodyCams' field of view is VERTICAL, so a wider texture gains image at the sides rather than losing any at the top and bottom.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1920), Array.Empty<object>()));
			FullscreenResolutionHeight = file.Bind<int>("Switchboard", "FullscreenResolutionHeight", 480, new ConfigDescription("Height of the switchboard monitor feed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 1080), Array.Empty<object>()));
			FullscreenFramerate = file.Bind<float>("Switchboard", "FullscreenFramerate", 30f, new ConfigDescription("Frames per second for the switchboard monitor feed. 0 renders every frame.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
			MonitorKnobSpacing = file.Bind<float>("Switchboard", "MonitorKnobSpacing", 0f, new ConfigDescription("Where the new knob sits along the line joining the select-next and select-previous knobs. 0 is exactly halfway between them (the default); -1 and 1 sit on top of one or the other; values beyond that continue past them. The prefab's real positions live in an AssetBundle and are only measurable in game, so this is the dial to turn if it lands somewhere awkward.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-3f, 3f), Array.Empty<object>()));
			MonitorKnobScale = file.Bind<float>("Switchboard", "MonitorKnobScale", 0.28f, new ConfigDescription("Diameter of the visible knob as a fraction of the gap between the switchboard's select-next and select-previous knobs. Sized against that gap rather than in metres so it stays right whatever scale the switchboard is placed at.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
			MonitorKnobOutwardOffset = file.Bind<float>("Switchboard", "MonitorKnobOutwardOffset", 0f, new ConfigDescription("Pushes the knob out of the panel along the switchboard's own forward axis, in metres. Raise this if the knob is buried in the cabinet - a knob whose collider is inside solid geometry cannot be interacted with, because the interact raycast hits the cabinet first.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-0.5f, 0.5f), Array.Empty<object>()));
			LogSwitchboardHierarchy = file.Bind<bool>("Switchboard", "LogSwitchboardHierarchy", true, "Dumps the switchboard's full child hierarchy to the log once - names, local positions, and which children have renderers or colliders. The switchboard prefab lives inside an AssetBundle, so this is the only way to see what is actually in there. Turn it on if the knob lands in the wrong place, and send the log. On by default for now, because the knob placement is still being dialled in; set it false once you are happy.");
			HidePhoneWidgetsDuringCall = file.Bind<bool>("Screen", "HidePhoneWidgetsDuringCall", true, "Take LethalPhones' own screen widgets - dialled number, call status, signal bars, volume icon - out of the picture entirely while video is playing, rather than just fading them. They are restored the moment the video stops, so the phone is still fully usable for dialling. Set false to leave them over the video.");
			HideFirstPersonPhoneFromCalls = file.Bind<bool>("Screen", "HideFirstPersonPhoneFromCalls", true, "Keeps your own held handset out of your outgoing picture, by removing its layer from this mod's cameras only. Every other camera - including your own eyes - still draws it normally. Turn it off if your phone stops rendering in your hands.");
			LetterboxPhoneVideo = file.Bind<bool>("Screen", "LetterboxPhoneVideo", true, "Fit the video inside the handset screen so the whole subject is visible, instead of filling the screen and cropping the sides off.");
			ScreenTint = file.Bind<Color>("Screen", "ScreenTint", new Color(0.42f, 1f, 0.55f), "The colour the feed is tinted towards, so it reads as something coming through a phone screen rather than a clean camera render. The default is a barely-there phosphor green.");
			ScreenTintStrength = file.Bind<float>("Screen", "ScreenTintStrength", 0.45f, new ConfigDescription("How far the picture is pushed towards ScreenTint. 0 is a completely clean render, 1 is the full tint colour. Applies to the handset and the switchboard alike.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			ScreenEffectStrength = file.Bind<float>("Screen", "ScreenEffectStrength", 0.18f, new ConfigDescription("Strength of the faint scanline overlay that sells the 'displayed on a screen' look. Deliberately low - it should be something you notice only if you look for it. 0 turns it off entirely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			NightVisionStrength = file.Bind<float>("Screen", "NightVisionStrength", 0.4f, new ConfigDescription("Scales OpenBodyCams' night-vision light on this mod's cameras only, never on the ship monitor. That light is cloned from the player's own night vision, which is green, and at full strength it is most of why a body cam feed looks so heavily tinted. Lower means a more natural, less washed-out picture; 1 matches the ship monitor exactly. Dark interiors will be genuinely darker as you lower it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			SwitchboardScreenInset = file.Bind<float>("Switchboard", "SwitchboardScreenInset", 0f, new ConfigDescription("Padding in canvas units between the edge of the switchboard's display and the caller view drawn on it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 400f), Array.Empty<object>()));
			RequireBodyCamShipUpgrade = file.Bind<bool>("Compatibility", "RequireBodyCamShipUpgrade", false, "OpenBodyCams can put the ship's body cam behind a purchasable upgrade (its own ShipUpgradeEnabled option, active whenever LethalLib is installed). When this is true, the phone video feed waits on that same purchase.\nDefault false, because a phone call is a different feature from the ship monitor, and silently showing nothing until someone buys an antenna makes the mod look broken. Set it true if you would rather the phone respect the upgrade.");
			ShowVideoForNonPlayerCallers = file.Bind<bool>("Flavour", "ShowVideoForNonPlayerCallers", true, "Also show a feed when the other end of the call is not a player - the switchboard, or one of the creatures that can pick up a phone. Disable for players-only FaceTime.");
			ReleaseOrphanedRenderTextures = file.Bind<bool>("Advanced", "ReleaseOrphanedRenderTextures", true, "OpenBodyCams allocates a fresh RenderTexture every time a camera's settings change and does not release the old one. When this is enabled the mod releases the previous texture of its OWN camera (never anyone else's) to stop VRAM creeping up over a long session.");
		}
	}
	internal enum CameraFacing
	{
		Front,
		Rear
	}
	internal enum TeardownMode
	{
		DestroyImmediately,
		DisableThenDestroyWhenIdle,
		DisableOnly
	}
	public static class FaceTimeEvents
	{
		public static event Action<CallConnectedInfo> CallConnected;

		public static event Action<PlayerPhone> CallEnded;

		public static event Action<RenderTexture> VideoTextureChanged;

		internal static void RaiseCallConnected(CallConnectedInfo info)
		{
			Raise(FaceTimeEvents.CallConnected, info, "CallConnected");
		}

		internal static void RaiseCallEnded(PlayerPhone localPhone)
		{
			Raise(FaceTimeEvents.CallEnded, localPhone, "CallEnded");
		}

		internal static void RaiseVideoTextureChanged(RenderTexture texture)
		{
			Raise(FaceTimeEvents.VideoTextureChanged, texture, "VideoTextureChanged");
		}

		private static void Raise<T>(Action<T> handler, T argument, string name)
		{
			if (handler == null)
			{
				return;
			}
			Delegate[] invocationList = handler.GetInvocationList();
			foreach (Delegate obj in invocationList)
			{
				try
				{
					((Action<T>)obj)(argument);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("A " + name + " subscriber threw: " + ex));
				}
			}
		}
	}
	public readonly struct CallConnectedInfo
	{
		public readonly PlayerPhone LocalPhone;

		public readonly PhoneBehavior RemotePhone;

		public readonly short RemoteNumber;

		public readonly bool RemoteIsPlayer;

		internal CallConnectedInfo(PlayerPhone localPhone, PhoneBehavior remotePhone, short remoteNumber, bool remoteIsPlayer)
		{
			LocalPhone = localPhone;
			RemotePhone = remotePhone;
			RemoteNumber = remoteNumber;
			RemoteIsPlayer = remoteIsPlayer;
		}
	}
	[BepInPlugin("rayan.LethalFaceTime", "LethalFaceTime", "1.0.0")]
	[BepInDependency("LethalPhones", "1.3.0")]
	[BepInDependency("Zaggy1024.OpenBodyCams", "3.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private bool _loggedTickFailure;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static FaceTimeConfig Settings { get; private set; }

		private void Awake()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				Settings = new FaceTimeConfig(((BaseUnityPlugin)this).Config);
				((BaseUnityPlugin)this).Config.SettingChanged += HandleSettingChanged;
				_harmony = new Harmony("rayan.LethalFaceTime");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				FaceTimeManager.Create(Settings, (MonoBehaviour)(object)this);
				Log.LogInfo((object)"LethalFaceTime 1.0.0 loaded.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to initialise LethalFaceTime: " + ex));
			}
		}

		private void Update()
		{
			try
			{
				FaceTimeManager.Instance?.Tick();
			}
			catch (Exception ex)
			{
				if (!_loggedTickFailure)
				{
					_loggedTickFailure = true;
					Log.LogError((object)("LethalFaceTime's update loop threw; further occurrences suppressed: " + ex));
				}
			}
		}

		private void LateUpdate()
		{
			try
			{
				FaceTimeManager.Instance?.LateTick();
			}
			catch (Exception ex)
			{
				if (!_loggedTickFailure)
				{
					_loggedTickFailure = true;
					Log.LogError((object)("LethalFaceTime's late update threw; further occurrences suppressed: " + ex));
				}
			}
		}

		private void OnDestroy()
		{
			try
			{
				FaceTimeManager.Instance?.Shutdown();
				((BaseUnityPlugin)this).Config.SettingChanged -= HandleSettingChanged;
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Failed to unpatch cleanly: " + ex.Message));
			}
		}

		private static void HandleSettingChanged(object sender, SettingChangedEventArgs args)
		{
			FaceTimeManager.NotifySettingsChanged();
		}

		internal static void LogVerbose(string message)
		{
			if (Settings != null && Settings.VerboseLogging.Value)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	internal static class PluginMetadata
	{
		public const string Guid = "rayan.LethalFaceTime";

		public const string Name = "LethalFaceTime";

		public const string Version = "1.0.0";
	}
	internal static class Dependencies
	{
		public const string LethalPhonesGuid = "LethalPhones";

		public const string LethalPhonesMinimumVersion = "1.3.0";

		public const string OpenBodyCamsGuid = "Zaggy1024.OpenBodyCams";

		public const string OpenBodyCamsMinimumVersion = "3.0.0";
	}
}
namespace LethalFaceTime.Runtime
{
	internal sealed class FaceTimeFeed : IDisposable
	{
		private const string GeneratedMonitorMaterialName = "BodyCamMaterial";

		private static readonly FieldInfo MonitorOnMaterialField = AccessTools.Field(typeof(BodyCamComponent), "MonitorOnMaterial");

		private static readonly FieldInfo NightVisionLightField = AccessTools.Field(typeof(BodyCamComponent), "nightVisionLight");

		private readonly FaceTimeConfig _config;

		private readonly MonoBehaviour _coroutineHost;

		private Vector2Int _resolution;

		private float _framerate;

		private float _baseNightVisionIntensity = -1f;

		private int _extraCullingLayers;

		private int _removedCullingLayers;

		private GameObject _host;

		private BodyCamComponent _bodyCam;

		private RenderTexture _texture;

		private bool _rendering = true;

		public bool IsDead
		{
			get
			{
				if (!((Object)(object)_bodyCam == (Object)null))
				{
					return (Object)(object)_host == (Object)null;
				}
				return true;
			}
		}

		public RenderTexture Texture => _texture;

		public bool HasSignal
		{
			get
			{
				if (IsDead || (Object)(object)_texture == (Object)null || !_rendering)
				{
					return false;
				}
				try
				{
					return !_bodyCam.IsBlanked;
				}
				catch (Exception)
				{
					return false;
				}
			}
		}

		public Lens Lens
		{
			get
			{
				if (IsDead)
				{
					return new Lens(null);
				}
				try
				{
					return new Lens(_bodyCam.GetCamera());
				}
				catch (Exception)
				{
					return new Lens(null);
				}
			}
		}

		public int CameraCullingMask
		{
			get
			{
				if (IsDead)
				{
					return 0;
				}
				try
				{
					Camera camera = _bodyCam.GetCamera();
					return ((Object)(object)camera != (Object)null) ? camera.cullingMask : 0;
				}
				catch (Exception)
				{
					return 0;
				}
			}
		}

		public event Action<RenderTexture> TextureChanged;

		private FaceTimeFeed(FaceTimeConfig config, MonoBehaviour coroutineHost, Vector2Int resolution, float framerate)
		{
			//IL_0027: 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)
			_config = config;
			_coroutineHost = coroutineHost;
			_resolution = resolution;
			_framerate = framerate;
		}

		public void AddCullingLayer(int layer)
		{
			if (IsDead || layer < 0 || layer > 31)
			{
				return;
			}
			_extraCullingLayers |= 1 << layer;
			try
			{
				Camera camera = _bodyCam.GetCamera();
				if ((Object)(object)camera != (Object)null)
				{
					camera.cullingMask |= _extraCullingLayers;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not extend the camera culling mask: " + ex.Message);
			}
		}

		public void RemoveCullingLayer(int layer)
		{
			if (IsDead || layer <= 0 || layer > 31)
			{
				return;
			}
			int num = 1 << layer;
			if ((_removedCullingLayers & num) != 0)
			{
				return;
			}
			_removedCullingLayers |= num;
			try
			{
				Camera camera = _bodyCam.GetCamera();
				if ((Object)(object)camera != (Object)null)
				{
					camera.cullingMask &= ~_removedCullingLayers;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not trim the camera culling mask: " + ex.Message);
			}
		}

		public static FaceTimeFeed TryCreate(FaceTimeConfig config, MonoBehaviour coroutineHost, Vector2Int resolution, float framerate)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			if (!OpenBodyCamsBridge.IsReady())
			{
				Plugin.LogVerbose("OpenBodyCams is not initialised yet; deferring camera creation.");
				return null;
			}
			FaceTimeFeed faceTimeFeed = new FaceTimeFeed(config, coroutineHost, resolution, framerate);
			GameObject val = null;
			try
			{
				val = new GameObject("LethalFaceTime.PhoneCamera");
				BodyCamComponent val2 = BodyCam.CreateBodyCam(val, (Renderer)null, -1, (ManualCameraRenderer)null);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogWarning((object)"OpenBodyCams returned no body cam component.");
					Object.Destroy((Object)(object)val);
					return null;
				}
				if (Array.IndexOf(BodyCamComponent.GetAllBodyCams(), val2) < 0)
				{
					Plugin.Log.LogWarning((object)"OpenBodyCams rejected the phone camera - its per-game setup has not finished. Will retry.");
					Object.Destroy((Object)(object)val);
					return null;
				}
				faceTimeFeed._host = val;
				faceTimeFeed._bodyCam = val2;
				val2.OnRenderTextureCreated += faceTimeFeed.HandleRenderTextureCreated;
				faceTimeFeed.CaptureCurrentTexture();
				val2.ForceEnableCamera = true;
				val2.Framerate = framerate;
				if (val2.Resolution != resolution)
				{
					val2.Resolution = resolution;
				}
				faceTimeFeed.ApplyNightVision();
				faceTimeFeed.ApplyFieldOfView();
				Plugin.LogVerbose("Created a camera at " + ((Vector2Int)(ref resolution)).x + "x" + ((Vector2Int)(ref resolution)).y + " @ " + framerate + "fps.");
				return faceTimeFeed;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to create the OpenBodyCams feed: " + ex));
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				faceTimeFeed._host = null;
				faceTimeFeed._bodyCam = null;
				return null;
			}
		}

		public bool SetTargetToPlayer(PlayerControllerB player, string description)
		{
			if (IsDead || (Object)(object)player == (Object)null)
			{
				return false;
			}
			return Retarget(delegate
			{
				_bodyCam.SetTargetToPlayer(player);
			}, description);
		}

		public bool SetTargetToTransform(Transform transform, string description)
		{
			if (IsDead || (Object)(object)transform == (Object)null)
			{
				return false;
			}
			return Retarget(delegate
			{
				_bodyCam.SetTargetToTransform(transform);
			}, description);
		}

		private bool Retarget(Action apply, string description)
		{
			try
			{
				apply();
				_bodyCam.StartTargetTransition();
				Plugin.LogVerbose("Phone camera now targeting " + description + ".");
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to target the phone camera: " + ex));
				return false;
			}
		}

		public void SetRendering(bool rendering)
		{
			if (IsDead || _rendering == rendering)
			{
				return;
			}
			_rendering = rendering;
			try
			{
				((Behaviour)_bodyCam).enabled = rendering;
				Plugin.LogVerbose(rendering ? "Phone camera resumed." : "Phone camera paused.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to toggle the phone camera: " + ex));
			}
		}

		public void ClearTarget()
		{
			if (IsDead)
			{
				return;
			}
			try
			{
				_bodyCam.SetTargetToNone();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to clear the phone camera target: " + ex));
			}
		}

		public void ApplySettings(Vector2Int resolution, float framerate)
		{
			//IL_000a: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (IsDead)
			{
				return;
			}
			_resolution = resolution;
			_framerate = framerate;
			try
			{
				_bodyCam.Framerate = framerate;
				if (_bodyCam.Resolution != resolution)
				{
					_bodyCam.Resolution = resolution;
				}
				CaptureCurrentTexture();
				ApplyNightVision();
				ApplyFieldOfView();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to apply camera settings: " + ex));
			}
		}

		public void ApplyFieldOfView()
		{
			if (IsDead)
			{
				return;
			}
			float value = _config.SelfieFieldOfView.Value;
			if (value <= 1f)
			{
				return;
			}
			try
			{
				Camera camera = _bodyCam.GetCamera();
				if ((Object)(object)camera != (Object)null && !Mathf.Approximately(camera.fieldOfView, value))
				{
					camera.fieldOfView = value;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not set the camera field of view: " + ex.Message);
			}
		}

		public void ApplyNightVision()
		{
			if (IsDead || NightVisionLightField == null)
			{
				return;
			}
			try
			{
				object? value = NightVisionLightField.GetValue(_bodyCam);
				Light val = (Light)((value is Light) ? value : null);
				if (val != null)
				{
					float num = Mathf.Clamp01(_config.NightVisionStrength.Value);
					if (_baseNightVisionIntensity < 0f)
					{
						_baseNightVisionIntensity = val.intensity;
					}
					val.intensity = _baseNightVisionIntensity * num;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not adjust the night-vision light: " + ex.Message);
			}
		}

		private void CaptureCurrentTexture()
		{
			if (IsDead)
			{
				return;
			}
			try
			{
				Camera camera = _bodyCam.GetCamera();
				if ((Object)(object)camera != (Object)null && (Object)(object)camera.targetTexture != (Object)null)
				{
					HandleRenderTextureCreated(camera.targetTexture);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to read the camera's render texture: " + ex));
			}
		}

		private void HandleRenderTextureCreated(RenderTexture texture)
		{
			if (!((Object)(object)texture == (Object)(object)_texture))
			{
				RenderTexture texture2 = _texture;
				_texture = texture;
				try
				{
					this.TextureChanged?.Invoke(texture);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("A texture consumer threw: " + ex));
				}
				ReleaseSupersededTexture(texture2, texture);
			}
		}

		private void ReleaseSupersededTexture(RenderTexture previous, RenderTexture current)
		{
			if (!_config.ReleaseOrphanedRenderTextures.Value || (Object)(object)previous == (Object)null || (Object)(object)previous == (Object)(object)current)
			{
				return;
			}
			try
			{
				Camera val = (IsDead ? null : _bodyCam.GetCamera());
				if ((Object)(object)val != (Object)null && (Object)(object)val.targetTexture == (Object)(object)previous)
				{
					return;
				}
			}
			catch (Exception)
			{
				return;
			}
			ReleaseDeferred(previous);
		}

		private void ReleaseDeferred(RenderTexture texture)
		{
			if (!((Object)(object)texture == (Object)null))
			{
				if ((Object)(object)_coroutineHost == (Object)null || !((Component)_coroutineHost).gameObject.activeInHierarchy)
				{
					DestroyTexture(texture);
				}
				else
				{
					_coroutineHost.StartCoroutine(ReleaseAfterFrames(texture, 2));
				}
			}
		}

		private static IEnumerator ReleaseAfterFrames(RenderTexture texture, int frames)
		{
			for (int i = 0; i < frames; i++)
			{
				yield return null;
			}
			DestroyTexture(texture);
		}

		private void DestroyOwnedMonitorMaterial()
		{
			if ((Object)(object)_bodyCam == (Object)null || MonitorOnMaterialField == null)
			{
				return;
			}
			try
			{
				object? value = MonitorOnMaterialField.GetValue(_bodyCam);
				Material val = (Material)((value is Material) ? value : null);
				if ((Object)(object)val != (Object)null && ((Object)val).name == "BodyCamMaterial")
				{
					Object.Destroy((Object)(object)val);
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not free the body cam's generated material: " + ex.Message);
			}
		}

		private static void DestroyTexture(RenderTexture texture)
		{
			if ((Object)(object)texture == (Object)null)
			{
				return;
			}
			try
			{
				Object.Destroy((Object)(object)texture);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to free an orphaned render texture: " + ex.Message));
			}
		}

		public void Dispose()
		{
			RenderTexture texture = _texture;
			_texture = null;
			if ((Object)(object)_bodyCam != (Object)null)
			{
				try
				{
					_bodyCam.OnRenderTextureCreated -= HandleRenderTextureCreated;
					_bodyCam.ForceEnableCamera = false;
					((Behaviour)_bodyCam).enabled = false;
					_bodyCam.SetTargetToNone();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Error while detaching from the phone camera: " + ex.Message));
				}
			}
			_rendering = false;
			DestroyOwnedMonitorMaterial();
			if ((Object)(object)_host != (Object)null)
			{
				try
				{
					Object.Destroy((Object)(object)_host);
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("Error while destroying the phone camera host: " + ex2.Message));
				}
			}
			_host = null;
			_bodyCam = null;
			if (_config.ReleaseOrphanedRenderTextures.Value)
			{
				ReleaseDeferred(texture);
			}
			Plugin.LogVerbose("Phone camera destroyed.");
		}
	}
	internal sealed class FaceTimeManager
	{
		private const int NoTargetKey = 0;

		private static FaceTimeManager _instance;

		private FaceTimeConfig _config;

		private PhoneScreen _screen;

		private FaceTimeFeed _feed;

		private PlayerPhone _trackedPhone;

		private ulong _sessionIdentity;

		private int _targetKey;

		private bool _inCall;

		private float _reconcileTimer;

		private float _idleTimer;

		private bool _idleTeardownDone;

		private bool _settingsDirty;

		private string _lastTraceKey;

		private bool _loggedFirstTick;

		private CameraFacing _facing;

		private readonly PhoneCameraRig _rig;

		private readonly SwitchboardMonitor _switchboardMonitor;

		private readonly FacingSync _facingSync = new FacingSync();

		private CallTarget _currentTarget;

		private int _infoTraceBudget = 6;

		private readonly HashSet<string> _warnedKeys = new HashSet<string>();

		private readonly MonoBehaviour _host;

		internal static FaceTimeManager Instance => _instance;

		public CameraFacing SelfFacing { get; private set; }

		private FaceTimeManager(FaceTimeConfig config, MonoBehaviour host)
		{
			_config = config;
			_host = host;
			_screen = new PhoneScreen(config);
			_rig = new PhoneCameraRig(config);
			_switchboardMonitor = new SwitchboardMonitor(config, host);
			_facing = config.DefaultFacing.Value;
		}

		public static void Create(FaceTimeConfig config, MonoBehaviour host)
		{
			if (_instance == null)
			{
				_instance = new FaceTimeManager(config, host);
				Plugin.Log.LogInfo((object)"Manager created; tick is driven by the plugin object.");
			}
		}

		public static void NotifyPhoneStateChanged(PlayerPhone phone)
		{
			if (_instance != null && !((Object)(object)phone == (Object)null) && !((Object)(object)phone != (Object)(object)LethalPhonesBridge.GetLocalPhone()))
			{
				_instance.Reconcile();
			}
		}

		public static void NotifySettingsChanged()
		{
			if (_instance != null)
			{
				_instance._settingsDirty = true;
			}
		}

		public void Shutdown()
		{
			EndCall("the manager is shutting down");
			DestroyFeed();
			_screen?.Detach();
			_rig?.Dispose();
			_switchboardMonitor?.Dispose();
			_facingSync.Shutdown();
			if (_instance == this)
			{
				_instance = null;
			}
		}

		public void Tick()
		{
			if (_config == null)
			{
				return;
			}
			if (!_loggedFirstTick)
			{
				_loggedFirstTick = true;
				Plugin.Log.LogInfo((object)("Manager ticking. Enabled=" + _config.Enabled.Value + " Verbose=" + _config.VerboseLogging.Value + " ReconcileInterval=" + _config.ReconcileInterval.Value + "s Teardown=" + _config.TeardownBehaviour.Value));
			}
			if (!_config.Enabled.Value)
			{
				if (_inCall || _feed != null || _screen.IsAttached)
				{
					EndCall("the mod was disabled");
					DestroyFeed();
					_screen.Detach();
				}
				return;
			}
			if (_settingsDirty)
			{
				_settingsDirty = false;
				ApplyChangedSettings();
			}
			_reconcileTimer += Time.deltaTime;
			if (_reconcileTimer >= _config.ReconcileInterval.Value)
			{
				_reconcileTimer = 0f;
				Reconcile();
			}
			if (_inCall)
			{
				PollFlipKey();
				TickActiveCall();
			}
			else
			{
				TickIdle();
			}
			_facingSync.Tick(SelfFacing, Time.deltaTime);
			_switchboardMonitor.Tick();
		}

		private void PollFlipKey()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return;
			}
			KeyControl val = current[_config.FlipCameraKey.Value];
			if (val != null && ((ButtonControl)val).wasPressedThisFrame)
			{
				PlayerControllerB val2 = StartOfRound.Instance?.localPlayerController;
				if ((!((Object)(object)val2 != (Object)null) || (!val2.inTerminalMenu && !val2.isTypingChat)) && LethalPhonesBridge.IsPhoneOpen(_trackedPhone))
				{
					FlipCamera();
				}
			}
		}

		private void ApplyChangedSettings()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			_screen.ApplyLayout();
			_feed?.ApplySettings(_config.Resolution, _config.Framerate.Value);
			if (_inCall && _feed != null && !_feed.IsDead)
			{
				_screen.SetTexture((Texture)(object)_feed.Texture);
			}
			if (!_inCall)
			{
				_idleTeardownDone = false;
				if (_config.TeardownBehaviour.Value == TeardownMode.DestroyImmediately)
				{
					DestroyFeed();
				}
			}
		}

		private void Reconcile()
		{
			if (_config == null || !_config.Enabled.Value)
			{
				return;
			}
			PlayerPhone localPhone = LethalPhonesBridge.GetLocalPhone();
			if ((Object)(object)localPhone == (Object)null)
			{
				Trace("waiting-for-phone", "No local phone yet (LethalPhones has not assigned one).");
				EndCall("the local phone is gone");
				return;
			}
			CallSnapshot call = LethalPhonesBridge.ReadCall((PhoneBehavior)(object)localPhone);
			if (!call.IsConnected)
			{
				Trace("idle", "Local phone found, no active call.");
				EndCall("the call ended");
				return;
			}
			CallTarget target = LethalPhonesBridge.ResolveTarget(call.RemotePhone);
			if (target.IsValid && !target.IsPlayer && !_config.ShowVideoForNonPlayerCallers.Value)
			{
				EndCall("the far end is not a player and non-player video is disabled");
				return;
			}
			Trace("connected", "Active call to " + target.Describe() + " (number " + call.RemoteNumber + ").");
			if (!_inCall || _sessionIdentity != call.Identity || (Object)(object)_trackedPhone != (Object)(object)localPhone)
			{
				BeginCall(localPhone, call, target);
			}
			else
			{
				TryBringUpVideo(target);
			}
		}

		private void BeginCall(PlayerPhone phone, CallSnapshot call, CallTarget target)
		{
			if (_inCall)
			{
				RaiseCallEnded();
			}
			_trackedPhone = phone;
			_sessionIdentity = call.Identity;
			_targetKey = 0;
			_inCall = true;
			_idleTimer = 0f;
			_idleTeardownDone = false;
			_facing = _config.DefaultFacing.Value;
			_rig.InvalidateMeasurement();
			Plugin.Log.LogInfo((object)("FaceTime call connected to " + target.Describe() + "."));
			FaceTimeEvents.RaiseCallConnected(new CallConnectedInfo(phone, call.RemotePhone, call.RemoteNumber, target.IsPlayer));
			TryBringUpVideo(target);
		}

		private bool TryBringUpVideo(CallTarget target)
		{
			if (!_inCall || (Object)(object)_trackedPhone == (Object)null)
			{
				return false;
			}
			if (!target.IsValid)
			{
				Trace("unresolved-target", "Call connected but the far end could not be resolved yet.");
				return false;
			}
			if (_config.RequireBodyCamShipUpgrade.Value && !OpenBodyCamsBridge.BodyCamsAreUnlocked())
			{
				WarnOnce("bodycam-locked", "The phone video feed is waiting on the ship's Bodycam upgrade, because Compatibility/RequireBodyCamShipUpgrade is enabled. Buy the upgrade, or set that option to false to decouple the phone from it.");
				return false;
			}
			if (!_screen.IsAttached && !_screen.TryAttach(_trackedPhone))
			{
				Trace("no-screen", "Phone screen canvas not available yet; will retry.");
				return false;
			}
			if (!EnsureFeed())
			{
				Trace("no-feed", "Could not create the OpenBodyCams camera yet; will retry.");
				return false;
			}
			if (!AimCamera(target))
			{
				return false;
			}
			if (_config.HideFirstPersonPhoneFromCalls.Value)
			{
				int num = OpenBodyCamsBridge.ResolveOwnerOnlyLayer(_feed.CameraCullingMask);
				if (num >= 0)
				{
					_screen.HideHandsetFromCalls(num);
				}
				else
				{
					WarnOnce("owner-only-layer", "Found no layer that your own camera draws but body cams skip, so your held phone may appear in your outgoing picture. Set Camera/MountCameraOnPhone = false if that bothers you more than the wider framing helps.");
				}
			}
			_feed.SetRendering(ShouldRender());
			_screen.SetTexture((Texture)(object)_feed.Texture);
			_screen.WarnIfVisibleToBodyCam(_feed.CameraCullingMask);
			Trace("video-up", "Video layer live. facing=" + _facing.ToString() + " texture=" + (((Object)(object)_feed.Texture != (Object)null) ? (((Texture)_feed.Texture).width + "x" + ((Texture)_feed.Texture).height) : "<null>") + " signal=" + _feed.HasSignal + " rendering=" + ShouldRender());
			return true;
		}

		private bool AimCamera(CallTarget target)
		{
			_currentTarget = target;
			if (target.IsPlayer)
			{
				_facing = _facingSync.GetFacing(target.Player);
			}
			int num = ComputeTargetKey(target, _facing);
			if (_facing == CameraFacing.Front || !target.IsPlayer)
			{
				_rig.RearFacing = _facing == CameraFacing.Rear;
				if (!_rig.Aim(target, _feed.Lens))
				{
					Trace("no-anchor", "Could not frame a selfie shot for " + target.Describe() + " yet.");
					return false;
				}
				if (num == _targetKey)
				{
					return true;
				}
				if (!_feed.SetTargetToTransform(_rig.Anchor, "front camera on " + target.Describe()))
				{
					return false;
				}
				_targetKey = num;
				return true;
			}
			if (num == _targetKey)
			{
				return true;
			}
			if (target.IsPlayer)
			{
				if (!_feed.SetTargetToPlayer(target.Player, "rear camera on " + target.Describe()))
				{
					return false;
				}
				_targetKey = num;
				return true;
			}
			Trace("unaimed", "No aiming strategy for " + target.Describe() + ".");
			return false;
		}

		public void FlipCamera()
		{
			SelfFacing = ((SelfFacing == CameraFacing.Front) ? CameraFacing.Rear : CameraFacing.Front);
			Plugin.Log.LogInfo((object)("Your outgoing camera flipped to " + SelfFacing.ToString() + "; announcing it to the other players."));
		}

		private void TickActiveCall()
		{
			if ((Object)(object)_trackedPhone == (Object)null)
			{
				EndCall("the tracked phone was destroyed");
			}
			else if (_feed != null && !_feed.IsDead && _screen.IsAttached)
			{
				_feed.SetRendering(ShouldRender());
				bool hasSignal = _feed.HasSignal;
				bool phoneOpen = LethalPhonesBridge.IsPhoneOpen(_trackedPhone);
				float interference = LethalPhonesBridge.GetInterference((PhoneBehavior)(object)_trackedPhone);
				_screen.UpdateAppearance(hasSignal, phoneOpen, interference);
			}
		}

		public void LateTick()
		{
			if (_config != null && _config.Enabled.Value)
			{
				_switchboardMonitor.LateTick();
				if (_inCall && _currentTarget.IsValid && (_facing != CameraFacing.Rear || !_currentTarget.IsPlayer) && _feed != null && !_feed.IsDead)
				{
					_rig.Aim(_currentTarget, _feed.Lens);
				}
			}
		}

		private void TickIdle()
		{
			if (_feed != null && !_idleTeardownDone && _config.TeardownBehaviour.Value == TeardownMode.DisableThenDestroyWhenIdle)
			{
				_idleTimer += Time.deltaTime;
				if (!(_idleTimer < _config.IdleKeepAliveSeconds.Value))
				{
					_idleTeardownDone = true;
					Plugin.LogVerbose("Idle keep-alive elapsed; destroying the phone camera.");
					DestroyFeed();
				}
			}
		}

		private bool ShouldRender()
		{
			if (_config.PauseFeedWhenPhoneStowed.Value)
			{
				return LethalPhonesBridge.IsPhoneOpen(_trackedPhone);
			}
			return true;
		}

		private bool EnsureFeed()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (_feed != null && !_feed.IsDead)
			{
				return true;
			}
			if (_feed != null)
			{
				DestroyFeed();
			}
			_feed = FaceTimeFeed.TryCreate(_config, _host, _config.Resolution, _config.Framerate.Value);
			if (_feed == null)
			{
				return false;
			}
			_feed.TextureChanged += HandleFeedTextureChanged;
			_targetKey = 0;
			return true;
		}

		private void HandleFeedTextureChanged(RenderTexture texture)
		{
			if (_inCall)
			{
				_screen.SetTexture((Texture)(object)texture);
			}
			FaceTimeEvents.RaiseVideoTextureChanged(texture);
		}

		private void EndCall(string reason)
		{
			if (!_inCall)
			{
				return;
			}
			Plugin.Log.LogInfo((object)("FaceTime call disconnected because " + reason + "."));
			RaiseCallEnded();
			_inCall = false;
			_sessionIdentity = 0uL;
			_targetKey = 0;
			_idleTimer = 0f;
			_idleTeardownDone = false;
			_trackedPhone = null;
			_currentTarget = CallTarget.None;
			_screen.SetWidgetsHidden(hidden: false);
			_screen.SetTexture(null);
			switch (_config.TeardownBehaviour.Value)
			{
			case TeardownMode.DestroyImmediately:
				DestroyFeed();
				break;
			case TeardownMode.DisableThenDestroyWhenIdle:
			case TeardownMode.DisableOnly:
				if (_feed != null && !_feed.IsDead)
				{
					_feed.SetRendering(rendering: false);
					_feed.ClearTarget();
					_targetKey = 0;
				}
				break;
			}
		}

		private void RaiseCallEnded()
		{
			FaceTimeEvents.RaiseCallEnded(_trackedPhone);
		}

		private void DestroyFeed()
		{
			if (_feed != null)
			{
				_feed.TextureChanged -= HandleFeedTextureChanged;
				_feed.Dispose();
				_feed = null;
				_targetKey = 0;
				_screen.SetTexture(null);
				FaceTimeEvents.RaiseVideoTextureChanged(null);
			}
		}

		private void Trace(string stateKey, string message)
		{
			if (!(_lastTraceKey == stateKey))
			{
				_lastTraceKey = stateKey;
				string text = "[" + stateKey + "] " + message;
				if (_infoTraceBudget > 0)
				{
					_infoTraceBudget--;
					Plugin.Log.LogInfo((object)text);
				}
				else
				{
					Plugin.LogVerbose(text);
				}
			}
		}

		private void WarnOnce(string key, string message)
		{
			if (!_warnedKeys.Contains(key))
			{
				_warnedKeys.Add(key);
				Plugin.Log.LogWarning((object)message);
			}
		}

		private static int ComputeTargetKey(CallTarget target, CameraFacing facing)
		{
			if (!target.IsValid)
			{
				return 0;
			}
			int num = ((target.IsPlayer && (Object)(object)target.Player != (Object)null) ? ((Object)target.Player).GetInstanceID() : (((Object)(object)target.Root != (Object)null) ? ((Object)target.Root).GetInstanceID() : 0));
			if (num == 0)
			{
				return 0;
			}
			return num ^ ((int)(facing + 1) * 1597463007);
		}
	}
	internal sealed class FacingSync
	{
		private const string MessageName = "LethalFaceTime.Facing";

		private const float ResendInterval = 5f;

		private readonly Dictionary<ulong, CameraFacing> _facings = new Dictionary<ulong, CameraFacing>();

		private NetworkManager _registeredWith;

		private CameraFacing _lastSent;

		private bool _everSent;

		private float _resendTimer;

		private bool _warnedSendFailed;

		public void Tick(CameraFacing selfFacing, float deltaTime)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
			{
				_registeredWith = null;
				_everSent = false;
				_facings.Clear();
				return;
			}
			EnsureRegistered(singleton);
			_resendTimer += deltaTime;
			if (!_everSent || selfFacing != _lastSent || !(_resendTimer < 5f))
			{
				_resendTimer = 0f;
				_lastSent = selfFacing;
				_everSent = true;
				Announce(singleton, selfFacing);
			}
		}

		public CameraFacing GetFacing(PlayerControllerB player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return CameraFacing.Front;
			}
			if (!_facings.TryGetValue(player.actualClientId, out var value))
			{
				return CameraFacing.Front;
			}
			return value;
		}

		private void EnsureRegistered(NetworkManager manager)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if ((Object)(object)_registeredWith == (Object)(object)manager)
			{
				return;
			}
			try
			{
				CustomMessagingManager customMessagingManager = manager.CustomMessagingManager;
				if (customMessagingManager != null)
				{
					customMessagingManager.RegisterNamedMessageHandler("LethalFaceTime.Facing", new HandleNamedMessageDelegate(HandleMessage));
					_registeredWith = manager;
					Plugin.LogVerbose("Registered the camera-facing message handler.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not register the camera-facing handler: " + ex.Message));
			}
		}

		private unsafe void Announce(NetworkManager manager, CameraFacing facing)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (manager.IsServer)
				{
					_facings[manager.LocalClientId] = facing;
					Broadcast(manager, manager.LocalClientId, facing);
					return;
				}
				FastBufferWriter val = Build(manager.LocalClientId, facing);
				try
				{
					manager.CustomMessagingManager.SendNamedMessage("LethalFaceTime.Facing", 0uL, val, (NetworkDelivery)3);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				if (!_warnedSendFailed)
				{
					_warnedSendFailed = true;
					Plugin.Log.LogWarning((object)("Could not send your camera facing to the other players, so flipping will only change what the switchboard shows: " + ex.Message));
				}
			}
		}

		private unsafe void Broadcast(NetworkManager manager, ulong subject, CameraFacing facing)
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = Build(subject, facing);
			try
			{
				manager.CustomMessagingManager.SendNamedMessageToAll("LethalFaceTime.Facing", val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static FastBufferWriter Build(ulong subject, CameraFacing facing)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter result = default(FastBufferWriter);
			((FastBufferWriter)(ref result))..ctor(9, (Allocator)2, -1);
			((FastBufferWriter)(ref result)).WriteValueSafe<ulong>(ref subject, default(ForPrimitives));
			byte b = (byte)facing;
			((FastBufferWriter)(ref result)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			return result;
		}

		private void HandleMessage(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			try
			{
				ulong key = default(ulong);
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref key, default(ForPrimitives));
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				CameraFacing cameraFacing = ((b == 1) ? CameraFacing.Rear : CameraFacing.Front);
				NetworkManager singleton = NetworkManager.Singleton;
				if (!((Object)(object)singleton == (Object)null))
				{
					if (singleton.IsServer && senderClientId != singleton.LocalClientId)
					{
						_facings[senderClientId] = cameraFacing;
						Broadcast(singleton, senderClientId, cameraFacing);
					}
					else
					{
						_facings[key] = cameraFacing;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Malformed camera-facing message ignored: " + ex.Message);
			}
		}

		public void Shutdown()
		{
			if ((Object)(object)_registeredWith != (Object)null)
			{
				try
				{
					CustomMessagingManager customMessagingManager = _registeredWith.CustomMessagingManager;
					if (customMessagingManager != null)
					{
						customMessagingManager.UnregisterNamedMessageHandler("LethalFaceTime.Facing");
					}
				}
				catch (Exception ex)
				{
					Plugin.LogVerbose("Error while unregistering the camera-facing handler: " + ex.Message);
				}
			}
			_registeredWith = null;
			_facings.Clear();
			_everSent = false;
		}
	}
	internal readonly struct Lens
	{
		private const float FallbackVerticalFov = 65f;

		private const float FallbackNearClip = 0.01f;

		private const float FallbackAspect = 1.3333334f;

		public readonly float TanHalfVertical;

		public readonly float TanHalfHorizontal;

		public readonly float NearClip;

		public Lens(Camera camera)
		{
			float num = 65f;
			float num2 = 1.3333334f;
			float nearClip = 0.01f;
			if ((Object)(object)camera != (Object)null)
			{
				if (camera.fieldOfView > 1f)
				{
					num = camera.fieldOfView;
				}
				if (camera.aspect > 0.01f)
				{
					num2 = camera.aspect;
				}
				if (camera.nearClipPlane > 0f)
				{
					nearClip = camera.nearClipPlane;
				}
			}
			TanHalfVertical = Mathf.Tan(num * 0.5f * (MathF.PI / 180f));
			TanHalfHorizontal = TanHalfVertical * num2;
			NearClip = nearClip;
		}

		public float DistanceToFit(Vector3 size, float fill)
		{
			//IL_0012: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			fill = Mathf.Clamp(fill, 0.05f, 1f);
			float num = Mathf.Max(size.y, 0.05f) * 0.5f;
			float num2 = Mathf.Max(Mathf.Max(size.x, size.z), 0.05f) * 0.5f;
			float num3 = num / (TanHalfVertical * fill);
			float num4 = num2 / (TanHalfHorizontal * fill);
			return Mathf.Max(num3, num4);
		}
	}
	internal sealed class LocalPlayerPose
	{
		private static readonly FieldInfo ServerLeftArmRigField = AccessTools.Field(typeof(PlayerPhone), "serverLeftArmRig");

		private static int _privateLayer = -1;

		private PlayerPhone _posed;

		private GameObject _model;

		private int[] _originalLayers;

		private Transform[] _modelTransforms;

		private bool _warnedNoRig;

		private bool _warnedNoLayer;

		private bool _loggedApplied;

		public void Apply(PlayerPhone localPhone, bool shouldPose, FaceTimeFeed feed, bool showPhoneToFeed = true)
		{
			if (!shouldPose || (Object)(object)localPhone == (Object)null)
			{
				Release();
				return;
			}
			try
			{
				if ((Object)(object)_posed != (Object)(object)localPhone)
				{
					Release();
					_posed = localPhone;
					_model = localPhone.serverPhoneModel;
					HideFromNormalCameras(showPhoneToFeed ? feed : null);
					PlayFlip(localPhone, "PhoneFlipOpen");
				}
				localPhone.SetPhoneServerModelActive(true);
				SetArmWeight(localPhone, 1f);
				EnableRig(localPhone);
				if (!_loggedApplied)
				{
					_loggedApplied = true;
					Plugin.Log.LogInfo((object)("Posing the local player's third-person body to hold its phone" + ((_privateLayer >= 0) ? (" (on private layer " + _privateLayer + ")") : "") + "."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not pose the local player's phone hand: " + ex.Message));
			}
		}

		public void Release()
		{
			if ((Object)(object)_posed == (Object)null)
			{
				_model = null;
				return;
			}
			try
			{
				RestoreLayers();
				SetArmWeight(_posed, 0f);
				PlayFlip(_posed, "PhoneFlipClosed");
				_posed.SetPhoneServerModelActive(false);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not restore the local player's phone hand: " + ex.Message));
			}
			_posed = null;
			_model = null;
			_modelTransforms = null;
			_originalLayers = null;
		}

		private void HideFromNormalCameras(FaceTimeFeed feed)
		{
			if ((Object)(object)_model == (Object)null)
			{
				return;
			}
			int num = ResolvePrivateLayer();
			if (num < 0)
			{
				if (!_warnedNoLayer)
				{
					_warnedNoLayer = true;
					Plugin.Log.LogWarning((object)"Found no spare Unity layer, so the third-person phone will be visible in your own view as well as in the call.");
				}
				return;
			}
			_modelTransforms = _model.GetComponentsInChildren<Transform>(true);
			_originalLayers = new int[_modelTransforms.Length];
			for (int i = 0; i < _modelTransforms.Length; i++)
			{
				_originalLayers[i] = ((Component)_modelTransforms[i]).gameObject.layer;
				((Component)_modelTransforms[i]).gameObject.layer = num;
			}
			feed?.AddCullingLayer(num);
		}

		private void RestoreLayers()
		{
			if (_modelTransforms == null || _originalLayers == null)
			{
				return;
			}
			for (int i = 0; i < _modelTransforms.Length; i++)
			{
				if ((Object)(object)_modelTransforms[i] != (Object)null)
				{
					((Component)_modelTransforms[i]).gameObject.layer = _originalLayers[i];
				}
			}
		}

		private static int ResolvePrivateLayer()
		{
			if (_privateLayer != -1)
			{
				if (_privateLayer != -2)
				{
					return _privateLayer;
				}
				return -1;
			}
			_privateLayer = -2;
			PlayerControllerB val = StartOfRound.Instance?.localPlayerController;
			Camera val2 = (((Object)(object)val != (Object)null) ? val.gameplayCamera : null);
			if ((Object)(object)val2 == (Object)null)
			{
				_privateLayer = -1;
				return -1;
			}
			int cullingMask = val2.cullingMask;
			for (int num = 31; num >= 8; num--)
			{
				if ((cullingMask & (1 << num)) == 0 && string.IsNullOrEmpty(LayerMask.LayerToName(num)))
				{
					_privateLayer = num;
					break;
				}
			}
			if (_privateLayer != -2)
			{
				return _privateLayer;
			}
			return -1;
		}

		private void EnableRig(PlayerPhone phone)
		{
			PlayerControllerB player = phone.player;
			if ((Object)(object)player == (Object)null || (Object)(object)player.meshContainer == (Object)null)
			{
				return;
			}
			Transform val = player.meshContainer.Find("metarig/Rig 1");
			if (!((Object)(object)val == (Object)null))
			{
				Rig component = ((Component)val).GetComponent<Rig>();
				if ((Object)(object)component != (Object)null && component.weight < 1f)
				{
					component.weight = 1f;
				}
				Animator playerBodyAnimator = player.playerBodyAnimator;
				RigBuilder val2 = (((Object)(object)playerBodyAnimator != (Object)null) ? ((Component)playerBodyAnimator).GetComponent<RigBuilder>() : null);
				if ((Object)(object)val2 != (Object)null && !((Behaviour)val2).enabled)
				{
					((Behaviour)val2).enabled = true;
				}
			}
		}

		private static void PlayFlip(PlayerPhone phone, string clip)
		{
			GameObject serverPhoneModel = phone.serverPhoneModel;
			if (!((Object)(object)serverPhoneModel == (Object)null))
			{
				Animator component = serverPhoneModel.GetComponent<Animator>();
				if ((Object)(object)component != (Object)null)
				{
					component.Play(clip);
				}
			}
		}

		private void SetArmWeight(PlayerPhone phone, float weight)
		{
			if (ServerLeftArmRigField == null)
			{
				if (!_warnedNoRig)
				{
					_warnedNoRig = true;
					Plugin.Log.LogWarning((object)"Could not find PlayerPhone.serverLeftArmRig, so the local player's arm will not be posed and the phone may look like it is floating in your own feed.");
				}
				return;
			}
			object? value = ServerLeftArmRigField.GetValue(phone);
			ChainIKConstraint val = (ChainIKConstraint)((value is ChainIKConstraint) ? value : null);
			if (val != null)
			{
				((RigConstraint<ChainIKConstraintJob, ChainIKConstraintData, ChainIKConstraintJobBinder<ChainIKConstraintData>>)(object)val).weight = weight;
			}
		}
	}
	internal sealed class NetworkedFlag
	{
		private const float ResendInterval = 5f;

		private readonly string _messageName;

		private NetworkManager _registeredWith;

		private float _resendTimer;

		private bool _warnedSendFailed;

		public bool Value { get; private set; }

		public event Action<bool> Changed;

		public NetworkedFlag(string messageName)
		{
			_messageName = messageName;
		}

		public void Tick(float deltaTime)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
			{
				_registeredWith = null;
				return;
			}
			EnsureRegistered(singleton);
			if (singleton.IsServer)
			{
				_resendTimer += deltaTime;
				if (!(_resendTimer < 5f))
				{
					_resendTimer = 0f;
					TryBroadcast(singleton, Value);
				}
			}
		}

		public unsafe void Request(bool value)
		{
			//IL_0033: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			Apply(value);
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
			{
				return;
			}
			try
			{
				if (singleton.IsServer)
				{
					TryBroadcast(singleton, value);
					return;
				}
				FastBufferWriter val = Build(value);
				try
				{
					singleton.CustomMessagingManager.SendNamedMessage(_messageName, 0uL, val, (NetworkDelivery)3);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				if (!_warnedSendFailed)
				{
					_warnedSendFailed = true;
					Plugin.Log.LogWarning((object)("Could not share the switchboard view with the other players, so it will only change on your screen: " + ex.Message));
				}
			}
		}

		private void Apply(bool value)
		{
			if (Value == value)
			{
				return;
			}
			Value = value;
			try
			{
				this.Changed?.Invoke(value);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("A shared-flag subscriber threw: " + ex));
			}
		}

		private void EnsureRegistered(NetworkManager manager)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			if ((Object)(object)_registeredWith == (Object)(object)manager)
			{
				return;
			}
			try
			{
				CustomMessagingManager customMessagingManager = manager.CustomMessagingManager;
				if (customMessagingManager != null)
				{
					customMessagingManager.RegisterNamedMessageHandler(_messageName, new HandleNamedMessageDelegate(HandleMessage));
					_registeredWith = manager;
					Plugin.LogVerbose("Registered the shared-flag handler for " + _messageName + ".");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not register " + _messageName + ": " + ex.Message));
			}
		}

		private unsafe void TryBroadcast(NetworkManager manager, bool value)
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				FastBufferWriter val = Build(value);
				try
				{
					manager.CustomMessagingManager.SendNamedMessageToAll(_messageName, val, (NetworkDelivery)3);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not broadcast " + _messageName + ": " + ex.Message);
			}
		}

		private static FastBufferWriter Build(bool value)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter result = default(FastBufferWriter);
			((FastBufferWriter)(ref result))..ctor(1, (Allocator)2, -1);
			byte b = (value ? ((byte)1) : ((byte)0));
			((FastBufferWriter)(ref result)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			return result;
		}

		private void HandleMessage(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				bool value = b != 0;
				NetworkManager singleton = NetworkManager.Singleton;
				if (!((Object)(object)singleton == (Object)null))
				{
					Apply(value);
					if (singleton.IsServer && senderClientId != singleton.LocalClientId)
					{
						TryBroadcast(singleton, value);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Malformed " + _messageName + " message ignored: " + ex.Message);
			}
		}

		public void Shutdown()
		{
			if ((Object)(object)_registeredWith != (Object)null)
			{
				try
				{
					CustomMessagingManager customMessagingManager = _registeredWith.CustomMessagingManager;
					if (customMessagingManager != null)
					{
						customMessagingManager.UnregisterNamedMessageHandler(_messageName);
					}
				}
				catch (Exception ex)
				{
					Plugin.LogVerbose("Error unregistering " + _messageName + ": " + ex.Message);
				}
			}
			_registeredWith = null;
		}
	}
	internal sealed class PhoneCameraRig : IDisposable
	{
		private const string AnchorName = "LethalFaceTime.CameraAnchor";

		private const float SubjectFrameFill = 0.6f;

		private const float ClipEscape = 0.12f;

		private const float CollisionRadius = 0.15f;

		private const float MinimumDistance = 0.3f;

		private readonly FaceTimeConfig _config;

		private GameObject _anchorObject;

		private Transform _anchor;

		private Transform _measuredFor;

		private Bounds _localBounds;

		private bool _hasMeasurement;

		private Transform _mountedTo;

		private Vector3 _mountLocalDirection;

		private Vector3 _mountLocalUp;

		public bool FullBody { get; set; }

		public bool RearFacing { get; set; }

		public Transform Anchor
		{
			get
			{
				if (!((Object)(object)_anchor == (Object)null))
				{
					return _anchor;
				}
				return null;
			}
		}

		public bool IsMounted => (Object)(object)_mountedTo != (Object)null;

		public PhoneCameraRig(FaceTimeConfig config)
		{
			_config = config;
		}

		public bool Aim(CallTarget target, Lens lens)
		{
			if (!target.IsValid)
			{
				return false;
			}
			Transform val = EnsureAnchor();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				switch (target.Kind)
				{
				case CallTargetKind.Player:
					if (_config.MountCameraOnPhone.Value && (Object)(object)target.PhoneModel != (Object)null && AimMountedOnPhone(val, target))
					{
						return true;
					}
					return AimAtPlayer(val, target);
				case CallTargetKind.Switchboard:
					return AimFromSwitchboardScreen(val, target, lens);
				default:
					return AimAtSubject(val, target, lens);
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Could not aim the phone camera: " + ex.Message);
				return false;
			}
		}

		private bool AimMountedOnPhone(Transform anchor, CallTarget target)
		{
			//IL_0031: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			Transform phoneModel = target.PhoneModel;
			Transform focus = target.Focus;
			if ((Object)(object)phoneModel == (Object)null || (Object)(object)focus == (Object)null)
			{
				return false;
			}
			if ((Object)(object)_mountedTo != (Object)(object)phoneModel)
			{
				Vector3 val = focus.position - phoneModel.position;
				if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
				{
					return false;
				}
				_mountLocalDirection = phoneModel.InverseTransformDirection(((Vector3)(ref val)).normalized);
				_mountLocalUp = phoneModel.InverseTransformDirection(Vector3.up);
				_mountedTo = phoneModel;
				anchor.SetParent(phoneModel, false);
				Plugin.LogVerbose("Mounted the phone camera on '" + ((Object)phoneModel).name + "'.");
			}
			Vector3 val2 = (RearFacing ? (-_mountLocalDirection) : _mountLocalDirection);
			anchor.localPosition = val2 * _config.PhoneLensClearance.Value;
			Vector3 position = focus.position;
			Vector3 position2 = anchor.position;
			Vector3 val3 = (RearFacing ? (position2 - position) : (position - position2));
			if (((Vector3)(ref val3)).sqrMagnitude < 1E-06f)
			{
				return false;
			}
			anchor.rotation = Quaternion.LookRotation(((Vector3)(ref val3)).normalized, Vector3.up);
			return true;
		}

		private void Unmount(Transform anchor)
		{
			if (!((Object)(object)_mountedTo == (Object)null))
			{
				_mountedTo = null;
				if ((Object)(object)anchor != (Object)null)
				{
					anchor.SetParent((Transform)null, false);
				}
			}
		}

		private bool AimAtPlayer(Transform anchor, CallTarget target)
		{
			//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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_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_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: 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_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: 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_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: 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_00d8: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			Unmount(anchor);
			PlayerControllerB player = target.Player;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Transform focus = target.Focus;
			if ((Object)(object)focus == (Object)null)
			{
				return false;
			}
			Transform root = target.Root;
			if ((Object)(object)root == (Object)null)
			{
				return false;
			}
			Vector3 position = focus.position;
			Vector3 val = root.forward;
			Camera gameplayCamera = player.gameplayCamera;
			if ((Object)(object)gameplayCamera != (Object)null)
			{
				Vector3 forward = ((Component)gameplayCamera).transform.forward;
				if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f)
				{
					val = ((Vector3)(ref forward)).normalized;
				}
			}
			Vector3 val2 = val;
			if ((Object)(object)target.PhoneModel != (Object)null)
			{
				Vector3 val3 = target.PhoneModel.position - position;
				if (((Vector3)(ref val3)).sqrMagnitude > 0.0025f)
				{
					float num = Mathf.Clamp01(_config.PhoneFollowStrength.Value);
					Vector3 val4 = Vector3.Slerp(val, ((Vector3)(ref val3)).normalized, num);
					if (((Vector3)(ref val4)).sqrMagnitude > 0.0001f)
					{
						val2 = ((Vector3)(ref val4)).normalized;
					}
				}
			}
			Vector3 val5 = Vector3.Cross(Vector3.up, val2);
			if (((Vector3)(ref val5)).sqrMagnitude < 0.0001f)
			{
				val5 = root.right;
			}
			Vector3 val6 = Quaternion.AngleAxis(_config.FrontYawAngle.Value, Vector3.up) * Quaternion.AngleAxis(_config.FrontPitchAngle.Value, ((Vector3)(ref val5)).normalized) * val2;
			val2 = ((Vector3)(ref val6)).normalized;
			float num2 = Mathf.Max(_config.FrontDistance.Value, 0.3f);
			Vector3 desired = position + val2 * num2 + Vector3.up * _config.FrontHeightOffset.Value;
			return Point(anchor, PullInsideGeometry(position, desired), position, Vector3.up);
		}

		private bool AimAtSubject(Transform anchor, CallTarget target, Lens lens)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00a8: 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_0031: 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_0043: 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_0064: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: 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_0132: 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_014f: 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_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: 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_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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			Unmount(anchor);
			Transform root = target.Root;
			if ((Object)(object)root == (Object)null)
			{
				return false;
			}
			Vector3 size = default(Vector3);
			Vector3 val2 = default(Vector3);
			if (target.Kind == CallTargetKind.Player)
			{
				Vector3 val = (((Object)(object)target.Focus != (Object)null) ? target.Focus.position : root.position);
				((Vector3)(ref size))..ctor(0.7f, 2f, 0.7f);
				((Vector3)(ref val2))..ctor(val.x, val.y - 0.75f, val.z);
			}
			else
			{
				Bounds val3 = Measure(root);
				val2 = root.TransformPoint(((Bounds)(ref val3)).center);
				size = Vector3.Scale(((Bounds)(ref val3)).size, AbsoluteScale(root));
			}
			Vector3 val4 = ((target.Kind == CallTargetKind.Player) ? val2 : (((Object)(object)target.Focus != (Object)null) ? target.Focus.position : val2));
			float fill = Mathf.Clamp01(0.6f / Mathf.Max(_config.SubjectFraming.Value, 0.1f));
			float num = lens.DistanceToFit(size, fill);
			float num2 = Mathf.Max(((Vector3)(ref size)).magnitude * 0.5f, 0.25f);
			Vector3 position = val4 + root.forward * num + root.up * (_config.FrontHeightOffset.Value * num2) + root.right * (_config.FrontLateralOffset.Value * num2);
			return Point(anchor, position, val4, Vector3.up);
		}

		private bool AimFromSwitchboardScreen(Transform anchor, CallTarget target, Lens lens)
		{
			//IL_0033: 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_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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0063: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			Unmount(anchor);
			Transform root = target.Root;
			if ((Object)(object)root == (Object)null)
			{
				return false;
			}
			Transform val = (((Object)(object)target.Focus != (Object)null) ? target.Focus : root);
			Bounds val2 = Measure(val);
			Vector3 val3 = ((((Bounds)(ref val2)).size == Vector3.zero) ? val.position : val.TransformPoint(((Bounds)(ref val2)).center));
			Vector3 val4 = root.forward;
			PlayerControllerB switchboardOperator = LethalPhonesBridge.GetSwitchboardOperator();
			if ((Object)(object)switchboardOperator != (Object)null)
			{
				Vector3 val5 = ((Component)switchboardOperator).transform.position - val3;
				val5.y = 0f;
				if (((Vector3)(ref val5)).sqrMagnitude > 0.04f)
				{
					val4 = ((Vector3)(ref val5)).normalized;
				}
			}
			if (RearFacing)
			{
				val4 = -val4;
			}
			float num = Mathf.Max(0.12f, lens.NearClip * 4f) + _config.SwitchboardScreenOffset.Value;
			Vector3 val6 = val3 + val4 * num;
			return Point(anchor, val6, val6 + val4, Vector3.up);
		}

		private Vector3 PullInsideGeometry(Vector3 from, Vector3 desired)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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_001a: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = desired - from;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.05f)
			{
				return desired;
			}
			Vector3 val2 = val / magnitude;
			StartOfRound instance = StartOfRound.Instance;
			int num = (((Object)(object)instance != (Object)null) ? instance.collidersAndRoomMaskAndDefault : (-1));
			try
			{
				RaycastHit val3 = default(RaycastHit);
				if (Physics.SphereCast(from, 0.15f, val2, ref val3, magnitude, num, (QueryTriggerInteraction)1))
				{
					float num2 = Mathf.Max(((RaycastHit)(ref val3)).distance - 0.075f, 0.3f);
					return from + val2 * Mathf.Min(num2, magnitude);
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Camera collision check failed: " + ex.Message);
			}
			return desired;
		}

		private static bool Point(Transform anchor, Vector3 position, Vector3 lookAt, Vector3 up)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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)
			Vector3 val = lookAt - position;
			if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
			{
				return false;
			}
			anchor.SetPositionAndRotation(position, Quaternion.LookRotation(val, up));
			return true;
		}

		private Bounds Measure(Transform root)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00b0: 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_00ca: Unknown re