using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
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(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Valheim.NjordsHond")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5604e7a4b7071a41b780caf038a8988c7f64a694")]
[assembly: AssemblyProduct("Valheim.NjordsHond")]
[assembly: AssemblyTitle("Valheim.NjordsHond")]
[assembly: AssemblyVersion("1.0.0.0")]
[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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
internal static class ModInfo
{
public const string ModGUID = "com.scoobymooch.njords_hond";
public const string ModName = "Njords Hond";
public const string ModVersion = "0.1.0";
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("com.scoobymooch.njords_hond", "Njords Hond", "0.1.0")]
public class VegvisirPlugin : BaseUnityPlugin
{
internal static bool _autoSetSailSpeed = true;
private readonly Harmony _harmony = new Harmony("com.scoobymooch.njords_hond");
private FieldInfo steerDirectionField;
private float _updateTimer;
private static List<Vector3> _waypointTargets = new List<Vector3>();
private static List<string> _waypointNames = new List<string>();
private static int _currentWaypointIndex = 0;
private static int _lastMessageWaypointIndex = -1;
private bool _previousIsAutoSteerOn;
public static bool AutoPilotEnabled { get; private set; } = false;
private void Awake()
{
Log.CreateInstance(((BaseUnityPlugin)this).Logger);
Log.Info("Initializing mod. Version: 0.1.0");
_harmony.PatchAll();
Type type = AccessTools.TypeByName("ShipNavigator.Patches.Ship_Patch");
if (type != null)
{
steerDirectionField = type.GetField("steerDirection", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (steerDirectionField == null)
{
Log.Warning("Could not find 'steerDirection' field.");
}
else
{
Log.Info("steerDirectionField successfully acquired.");
}
}
else
{
Log.Warning("Could not find Ship_Patch type.");
}
}
private string GetPath(Transform current)
{
string text = ((Object)current).name;
while ((Object)(object)current.parent != (Object)null)
{
current = current.parent;
text = ((Object)current.parent).name + "/" + text;
}
return text;
}
private void OnEnable()
{
}
private void OnDisable()
{
}
private void OnDestroy()
{
_harmony.UnpatchSelf();
Log.Info("Unpatching mod.");
}
private void Update()
{
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0302: Unknown result type (might be due to invalid IL or missing references)
//IL_0307: Unknown result type (might be due to invalid IL or missing references)
//IL_030c: Unknown result type (might be due to invalid IL or missing references)
//IL_031f: Unknown result type (might be due to invalid IL or missing references)
//IL_04cb: Unknown result type (might be due to invalid IL or missing references)
//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
//IL_04a5: Unknown result type (might be due to invalid IL or missing references)
//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
//IL_04b8: Unknown result type (might be due to invalid IL or missing references)
//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
//IL_0214: Unknown result type (might be due to invalid IL or missing references)
//IL_058d: Unknown result type (might be due to invalid IL or missing references)
//IL_05a5: Unknown result type (might be due to invalid IL or missing references)
//IL_05b6: Unknown result type (might be due to invalid IL or missing references)
//IL_0230: Unknown result type (might be due to invalid IL or missing references)
//IL_0232: Unknown result type (might be due to invalid IL or missing references)
//IL_0239: Unknown result type (might be due to invalid IL or missing references)
//IL_024a: Unknown result type (might be due to invalid IL or missing references)
_updateTimer += Time.deltaTime;
if (_updateTimer < 0.5f)
{
return;
}
_updateTimer = 0f;
Player localPlayer = Player.m_localPlayer;
if ((Object)(object)((localPlayer != null) ? localPlayer.GetControlledShip() : null) == (Object)null)
{
Log.Debug("Skipping update: player is not controlling a ship.");
return;
}
bool flag = false;
Type type = AccessTools.TypeByName("ShipNavigator.Patches.Ship_Patch");
FieldInfo fieldInfo = type?.GetField("isAutoSteerOn", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (fieldInfo != null)
{
flag = (bool)fieldInfo.GetValue(null);
Log.Debug("Auto-steer is " + (flag ? "ON" : "OFF"));
}
if (!flag)
{
Log.Debug("ShipNavigator steering set to manual. AutoPilot set to off");
if (_previousIsAutoSteerOn)
{
Log.Info("ShipNavigator steering set to manual. AutoPilot set to off");
}
_previousIsAutoSteerOn = false;
AutoPilotEnabled = false;
ShipHudIcon.SetColor(Color32.op_Implicit(new Color32(byte.MaxValue, (byte)187, (byte)66, byte.MaxValue)));
return;
}
if (!AutoPilotEnabled)
{
ShipHudIcon.SetColor(Color32.op_Implicit(new Color32((byte)34, (byte)133, (byte)200, byte.MaxValue)));
}
else
{
ShipHudIcon.SetColor(Color32.op_Implicit(new Color32((byte)150, (byte)171, (byte)91, byte.MaxValue)));
}
_previousIsAutoSteerOn = true;
if (!AutoPilotEnabled)
{
Log.Debug("AutoPilot is OFF");
return;
}
if (AutoPilotEnabled && (Object)(object)Player.m_localPlayer != (Object)null)
{
Ship controlledShip = Player.m_localPlayer.GetControlledShip();
if (controlledShip != null && _autoSetSailSpeed)
{
float windAngle = controlledShip.GetWindAngle();
float num = Mathf.DeltaAngle(0f, windAngle);
bool flag2 = Mathf.Abs(Mathf.DeltaAngle(180f, windAngle)) <= 45f;
Log.Debug(string.Format("Ship.GetWindAngle() = {0:F1}° (normalized: {1:F1}°) → {2}", windAngle, num, flag2 ? "Head to wind" : "Not head to wind"));
if (controlledShip.IsOwner())
{
FieldInfo fieldInfo2 = AccessTools.Field(((object)controlledShip).GetType(), "m_speed");
if (fieldInfo2 != null)
{
Speed val = (Speed)fieldInfo2.GetValue(controlledShip);
Speed val2 = (Speed)((_waypointTargets.Count != 0) ? (flag2 ? 2 : 4) : 0);
if (val != val2)
{
fieldInfo2.SetValue(controlledShip, val2);
Log.Debug($"Auto-set ship speed to {val2} due to wind angle and waypoint count.");
}
}
}
}
}
if (!AutoPilotEnabled)
{
return;
}
if (!flag)
{
Log.Debug("Not steering: Auto-steer is OFF");
return;
}
if (_waypointTargets.Count == 0)
{
Log.Debug("Not steering: No waypoint targets");
return;
}
if (steerDirectionField == null)
{
Log.Debug("Not steering: steerDirectionField is NULL");
return;
}
if ((Object)(object)Player.m_localPlayer == (Object)null)
{
Log.Debug("Not steering: local player is NULL");
return;
}
Vector3 val3 = _waypointTargets[_currentWaypointIndex];
Log.Debug($"Target position: {val3}, Player position: {((Component)Player.m_localPlayer).transform.position}");
Vector3 val4 = val3 - ((Component)Player.m_localPlayer).transform.position;
val4.y = 0f;
Log.Debug($"Vector to target (flattened): {val4}");
float magnitude = ((Vector3)(ref val4)).magnitude;
Log.Debug($"Distance to target: {magnitude}");
Log.Debug($"Current waypoint #{_currentWaypointIndex + 1}, distance: {magnitude:F1}m");
if (magnitude < 10f)
{
_currentWaypointIndex++;
if (_currentWaypointIndex >= _waypointTargets.Count)
{
Log.Info("Course complete.");
if ((Object)(object)Player.m_localPlayer != (Object)null)
{
Ship controlledShip2 = Player.m_localPlayer.GetControlledShip();
if (controlledShip2 != null && controlledShip2.IsOwner())
{
FieldInfo fieldInfo3 = AccessTools.Field(((object)controlledShip2).GetType(), "m_speed");
if (fieldInfo3 != null)
{
fieldInfo3.SetValue(controlledShip2, (object)(Speed)0);
Log.Info("Final waypoint reached. Auto-set ship speed to Stop.");
}
}
}
Player localPlayer2 = Player.m_localPlayer;
if (localPlayer2 != null)
{
((Character)localPlayer2).Message((MessageType)2, "You have reached your destination.", 0, (Sprite)null);
}
_waypointTargets.Clear();
_waypointNames.Clear();
try
{
FieldInfo fieldInfo4 = type?.GetField("isAutoSteerOn", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (fieldInfo4 != null)
{
fieldInfo4.SetValue(null, false);
Log.Info("ShipNavigator auto-steer set to OFF (final waypoint reached).");
}
else
{
Log.Warning("Could not find 'isAutoSteerOn' field to disable auto-steer.");
}
return;
}
catch (Exception ex)
{
Log.Error("Error disabling ShipNavigator auto-steer at final waypoint: " + ex);
return;
}
}
val3 = _waypointTargets[_currentWaypointIndex];
val4 = val3 - ((Component)Player.m_localPlayer).transform.position;
val4.y = 0f;
}
float num2 = Mathf.Atan2(val4.x, val4.z) * 57.29578f;
if (num2 < 0f)
{
num2 += 360f;
}
if (_currentWaypointIndex != _lastMessageWaypointIndex)
{
string arg = ((_waypointNames.Count > _currentWaypointIndex) ? _waypointNames[_currentWaypointIndex] : $"#{_currentWaypointIndex + 1}");
((Character)Player.m_localPlayer).Message((MessageType)2, $"Setting course to new waypoint: {arg}, {num2:F0}° from north.", 0, (Sprite)null);
Log.Info($"Setting course to new waypoint: {arg}, {num2:F0}° from north.");
_lastMessageWaypointIndex = _currentWaypointIndex;
}
((Vector3)(ref val4)).Normalize();
Log.Debug($"Normalized direction vector: {val4}");
steerDirectionField.SetValue(null, val4);
Log.Debug($"Updated steerDirection to {val4} ({num2:F1}° from north)");
}
public static void SetWaypointCourse(List<(string name, Vector3 pos)> targets)
{
//IL_0091: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
Log.Info("Waypoint targets received: " + string.Join(", ", targets.Select(((string name, Vector3 pos) t) => t.name)));
_waypointTargets.Clear();
_waypointNames.Clear();
_currentWaypointIndex = 0;
_lastMessageWaypointIndex = -1;
if (targets.Count > 0)
{
foreach (var target in targets)
{
_waypointNames.Add(target.name);
_waypointTargets.Add(target.pos);
}
foreach (var (arg, val) in targets)
{
Log.Debug($"Waypoint: {arg} at {val}");
}
Log.Info($"Found {targets.Count} valid pins from input.");
Log.Info($"Charting course to {_waypointTargets.Count} pins.");
}
else
{
Log.Warning("No valid pins found for course.");
}
}
public static void ClearWaypointCourse()
{
_waypointTargets.Clear();
_waypointNames.Clear();
_currentWaypointIndex = 0;
_lastMessageWaypointIndex = -1;
((Character)Player.m_localPlayer).Message((MessageType)2, "Waypoint course cleared.", 0, (Sprite)null);
Log.Info("Waypoint course cleared.");
}
}
internal class Log
{
public enum VegvisirLogLevel
{
Debug,
Info,
Warning,
Error,
Fatal
}
private static Log _instance;
private ManualLogSource _source;
public static VegvisirLogLevel LogLevel { get; set; } = VegvisirLogLevel.Info;
public static Log CreateInstance(ManualLogSource source)
{
_instance = new Log
{
_source = source
};
return _instance;
}
private Log()
{
}
public static void Info(object msg)
{
if (LogLevel > VegvisirLogLevel.Info)
{
return;
}
Log instance = _instance;
if (instance != null)
{
ManualLogSource source = instance._source;
if (source != null)
{
source.LogInfo((object)FormatMessage(msg));
}
}
}
public static void Message(object msg)
{
if (LogLevel > VegvisirLogLevel.Info)
{
return;
}
Log instance = _instance;
if (instance != null)
{
ManualLogSource source = instance._source;
if (source != null)
{
source.LogMessage((object)FormatMessage(msg));
}
}
}
public static void Debug(object msg)
{
if (LogLevel > VegvisirLogLevel.Debug)
{
return;
}
Log instance = _instance;
if (instance != null)
{
ManualLogSource source = instance._source;
if (source != null)
{
source.LogDebug((object)FormatMessage(msg));
}
}
}
public static void Warning(object msg)
{
if (LogLevel > VegvisirLogLevel.Warning)
{
return;
}
Log instance = _instance;
if (instance != null)
{
ManualLogSource source = instance._source;
if (source != null)
{
source.LogWarning((object)FormatMessage(msg));
}
}
}
public static void Error(object msg)
{
if (LogLevel > VegvisirLogLevel.Error)
{
return;
}
Log instance = _instance;
if (instance != null)
{
ManualLogSource source = instance._source;
if (source != null)
{
source.LogError((object)FormatMessage(msg));
}
}
}
public static void Fatal(object msg)
{
if (LogLevel > VegvisirLogLevel.Fatal)
{
return;
}
Log instance = _instance;
if (instance != null)
{
ManualLogSource source = instance._source;
if (source != null)
{
source.LogFatal((object)FormatMessage(msg));
}
}
}
private static string FormatMessage(object msg)
{
return string.Format("[{0}] [{1}] {2}", DateTime.UtcNow, "Njords Hond", msg);
}
}
[HarmonyPatch(typeof(Chat), "InputText")]
public static class Chat_InputText_Patch
{
[HarmonyPrefix]
public static void Prefix(Chat __instance)
{
//IL_0158: Unknown result type (might be due to invalid IL or missing references)
string text = ((TMP_InputField)((Terminal)__instance).m_input).text;
if (string.IsNullOrWhiteSpace(text) || (!text.StartsWith("/njordshond ", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("/nh ", StringComparison.OrdinalIgnoreCase)))
{
return;
}
Log.Info("Chat command received: " + text);
string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (array.Length < 2)
{
return;
}
string text2 = array[1].ToLowerInvariant();
if ((text2 == "setcourse" || text2 == "sc") && array.Length >= 3)
{
Log.Info("AutoPilot enabled. Adding waypoints...");
typeof(VegvisirPlugin).GetProperty("AutoPilotEnabled").SetValue(null, true);
List<(string, Vector3)> list = new List<(string, Vector3)>();
int num = 2;
Log.Debug("Searching pins for names: " + string.Join(", ", array.Skip(num)));
if (!((Object)(object)Minimap.instance == (Object)null))
{
for (int i = num; i < array.Length; i++)
{
string name = array[i];
PinData val = Traverse.Create((object)Minimap.instance).Field<List<PinData>>("m_pins").Value?.FirstOrDefault((Func<PinData, bool>)((PinData p) => p.m_name.Equals(name, StringComparison.OrdinalIgnoreCase)));
if (val != null)
{
list.Add((val.m_name, val.m_pos));
}
else
{
Log.Warning("No pin named '" + name + "' found.");
}
}
Log.Info($"Total matched pins: {list.Count}");
VegvisirPlugin.SetWaypointCourse(list);
try
{
FieldInfo fieldInfo = AccessTools.TypeByName("ShipNavigator.Patches.Ship_Patch")?.GetField("isAutoSteerOn", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (fieldInfo != null)
{
fieldInfo.SetValue(null, true);
Log.Info("ShipNavigator auto-steer set to ON.");
}
else
{
Log.Warning("Could not find 'isAutoSteerOn' field.");
}
return;
}
catch (Exception ex)
{
Log.Error("Error setting ShipNavigator auto-steer ON: " + ex);
return;
}
}
Log.Warning("Minimap instance is null. Cannot find pins.");
}
else
{
if (!(text2 == "clearcourse") && !(text2 == "cc"))
{
return;
}
Log.Info("Clearing course. AutoPilot disabled.");
typeof(VegvisirPlugin).GetProperty("AutoPilotEnabled").SetValue(null, false);
VegvisirPlugin.ClearWaypointCourse();
try
{
FieldInfo fieldInfo2 = AccessTools.TypeByName("ShipNavigator.Patches.Ship_Patch")?.GetField("isAutoSteerOn", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (fieldInfo2 != null)
{
fieldInfo2.SetValue(null, false);
Log.Info("ShipNavigator auto-steer set to OFF.");
}
else
{
Log.Warning("Could not find 'isAutoSteerOn' field.");
}
}
catch (Exception ex2)
{
Log.Error("Error setting ShipNavigator auto-steer OFF: " + ex2);
}
}
}
}
public static class ShipHudIcon
{
private static GameObject _shipIcon;
public static bool TryFind()
{
if ((Object)(object)_shipIcon == (Object)null)
{
_shipIcon = GameObject.Find("/_GameMain/LoadingGUI/PixelFix/IngameGui/HUD/hudroot/ShipHud/WindIndicator/Ship");
}
return (Object)(object)_shipIcon != (Object)null;
}
public static void SetColor(Color color)
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (!TryFind())
{
Log.Warning("Ship icon not found.");
return;
}
Image component = _shipIcon.GetComponent<Image>();
if ((Object)(object)component != (Object)null)
{
((Graphic)component).color = color;
}
else
{
Log.Warning("Ship icon found, but Image component missing.");
}
}
}
[HarmonyPatch]
public static class ShipNavigatorInputBlocker
{
private static MethodBase TargetMethod()
{
return AccessTools.Method("ShipNavigator.ShipNavigator:Update", (Type[])null, (Type[])null);
}
private static bool Prefix()
{
if ((Object)(object)((Terminal)(Chat.instance?)).m_input != (Object)null && ((TMP_InputField)((Terminal)Chat.instance).m_input).isFocused)
{
Log.Debug("Blocking ShipNavigator input: chat input is focused.");
return false;
}
return true;
}
}