Decompiled source of ShipSense v1.0.0

ShipSense.dll

Decompiled a week ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Microsoft.CodeAnalysis;
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("ShipSense")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+901b84ab23f2f10926b13865f9b40bb283490874")]
[assembly: AssemblyProduct("ShipSense")]
[assembly: AssemblyTitle("ShipSense")]
[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 ShipSense
{
	public static class ShipSenseHud
	{
		private static readonly Vector3[] s_corners = (Vector3[])(object)new Vector3[4];

		private static Texture2D s_whiteTex;

		private static GUIStyle s_titleStyle;

		private static GUIStyle s_badgeStyle;

		private static GUIStyle s_healthTextStyle;

		private static GUIStyle s_speedStyle;

		private static float s_lastSpeedSampleTime;

		private static float s_previousSpeed;

		private static float s_currentSpeed;

		private static float s_speedDelta;

		private static float s_lastWarningTime;

		private static bool s_hasActiveShip;

		private static string s_shipName = string.Empty;

		private static float s_currentHp;

		private static float s_maxHp;

		private static float s_hpPct;

		private static float s_signedSpeedMs;

		private static float s_linearMagnitudeMs;

		private static bool s_isTailwindActive;

		public static void Update()
		{
			//IL_011a: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || !IsHudVisible())
			{
				s_hasActiveShip = false;
				return;
			}
			Ship val = FindActiveShip(localPlayer);
			if ((Object)(object)val == (Object)null)
			{
				s_hasActiveShip = false;
				return;
			}
			s_hasActiveShip = true;
			Piece component = ((Component)val).GetComponent<Piece>();
			if ((Object)(object)component != (Object)null && Localization.instance != null)
			{
				s_shipName = Localization.instance.Localize(component.m_name);
			}
			else
			{
				s_shipName = ((Object)((Component)val).gameObject).name.Replace("(Clone)", "").Trim();
			}
			WearNTear component2 = ((Component)val).GetComponent<WearNTear>();
			if ((Object)(object)component2 != (Object)null)
			{
				s_maxHp = component2.m_health;
				s_hpPct = Mathf.Clamp01(component2.GetHealthPercentage());
				s_currentHp = s_hpPct * s_maxHp;
			}
			else
			{
				s_maxHp = 1000f;
				s_hpPct = 1f;
				s_currentHp = 1000f;
			}
			s_signedSpeedMs = val.GetSpeed();
			Rigidbody component3 = ((Component)val).GetComponent<Rigidbody>();
			float num;
			if (!((Object)(object)component3 != (Object)null))
			{
				num = Mathf.Abs(s_signedSpeedMs);
			}
			else
			{
				Vector3 linearVelocity = component3.linearVelocity;
				num = ((Vector3)(ref linearVelocity)).magnitude;
			}
			s_linearMagnitudeMs = num;
			if (Time.unscaledTime - s_lastSpeedSampleTime >= 0.25f)
			{
				s_speedDelta = s_linearMagnitudeMs - s_previousSpeed;
				s_previousSpeed = s_linearMagnitudeMs;
				s_lastSpeedSampleTime = Time.unscaledTime;
			}
			s_currentSpeed = s_linearMagnitudeMs;
			s_isTailwindActive = val.IsWindControllActive();
			if (ShipSensePlugin.LowHealthWarning.Value && s_hpPct <= ShipSensePlugin.LowHealthThreshold.Value && Time.unscaledTime - s_lastWarningTime > 20f)
			{
				s_lastWarningTime = Time.unscaledTime;
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, $"<color=#ef4444><b>⚠ WARNING: Ship Hull Damaged! ({s_hpPct * 100f:F0}% HP)</b></color>", 0, (Sprite)null, false, false);
				}
			}
		}

		public static void Render()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0053: 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)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			if (!s_hasActiveShip || (int)Event.current.type != 7 || !IsHudVisible())
			{
				return;
			}
			GUI.depth = -100;
			EnsureTexturesAndStyles();
			float num = Mathf.Clamp(ShipSensePlugin.UiScale.Value, 0.5f, 2f);
			float num2 = 250f * num;
			float height = 80f * num;
			Rect rect = CalculatePanelRect(num2, height);
			DrawPanelBox(rect);
			float num3 = 10f * num;
			float num4 = 8f * num;
			float num5 = num2 - num3 * 2f;
			float num6 = 20f * num;
			Rect val = new Rect(((Rect)(ref rect)).x + num3, ((Rect)(ref rect)).y + num4, num5 * 0.58f, num6);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(((Rect)(ref rect)).x + num3 + num5 * 0.58f, ((Rect)(ref rect)).y + num4, num5 * 0.42f, num6);
			s_titleStyle.fontSize = Mathf.RoundToInt(13f * num);
			s_badgeStyle.fontSize = Mathf.RoundToInt(11f * num);
			GUI.Label(val, "⛵ " + s_shipName, s_titleStyle);
			if (ShipSensePlugin.ShowTailwindBadge.Value)
			{
				string text;
				Color textColor = default(Color);
				if (s_isTailwindActive)
				{
					text = "Tailwind: ON";
					((Color)(ref textColor))..ctor(0.35f, 0.78f, 0.98f, 1f);
				}
				else
				{
					string text2 = (((int)ShipSensePlugin.TailwindHotkey.Value != 0) ? $" ({ShipSensePlugin.TailwindHotkey.Value})" : "");
					text = "Tailwind: OFF" + text2;
					((Color)(ref textColor))..ctor(0.6f, 0.65f, 0.7f, 0.85f);
				}
				s_badgeStyle.normal.textColor = textColor;
				GUI.Label(val2, text, s_badgeStyle);
			}
			float num7 = ((Rect)(ref rect)).y + num4 + num6 + 3f * num;
			float num8 = 18f * num;
			Rect rect2 = default(Rect);
			((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + num3, num7, num5, num8);
			if (ShipSensePlugin.ShowHealth.Value && ShipSensePlugin.ShowHealthBar.Value)
			{
				DrawHealthBar(rect2, s_hpPct, s_currentHp, s_maxHp, num);
			}
			if (ShipSensePlugin.ShowSpeed.Value)
			{
				float num9 = num7 + num8 + 5f * num;
				float num10 = 20f * num;
				Rect val3 = new Rect(((Rect)(ref rect)).x + num3, num9, num5, num10);
				s_speedStyle.fontSize = Mathf.RoundToInt(12f * num);
				string text3 = FormatSpeedText();
				GUI.Label(val3, text3, s_speedStyle);
			}
		}

		private static string FormatSpeedText()
		{
			float num;
			string text;
			switch (ShipSensePlugin.SelectedSpeedUnit.Value)
			{
			case SpeedUnit.KilometersPerHour:
				num = s_linearMagnitudeMs * 3.6f;
				text = "km/h";
				break;
			case SpeedUnit.MetersPerSecond:
				num = s_linearMagnitudeMs;
				text = "m/s";
				break;
			case SpeedUnit.MilesPerHour:
				num = s_linearMagnitudeMs * 2.236936f;
				text = "mph";
				break;
			default:
				num = s_linearMagnitudeMs * 1.943844f;
				text = "kts";
				break;
			}
			string text2 = string.Empty;
			if (ShipSensePlugin.ShowTrendIndicator.Value)
			{
				text2 = ((s_speedDelta > 0.12f) ? " <color=#4ade80>▲</color>" : ((!(s_speedDelta < -0.12f)) ? " <color=#94a3b8>●</color>" : " <color=#f59e0b>▼</color>"));
			}
			string text3 = ((s_signedSpeedMs < -0.25f) ? " <color=#f43f5e>[Reverse]</color>" : "");
			return $"Speed: <b>{num:F1} {text}</b>{text2}{text3}";
		}

		private static void DrawHealthBar(Rect rect, float pct, float curHp, float maxHp, float scale)
		{
			//IL_0000: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			GUI.color = new Color(0.08f, 0.08f, 0.1f, 0.95f);
			GUI.DrawTexture(rect, (Texture)(object)s_whiteTex);
			float num = Mathf.Max(2f, (((Rect)(ref rect)).width - 2f) * pct);
			Rect val = new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, num, ((Rect)(ref rect)).height - 2f);
			Color color2 = default(Color);
			if (pct > 0.6f)
			{
				((Color)(ref color2))..ctor(0.18f, 0.78f, 0.35f, 0.95f);
			}
			else if (pct > 0.3f)
			{
				((Color)(ref color2))..ctor(0.95f, 0.65f, 0.12f, 0.95f);
			}
			else
			{
				float num2 = 0.7f + 0.3f * Mathf.Sin(Time.unscaledTime * 6f);
				((Color)(ref color2))..ctor(0.92f * num2, 0.18f, 0.18f, 0.95f);
			}
			GUI.color = color2;
			GUI.DrawTexture(val, (Texture)(object)s_whiteTex);
			GUI.color = new Color(0.35f, 0.35f, 0.4f, 0.7f);
			DrawBorder(rect, 1f);
			GUI.color = color;
			s_healthTextStyle.fontSize = Mathf.RoundToInt(11f * scale);
			string text = ShipSensePlugin.SelectedHealthFormat.Value switch
			{
				HealthFormat.CurrentAndMax => $"{Mathf.CeilToInt(curHp)} / {Mathf.CeilToInt(maxHp)} HP", 
				HealthFormat.PercentageOnly => $"{pct * 100f:F0}% HP", 
				_ => $"{Mathf.CeilToInt(curHp)} / {Mathf.CeilToInt(maxHp)} ({pct * 100f:F0}%)", 
			};
			GUI.Label(rect, text, s_healthTextStyle);
		}

		private static void DrawPanelBox(Rect rect)
		{
			//IL_0000: 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_0023: 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)
			Color color = GUI.color;
			GUI.color = new Color(0.06f, 0.07f, 0.09f, 0.88f);
			GUI.DrawTexture(rect, (Texture)(object)s_whiteTex);
			GUI.color = new Color(0.72f, 0.58f, 0.28f, 0.75f);
			DrawBorder(rect, 1.5f);
			GUI.color = color;
		}

		private static void DrawBorder(Rect rect, float thickness)
		{
			//IL_0016: 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_006a: 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)
			GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, thickness), (Texture)(object)s_whiteTex);
			GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height - thickness, ((Rect)(ref rect)).width, thickness), (Texture)(object)s_whiteTex);
			GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), (Texture)(object)s_whiteTex);
			GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + ((Rect)(ref rect)).width - thickness, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), (Texture)(object)s_whiteTex);
		}

		private static Rect CalculatePanelRect(float width, float height)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			float value = ShipSensePlugin.OffsetX.Value;
			float value2 = ShipSensePlugin.OffsetY.Value;
			Hud instance = Hud.instance;
			Rect rect = default(Rect);
			bool flag = (Object)(object)instance != (Object)null && (Object)(object)instance.m_shipHudRoot != (Object)null && TryGetScreenRect(instance.m_shipHudRoot.GetComponent<RectTransform>(), out rect);
			float num;
			float num2;
			switch (ShipSensePlugin.SelectedUiAnchor.Value)
			{
			case UiAnchor.LeftOfCompass:
				if (flag)
				{
					num = ((Rect)(ref rect)).x - width - 12f;
					num2 = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - height) * 0.5f;
				}
				else
				{
					num = (float)Screen.width - width - 190f;
					num2 = (float)Screen.height - height - 30f;
				}
				break;
			case UiAnchor.AboveCompass:
				if (flag)
				{
					num = ((Rect)(ref rect)).x + (((Rect)(ref rect)).width - width) * 0.5f;
					num2 = ((Rect)(ref rect)).y - height - 10f;
				}
				else
				{
					num = (float)Screen.width - width - 30f;
					num2 = (float)Screen.height - height - 180f;
				}
				break;
			case UiAnchor.BottomRight:
				num = (float)Screen.width - width - 25f;
				num2 = (float)Screen.height - height - 25f;
				break;
			case UiAnchor.BottomCenter:
				num = ((float)Screen.width - width) * 0.5f;
				num2 = (float)Screen.height - height - 120f;
				break;
			case UiAnchor.Custom:
				num = (float)Screen.width - width - 25f;
				num2 = (float)Screen.height - height - 25f;
				break;
			default:
				if (flag)
				{
					num = ((Rect)(ref rect)).x + (((Rect)(ref rect)).width - width) * 0.5f;
					num2 = ((Rect)(ref rect)).y + ((Rect)(ref rect)).height + 8f;
					if (num2 + height > (float)Screen.height - 10f)
					{
						num = ((Rect)(ref rect)).x - width - 12f;
						num2 = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - height) * 0.5f;
					}
				}
				else
				{
					num = (float)Screen.width - width - 30f;
					num2 = (float)Screen.height - height - 25f;
				}
				break;
			}
			num += value;
			num2 += value2;
			num = Mathf.Clamp(num, 10f, (float)Screen.width - width - 10f);
			num2 = Mathf.Clamp(num2, 10f, (float)Screen.height - height - 10f);
			return new Rect(num, num2, width, height);
		}

		private static bool TryGetScreenRect(RectTransform rectTransform, out Rect rect)
		{
			//IL_0001: 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_0047: 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_004f: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			rect = default(Rect);
			if ((Object)(object)rectTransform == (Object)null)
			{
				return false;
			}
			rectTransform.GetWorldCorners(s_corners);
			float num = float.MaxValue;
			float num2 = float.MaxValue;
			float num3 = float.MinValue;
			float num4 = float.MinValue;
			for (int i = 0; i < s_corners.Length; i++)
			{
				Vector2 val = RectTransformUtility.WorldToScreenPoint((Camera)null, s_corners[i]);
				num = Mathf.Min(num, val.x);
				num2 = Mathf.Min(num2, val.y);
				num3 = Mathf.Max(num3, val.x);
				num4 = Mathf.Max(num4, val.y);
			}
			rect = new Rect(num, (float)Screen.height - num4, num3 - num, num4 - num2);
			if (((Rect)(ref rect)).width > 0f)
			{
				return ((Rect)(ref rect)).height > 0f;
			}
			return false;
		}

		private static bool IsHudVisible()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			if ((Object)(object)Hud.instance == (Object)null || !Hud.instance.IsVisible())
			{
				return false;
			}
			if (Menu.IsVisible() || ((Object)(object)Minimap.instance != (Object)null && (int)Minimap.instance.m_mode == 2))
			{
				return false;
			}
			return true;
		}

		public static Ship FindActiveShip(Player player)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			Ship controlledShip = player.GetControlledShip();
			if ((Object)(object)controlledShip != (Object)null)
			{
				return controlledShip;
			}
			if (ShipSensePlugin.ShowWhenPassenger.Value)
			{
				Ship localShip = Ship.GetLocalShip();
				if ((Object)(object)localShip != (Object)null)
				{
					return localShip;
				}
				Ship standingOnShip = ((Character)player).GetStandingOnShip();
				if ((Object)(object)standingOnShip != (Object)null)
				{
					return standingOnShip;
				}
				Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, 6f);
				for (int i = 0; i < array.Length; i++)
				{
					Ship componentInParent = ((Component)array[i]).GetComponentInParent<Ship>();
					if ((Object)(object)componentInParent != (Object)null && componentInParent.IsPlayerInBoat(player))
					{
						return componentInParent;
					}
				}
			}
			return null;
		}

		private static void EnsureTexturesAndStyles()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_0091: Expected O, but got Unknown
			//IL_009b: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_whiteTex == (Object)null)
			{
				s_whiteTex = Texture2D.whiteTexture;
			}
			if (s_titleStyle == null)
			{
				s_titleStyle = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)3
				};
				s_titleStyle.normal.textColor = new Color(0.95f, 0.95f, 0.98f, 1f);
			}
			if (s_badgeStyle == null)
			{
				s_badgeStyle = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)5
				};
				s_badgeStyle.normal.textColor = Color.white;
			}
			if (s_healthTextStyle == null)
			{
				s_healthTextStyle = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4
				};
				s_healthTextStyle.normal.textColor = Color.white;
			}
			if (s_speedStyle == null)
			{
				s_speedStyle = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)0,
					alignment = (TextAnchor)3,
					richText = true
				};
				s_speedStyle.normal.textColor = new Color(0.9f, 0.92f, 0.95f, 1f);
			}
		}
	}
	public static class ShipSensePatches
	{
		[HarmonyPatch(typeof(Ship), "IsWindControllActive")]
		public static class Ship_IsWindControllActive_Patch
		{
			public static void Postfix(Ship __instance, ref bool __result)
			{
				if (__result || !ShipSensePlugin.Enabled.Value || !ShipSensePlugin.TailwindActive)
				{
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return;
				}
				if (ShipSensePlugin.RequireRudder.Value)
				{
					if ((Object)(object)localPlayer.GetControlledShip() == (Object)(object)__instance)
					{
						__result = true;
					}
				}
				else if ((Object)(object)localPlayer.GetControlledShip() == (Object)(object)__instance || (Object)(object)Ship.GetLocalShip() == (Object)(object)__instance)
				{
					__result = true;
				}
			}
		}

		[HarmonyPatch(typeof(EnvMan), "SetTargetWind")]
		public static class EnvMan_SetTargetWind_Patch
		{
			public static void Prefix(ref Vector3 dir, ref float intensity)
			{
				if (ShipSensePlugin.Enabled.Value && ShipSensePlugin.TailwindActive && ShipSensePlugin.BoostWindForce.Value)
				{
					Ship localShip = Ship.GetLocalShip();
					if ((Object)(object)localShip != (Object)null && localShip.IsWindControllActive())
					{
						intensity = Mathf.Max(intensity, ShipSensePlugin.MinWindIntensity.Value);
					}
				}
			}
		}

		[HarmonyPatch(typeof(ShipControlls), "GetHoverText")]
		public static class ShipControlls_GetHoverText_Patch
		{
			public static void Postfix(ShipControlls __instance, ref string __result)
			{
				if (!ShipSensePlugin.Enabled.Value || !ShipSensePlugin.ShowHoverHealth.Value)
				{
					return;
				}
				Ship ship = __instance.m_ship;
				if ((Object)(object)ship == (Object)null)
				{
					return;
				}
				WearNTear component = ((Component)ship).GetComponent<WearNTear>();
				if (!((Object)(object)component == (Object)null))
				{
					float health = component.m_health;
					float num = Mathf.Max(0f, component.GetHealthPercentage() * health);
					float num2 = component.GetHealthPercentage() * 100f;
					string text = ((num2 > 60f) ? "#4ade80" : ((num2 > 30f) ? "#facc15" : "#f87171"));
					string text2 = $"<color={text}>Hull HP: {Mathf.CeilToInt(num)} / {Mathf.CeilToInt(health)} ({num2:F0}%)</color>";
					if (!string.IsNullOrEmpty(__result))
					{
						__result = __result + "\n" + text2;
					}
					else
					{
						__result = text2;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Hud), "UpdateShipHud")]
		public static class Hud_UpdateShipHud_Patch
		{
			public static void Postfix(Hud __instance, Player player, float dt)
			{
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
				//IL_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_010f: Unknown result type (might be due to invalid IL or missing references)
				if (!ShipSensePlugin.Enabled.Value || !ShipSensePlugin.ShowWhenPassenger.Value || (Object)(object)player == (Object)null || (Object)(object)player.GetControlledShip() != (Object)null)
				{
					return;
				}
				Ship localShip = Ship.GetLocalShip();
				if (!((Object)(object)localShip == (Object)null) && __instance.IsVisible() && ShipSensePlugin.ShowCompassForPassengers.Value && (Object)(object)__instance.m_shipHudRoot != (Object)null)
				{
					__instance.m_shipHudRoot.SetActive(true);
					float shipYawAngle = localShip.GetShipYawAngle();
					if ((Object)(object)__instance.m_shipWindIndicatorRoot != (Object)null)
					{
						((Transform)__instance.m_shipWindIndicatorRoot).localRotation = Quaternion.Euler(0f, 0f, shipYawAngle);
					}
					float windAngle = localShip.GetWindAngle();
					if ((Object)(object)__instance.m_shipWindIconRoot != (Object)null)
					{
						((Transform)__instance.m_shipWindIconRoot).localRotation = Quaternion.Euler(0f, 0f, windAngle);
					}
					float windAngleFactor = localShip.GetWindAngleFactor();
					if ((Object)(object)__instance.m_shipWindIcon != (Object)null)
					{
						((Graphic)__instance.m_shipWindIcon).color = Color.Lerp(new Color(0.2f, 0.2f, 0.2f, 1f), Color.white, windAngleFactor);
					}
				}
			}
		}
	}
	public enum SpeedUnit
	{
		Knots,
		KilometersPerHour,
		MetersPerSecond,
		MilesPerHour
	}
	public enum HealthFormat
	{
		Both,
		CurrentAndMax,
		PercentageOnly
	}
	public enum UiAnchor
	{
		BelowCompass,
		LeftOfCompass,
		AboveCompass,
		BottomRight,
		BottomCenter,
		Custom
	}
	[BepInPlugin("com.dfg.shipsense", "ShipSense", "1.0.0")]
	public class ShipSensePlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.dfg.shipsense";

		public const string ModName = "ShipSense";

		public const string ModVersion = "1.0.0";

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> ShowWhenPassenger;

		public static ConfigEntry<bool> ShowCompassForPassengers;

		public static ConfigEntry<bool> ShowHoverHealth;

		public static ConfigEntry<bool> ShowSpeed;

		public static ConfigEntry<SpeedUnit> SelectedSpeedUnit;

		public static ConfigEntry<bool> ShowTrendIndicator;

		public static ConfigEntry<bool> ShowHealth;

		public static ConfigEntry<HealthFormat> SelectedHealthFormat;

		public static ConfigEntry<bool> ShowHealthBar;

		public static ConfigEntry<bool> LowHealthWarning;

		public static ConfigEntry<float> LowHealthThreshold;

		public static ConfigEntry<bool> AlwaysTailwind;

		public static ConfigEntry<KeyCode> TailwindHotkey;

		public static ConfigEntry<bool> RequireRudder;

		public static ConfigEntry<bool> BoostWindForce;

		public static ConfigEntry<float> MinWindIntensity;

		public static ConfigEntry<bool> ShowTailwindBadge;

		public static ConfigEntry<UiAnchor> SelectedUiAnchor;

		public static ConfigEntry<float> OffsetX;

		public static ConfigEntry<float> OffsetY;

		public static ConfigEntry<float> UiScale;

		private Harmony m_harmony;

		public static bool TailwindActive { get; set; } = true;

		private void Awake()
		{
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Expected O, but got Unknown
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Expected O, but got Unknown
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Expected O, but got Unknown
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Enable or disable ShipSense.");
			ShowWhenPassenger = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowWhenPassenger", true, "Show ship stats when on board even if not actively piloting at the rudder.");
			ShowCompassForPassengers = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowCompassForPassengers", true, "Keep the vanilla sailing wind compass visible for passengers aboard the ship.");
			ShowHoverHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowHoverHealth", true, "Show current ship HP on the hover tooltip when aiming at the rudder or hull.");
			ShowSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Speedometer", "ShowSpeed", true, "Show real-time sailing speed on the HUD.");
			SelectedSpeedUnit = ((BaseUnityPlugin)this).Config.Bind<SpeedUnit>("2 - Speedometer", "SpeedUnit", SpeedUnit.Knots, "Unit of speed to display: Knots (kts), KilometersPerHour (km/h), MetersPerSecond (m/s), or MilesPerHour (mph).");
			ShowTrendIndicator = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Speedometer", "ShowTrendIndicator", true, "Show acceleration tendency arrow (▲ accelerating, ● steady, ▼ decelerating).");
			ShowHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Health", "ShowHealth", true, "Show ship health on the HUD.");
			SelectedHealthFormat = ((BaseUnityPlugin)this).Config.Bind<HealthFormat>("3 - Health", "HealthFormat", HealthFormat.Both, "Health text format: Both (e.g. 850 / 1000 [85%]), CurrentAndMax, or PercentageOnly.");
			ShowHealthBar = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Health", "ShowHealthBar", true, "Show a visual color-coded health bar under the ship compass.");
			LowHealthWarning = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Health", "LowHealthWarning", true, "Warn the player and pulse the health bar when hull drops into critical condition.");
			LowHealthThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Health", "LowHealthThreshold", 0.3f, "Threshold below which ship hull is considered critical (0.30 = 30%).");
			AlwaysTailwind = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Tailwind", "AlwaysTailwind", true, "Enable Moder tailwind power (wind always blows forward in ship's heading).");
			TailwindHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("4 - Tailwind", "TailwindHotkey", (KeyCode)289, "Hotkey to toggle tailwind on/off at will.");
			RequireRudder = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Tailwind", "RequireRudder", false, "If true, tailwind only engages when holding the rudder; if false, engages whenever players are aboard.");
			BoostWindForce = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Tailwind", "BoostWindForce", false, "If true, boosts wind to full gale force when tailwind is active. If false, preserves natural weather wind strength.");
			MinWindIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Tailwind", "MinWindIntensity", 1f, new ConfigDescription("Minimum wind force intensity when boost is enabled (0.5 to 1.0).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			ShowTailwindBadge = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Tailwind", "ShowTailwindBadge", true, "Display tailwind status badge on the ship HUD.");
			SelectedUiAnchor = ((BaseUnityPlugin)this).Config.Bind<UiAnchor>("5 - UI", "Anchor", UiAnchor.BelowCompass, "Screen anchor position for the ShipSense HUD.");
			OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("5 - UI", "OffsetX", 0f, "Custom horizontal pixel offset.");
			OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("5 - UI", "OffsetY", 0f, "Custom vertical pixel offset.");
			UiScale = ((BaseUnityPlugin)this).Config.Bind<float>("5 - UI", "UiScale", 1f, new ConfigDescription("Scale of the ShipSense HUD panel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			TailwindActive = AlwaysTailwind.Value;
			try
			{
				m_harmony = new Harmony("com.dfg.shipsense");
				m_harmony.PatchAll();
				Logger.LogInfo((object)"ShipSense v1.0.0 patches applied successfully.");
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("ShipSense: Error applying Harmony patches: " + ex));
			}
		}

		private void Update()
		{
			//IL_0012: 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)
			if (!Enabled.Value)
			{
				return;
			}
			if ((int)TailwindHotkey.Value != 0 && Input.GetKeyDown(TailwindHotkey.Value))
			{
				TailwindActive = !TailwindActive;
				string text = (TailwindActive ? "ShipSense: Tailwind Enabled" : "ShipSense: Tailwind Disabled");
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, false);
				}
			}
			ShipSenseHud.Update();
		}

		private void OnGUI()
		{
			if (Enabled.Value)
			{
				ShipSenseHud.Render();
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = m_harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}