Decompiled source of CrewMonitors v1.0.0

CrewMonitors.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using OpenBodyCams;
using OpenBodyCams.API;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CrewMonitors")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CrewMonitors")]
[assembly: AssemblyTitle("CrewMonitors")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CrewMonitors
{
	public sealed class CrewMonitorManager : MonoBehaviour
	{
		private sealed class CrewSlot
		{
			public int Index;

			public GameObject Host;

			public MeshRenderer Renderer;

			public BodyCamComponent BodyCam;

			public bool IsClone;

			public PlayerControllerB? AssignedPlayer;

			public TextMeshPro? Nameplate;
		}

		private const string MonitorWallPath = "Environment/HangarShip/ShipModels2b/MonitorWall";

		private const string SingleScreenPath = "Environment/HangarShip/ShipModels2b/MonitorWall/SingleScreen";

		private const string MainWallCubeName = "Cube.001";

		private const string ClonePrefix = "CrewMonitor_Clone_";

		private const string SlotRootName = "CrewMonitors_Root";

		private readonly List<CrewSlot> _slots = new List<CrewSlot>();

		private Transform? _monitorWall;

		private MeshRenderer? _cloneTemplate;

		private GameObject? _slotRoot;

		private bool _setupStarted;

		private bool _setupComplete;

		private bool _refreshRequested;

		private bool _setupSoonRequested;

		private float _refreshTimer;

		private float _setupWaitElapsed;

		internal static CrewMonitorManager? Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			TeardownSlots();
		}

		private void Start()
		{
			((MonoBehaviour)this).StartCoroutine(SetupWhenReady());
		}

		private void Update()
		{
			if (!Plugin.Enabled.Value)
			{
				if (_slots.Count > 0)
				{
					PowerDownAll();
				}
				return;
			}
			if (_setupSoonRequested && !_setupComplete)
			{
				_setupSoonRequested = false;
				if (!_setupStarted)
				{
					((MonoBehaviour)this).StartCoroutine(SetupWhenReady());
				}
			}
			if (_setupComplete)
			{
				float num = Mathf.Max(0.25f, Plugin.RefreshSeconds.Value);
				_refreshTimer += Time.deltaTime;
				if (_refreshRequested || _refreshTimer >= num)
				{
					_refreshRequested = false;
					_refreshTimer = 0f;
					RefreshAssignments();
				}
			}
		}

		internal void RequestRefresh()
		{
			_refreshRequested = true;
		}

		internal void RequestSetupSoon()
		{
			_setupSoonRequested = true;
		}

		private IEnumerator SetupWhenReady()
		{
			if (_setupStarted)
			{
				yield break;
			}
			_setupStarted = true;
			_setupWaitElapsed = 0f;
			while (_setupWaitElapsed < 12f)
			{
				GameObject val = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall");
				bool flag = (Object)(object)BodyCam.MainBodyCam != (Object)null || BodyCamComponent.GetAllBodyCams().Length != 0;
				if ((Object)(object)val != (Object)null && (flag || _setupWaitElapsed > 3f))
				{
					_monitorWall = val.transform;
					break;
				}
				_setupWaitElapsed += 0.25f;
				yield return (object)new WaitForSeconds(0.25f);
			}
			if ((Object)(object)_monitorWall == (Object)null)
			{
				Plugin.Log.LogWarning((object)"MonitorWall not found; crew monitors will not set up.");
				yield break;
			}
			try
			{
				BuildSlots();
				_setupComplete = true;
				RefreshAssignments();
				Plugin.Log.LogInfo((object)$"Crew monitors ready with {_slots.Count} slot(s).");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Crew monitor setup failed: {arg}");
			}
		}

		private void BuildSlots()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			TeardownSlots();
			int num = Mathf.Clamp(Plugin.MaxMonitors.Value, 1, 8);
			_slotRoot = new GameObject("CrewMonitors_Root");
			_slotRoot.transform.SetParent(_monitorWall, false);
			_slotRoot.transform.localPosition = Vector3.zero;
			_slotRoot.transform.localRotation = Quaternion.identity;
			_slotRoot.transform.localScale = Vector3.one;
			List<MeshRenderer> list = CollectReusableRenderers();
			EnsureCloneTemplate(list);
			for (int i = 0; i < num; i++)
			{
				MeshRenderer renderer = null;
				bool flag = false;
				GameObject val;
				if (i < list.Count)
				{
					renderer = list[i];
					val = ((Component)renderer).gameObject;
				}
				else
				{
					if ((Object)(object)_cloneTemplate == (Object)null)
					{
						Plugin.Log.LogWarning((object)"No template MeshRenderer available to clone; stopping slot creation.");
						break;
					}
					int slotIndex = i - list.Count;
					val = CloneMonitor(slotIndex, out renderer);
					flag = true;
					if ((Object)(object)renderer == (Object)null)
					{
						break;
					}
				}
				int num2 = PickMaterialIndex(renderer);
				BodyCamComponent val2;
				try
				{
					val2 = BodyCam.CreateBodyCam(val, (Renderer)(object)renderer, num2, (ManualCameraRenderer)null);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)$"CreateBodyCam failed for slot {i} on {((Object)val).name}: {ex.Message}");
					if (flag)
					{
						Object.Destroy((Object)(object)val);
					}
					continue;
				}
				val2.SetTargetToNone();
				val2.SetScreenPowered(false);
				CrewSlot item = new CrewSlot
				{
					Index = i,
					Host = val,
					Renderer = renderer,
					BodyCam = val2,
					IsClone = flag,
					Nameplate = (Plugin.ShowNameplates.Value ? CreateNameplate(val, renderer) : null)
				};
				_slots.Add(item);
			}
		}

		private List<MeshRenderer> CollectReusableRenderers()
		{
			List<MeshRenderer> result = new List<MeshRenderer>();
			HashSet<int> claimed = new HashSet<int>();
			if (Plugin.PreferReuseVanillaScreens.Value)
			{
				GameObject val = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/SingleScreen");
				TryAdd(((Object)(object)val != (Object)null) ? val.GetComponent<MeshRenderer>() : null, "SingleScreen");
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					TryAdd(((Object)(object)instance.insideCameraScreen != (Object)null) ? instance.insideCameraScreen.mesh : null, "insideCameraScreen.mesh");
					TryAdd(((Object)(object)instance.securityCameraScreen != (Object)null) ? instance.securityCameraScreen.mesh : null, "securityCameraScreen.mesh");
				}
				if ((Object)(object)_monitorWall != (Object)null)
				{
					MeshRenderer[] componentsInChildren = ((Component)_monitorWall).GetComponentsInChildren<MeshRenderer>(true);
					foreach (MeshRenderer val2 in componentsInChildren)
					{
						if (!((Object)((Component)val2).gameObject).name.StartsWith("CrewMonitor_Clone_", StringComparison.Ordinal) && !(((Object)((Component)val2).gameObject).name == "Cube.001") && !((Object)((Component)val2).gameObject).name.Contains("Cube.001"))
						{
							TryAdd(val2, "MonitorWall/" + ((Object)val2).name);
						}
					}
				}
			}
			return result;
			void TryAdd(MeshRenderer? mr, string label)
			{
				if (!((Object)(object)mr == (Object)null))
				{
					int instanceID = ((Object)mr).GetInstanceID();
					if (!claimed.Contains(instanceID))
					{
						if (IsRendererForbidden(mr))
						{
							Plugin.Log.LogDebug((object)("Skipping " + label + " (" + ((Object)mr).name + "): claimed or reserved."));
						}
						else
						{
							claimed.Add(instanceID);
							result.Add(mr);
							Plugin.Log.LogInfo((object)("Reusable screen: " + label + " -> " + GetPath(((Component)mr).transform)));
						}
					}
				}
			}
		}

		private static bool IsRendererForbidden(MeshRenderer mr)
		{
			if (BodyCamComponent.AnyBodyCamHasReference((Renderer)(object)mr))
			{
				return true;
			}
			if (((Object)((Component)mr).gameObject).name == "Cube.001" || ((Object)((Component)mr).gameObject).name.Contains("Cube.001"))
			{
				return true;
			}
			ManualCameraRenderer val = StartOfRound.Instance?.mapScreen;
			if ((Object)(object)val != (Object)null && (Object)(object)val.mesh == (Object)(object)mr)
			{
				return true;
			}
			return false;
		}

		private void EnsureCloneTemplate(List<MeshRenderer> reusable)
		{
			if (reusable.Count > 0)
			{
				_cloneTemplate = reusable[0];
				return;
			}
			GameObject val = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/SingleScreen");
			if ((Object)(object)val != (Object)null)
			{
				_cloneTemplate = val.GetComponent<MeshRenderer>();
				if ((Object)(object)_cloneTemplate != (Object)null)
				{
					return;
				}
			}
			if (!((Object)(object)_monitorWall != (Object)null))
			{
				return;
			}
			MeshRenderer[] componentsInChildren = ((Component)_monitorWall).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val2 in componentsInChildren)
			{
				if (!(((Object)((Component)val2).gameObject).name == "Cube.001"))
				{
					_cloneTemplate = val2;
					return;
				}
			}
			Transform val3 = _monitorWall.Find("Cube.001");
			if ((Object)(object)val3 != (Object)null)
			{
				_cloneTemplate = ((Component)val3).GetComponent<MeshRenderer>();
			}
		}

		private GameObject CloneMonitor(int slotIndex, out MeshRenderer? renderer)
		{
			//IL_00bb: 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)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			renderer = null;
			if ((Object)(object)_cloneTemplate == (Object)null || (Object)(object)_monitorWall == (Object)null)
			{
				return null;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)_cloneTemplate).gameObject, _monitorWall);
			((Object)val).name = string.Format("{0}{1}", "CrewMonitor_Clone_", slotIndex);
			BodyCamComponent[] components = val.GetComponents<BodyCamComponent>();
			for (int i = 0; i < components.Length; i++)
			{
				Object.Destroy((Object)(object)components[i]);
			}
			ManualCameraRenderer[] components2 = val.GetComponents<ManualCameraRenderer>();
			for (int i = 0; i < components2.Length; i++)
			{
				Object.Destroy((Object)(object)components2[i]);
			}
			float value = Plugin.CloneSpacing.Value;
			int num = slotIndex % 4;
			int num2 = slotIndex / 4;
			Vector3 localPosition = ((Component)_cloneTemplate).transform.localPosition;
			val.transform.localPosition = localPosition + new Vector3((float)num * value, (float)(num2 + 1) * value * 0.85f, 0f);
			val.transform.localRotation = ((Component)_cloneTemplate).transform.localRotation;
			val.transform.localScale = ((Component)_cloneTemplate).transform.localScale;
			renderer = val.GetComponent<MeshRenderer>();
			if ((Object)(object)renderer == (Object)null)
			{
				renderer = val.GetComponentInChildren<MeshRenderer>();
			}
			val.SetActive(true);
			Plugin.Log.LogInfo((object)$"Cloned monitor slot {slotIndex} at local {val.transform.localPosition}");
			return val;
		}

		private static int PickMaterialIndex(MeshRenderer renderer)
		{
			Material[] sharedMaterials = ((Renderer)renderer).sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				return 0;
			}
			if (sharedMaterials.Length > 1)
			{
				return 1;
			}
			return 0;
		}

		private TextMeshPro? CreateNameplate(GameObject host, MeshRenderer renderer)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = new GameObject("CrewMonitor_Nameplate");
				val.transform.SetParent(host.transform, false);
				Bounds localBounds = ((Renderer)renderer).localBounds;
				val.transform.localPosition = new Vector3(0f, ((Bounds)(ref localBounds)).min.y - 0.05f, ((Bounds)(ref localBounds)).center.z);
				val.transform.localRotation = Quaternion.identity;
				val.transform.localScale = Vector3.one * 0.02f;
				TextMeshPro val2 = val.AddComponent<TextMeshPro>();
				((TMP_Text)val2).text = "";
				((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
				((TMP_Text)val2).fontSize = 36f;
				((Graphic)val2).color = new Color(1f, 0.55f, 0.15f, 1f);
				((TMP_Text)val2).enableAutoSizing = false;
				((TMP_Text)val2).rectTransform.sizeDelta = new Vector2(20f, 4f);
				StartOfRound instance = StartOfRound.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					TextMeshProUGUI mapScreenPlayerName = instance.mapScreenPlayerName;
					obj = ((mapScreenPlayerName != null) ? ((TMP_Text)mapScreenPlayerName).font : null);
				}
				TMP_FontAsset val3 = (TMP_FontAsset)obj;
				if ((Object)(object)val3 != (Object)null)
				{
					((TMP_Text)val2).font = val3;
				}
				return val2;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Nameplate create failed: " + ex.Message));
				return null;
			}
		}

		private void RefreshAssignments()
		{
			if (!_setupComplete || _slots.Count == 0)
			{
				return;
			}
			List<PlayerControllerB> livingPlayers = GetLivingPlayers();
			for (int i = 0; i < _slots.Count; i++)
			{
				CrewSlot crewSlot = _slots[i];
				if ((Object)(object)crewSlot.BodyCam == (Object)null)
				{
					continue;
				}
				if (i < livingPlayers.Count)
				{
					PlayerControllerB val = livingPlayers[i];
					if ((Object)(object)crewSlot.AssignedPlayer != (Object)(object)val)
					{
						crewSlot.BodyCam.SetTargetToPlayer(val);
						crewSlot.AssignedPlayer = val;
					}
					crewSlot.BodyCam.SetScreenPowered(true);
					if (crewSlot.IsClone && (Object)(object)crewSlot.Host != (Object)null && !crewSlot.Host.activeSelf)
					{
						crewSlot.Host.SetActive(true);
					}
					if ((Object)(object)crewSlot.Nameplate != (Object)null)
					{
						((Component)crewSlot.Nameplate).gameObject.SetActive(true);
						((TMP_Text)crewSlot.Nameplate).text = (string.IsNullOrEmpty(val.playerUsername) ? $"Player {val.playerClientId}" : val.playerUsername);
					}
				}
				else
				{
					crewSlot.AssignedPlayer = null;
					crewSlot.BodyCam.SetTargetToNone();
					crewSlot.BodyCam.SetScreenPowered(false);
					if (crewSlot.IsClone && (Object)(object)crewSlot.Host != (Object)null && crewSlot.Host.activeSelf)
					{
						crewSlot.Host.SetActive(false);
					}
					if ((Object)(object)crewSlot.Nameplate != (Object)null)
					{
						((TMP_Text)crewSlot.Nameplate).text = "";
						((Component)crewSlot.Nameplate).gameObject.SetActive(false);
					}
				}
			}
		}

		private static List<PlayerControllerB> GetLivingPlayers()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null)
			{
				return new List<PlayerControllerB>();
			}
			IEnumerable<PlayerControllerB> source = instance.allPlayerScripts.Where((PlayerControllerB p) => (Object)(object)p != (Object)null && p.isPlayerControlled && !p.isPlayerDead);
			if (!Plugin.IncludeLocalPlayer.Value)
			{
				PlayerControllerB local = GameNetworkManager.Instance?.localPlayerController;
				if ((Object)(object)local != (Object)null)
				{
					source = source.Where((PlayerControllerB p) => (Object)(object)p != (Object)(object)local);
				}
			}
			return source.OrderBy((PlayerControllerB p) => p.playerClientId).ToList();
		}

		private void PowerDownAll()
		{
			foreach (CrewSlot slot in _slots)
			{
				if (!((Object)(object)slot.BodyCam == (Object)null))
				{
					slot.BodyCam.SetTargetToNone();
					slot.BodyCam.SetScreenPowered(false);
					slot.AssignedPlayer = null;
					if (slot.IsClone && (Object)(object)slot.Host != (Object)null)
					{
						slot.Host.SetActive(false);
					}
					if ((Object)(object)slot.Nameplate != (Object)null)
					{
						((Component)slot.Nameplate).gameObject.SetActive(false);
					}
				}
			}
		}

		private void TeardownSlots()
		{
			foreach (CrewSlot slot in _slots)
			{
				if ((Object)(object)slot.BodyCam != (Object)null)
				{
					try
					{
						slot.BodyCam.SetTargetToNone();
						slot.BodyCam.SetScreenPowered(false);
					}
					catch
					{
					}
					Object.Destroy((Object)(object)slot.BodyCam);
				}
				if ((Object)(object)slot.Nameplate != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)slot.Nameplate).gameObject);
				}
				if (slot.IsClone && (Object)(object)slot.Host != (Object)null)
				{
					Object.Destroy((Object)(object)slot.Host);
				}
			}
			_slots.Clear();
			if ((Object)(object)_slotRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_slotRoot);
				_slotRoot = null;
			}
		}

		private static string GetPath(Transform t)
		{
			Stack<string> stack = new Stack<string>();
			while ((Object)(object)t != (Object)null)
			{
				stack.Push(((Object)t).name);
				t = t.parent;
			}
			return string.Join("/", stack);
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class StartOfRoundPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void StartPostfix(StartOfRound __instance)
		{
			if (Plugin.Enabled.Value && !((Object)(object)((Component)__instance).GetComponent<CrewMonitorManager>() != (Object)null))
			{
				((Component)__instance).gameObject.AddComponent<CrewMonitorManager>();
				Plugin.Log.LogInfo((object)"CrewMonitorManager attached after StartOfRound.Start.");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("OnDestroy")]
		private static void OnDestroyPostfix()
		{
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class PlayerPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("KillPlayerClientRpc")]
		private static void KillPlayerClientRpcPostfix()
		{
			CrewMonitorManager.Instance?.RequestRefresh();
		}

		[HarmonyPostfix]
		[HarmonyPatch("ConnectClientToPlayerObject")]
		private static void ConnectClientToPlayerObjectPostfix()
		{
			CrewMonitorManager.Instance?.RequestSetupSoon();
		}
	}
	[BepInPlugin("com.benhough.lethal.CrewMonitors", "CrewMonitors", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.benhough.lethal.CrewMonitors";

		public const string ModName = "CrewMonitors";

		public const string ModVersion = "1.0.0";

		public const string OpenBodyCamsGuid = "Zaggy1024.OpenBodyCams";

		private readonly Harmony _harmony = new Harmony("com.benhough.lethal.CrewMonitors");

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

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

		internal static ConfigEntry<int> MaxMonitors { get; private set; }

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

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

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

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

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

		private void Awake()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for crew body-cam monitors on the ship.");
			MaxMonitors = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxMonitors", 8, new ConfigDescription("Maximum number of crew monitor slots (1–8).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			IncludeLocalPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeLocalPlayer", true, "Include the local player in crew monitor assignments.");
			CloneSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CloneSpacing", 0.42f, "Local-space spacing between cloned monitor panels along the wall.");
			RefreshSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RefreshSeconds", 1f, "How often (seconds) to re-assign living players to monitor slots.");
			PreferReuseVanillaScreens = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreferReuseVanillaScreens", true, "Reuse unused vanilla ship screens (door SingleScreen, inside/security cam meshes) before cloning.");
			ShowNameplates = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowNameplates", true, "Show a simple world-space nameplate under each crew monitor.");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)"CrewMonitors v1.0.0 loaded (depends on Zaggy1024.OpenBodyCams).");
		}
	}
	internal static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.benhough.lethal.CrewMonitors";

		public const string PLUGIN_NAME = "CrewMonitors";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}