using System; using System.Diagnostics; 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 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("ShipBeacon")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.6.0")] [assembly: AssemblyInformationalVersion("1.0.6")] [assembly: AssemblyProduct("ShipBeacon")] [assembly: AssemblyTitle("ShipBeacon")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.6.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 ShipBeacon { [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class StartOfRoundStartPatch { private static void Postfix() { ShipBeaconHud.EnsureExists(); Plugin.Log.LogInfo((object)"ShipBeacon HUD ensured after StartOfRound.Start."); } } [HarmonyPatch(typeof(HUDManager), "Start")] internal static class HudManagerStartPatch { private static void Postfix() { ShipBeaconHud.EnsureExists(); } } [BepInPlugin("com.benhough.lethal.ShipBeacon", "ShipBeacon", "1.0.12")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.benhough.lethal.ShipBeacon"; public const string ModName = "ShipBeacon"; public const string ModVersion = "1.0.12"; private readonly Harmony _harmony = new Harmony("com.benhough.lethal.ShipBeacon"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ConfigEntry Enabled { get; private set; } internal static ConfigEntry ShowDistance { get; private set; } internal static ConfigEntry HudScale { get; private set; } internal static ConfigEntry VerticalOffset { get; private set; } internal static ConfigEntry MatchClockStyle { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Show the outdoor ship beacon HUD."); ShowDistance = ((BaseUnityPlugin)this).Config.Bind("General", "ShowDistance", true, "Show distance to the ship in meters."); HudScale = ((BaseUnityPlugin)this).Config.Bind("General", "HudScale", 1f, "Scale of the beacon HUD text."); VerticalOffset = ((BaseUnityPlugin)this).Config.Bind("General", "VerticalOffset", 0.1f, "Beacon text vertical position (0 = bottom, 1 = top). Default sits near the bottom of the screen."); MatchClockStyle = ((BaseUnityPlugin)this).Config.Bind("General", "MatchClockStyle", true, "Use the orange clock HUD color/font when available."); _harmony.PatchAll(typeof(Plugin).Assembly); ShipBeaconHud.EnsureExists(); Log.LogInfo((object)"ShipBeacon v1.0.12 loaded (bottom orange HUD style)."); } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.benhough.lethal.ShipBeacon"; public const string PLUGIN_NAME = "ShipBeacon"; public const string PLUGIN_VERSION = "1.0.12"; } internal sealed class ShipBeaconHud : MonoBehaviour { private static ShipBeaconHud? _instance; private Canvas? _canvas; private TextMeshProUGUI? _label; private RectTransform? _labelRt; private float _nextDebugLog; private string _lastHideReason = ""; private bool _loggedVisible; private float _nextHeartbeat; private bool _fontAssigned; private string _lastBeaconDetail = ""; private static readonly Color ClockOrange = new Color(0.855f, 0.4f, 0.185f, 1f); internal static void EnsureExists() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("ShipBeaconHUD"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); } } private void Awake() { _instance = this; BuildUi(); } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } private void BuildUi() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_00bf: 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_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvas != (Object)null)) { _canvas = ((Component)this).gameObject.AddComponent(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 5000; CanvasScaler obj = ((Component)this).gameObject.AddComponent(); obj.uiScaleMode = (ScaleMode)1; obj.referenceResolution = new Vector2(1920f, 1080f); ((Component)this).gameObject.AddComponent(); GameObject val = new GameObject("ShipBeaconLabel", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)this).transform, false); _labelRt = val.GetComponent(); _labelRt.anchorMin = new Vector2(0.5f, Plugin.VerticalOffset.Value); _labelRt.anchorMax = new Vector2(0.5f, Plugin.VerticalOffset.Value); _labelRt.pivot = new Vector2(0.5f, 0.5f); _labelRt.sizeDelta = new Vector2(980f, 56f); _labelRt.anchoredPosition = Vector2.zero; _label = val.AddComponent(); ((TMP_Text)_label).alignment = (TextAlignmentOptions)514; ((TMP_Text)_label).fontStyle = (FontStyles)1; ((Graphic)_label).color = ClockOrange; ((TMP_Text)_label).enableWordWrapping = false; ((Graphic)_label).raycastTarget = false; ((TMP_Text)_label).overflowMode = (TextOverflowModes)0; ((TMP_Text)_label).text = ""; ((TMP_Text)_label).fontSharedMaterial = null; TryAssignFont(); ApplyClockStyle(); ApplyScale(); ((Behaviour)_canvas).enabled = false; Plugin.Log.LogInfo((object)"ShipBeacon canvas HUD built."); } } private void TryAssignFont() { if ((Object)(object)_label == (Object)null || _fontAssigned) { return; } try { TMP_FontAsset val = null; HUDManager instance = HUDManager.Instance; if ((Object)(object)instance != (Object)null) { if ((Object)(object)instance.clockNumber != (Object)null && (Object)(object)((TMP_Text)instance.clockNumber).font != (Object)null) { val = ((TMP_Text)instance.clockNumber).font; } if ((Object)(object)val == (Object)null && instance.controlTipLines != null) { TextMeshProUGUI[] controlTipLines = instance.controlTipLines; foreach (TextMeshProUGUI val2 in controlTipLines) { if ((Object)(object)val2 != (Object)null && (Object)(object)((TMP_Text)val2).font != (Object)null) { val = ((TMP_Text)val2).font; break; } } } if ((Object)(object)val == (Object)null && (Object)(object)instance.weightCounter != (Object)null) { val = ((TMP_Text)instance.weightCounter).font; } } if ((Object)(object)val == (Object)null && (Object)(object)TMP_Settings.defaultFontAsset != (Object)null) { val = TMP_Settings.defaultFontAsset; } if ((Object)(object)val != (Object)null) { ((TMP_Text)_label).font = val; _fontAssigned = true; Plugin.Log.LogInfo((object)("ShipBeacon TMP font assigned: " + ((Object)val).name)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("ShipBeacon font assign failed: " + ex.Message)); } } private void ApplyScale() { if ((Object)(object)_label == (Object)null) { return; } float num = 26f; try { TextMeshProUGUI val = HUDManager.Instance?.clockNumber; if ((Object)(object)val != (Object)null && ((TMP_Text)val).fontSize > 1f) { num = ((TMP_Text)val).fontSize * 0.92f; } } catch { } ((TMP_Text)_label).fontSize = num * Plugin.HudScale.Value; } private void ApplyClockStyle() { //IL_00ae: 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) if ((Object)(object)_label == (Object)null) { return; } try { ((Graphic)_label).color = ClockOrange; ((TMP_Text)_label).fontStyle = (FontStyles)1; if (Plugin.MatchClockStyle != null && Plugin.MatchClockStyle.Value) { HUDManager instance = HUDManager.Instance; TextMeshProUGUI val = (((Object)(object)instance != (Object)null) ? instance.clockNumber : null); if (!((Object)(object)val == (Object)null) && (Object)(object)((TMP_Text)val).font != (Object)null) { ((TMP_Text)_label).font = ((TMP_Text)val).font; _fontAssigned = true; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("ApplyClockStyle: " + ex.Message)); try { ((Graphic)_label).color = ClockOrange; } catch { } } } private void LateUpdate() { //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_canvas == (Object)null || (Object)(object)_label == (Object)null || (Object)(object)_labelRt == (Object)null) { BuildUi(); } TryAssignFont(); ApplyClockStyle(); ApplyScale(); if (Time.unscaledTime >= _nextHeartbeat) { _nextHeartbeat = Time.unscaledTime + 10f; StartOfRound instance = StartOfRound.Instance; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; ManualLogSource log = Plugin.Log; object arg = Plugin.Enabled.Value; object arg2 = (Object)(object)_canvas != (Object)null; Canvas? canvas = _canvas; log.LogInfo((object)($"[Heartbeat] enabled={arg}, canvas={arg2}, canvasOn={((canvas != null) ? new bool?(((Behaviour)canvas).enabled) : ((bool?)null))}, " + $"font={_fontAssigned}, hide='{_lastHideReason}', detail='{_lastBeaconDetail}', " + $"inShipPhase={instance?.inShipPhase}, playerNull={(Object)(object)val == (Object)null}, dead={val?.isPlayerDead}, " + $"insideFactory={val?.isInsideFactory}, inShip={val?.isInHangarShipRoom}")); } if (Plugin.Enabled == null || !Plugin.Enabled.Value) { SetVisible(visible: false); _lastHideReason = "disabled"; return; } if ((Object)(object)_labelRt != (Object)null) { float num = Mathf.Clamp01(Plugin.VerticalOffset.Value); _labelRt.anchorMin = new Vector2(0.5f, num); _labelRt.anchorMax = new Vector2(0.5f, num); } ApplyScale(); if (!TryGetBeacon(out float signedAngleDeg, out float distance, out string hideReason)) { SetVisible(visible: false); MaybeLogHidden(hideReason); _loggedVisible = false; return; } try { ((TMP_Text)_label).text = FormatBeacon(signedAngleDeg, distance); SetVisible(visible: true); _lastHideReason = ""; _lastBeaconDetail = $"{distance:0}m bearing={signedAngleDeg:0}"; if (!_loggedVisible) { _loggedVisible = true; Plugin.Log.LogInfo((object)$"ShipBeacon visible: {distance:0}m, bearing {signedAngleDeg:0} deg."); } } catch (Exception ex) { SetVisible(visible: false); MaybeLogHidden("label update: " + ex.Message); _loggedVisible = false; } } private void SetVisible(bool visible) { if ((Object)(object)_canvas != (Object)null && ((Behaviour)_canvas).enabled != visible) { ((Behaviour)_canvas).enabled = visible; } if ((Object)(object)_label != (Object)null && ((Component)_label).gameObject.activeSelf != visible) { ((Component)_label).gameObject.SetActive(visible); } } private void MaybeLogHidden(string reason) { if (reason == null) { reason = ""; } bool num = reason != _lastHideReason; _lastHideReason = reason; if (num || !(Time.unscaledTime < _nextDebugLog)) { _nextDebugLog = Time.unscaledTime + 8f; if (!string.IsNullOrEmpty(reason)) { Plugin.Log.LogInfo((object)("ShipBeacon hidden: " + reason)); } } } private static bool TryGetBeacon(out float signedAngleDeg, out float distance, out string hideReason) { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) signedAngleDeg = 0f; distance = 0f; hideReason = ""; try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { hideReason = "StartOfRound missing"; return false; } if (instance.inShipPhase) { hideReason = "inShipPhase (orbit)"; return false; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || val.isPlayerDead) { hideReason = "no local player / dead"; return false; } if (val.isInsideFactory) { hideReason = "inside factory"; return false; } if (val.isInHangarShipRoom) { hideReason = "inside ship"; return false; } Transform val2 = ResolveShipTransform(instance); if ((Object)(object)val2 == (Object)null) { hideReason = "no ship transform"; return false; } Transform val3 = (((Object)(object)val.gameplayCamera != (Object)null) ? ((Component)val.gameplayCamera).transform : ((Component)val).transform); Vector3 val4 = val2.position - val3.position; val4.y = 0f; distance = ((Vector3)(ref val4)).magnitude; if (distance < 0.5f) { hideReason = $"too close ({distance:0.0}m)"; return false; } Vector3 forward = val3.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { hideReason = "bad camera forward"; return false; } signedAngleDeg = Vector3.SignedAngle(((Vector3)(ref forward)).normalized, ((Vector3)(ref val4)).normalized, Vector3.up); return true; } catch (Exception ex) { hideReason = ex.Message; return false; } } private static Transform? ResolveShipTransform(StartOfRound start) { try { GameObject val = GameObject.Find("/Environment/HangarShip") ?? GameObject.Find("Environment/HangarShip") ?? GameObject.Find("HangarShip"); if ((Object)(object)val != (Object)null) { return val.transform; } } catch { } if ((Object)(object)start.shipDoorNode != (Object)null) { return start.shipDoorNode; } if ((Object)(object)start.middleOfShipNode != (Object)null) { return start.middleOfShipNode; } if ((Object)(object)start.elevatorTransform != (Object)null) { return start.elevatorTransform; } if ((Object)(object)start.shipBounds != (Object)null) { return ((Component)start.shipBounds).transform; } if ((Object)(object)start.shipLandingPosition != (Object)null) { return start.shipLandingPosition; } try { HangarShipDoor val2 = Object.FindObjectOfType(); if ((Object)(object)val2 != (Object)null) { return ((Component)val2).transform; } } catch { } return null; } private static string FormatBeacon(float signedAngleDeg, float distance) { float num = Mathf.Abs(signedAngleDeg); string text = (Plugin.ShowDistance.Value ? $"SHIP {distance:0}m" : "SHIP"); if (num <= 25f) { return "^ " + text + " ^"; } if (num >= 155f) { return "v " + text + " v"; } int count = 1; if (num > 45f) { count = 2; } if (num > 70f) { count = 3; } if (num > 100f) { count = 4; } if (signedAngleDeg < 0f) { return new string('<', count) + " " + text; } return text + " " + new string('>', count); } } }