Decompiled source of ValheimDryDock v1.1.0

ValheimDryDock.dll

Decompiled 3 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimDryDock")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimDryDock")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0a05fd87-cc17-4dfd-95de-c925ea56f3c4")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace ValheimDryDock;

[BepInPlugin("com.github.seniormodder.valheimdrydock", "ValheimDryDock", "1.1.0")]
[BepInProcess("valheim.exe")]
public class Plugin : BaseUnityPlugin
{
	public const string ModGUID = "com.github.seniormodder.valheimdrydock";

	public const string ModName = "ValheimDryDock";

	public const string ModVersion = "1.1.0";

	public static ManualLogSource ModLogger;

	private Harmony _harmony;

	public static Plugin Instance { get; private set; }

	private void Awake()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		Instance = this;
		ModLogger = ((BaseUnityPlugin)this).Logger;
		Configuration.BindConfig(((BaseUnityPlugin)this).Config);
		_harmony = new Harmony("com.github.seniormodder.valheimdrydock");
		_harmony.PatchAll();
		ModLogger.LogInfo((object)"ValheimDryDock Structural System Module v1.1.0 Initialized.");
	}

	private void Update()
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		Chat instance = Chat.instance;
		if ((instance == null || !instance.HasFocus()) && !Menu.IsVisible() && !Console.IsVisible() && !(DateTime.Now < Configuration.LastActionTime.AddMilliseconds(600.0)))
		{
			if (Input.GetKeyDown(Configuration.DockHotkey.Value))
			{
				Configuration.LastActionTime = DateTime.Now;
				DryDockController.ExecuteDock();
			}
			if (Input.GetKeyDown(Configuration.UndockHotkey.Value))
			{
				Configuration.LastActionTime = DateTime.Now;
				DryDockController.ExecuteUndock();
			}
		}
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
public static class Configuration
{
	public static DateTime LastActionTime = DateTime.MinValue;

	public static ConfigEntry<KeyCode> DockHotkey { get; private set; }

	public static ConfigEntry<KeyCode> UndockHotkey { get; private set; }

	public static ConfigEntry<float> SafetyClearanceBuffer { get; private set; }

	public static ConfigEntry<float> RaycastDistance { get; private set; }

	public static ConfigEntry<string> VerticalPostPrefab { get; private set; }

	public static ConfigEntry<string> AngledBracePrefab { get; private set; }

	public static void BindConfig(ConfigFile config)
	{
		DockHotkey = config.Bind<KeyCode>("Controls", "DockHotkey", (KeyCode)121, "Press to lift and dock the targeted ship.");
		UndockHotkey = config.Bind<KeyCode>("Controls", "UndockHotkey", (KeyCode)104, "Press to restore and release the targeted ship into water.");
		SafetyClearanceBuffer = config.Bind<float>("Mechanics", "SafetyClearanceBuffer", 3f, "Vertical buffer clearance in meters added directly above the highest local wave crest.");
		RaycastDistance = config.Bind<float>("Mechanics", "RaycastDistance", 30f, "Maximum distance targeting vector for ship selection scans.");
		VerticalPostPrefab = config.Bind<string>("Assets", "VerticalPostPrefab", "wood_pole2", "Vanilla prefab used to build the center support columns.");
		AngledBracePrefab = config.Bind<string>("Assets", "AngledBracePrefab", "wood_beam2_26", "Vanilla prefab used to build the dynamic angled side hull supports.");
	}
}
public static class DryDockController
{
	private const string ZdoKeyDocked = "DryDock_IsDocked";

	private const string ZdoKeyOrigPos = "DryDock_OrigPos";

	private const string ZdoKeyOrigRot = "DryDock_OrigRot";

