Decompiled source of Player Status Bars v0.1.0

BepInEx/plugins/PlayerStatusBars/PlayerStatusBars.dll

Decompiled 5 hours ago
using System;
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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 OtherPlayerStatusBars
{
	internal sealed class CadaverGrowthInfectionProvider
	{
		private Type? cadaverGrowthType;

		private FieldInfo? playerInfectionsField;

		private FieldInfo? infectionMeterField;

		private Object? cachedCadaverGrowthInstance;

		private float nextFindTime;

		public bool TryGetNormalizedInfection(PlayerControllerB player, out float infectionMeter)
		{
			infectionMeter = 0f;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!TryResolveCadaverGrowthInstance(out object instance))
			{
				return false;
			}
			if (!(playerInfectionsField?.GetValue(instance) is Array array))
			{
				return false;
			}
			int num = (int)player.playerClientId;
			if (num < 0 || num >= array.Length)
			{
				return false;
			}
			object value = array.GetValue(num);
			if (value == null || infectionMeterField == null)
			{
				return false;
			}
			if (!(infectionMeterField.GetValue(value) is float num2) || 1 == 0)
			{
				return false;
			}
			infectionMeter = Mathf.Clamp01(num2);
			return true;
		}

		private bool TryResolveCadaverGrowthInstance(out object instance)
		{
			instance = null;
			if (!TryResolveCadaverGrowthMetadata())
			{
				return false;
			}
			if (cachedCadaverGrowthInstance == (Object)null && Time.unscaledTime >= nextFindTime)
			{
				nextFindTime = Time.unscaledTime + 1f;
				cachedCadaverGrowthInstance = Object.FindObjectOfType(cadaverGrowthType);
			}
			if (cachedCadaverGrowthInstance == (Object)null)
			{
				return false;
			}
			instance = cachedCadaverGrowthInstance;
			return true;
		}

		private bool TryResolveCadaverGrowthMetadata()
		{
			if (cadaverGrowthType != null && playerInfectionsField != null && infectionMeterField != null)
			{
				return true;
			}
			cadaverGrowthType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
				select assembly.GetType("CadaverGrowthAI", throwOnError: false)).FirstOrDefault((Type type) => type != null);
			if (cadaverGrowthType == null)
			{
				return false;
			}
			playerInfectionsField = cadaverGrowthType.GetField("playerInfections", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (playerInfectionsField == null)
			{
				return false;
			}
			Type type2 = cadaverGrowthType.GetNestedType("PlayerInfection", BindingFlags.Public | BindingFlags.NonPublic);
			if (type2 == null)
			{
				Type elementType = playerInfectionsField.FieldType.GetElementType();
				type2 = elementType;
			}
			if (type2 == null)
			{
				return false;
			}
			infectionMeterField = type2.GetField("infectionMeter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			return infectionMeterField != null;
		}
	}
	internal static class MyPluginInfo
	{
		public const string PluginGuid = "Codex.OtherPlayerStatusBars";

		public const string PluginName = "OtherPlayerStatusBars";

		public const string PluginVersion = "0.1.0";
	}
	internal sealed class PlayerStatusBarManager : MonoBehaviour
	{
		private readonly Dictionary<int, PlayerStatusBarView> trackedBars = new Dictionary<int, PlayerStatusBarView>();

		private readonly HashSet<int> seenPlayerIds = new HashSet<int>();

		private readonly List<int> staleIds = new List<int>();

		private float nextRefreshTime;

		private void Update()
		{
			if (!Plugin.Settings.Enabled)
			{
				ClearBars();
			}
			else if (!(Time.unscaledTime < nextRefreshTime))
			{
				nextRefreshTime = Time.unscaledTime + 0.5f;
				RefreshBars();
			}
		}

		private void RefreshBars()
		{
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			StartOfRound instance = StartOfRound.Instance;
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : instance?.localPlayerController);
			if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || instance.allPlayerScripts == null)
			{
				ClearBars();
				return;
			}
			if (ShouldSkipBarsInOrbit(instance))
			{
				ClearBars();
				return;
			}
			seenPlayerIds.Clear();
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			foreach (PlayerControllerB val2 in allPlayerScripts)
			{
				if (!ShouldTrackPlayer(val2, val, allPlayerScripts))
				{
					continue;
				}
				int num = (int)val2.playerClientId;
				seenPlayerIds.Add(num);
				if (!trackedBars.ContainsKey(num))
				{
					GameObject val3 = new GameObject($"OtherPlayerStatusBar_{num}");
					val3.transform.SetParent(((Component)this).transform, false);
					PlayerStatusBarView playerStatusBarView = val3.AddComponent<PlayerStatusBarView>();
					if (!playerStatusBarView.Initialize(val2))
					{
						Object.Destroy((Object)(object)val3);
						Plugin.Log.LogWarning((object)$"Failed to create player status bar for playerId={num}.");
					}
					else
					{
						trackedBars[num] = playerStatusBarView;
					}
				}
			}
			staleIds.Clear();
			foreach (KeyValuePair<int, PlayerStatusBarView> trackedBar in trackedBars)
			{
				if (!seenPlayerIds.Contains(trackedBar.Key) || (Object)(object)trackedBar.Value == (Object)null || !trackedBar.Value.IsStillValid(val))
				{
					staleIds.Add(trackedBar.Key);
				}
			}
			for (int j = 0; j < staleIds.Count; j++)
			{
				RemoveBar(staleIds[j]);
			}
		}

		private static bool ShouldTrackPlayer(PlayerControllerB player, PlayerControllerB localPlayer, PlayerControllerB[] allPlayers)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			if ((Object)(object)player == (Object)(object)localPlayer)
			{
				return false;
			}
			if (!player.isPlayerControlled || player.isPlayerDead)
			{
				return false;
			}
			if (player.health <= 0)
			{
				return false;
			}
			int num = (int)player.playerClientId;
			if (num < 0 || num >= allPlayers.Length || (Object)(object)allPlayers[num] != (Object)(object)player)
			{
				return false;
			}
			return ((Component)player).gameObject.activeInHierarchy;
		}

		private static bool ShouldSkipBarsInOrbit(StartOfRound startOfRound)
		{
			return Plugin.Settings.HideInOrbit && startOfRound.inShipPhase && !startOfRound.shipHasLanded;
		}

		private void RemoveBar(int playerId)
		{
			if (trackedBars.TryGetValue(playerId, out PlayerStatusBarView value))
			{
				trackedBars.Remove(playerId);
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
			}
		}

		private void ClearBars()
		{
			foreach (KeyValuePair<int, PlayerStatusBarView> trackedBar in trackedBars)
			{
				if ((Object)(object)trackedBar.Value != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)trackedBar.Value).gameObject);
				}
			}
			trackedBars.Clear();
		}

		private void OnDestroy()
		{
			ClearBars();
		}

		private void OnEnable()
		{
			Plugin.Settings.SettingsChanged += HandleSettingsChanged;
		}

		private void OnDisable()
		{
			Plugin.Settings.SettingsChanged -= HandleSettingsChanged;
		}

		private void HandleSettingsChanged()
		{
			nextRefreshTime = 0f;
		}
	}
	internal sealed class PlayerStatusBarStrip : MonoBehaviour
	{
		internal enum StripType
		{
			Health,
			Infection
		}

		private const float Width = 172f;

		private const float Height = 20f;

		private bool visible = true;

		private string label = string.Empty;

		private float currentValue;

		private float maxValue = 1f;

		private bool renderAsPercent;

		private bool labelOnly;

		private StripType stripType;

		private RectTransform rectTransform = null;

		private Image backgroundImage = null;

		private Image fillImage = null;

		private TextMeshProUGUI text = null;

		private Outline border = null;

		private RectTransform fillRect = null;

		private bool dirty = true;

		private bool lastAppliedVisible = true;

		private float lastAppliedNormalized = -1f;

		private string lastAppliedText = string.Empty;

		private bool lastAppliedTextEnabled = true;

		private Color lastAppliedBackgroundColor = Color.clear;

		private Color lastAppliedFillColor = Color.clear;

		private Color lastAppliedBorderColor = Color.clear;

		private int lastAppliedSettingsRevision = -1;

		private bool hasFillColorOverride;

		private Color fillColorOverride;

		public void Initialize(StripType type)
		{
			//IL_004e: 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_0084: 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)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_00fd: 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_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Expected O, but got Unknown
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			stripType = type;
			rectTransform = ((Component)this).gameObject.GetComponent<RectTransform>();
			if ((Object)(object)rectTransform == (Object)null)
			{
				rectTransform = ((Component)this).gameObject.AddComponent<RectTransform>();
			}
			rectTransform.sizeDelta = new Vector2(172f, 20f);
			rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
			rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
			rectTransform.pivot = new Vector2(0.5f, 0.5f);
			GameObject val = new GameObject("Background", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Outline)
			});
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			backgroundImage = val.GetComponent<Image>();
			border = val.GetComponent<Outline>();
			GameObject val2 = new GameObject("Fill", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val2.transform.SetParent(val.transform, false);
			RectTransform component2 = val2.GetComponent<RectTransform>();
			component2.anchorMin = new Vector2(0f, 0f);
			component2.anchorMax = new Vector2(0f, 1f);
			component2.pivot = new Vector2(0f, 0.5f);
			component2.offsetMin = new Vector2(1f, 1f);
			component2.offsetMax = new Vector2(-1f, -1f);
			fillImage = val2.GetComponent<Image>();
			fillRect = component2;
			GameObject val3 = new GameObject("Text", new Type[2]
			{
				typeof(RectTransform),
				typeof(TextMeshProUGUI)
			});
			val3.transform.SetParent(val.transform, false);
			RectTransform component3 = val3.GetComponent<RectTransform>();
			component3.anchorMin = Vector2.zero;
			component3.anchorMax = Vector2.one;
			component3.offsetMin = new Vector2(6f, 1f);
			component3.offsetMax = new Vector2(-6f, -1f);
			text = val3.GetComponent<TextMeshProUGUI>();
			((TMP_Text)text).alignment = (TextAlignmentOptions)514;
			((TMP_Text)text).fontSize = 12f;
			((TMP_Text)text).enableWordWrapping = false;
			((Graphic)text).color = Color.white;
		}

		public void SetDisplay(string displayLabel, float value, float max, bool showPercent)
		{
			float num = Mathf.Clamp(value, 0f, (max <= 0f) ? 1f : max);
			float num2 = Mathf.Max(max, 0.0001f);
			if (label == displayLabel && Mathf.Approximately(currentValue, num) && Mathf.Approximately(maxValue, num2) && renderAsPercent == showPercent && !labelOnly)
			{
				visible = true;
				return;
			}
			label = displayLabel;
			currentValue = num;
			maxValue = num2;
			renderAsPercent = showPercent;
			labelOnly = false;
			visible = true;
			dirty = true;
		}

		public void SetLabelOnly(string displayLabel, float fillValue, float max)
		{
			float num = Mathf.Clamp(fillValue, 0f, (max <= 0f) ? 1f : max);
			float num2 = Mathf.Max(max, 0.0001f);
			if (label == displayLabel && Mathf.Approximately(currentValue, num) && Mathf.Approximately(maxValue, num2) && labelOnly)
			{
				visible = true;
				return;
			}
			label = displayLabel;
			currentValue = num;
			maxValue = num2;
			renderAsPercent = false;
			labelOnly = true;
			visible = true;
			dirty = true;
		}

		public void SetVisible(bool isVisible)
		{
			if (visible != isVisible || ((Component)this).gameObject.activeSelf != isVisible)
			{
				visible = isVisible;
				((Component)this).gameObject.SetActive(isVisible);
				dirty = true;
			}
		}

		public void SetFillColorOverride(bool enabled, Color color)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			if (hasFillColorOverride != enabled || (enabled && !(fillColorOverride == color)))
			{
				hasFillColorOverride = enabled;
				fillColorOverride = color;
				dirty = true;
			}
		}

		private void LateUpdate()
		{
			//IL_009c: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_00b5: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)backgroundImage == (Object)null || (Object)(object)fillImage == (Object)null || (Object)(object)this.text == (Object)null || !visible)
			{
				return;
			}
			StatusBarConfig settings = Plugin.Settings;
			if (!dirty && settings.Revision == lastAppliedSettingsRevision)
			{
				return;
			}
			float num = Mathf.Clamp01(currentValue / maxValue);
			Color val = ((stripType == StripType.Health) ? settings.GetHealthFillColor() : settings.GetInfectionFillColor());
			if (hasFillColorOverride)
			{
				val = fillColorOverride;
			}
			Color backgroundColor = settings.GetBackgroundColor();
			Color borderColor = settings.GetBorderColor();
			bool flag = ((stripType == StripType.Health) ? settings.ShowHealthText : settings.ShowInfectionText);
			string text = (labelOnly ? label : (renderAsPercent ? $"{label} {Mathf.RoundToInt(num * 100f)}%" : $"{label} {Mathf.RoundToInt(currentValue)}/{Mathf.RoundToInt(maxValue)}"));
			bool flag2 = backgroundColor != lastAppliedBackgroundColor || val != lastAppliedFillColor || borderColor != lastAppliedBorderColor;
			bool flag3 = !Mathf.Approximately(num, lastAppliedNormalized);
			bool flag4 = flag != lastAppliedTextEnabled;
			bool flag5 = text != lastAppliedText;
			bool flag6 = visible != lastAppliedVisible;
			if (dirty || flag2 || flag3 || flag4 || flag5 || flag6)
			{
				if (flag2)
				{
					((Graphic)backgroundImage).color = backgroundColor;
					((Shadow)border).effectColor = borderColor;
					((Shadow)border).effectDistance = new Vector2(1f, -1f);
					((Graphic)fillImage).color = val;
					lastAppliedBackgroundColor = backgroundColor;
					lastAppliedFillColor = val;
					lastAppliedBorderColor = borderColor;
				}
				if (dirty || flag3)
				{
					fillRect.SetSizeWithCurrentAnchors((Axis)0, Mathf.Lerp(0f, 170f, num));
					lastAppliedNormalized = num;
				}
				if (dirty || flag4)
				{
					((Behaviour)this.text).enabled = flag;
					lastAppliedTextEnabled = flag;
				}
				if (flag && (dirty || flag5))
				{
					((TMP_Text)this.text).text = text;
					lastAppliedText = text;
				}
				lastAppliedVisible = visible;
				lastAppliedSettingsRevision = settings.Revision;
				dirty = false;
			}
		}
	}
	internal sealed class PlayerStatusBarView : MonoBehaviour
	{
		private static readonly Color CriticalHealthBarColor = new Color(0.88f, 0.15f, 0.12f, 0.95f);

		private const float LowHealthRecoveryDisplayDelay = 15f;

		private const float LowHealthConfirmDelay = 0.5f;

		private const float InitialStateStabilizationDelay = 3f;

		private readonly CadaverGrowthInfectionProvider infectionProvider = new CadaverGrowthInfectionProvider();

		private PlayerControllerB targetPlayer = null;

		private PlayerControllerB? localPlayer;

		private Transform? anchor;

		private PlayerStatusBarStrip healthStrip = null;

		private PlayerStatusBarStrip infectionStrip = null;

		private RectTransform healthStripRect = null;

		private RectTransform infectionStripRect = null;

		private Canvas worldCanvas = null;

		private RectTransform canvasRect = null;

		private int lastDisplayedHealth = int.MinValue;

		private int lastDisplayedInfectionPercent = int.MinValue;

		private bool wasHealthVisible;

		private bool wasInfectionVisible;

		private bool isLowHealthRecoveryTiming;

		private float lowHealthRecoveryTimer;

		private bool isLowHealthConfirming;

		private float lowHealthConfirmTimer;

		private string lastDisplayedHealthLabel = string.Empty;

		private bool lastCanvasEnabled = true;

		private float lastAppliedUiScale = -1f;

		private float lastAppliedHealthBarYOffset = float.NaN;

		private float lastAppliedInfectionBarYOffset = float.NaN;

		private float initializedAtTime;

		private bool suppressInitialStaleCriticalState;

		private bool hasCompletedInitialStateStabilization;

		private bool hasObservedStableNonCriticalState;

		public int PlayerId => ((Object)(object)targetPlayer != (Object)null) ? ((int)targetPlayer.playerClientId) : (-1);

		public bool Initialize(PlayerControllerB player)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			targetPlayer = player;
			anchor = ResolveAnchor(player);
			initializedAtTime = Time.unscaledTime;
			((Component)this).gameObject.AddComponent<StatusBarBillboard>();
			worldCanvas = ((Component)this).gameObject.AddComponent<Canvas>();
			worldCanvas.renderMode = (RenderMode)2;
			worldCanvas.sortingOrder = 50;
			((Component)this).gameObject.AddComponent<CanvasScaler>();
			canvasRect = ((Component)worldCanvas).GetComponent<RectTransform>();
			canvasRect.sizeDelta = new Vector2(220f, 84f);
			if (!TryCreateStrip("HealthStrip", out healthStrip))
			{
				return false;
			}
			healthStripRect = ((Component)healthStrip).GetComponent<RectTransform>();
			if (!TryCreateStrip("InfectionStrip", out infectionStrip))
			{
				return false;
			}
			infectionStripRect = ((Component)infectionStrip).GetComponent<RectTransform>();
			((Object)this).name = $"PlayerStatusBar_{player.playerClientId}";
			return true;
		}

		public bool IsStillValid(PlayerControllerB localPlayer)
		{
			if ((Object)(object)targetPlayer == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			if ((Object)(object)targetPlayer == (Object)(object)localPlayer || !targetPlayer.isPlayerControlled || targetPlayer.isPlayerDead)
			{
				return false;
			}
			return ((Component)targetPlayer).gameObject.activeInHierarchy;
		}

		private void LateUpdate()
		{
			//IL_009a: 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_009f: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)targetPlayer == (Object)null)
			{
				return;
			}
			localPlayer = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : StartOfRound.Instance?.localPlayerController);
			bool flag = ShouldShowGroup();
			if (anchor == null)
			{
				anchor = ResolveAnchor(targetPlayer);
			}
			float barSpacing = Plugin.Settings.BarSpacing;
			Vector3 val = (((Object)(object)anchor != (Object)null) ? anchor.position : ((Component)targetPlayer).transform.position);
			((Component)this).transform.position = val + Vector3.up * (Plugin.Settings.HeadOffset + barSpacing * Plugin.Settings.UiScale);
			ApplyCanvasScale(Plugin.Settings.UiScale);
			SetCanvasEnabled(flag);
			if (!flag || !IsTargetReadyForDisplay())
			{
				healthStrip.SetVisible(isVisible: false);
				infectionStrip.SetVisible(isVisible: false);
				wasHealthVisible = false;
				wasInfectionVisible = false;
				ResetLowHealthFallbackState();
				ResetInitialStateStabilization();
				return;
			}
			bool flag2 = ShouldShowForDistance();
			ApplyStripLayoutOffsets();
			int num = Mathf.Clamp(targetPlayer.health, 0, 100);
			bool criticallyInjured = targetPlayer.criticallyInjured;
			bool bleedingHeavily = targetPlayer.bleedingHeavily;
			bool flag3 = criticallyInjured || bleedingHeavily;
			UpdateInitialStateStabilization(flag3, num);
			bool flag4 = flag3 && !suppressInitialStaleCriticalState;
			int num2 = num;
			bool flag5 = false;
			if (!flag4 && num < 20 && !targetPlayer.isPlayerDead)
			{
				if (!isLowHealthRecoveryTiming)
				{
					if (!isLowHealthConfirming)
					{
						isLowHealthConfirming = true;
						lowHealthConfirmTimer = 0f;
					}
					else
					{
						lowHealthConfirmTimer += Time.unscaledDeltaTime;
					}
					if (lowHealthConfirmTimer >= 0.5f)
					{
						isLowHealthRecoveryTiming = true;
						lowHealthRecoveryTimer = 0f;
						isLowHealthConfirming = false;
						lowHealthConfirmTimer = 0f;
					}
				}
				else
				{
					lowHealthRecoveryTimer += Time.unscaledDeltaTime;
				}
				if (isLowHealthRecoveryTiming && lowHealthRecoveryTimer < 15f)
				{
					flag5 = true;
				}
				else if (isLowHealthRecoveryTiming)
				{
					num2 = 20;
				}
			}
			else if (isLowHealthRecoveryTiming || lowHealthRecoveryTimer > 0f || isLowHealthConfirming || lowHealthConfirmTimer > 0f)
			{
				ResetLowHealthFallbackState();
			}
			bool flag6 = flag2;
			bool flag7 = flag4 || flag5;
			string text = (flag7 ? GetCriticalHealthLabel() : "HP");
			healthStrip.SetFillColorOverride(flag7, CriticalHealthBarColor);
			if (flag6 && (!wasHealthVisible || num2 != lastDisplayedHealth || text != lastDisplayedHealthLabel))
			{
				if (flag7)
				{
					healthStrip.SetLabelOnly(text, 100f, 100f);
				}
				else
				{
					healthStrip.SetDisplay(text, num2, 100f, showPercent: false);
				}
				lastDisplayedHealth = num2;
				lastDisplayedHealthLabel = text;
			}
			healthStrip.SetVisible(flag6);
			wasHealthVisible = flag6;
			float infectionMeter = 0f;
			bool flag8 = false;
			if (flag2)
			{
				flag8 = infectionProvider.TryGetNormalizedInfection(targetPlayer, out infectionMeter);
			}
			bool num3;
			if (Plugin.Settings.InfectionDisplayMode != 0)
			{
				if (!(flag2 && flag8))
				{
					goto IL_0463;
				}
				num3 = infectionMeter > 0f;
			}
			else
			{
				num3 = flag2;
			}
			if (num3)
			{
				int num4 = Mathf.Clamp(Mathf.RoundToInt(infectionMeter * 100f), 0, 100);
				if (!wasInfectionVisible || num4 != lastDisplayedInfectionPercent)
				{
					infectionStrip.SetDisplay("INF", num4, 100f, showPercent: true);
					lastDisplayedInfectionPercent = num4;
				}
				infectionStrip.SetVisible(isVisible: true);
				wasInfectionVisible = true;
				return;
			}
			goto IL_0463;
			IL_0463:
			infectionStrip.SetVisible(isVisible: false);
			wasInfectionVisible = false;
		}

		private bool IsTargetReadyForDisplay()
		{
			return (Object)(object)targetPlayer != (Object)null && targetPlayer.isPlayerControlled && !targetPlayer.isPlayerDead && targetPlayer.health > 0;
		}

		private void ResetLowHealthFallbackState()
		{
			isLowHealthRecoveryTiming = false;
			lowHealthRecoveryTimer = 0f;
			isLowHealthConfirming = false;
			lowHealthConfirmTimer = 0f;
		}

		private static string GetCriticalHealthLabel()
		{
			return Plugin.UseChineseCriticalLabel ? "重伤" : "CRITICAL";
		}

		private void UpdateInitialStateStabilization(bool rawCriticalState, int rawHealth)
		{
			if (!rawCriticalState)
			{
				hasObservedStableNonCriticalState = true;
			}
			if (!hasCompletedInitialStateStabilization)
			{
				float num = Time.unscaledTime - initializedAtTime;
				if (num <= 3f && !hasObservedStableNonCriticalState && rawCriticalState && rawHealth >= 20)
				{
					suppressInitialStaleCriticalState = true;
				}
				else if (num > 3f)
				{
					hasCompletedInitialStateStabilization = true;
				}
			}
			if (suppressInitialStaleCriticalState && (!rawCriticalState || rawHealth < 20 || targetPlayer.isPlayerDead))
			{
				suppressInitialStaleCriticalState = false;
			}
		}

		private void ResetInitialStateStabilization()
		{
			initializedAtTime = Time.unscaledTime;
			suppressInitialStaleCriticalState = false;
			hasCompletedInitialStateStabilization = false;
			hasObservedStableNonCriticalState = false;
		}

		private void ApplyCanvasScale(float uiScale)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!Mathf.Approximately(lastAppliedUiScale, uiScale))
			{
				((Transform)canvasRect).localScale = Vector3.one * uiScale;
				lastAppliedUiScale = uiScale;
			}
		}

		private void ApplyStripLayoutOffsets()
		{
			//IL_002c: 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)
			float healthBarYOffset = Plugin.Settings.HealthBarYOffset;
			if (!Mathf.Approximately(lastAppliedHealthBarYOffset, healthBarYOffset))
			{
				healthStripRect.anchoredPosition = new Vector2(0f, healthBarYOffset);
				lastAppliedHealthBarYOffset = healthBarYOffset;
			}
			float infectionBarYOffset = Plugin.Settings.InfectionBarYOffset;
			if (!Mathf.Approximately(lastAppliedInfectionBarYOffset, infectionBarYOffset))
			{
				infectionStripRect.anchoredPosition = new Vector2(0f, infectionBarYOffset);
				lastAppliedInfectionBarYOffset = infectionBarYOffset;
			}
		}

		private void SetCanvasEnabled(bool enabled)
		{
			if (lastCanvasEnabled != enabled || ((Behaviour)worldCanvas).enabled != enabled)
			{
				((Behaviour)worldCanvas).enabled = enabled;
				lastCanvasEnabled = enabled;
			}
		}

		private bool TryCreateStrip(string stripName, out PlayerStatusBarStrip strip)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			strip = null;
			GameObject val = new GameObject(stripName, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(((Component)worldCanvas).transform, false);
			((Object)val).name = stripName;
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.identity;
			strip = val.AddComponent<PlayerStatusBarStrip>();
			strip.Initialize((!(stripName == "HealthStrip")) ? PlayerStatusBarStrip.StripType.Infection : PlayerStatusBarStrip.StripType.Health);
			return true;
		}

		private bool ShouldShowForDistance()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)localPlayer == (Object)null)
			{
				return true;
			}
			float maxDistance = Plugin.Settings.MaxDistance;
			float num = maxDistance * maxDistance;
			Vector3 val = ((Component)localPlayer).transform.position - ((Component)targetPlayer).transform.position;
			return ((Vector3)(ref val)).sqrMagnitude <= num;
		}

		private bool ShouldShowGroup()
		{
			if (!Plugin.Settings.HideInOrbit)
			{
				return true;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return true;
			}
			return !instance.inShipPhase || instance.shipHasLanded;
		}

		private static Transform? ResolveAnchor(PlayerControllerB player)
		{
			if ((Object)(object)player.playerGlobalHead != (Object)null)
			{
				return player.playerGlobalHead;
			}
			if (player.bodyParts != null && player.bodyParts.Length != 0 && (Object)(object)player.bodyParts[0] != (Object)null)
			{
				return player.bodyParts[0];
			}
			return ((Component)player).transform;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("Codex.OtherPlayerStatusBars", "OtherPlayerStatusBars", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log { get; private set; }

		internal static StatusBarConfig Settings { get; private set; }

		internal static bool UseChineseCriticalLabel { get; private set; }

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = StatusBarConfig.Create(((BaseUnityPlugin)this).Config);
			UseChineseCriticalLabel = DetectChineseLocalizationMod();
			TryRegisterLethalConfig();
			((Component)this).gameObject.AddComponent<PlayerStatusBarManager>();
			((BaseUnityPlugin)this).Config.Save();
			Log.LogInfo((object)"OtherPlayerStatusBars loaded.");
		}

		private static bool DetectChineseLocalizationMod()
		{
			foreach (PluginInfo value4 in Chainloader.PluginInfos.Values)
			{
				string value = value4.Metadata.GUID ?? string.Empty;
				string value2 = value4.Metadata.Name ?? string.Empty;
				string value3 = value4.Location ?? string.Empty;
				if (LooksLikeChineseLocalization(value) || LooksLikeChineseLocalization(value2) || LooksLikeChineseLocalization(value3))
				{
					return true;
				}
			}
			return false;
		}

		private static bool LooksLikeChineseLocalization(string value)
		{
			return Contains(value, "chinese") || Contains(value, "simplified_chinese") || Contains(value, "zh_cn") || Contains(value, "zh-cn") || Contains(value, "zh_hans") || Contains(value, "zh-hans") || Contains(value, "中文") || Contains(value, "汉化") || Contains(value, "FixGameTranslate");
		}

		private static bool Contains(string value, string term)
		{
			return value.IndexOf(term, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static void TryRegisterLethalConfig()
		{
			if (Type.GetType("LethalConfig.LethalConfigManager, LethalConfig", throwOnError: false) == null)
			{
				return;
			}
			try
			{
				StatusBarConfig.LethalConfigIntegration.Register(Settings);
			}
			catch (Exception arg)
			{
				Log.LogWarning((object)$"Failed to register LethalConfig items: {arg}");
			}
		}
	}
	internal sealed class StatusBarBillboard : MonoBehaviour
	{
		private void LateUpdate()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			Camera val = ResolveViewCamera();
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 forward = ((Component)val).transform.forward;
				Vector3 up = ((Component)val).transform.up;
				if (!(forward == Vector3.zero) && !(up == Vector3.zero))
				{
					((Component)this).transform.rotation = Quaternion.LookRotation(forward, up);
				}
			}
		}

		private static Camera? ResolveViewCamera()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return Camera.current;
			}
			PlayerControllerB localPlayerController = instance.localPlayerController;
			if ((Object)(object)localPlayerController != (Object)null && !localPlayerController.isPlayerDead)
			{
				return localPlayerController.gameplayCamera;
			}
			return ((Object)(object)instance.spectateCamera != (Object)null) ? instance.spectateCamera : Camera.current;
		}
	}
	internal sealed class StatusBarConfig
	{
		public enum InfectionBarDisplayMode
		{
			AlwaysVisible,
			ShowOnlyWhenInfected
		}

		public enum ColorPreset
		{
			Green,
			Red,
			Orange,
			Yellow,
			Blue,
			Cyan,
			White,
			Black,
			Slate
		}

		public static class LethalConfigIntegration
		{
			public static void Register(StatusBarConfig config)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Expected O, but got Unknown
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				//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_004e: Expected O, but got Unknown
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_0053: Expected O, but got Unknown
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Expected O, but got Unknown
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_007b: Expected O, but got Unknown
				//IL_007c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Expected O, but got Unknown
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Expected O, but got Unknown
				//IL_009a: 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)
				//IL_00aa: Expected O, but got Unknown
				//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: Expected O, but got Unknown
				//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c4: Expected O, but got Unknown
				//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c9: Expected O, but got Unknown
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00da: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Expected O, but got Unknown
				//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f1: Expected O, but got Unknown
				//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ff: Expected O, but got Unknown
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0104: Expected O, but got Unknown
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_0120: Expected O, but got Unknown
				//IL_0121: Unknown result type (might be due to invalid IL or missing references)
				//IL_012c: Expected O, but got Unknown
				//IL_012d: Unknown result type (might be due to invalid IL or missing references)
				//IL_013a: Expected O, but got Unknown
				//IL_0135: Unknown result type (might be due to invalid IL or missing references)
				//IL_013f: Expected O, but got Unknown
				//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_015b: Expected O, but got Unknown
				//IL_015c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0167: Expected O, but got Unknown
				//IL_0168: Unknown result type (might be due to invalid IL or missing references)
				//IL_0175: Expected O, but got Unknown
				//IL_0170: Unknown result type (might be due to invalid IL or missing references)
				//IL_017a: Expected O, but got Unknown
				//IL_0186: Unknown result type (might be due to invalid IL or missing references)
				//IL_018b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0196: Expected O, but got Unknown
				//IL_0197: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a2: Expected O, but got Unknown
				//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b0: Expected O, but got Unknown
				//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b5: Expected O, but got Unknown
				//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d3: Expected O, but got Unknown
				//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d8: Expected O, but got Unknown
				//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f6: Expected O, but got Unknown
				//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fb: Expected O, but got Unknown
				//IL_0273: Unknown result type (might be due to invalid IL or missing references)
				//IL_027d: Expected O, but got Unknown
				//IL_0278: Unknown result type (might be due to invalid IL or missing references)
				//IL_0282: Expected O, but got Unknown
				StatusBarConfig config2 = config;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(config2.enabled, new BoolCheckBoxOptions
				{
					RequiresRestart = false
				}));
				LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(config2.hideInOrbit, new BoolCheckBoxOptions
				{
					RequiresRestart = false
				}));
				ConfigEntry<float> maxDistance = config2.maxDistance;
				FloatSliderOptions val = new FloatSliderOptions();
				((BaseRangeOptions<float>)val).Min = 2f;
				((BaseRangeOptions<float>)val).Max = 80f;
				((BaseOptions)val).RequiresRestart = false;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(maxDistance, val));
				ConfigEntry<float> headOffset = config2.headOffset;
				FloatSliderOptions val2 = new FloatSliderOptions();
				((BaseRangeOptions<float>)val2).Min = 0f;
				((BaseRangeOptions<float>)val2).Max = 2f;
				((BaseOptions)val2).RequiresRestart = false;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(headOffset, val2));
				ConfigEntry<float> barSpacing = config2.barSpacing;
				FloatSliderOptions val3 = new FloatSliderOptions();
				((BaseRangeOptions<float>)val3).Min = 6f;
				((BaseRangeOptions<float>)val3).Max = 48f;
				((BaseOptions)val3).RequiresRestart = false;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(barSpacing, val3));
				ConfigEntry<float> healthBarYOffset = config2.healthBarYOffset;
				FloatSliderOptions val4 = new FloatSliderOptions();
				((BaseRangeOptions<float>)val4).Min = -64f;
				((BaseRangeOptions<float>)val4).Max = 64f;
				((BaseOptions)val4).RequiresRestart = false;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(healthBarYOffset, val4));
				ConfigEntry<float> infectionBarYOffset = config2.infectionBarYOffset;
				FloatSliderOptions val5 = new FloatSliderOptions();
				((BaseRangeOptions<float>)val5).Min = -64f;
				((BaseRangeOptions<float>)val5).Max = 64f;
				((BaseOptions)val5).RequiresRestart = false;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(infectionBarYOffset, val5));
				ConfigEntry<float> uiScale = config2.uiScale;
				FloatSliderOptions val6 = new FloatSliderOptions();
				((BaseRangeOptions<float>)val6).Min = 0.003f;
				((BaseRangeOptions<float>)val6).Max = 0.05f;
				((BaseOptions)val6).RequiresRestart = false;
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(uiScale, val6));
				LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(config2.showHealthText, new BoolCheckBoxOptions
				{
					RequiresRestart = false
				}));
				LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(config2.showInfectionText, new BoolCheckBoxOptions
				{
					RequiresRestart = false
				}));
				LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<ColorPreset>(config2.healthColor, false));
				LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<ColorPreset>(config2.infectionColor, false));
				LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<ColorPreset>(config2.backgroundColor, false));
				LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<InfectionBarDisplayMode>(config2.infectionDisplayMode, false));
				LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("General", "Refresh all player bars", "Forces all visible player bars to refresh on the next update.", "Refresh", (GenericButtonHandler)delegate
				{
					config2.NotifySettingsChanged();
				}));
			}
		}

		private readonly ConfigEntry<bool> enabled;

		private readonly ConfigEntry<bool> hideInOrbit;

		private readonly ConfigEntry<float> maxDistance;

		private readonly ConfigEntry<float> headOffset;

		private readonly ConfigEntry<float> barSpacing;

		private readonly ConfigEntry<float> healthBarYOffset;

		private readonly ConfigEntry<float> infectionBarYOffset;

		private readonly ConfigEntry<float> uiScale;

		private readonly ConfigEntry<bool> showHealthText;

		private readonly ConfigEntry<bool> showInfectionText;

		private readonly ConfigEntry<ColorPreset> healthColor;

		private readonly ConfigEntry<ColorPreset> infectionColor;

		private readonly ConfigEntry<ColorPreset> backgroundColor;

		private readonly ConfigEntry<InfectionBarDisplayMode> infectionDisplayMode;

		public bool Enabled => enabled.Value;

		public float MaxDistance => maxDistance.Value;

		public float HeadOffset => headOffset.Value;

		public float BarSpacing => barSpacing.Value;

		public float HealthBarYOffset => healthBarYOffset.Value;

		public float InfectionBarYOffset => infectionBarYOffset.Value;

		public float UiScale => uiScale.Value;

		public bool HideInOrbit => hideInOrbit.Value;

		public bool ShowHealthText => showHealthText.Value;

		public bool ShowInfectionText => showInfectionText.Value;

		public InfectionBarDisplayMode InfectionDisplayMode => infectionDisplayMode.Value;

		public int Revision { get; private set; }

		public event Action? SettingsChanged;

		public static StatusBarConfig Create(ConfigFile configFile)
		{
			return new StatusBarConfig(configFile);
		}

		private StatusBarConfig(ConfigFile configFile)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Expected O, but got Unknown
			enabled = configFile.Bind<bool>("General", "Enabled", true, "Enable other player status bars.");
			hideInOrbit = configFile.Bind<bool>("General", "Hide In Orbit", true, "Hide player status bars while the ship is still in orbit.");
			maxDistance = configFile.Bind<float>("General", "Max Display Distance", 18f, new ConfigDescription("Hide other player status bars beyond this distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 80f), Array.Empty<object>()));
			headOffset = configFile.Bind<float>("Layout", "Head Offset", 0.65f, new ConfigDescription("Vertical world-space offset above the player head anchor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			barSpacing = configFile.Bind<float>("Layout", "Bar Spacing", 14f, new ConfigDescription("Vertical spacing between the health bar and infection bar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 48f), Array.Empty<object>()));
			healthBarYOffset = configFile.Bind<float>("Layout", "Health Bar Y Offset", 0f, new ConfigDescription("Local UI Y offset for the health bar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-64f, 64f), Array.Empty<object>()));
			infectionBarYOffset = configFile.Bind<float>("Layout", "Infection Bar Y Offset", 25f, new ConfigDescription("Local UI Y offset for the infection bar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-64f, 64f), Array.Empty<object>()));
			uiScale = configFile.Bind<float>("Layout", "UI Scale", 0.0085f, new ConfigDescription("World-space scale of the player status bar canvas.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.003f, 0.05f), Array.Empty<object>()));
			showHealthText = configFile.Bind<bool>("Text", "Show Health Text", true, "Show health numbers on the health bar.");
			showInfectionText = configFile.Bind<bool>("Text", "Show Infection Text", true, "Show infection percentage on the infection bar.");
			healthColor = configFile.Bind<ColorPreset>("Colors", "Health Bar Color", ColorPreset.Green, "Fill color preset for the health bar.");
			infectionColor = configFile.Bind<ColorPreset>("Colors", "Infection Bar Color", ColorPreset.Orange, "Fill color preset for the infection bar.");
			backgroundColor = configFile.Bind<ColorPreset>("Colors", "Background Color", ColorPreset.Slate, "Background color preset for both bars.");
			infectionDisplayMode = configFile.Bind<InfectionBarDisplayMode>("General", "Infection Bar Display Mode", InfectionBarDisplayMode.ShowOnlyWhenInfected, "Always show the infection bar, or only show it when infection is above zero.");
			Subscribe<bool>(enabled);
			Subscribe<bool>(hideInOrbit);
			Subscribe<float>(maxDistance);
			Subscribe<float>(headOffset);
			Subscribe<float>(barSpacing);
			Subscribe<float>(healthBarYOffset);
			Subscribe<float>(infectionBarYOffset);
			Subscribe<float>(uiScale);
			Subscribe<bool>(showHealthText);
			Subscribe<bool>(showInfectionText);
			Subscribe<ColorPreset>(healthColor);
			Subscribe<ColorPreset>(infectionColor);
			Subscribe<ColorPreset>(backgroundColor);
			Subscribe<InfectionBarDisplayMode>(infectionDisplayMode);
		}

		public Color GetHealthFillColor()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			return ResolveColor(healthColor.Value, 0.95f);
		}

		public Color GetInfectionFillColor()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			return ResolveColor(infectionColor.Value, 0.95f);
		}

		public Color GetBackgroundColor()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			return ResolveColor(backgroundColor.Value, 0.78f);
		}

		public Color GetBorderColor()
		{
			//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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Color val = GetBackgroundColor();
			val.a = 1f;
			return Color.Lerp(val, Color.white, 0.35f);
		}

		private void Subscribe<T>(ConfigEntry<T> entry)
		{
			entry.SettingChanged += delegate
			{
				NotifySettingsChanged();
			};
		}

		private void NotifySettingsChanged()
		{
			Revision++;
			this.SettingsChanged?.Invoke();
		}

		private static Color ResolveColor(ColorPreset preset, float alpha)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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)
			//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_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_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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_0123: Unknown result type (might be due to invalid IL or missing references)
			if (1 == 0)
			{
			}
			Color val = (Color)(preset switch
			{
				ColorPreset.Green => new Color(0.18f, 0.86f, 0.32f, alpha), 
				ColorPreset.Red => new Color(0.88f, 0.25f, 0.22f, alpha), 
				ColorPreset.Orange => new Color(0.93f, 0.47f, 0.16f, alpha), 
				ColorPreset.Yellow => new Color(0.95f, 0.82f, 0.18f, alpha), 
				ColorPreset.Blue => new Color(0.28f, 0.58f, 0.92f, alpha), 
				ColorPreset.Cyan => new Color(0.18f, 0.83f, 0.88f, alpha), 
				ColorPreset.White => new Color(0.95f, 0.95f, 0.95f, alpha), 
				ColorPreset.Black => new Color(0.08f, 0.08f, 0.08f, alpha), 
				_ => new Color(0.12f, 0.15f, 0.2f, alpha), 
			});
			if (1 == 0)
			{
			}
			Color result = val;
			result.a = alpha;
			return result;
		}
	}
}