using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Managers; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("CaptainsLog")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+185e1c70765cc70e92e30acbece1449c14dba044")] [assembly: AssemblyProduct("CaptainsLog")] [assembly: AssemblyTitle("CaptainsLog")] [assembly: AssemblyVersion("1.0.0.0")] namespace CaptainsLog; [BepInPlugin("DeathMonger.CaptainsLog", "Captain's Log", "1.0.0")] [BepInProcess("valheim.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class CaptainsLogMod : BaseUnityPlugin { public const string ModGuid = "DeathMonger.CaptainsLog"; public static CaptainsLogMod Instance; internal static ManualLogSource Log; public ConfigEntry LoggingEnabled; public ConfigEntry SampleIntervalSeconds; public ConfigEntry ShowHud; public ConfigEntry HudOffsetX; public ConfigEntry HudOffsetY; private readonly Harmony harmony = new Harmony("DeathMonger.CaptainsLog"); private void Awake() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; LoggingEnabled = ((BaseUnityPlugin)this).Config.Bind("Logging", "Enabled", true, new ConfigDescription("Log ship telemetry (speed, wind, heading) to a CSV file while sailing.", (AcceptableValueBase)null, Array.Empty())); SampleIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind("Logging", "Sample Interval (s)", 0.5f, new ConfigDescription("Minimum time between logged samples.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 5f), Array.Empty())); ShowHud = ((BaseUnityPlugin)this).Config.Bind("HUD", "Enabled", true, new ConfigDescription("Show a small HUD widget with live ship speed and wind data while sailing.", (AcceptableValueBase)null, Array.Empty())); HudOffsetX = ((BaseUnityPlugin)this).Config.Bind("HUD", "Offset X (px)", 10f, new ConfigDescription("Horizontal offset from the upper-left corner of the screen.", (AcceptableValueBase)null, Array.Empty())); HudOffsetY = ((BaseUnityPlugin)this).Config.Bind("HUD", "Offset Y (px)", 40f, new ConfigDescription("Vertical offset from the top of the screen.", (AcceptableValueBase)null, Array.Empty())); harmony.PatchAll(); Log.LogInfo((object)"Captain's Log armed."); } private void Update() { ShipHud.Tick(); } private void OnDestroy() { ShipTelemetry.Shutdown(); } } internal static class ShipHud { private const float UpdateInterval = 0.2f; private const float StaleAfter = 0.75f; private const float Width = 280f; private const float Height = 70f; private static GameObject _go; private static Text _text; private static RectTransform _rect; private static float _lastUpdate; private static bool Enabled => CaptainsLogMod.Instance?.ShowHud?.Value ?? true; private static float OffsetX => CaptainsLogMod.Instance?.HudOffsetX?.Value ?? 10f; private static float OffsetY => CaptainsLogMod.Instance?.HudOffsetY?.Value ?? 40f; public static void Tick() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) EnsureCreated(); if ((Object)(object)_text == (Object)null) { return; } bool flag = Time.time - ShipTelemetry.LastSample.SampledAtTime < 0.75f; bool flag2 = Enabled && flag; if (_go.activeSelf != flag2) { _go.SetActive(flag2); } if (flag2) { _rect.anchoredPosition = new Vector2(OffsetX, 0f - OffsetY); float time = Time.time; if (!(time - _lastUpdate < 0.2f)) { _lastUpdate = time; ShipSample lastSample = ShipTelemetry.LastSample; _text.text = $"Speed: {lastSample.SpeedMs:F1} m/s ({lastSample.SpeedKnots:F1} kn) {lastSample.Propulsion}\n" + $"Wind: {lastSample.WindIntensity * 100f:F1}% @ {lastSample.WindAngleDeg:F0}°\n" + $"Sail: {lastSample.WindAngleFactor * 100f:F0}% Rudder: {lastSample.Rudder:F1}"; } } } private static void EnsureCreated() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_007c: 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_00dd: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_go != (Object)null) && GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null)) { _go = GUIManager.Instance.CreateText(string.Empty, GUIManager.CustomGUIFront.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(OffsetX, 0f - OffsetY), GUIManager.Instance.AveriaSerifBold, 16, GUIManager.Instance.ValheimOrange, true, Color.black, 280f, 70f, false); ((Object)_go).name = "CaptainsLog_ShipHud"; _rect = _go.GetComponent(); _rect.pivot = new Vector2(0f, 1f); _rect.anchoredPosition = new Vector2(OffsetX, 0f - OffsetY); _text = _go.GetComponent(); _text.alignment = (TextAnchor)0; ((Graphic)_text).raycastTarget = false; } } } internal struct ShipSample { public float SpeedMs; public float SpeedKnots; public Speed SpeedSetting; public string Propulsion; public float Rudder; public float WindAngleDeg; public float WindAngleFactor; public float WindIntensity; public float SampledAtTime; } internal static class ShipTelemetry { private const float MetersPerSecondToKnots = 1.9438444f; private static StreamWriter _writer; private static float _lastSampleTime = -999f; internal static ShipSample LastSample; internal unsafe static void Sample(Ship ship) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; float num = Mathf.Max(0.05f, CaptainsLogMod.Instance.SampleIntervalSeconds.Value); if (!(time - _lastSampleTime < num)) { _lastSampleTime = time; Vector3 position = ((Component)ship).transform.position; float num2 = NormalizeAngle(((Component)ship).transform.eulerAngles.y); Vector3 val = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindDir() : Vector3.zero); float windIntensity = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindIntensity() : 0f); float num3 = NormalizeAngle(Mathf.Atan2(val.x, val.z) * 57.29578f); Biome val2 = Heightmap.FindBiome(position); string text = ((Object)((Component)ship).gameObject).name.Replace("(Clone)", string.Empty).Trim(); float speed = ship.GetSpeed(); Speed speedSetting = ship.GetSpeedSetting(); ShipSample shipSample = (LastSample = new ShipSample { SpeedMs = speed, SpeedKnots = speed * 1.9438444f, SpeedSetting = speedSetting, Propulsion = PropulsionLabel(speedSetting), Rudder = ship.GetRudder(), WindAngleDeg = NormalizeSignedAngle(ship.GetWindAngle()), WindAngleFactor = ship.GetWindAngleFactor(), WindIntensity = windIntensity, SampledAtTime = time }); if (CaptainsLogMod.Instance.LoggingEnabled.Value) { EnsureWriter(); _writer.WriteLine(string.Join(",", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture), text, position.x.ToString("F1", CultureInfo.InvariantCulture), position.z.ToString("F1", CultureInfo.InvariantCulture), ((object)(*(Biome*)(&val2))/*cast due to .constrained prefix*/).ToString(), shipSample.SpeedMs.ToString("F2", CultureInfo.InvariantCulture), shipSample.SpeedKnots.ToString("F2", CultureInfo.InvariantCulture), ((object)(*(Speed*)(&shipSample.SpeedSetting))/*cast due to .constrained prefix*/).ToString(), shipSample.Propulsion, shipSample.Rudder.ToString("F2", CultureInfo.InvariantCulture), num2.ToString("F1", CultureInfo.InvariantCulture), shipSample.WindAngleDeg.ToString("F1", CultureInfo.InvariantCulture), shipSample.WindAngleFactor.ToString("F2", CultureInfo.InvariantCulture), shipSample.WindIntensity.ToString("F2", CultureInfo.InvariantCulture), num3.ToString("F1", CultureInfo.InvariantCulture))); } } } private static float NormalizeAngle(float deg) { deg %= 360f; if (!(deg < 0f)) { return deg; } return deg + 360f; } private static float NormalizeSignedAngle(float deg) { deg = NormalizeAngle(deg); if (!(deg > 180f)) { return deg; } return deg - 360f; } private static string PropulsionLabel(Speed speed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected I4, but got Unknown return (speed - 1) switch { 0 => "Oars-Reverse", 1 => "Oars-Forward", 2 => "Sail-Half", 3 => "Sail-Full", _ => "Idle", }; } private static void EnsureWriter() { if (_writer == null) { string text = Path.Combine(Paths.BepInExRootPath, "CaptainsLog"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, $"shiplog-{DateTime.Now:yyyyMMdd-HHmmss}.csv"); _writer = new StreamWriter(text2, append: false) { AutoFlush = true }; _writer.WriteLine("Timestamp,ShipName,PosX,PosZ,Biome,SpeedMs,SpeedKnots,SpeedSetting,Propulsion,Rudder,HeadingDeg,WindAngleDeg,WindAngleFactor,WindIntensity,WindDirWorldDeg"); CaptainsLogMod.Log.LogInfo((object)("Logging ship telemetry to " + text2)); } } internal static void Shutdown() { _writer?.Flush(); _writer?.Dispose(); _writer = null; } } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] internal static class Ship_CustomFixedUpdate_Telemetry { [HarmonyPostfix] private static void Postfix(Ship __instance) { if (!((Object)(object)Player.m_localPlayer == (Object)null) && __instance.IsPlayerInBoat(Player.m_localPlayer)) { ShipTelemetry.Sample(__instance); } } }