	public static void ExecuteDock()
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: 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_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: 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_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Ship val = FindTargetShip();
			if ((Object)(object)val == (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "No ship targeted.", 0, (Sprite)null, false);
				return;
			}
			Component component = ((Component)val).gameObject.GetComponent(typeof(ZNetView));
			ZNetView val2 = (ZNetView)(object)((component is ZNetView) ? component : null);
			if ((Object)(object)val2 == (Object)null || !val2.IsValid())
			{
				return;
			}
			if (!PrivateArea.CheckAccess(((Component)val).transform.position, 0f, true, false))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Building access restricted by Ward protection.", 0, (Sprite)null, false);
				return;
			}
			if (!val2.IsOwner())
			{
				val2.ClaimOwnership();
			}
			if (!val2.IsOwner())
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Network link sync deferred. Try again.", 0, (Sprite)null, false);
				return;
			}
			if (val2.GetZDO().GetInt("DryDock_IsDocked", 0) == 1)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Targeted ship is already docked.", 0, (Sprite)null, false);
				return;
			}
			Vector3 position = ((Component)val).transform.position;
			float num = Floating.GetLiquidLevel(position, 0f, (LiquidType)10) - position.y + Configuration.SafetyClearanceBuffer.Value;
			if (num < Configuration.SafetyClearanceBuffer.Value)
			{
				num = Configuration.SafetyClearanceBuffer.Value;
			}
			val2.GetZDO().Set("DryDock_OrigPos", position);
			ZDO zDO = val2.GetZDO();
			Quaternion rotation = ((Component)val).transform.rotation;
			zDO.Set("DryDock_OrigRot", ((Quaternion)(ref rotation)).eulerAngles);
			Component component2 = ((Component)val).gameObject.GetComponent(typeof(Rigidbody));
			Rigidbody val3 = (Rigidbody)(object)((component2 is Rigidbody) ? component2 : null);
			if ((Object)(object)val3 != (Object)null)
			{
				val3.linearVelocity = Vector3.zero;
				val3.angularVelocity = Vector3.zero;
				val3.isKinematic = true;
			}
			Bounds val4 = CalculateHullBounds(val);
			float z = ((Bounds)(ref val4)).size.z;
			float x = ((Bounds)(ref val4)).size.x;
			Vector3 position2 = position + new Vector3(0f, num, 0f);
			((Component)val).transform.position = position2;
			val2.GetZDO().SetPosition(position2);
			ConstructCradle(val, z, x);
			val2.GetZDO().Set("DryDock_IsDocked", 1);
			((Character)Player.m_localPlayer).Message((MessageType)2, $"Dry Dock Secure: Raised +{num:F1}m above sea level.", 0, (Sprite)null, false);
		}
		catch (Exception arg)
		{
			Plugin.ModLogger.LogError((object)$"Exception during ExecuteDock processing sequence: {arg}");
		}
	}

	public static void ExecuteUndock()
	{
		//IL_005e: 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_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: 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_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: 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_0160: 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_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Ship val = FindTargetShip();
			if ((Object)(object)val == (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "No ship targeted.", 0, (Sprite)null, false);
				return;
			}
			Component component = ((Component)val).gameObject.GetComponent(typeof(ZNetView));
			ZNetView val2 = (ZNetView)(object)((component is ZNetView) ? component : null);
			if ((Object)(object)val2 == (Object)null || !val2.IsValid())
			{
				return;
			}
			if (!PrivateArea.CheckAccess(((Component)val).transform.position, 0f, true, false))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Building access restricted by Ward protection.", 0, (Sprite)null, false);
				return;
			}
			if (!val2.IsOwner())
			{
				val2.ClaimOwnership();
			}
			if (!val2.IsOwner())
			{
				return;
			}
			if (val2.GetZDO().GetInt("DryDock_IsDocked", 0) == 0)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Targeted ship is not currently docked.", 0, (Sprite)null, false);
				return;
			}
			Vector3 val3 = val2.GetZDO().GetVec3("DryDock_OrigPos", Vector3.zero);
			Vector3 vec = val2.GetZDO().GetVec3("DryDock_OrigRot", Vector3.zero);
			if (val3 == Vector3.zero)
			{
				Plugin.ModLogger.LogWarning((object)"ZDO reference data empty. Falling back to dynamic water calculations.");
				val3 = ((Component)val).transform.position;
				val3.y = Floating.GetLiquidLevel(val3, 0f, (LiquidType)10);
			}
			DemolishCradleSubstructures(val);
			((Component)val).transform.position = val3;
			((Component)val).transform.rotation = Quaternion.Euler(vec);
			val2.GetZDO().SetPosition(val3);
			Component component2 = ((Component)val).gameObject.GetComponent(typeof(Rigidbody));
			Rigidbody val4 = (Rigidbody)(object)((component2 is Rigidbody) ? component2 : null);
			if ((Object)(object)val4 != (Object)null)
			{
				val4.isKinematic = false;
				val4.WakeUp();
			}
			val2.GetZDO().Set("DryDock_IsDocked", 0);
			((Character)Player.m_localPlayer).Message((MessageType)2, "Ship returned safely to the sea.", 0, (Sprite)null, false);
		}
		catch (Exception arg)
		{
			Plugin.ModLogger.LogError((object)$"Exception during ExecuteUndock processing sequence: {arg}");
		}
	}

	private static Ship FindTargetShip()
	{
		//IL_000e: 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_004a: 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)
		Transform transform = ((Component)GameCamera.instance).transform;
		Ray val = default(Ray);
		((Ray)(ref val))..ctor(transform.position, transform.forward);
		int mask = LayerMask.GetMask(new string[4] { "piece", "item", "viewblock", "vehicle" });
		RaycastHit val2 = default(RaycastHit);
		if (Physics.Raycast(val, ref val2, Configuration.RaycastDistance.Value, mask))
		{
			Component componentInParent = ((Component)((RaycastHit)(ref val2)).collider).gameObject.GetComponentInParent(typeof(Ship));
			Ship val3 = (Ship)(object)((componentInParent is Ship) ? componentInParent : null);
			if ((Object)(object)val3 != (Object)null)
			{
				return val3;
			}
			Collider[] array = Physics.OverlapSphere(((RaycastHit)(ref val2)).point, 4f, LayerMask.GetMask(new string[1] { "vehicle" }));
			for (int i = 0; i < array.Length; i++)
			{
				Component componentInParent2 = ((Component)array[i]).gameObject.GetComponentInParent(typeof(Ship));
				Ship val4 = (Ship)(object)((componentInParent2 is Ship) ? componentInParent2 : null);
				if ((Object)(object)val4 != (Object)null)
				{
					return val4;
				}
			}
		}
		return null;
	}

	private static Bounds CalculateHullBounds(Ship ship)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: 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)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		Bounds bounds = default(Bounds);
		((Bounds)(ref bounds))..ctor(((Component)ship).transform.position, Vector3.zero);
		Component[] componentsInChildren = ((Component)ship).gameObject.GetComponentsInChildren(typeof(Collider));
		bool flag = false;
		Component[] array = componentsInChildren;
		foreach (Component obj in array)
		{
			Collider val = (Collider)(object)((obj is Collider) ? obj : null);
			if (!((Object)(object)val == (Object)null) && !val.isTrigger)
			{
				if (!flag)
				{
					bounds = val.bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(val.bounds);
				}
			}
		}
		return bounds;
	}

	private static void ConstructCradle(Ship ship, float length, float width)
	{
		//IL_0097: 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)
		//IL_009e: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: 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_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_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: 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)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: 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_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = ZNetScene.instance.GetPrefab(Configuration.VerticalPostPrefab.Value);
		GameObject prefab2 = ZNetScene.instance.GetPrefab(Configuration.AngledBracePrefab.Value);
		if ((Object)(object)prefab == (Object)null || (Object)(object)prefab2 == (Object)null)
		{
			Plugin.ModLogger.LogError((object)"Configured cradle build prefabs are missing from the game's internal ZNetScene database.");
			return;
		}
		int num = Mathf.Max(4, Mathf.CeilToInt(length / 1.8f));
		float num2 = length / (float)(num - 1);
		Vector3 val = default(Vector3);
		Vector3 val2 = default(Vector3);
		Vector3 val3 = default(Vector3);
		for (int i = 0; i < num; i++)
		{
			float num3 = 0f - length / 2f + (float)i * num2;
			((Vector3)(ref val))..ctor(0f, -0.4f, num3);
			Vector3 worldPosition = ((Component)ship).transform.TransformPoint(val);
			BuildPieceServerSide(prefab, worldPosition, ((Component)ship).transform.rotation);
			((Vector3)(ref val2))..ctor(0f - width * 0.45f, -0.2f, num3);
			Vector3 worldPosition2 = ((Component)ship).transform.TransformPoint(val2);
			Quaternion rotation = ((Component)ship).transform.rotation * Quaternion.Euler(0f, 0f, -26f);
			BuildPieceServerSide(prefab2, worldPosition2, rotation);
			((Vector3)(ref val3))..ctor(width * 0.45f, -0.2f, num3);
			Vector3 worldPosition3 = ((Component)ship).transform.TransformPoint(val3);
			Quaternion rotation2 = ((Component)ship).transform.rotation * Quaternion.Euler(0f, 0f, 26f);
			BuildPieceServerSide(prefab2, worldPosition3, rotation2);
		}
	}

	private static void BuildPieceServerSide(GameObject prefab, Vector3 worldPosition, Quaternion rotation)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: 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_0092: Unknown result type (might be due to invalid IL or missing references)
		Component component = prefab.GetComponent(typeof(Piece));
		if ((Object)(object)((component is Piece) ? component : null) == (Object)null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		GameObject val = Object.Instantiate<GameObject>(prefab, worldPosition, rotation);
		Component component2 = val.GetComponent(typeof(ZNetView));
		ZNetView val2 = (ZNetView)(object)((component2 is ZNetView) ? component2 : null);
		if ((Object)(object)val2 != (Object)null && val2.IsValid())
		{
			val2.GetZDO().SetOwner(ZNet.GetUID());
			Component component3 = val.GetComponent(typeof(Piece));
			Piece val3 = (Piece)(object)((component3 is Piece) ? component3 : null);
			if ((Object)(object)val3 != (Object)null)
			{
				val3.SetCreator(localPlayer.GetPlayerID(), default(PlatformUserID));
			}
			Component component4 = val.GetComponent(typeof(WearNTear));
			WearNTear val4 = (WearNTear)(object)((component4 is WearNTear) ? component4 : null);
			if ((Object)(object)val4 != (Object)null)
			{
				val4.OnPlaced();
			}
		}
	}

	private static void DemolishCradleSubstructures(Ship ship)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		int mask = LayerMask.GetMask(new string[1] { "piece" });
		Collider[] array = Physics.OverlapSphere(((Component)ship).transform.position, 12f, mask);
		for (int i = 0; i < array.Length; i++)
		{
			Component componentInParent = ((Component)array[i]).gameObject.GetComponentInParent(typeof(Piece));
			Piece val = (Piece)(object)((componentInParent is Piece) ? componentInParent : null);
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			string text = ((Object)((Component)val).gameObject).name.Replace("(Clone)", "").Trim();
			if (!(text == Configuration.VerticalPostPrefab.Value) && !(text == Configuration.AngledBracePrefab.Value))
			{
				continue;
			}
			Component component = ((Component)val).gameObject.GetComponent(typeof(ZNetView));
			ZNetView val2 = (ZNetView)(object)((component is ZNetView) ? component : null);
			if ((Object)(object)val2 != (Object)null && val2.IsValid())
			{
				if (!val2.IsOwner())
				{
					val2.ClaimOwnership();
				}
				if (val2.IsOwner())
				{
					ZNetScene.instance.Destroy(((Component)val).gameObject);
				}
			}
		}
	}
}
[HarmonyPatch]
public static class DryDockNetworkPatches
{
	[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
	[HarmonyPrefix]
	public static bool PrefixShipPhysicsUpdate(Ship __instance, float fixedDeltaTime)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		Component component = ((Component)__instance).gameObject.GetComponent(typeof(ZNetView));
		ZNetView val = (ZNetView)(object)((component is ZNetView) ? component : null);
		if ((Object)(object)val != (Object)null && val.IsValid() && val.GetZDO().GetInt("DryDock_IsDocked", 0) == 1)
		{
			Component component2 = ((Component)__instance).gameObject.GetComponent(typeof(Rigidbody));
			Rigidbody val2 = (Rigidbody)(object)((component2 is Rigidbody) ? component2 : null);
			if ((Object)(object)val2 != (Object)null && !val2.isKinematic)
			{
				val2.linearVelocity = Vector3.zero;
				val2.angularVelocity = Vector3.zero;
				val2.isKinematic = true;
			}
			return false;
		}
		return true;
	}
}