using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FishNet; using FishNet.Broadcast; using FishNet.Connection; using FishNet.Serializing; using FishNet.Transporting; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Rendering; [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("BlirUBoat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+faaaf265c0d7028c431d7cc2f1491e5751925c20")] [assembly: AssemblyProduct("BlirUBoat")] [assembly: AssemblyTitle("BlirUBoat")] [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 BlirUBoat { internal static class BoatProps { private struct Placement { public Vector3 Position; public Quaternion Rotation; } private const string RootName = "BlirUBoat_Props"; private const float PlankThickness = 0.09f; private static Mesh _cubeTemplate; private static Shader _cachedShader; public static float PlankThicknessMetres => 0.09f; private static float PlankTopY => 0.09f; public static void Attach(Boat boat) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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_00fc: 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) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)boat == (Object)null) { return; } Transform val = ResolveVisualRoot(boat); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Could not resolve the boat's visual root; props not attached."); return; } Transform val2 = val.Find("BlirUBoat_Props"); if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)((Component)val2).gameObject); } Placement? placement = FindBowPlacement(boat, val); if (!placement.HasValue) { Plugin.Log.LogWarning((object)"Could not locate the bow; props not attached."); return; } float scale = Mathf.Max(0.1f, ModConfig.PropScale.Value); GameObject val3 = new GameObject("BlirUBoat_Props"); val3.transform.SetParent(val, false); val3.transform.localPosition = placement.Value.Position; val3.transform.localRotation = placement.Value.Rotation; float num = ResolvePlankWidth(val); Material material = MakeMaterial(new Color(0.4f, 0.26f, 0.15f)); Material white = MakeMaterial(new Color(0.93f, 0.92f, 0.89f)); Material red = MakeMaterial(new Color(0.7f, 0.08f, 0.1f)); Material tobacco = MakeMaterial(new Color(0.85f, 0.68f, 0.36f)); Material plateMat = MakeMaterial(new Color(0.16f, 0.16f, 0.18f)); Material capMat = MakeMaterial(new Color(0.78f, 0.09f, 0.09f)); BuildPlank(val3.transform, num, material); Transform buttonCap = BuildButton(val3.transform, num, scale, plateMat, capMat); BuildPack(val3.transform, num, scale, white, red, tobacco, buttonCap); BuildLighter(val3.transform, num, scale); Plugin.Log.LogInfo((object)($"Bow props attached under '{((Object)val).name}' at local {val3.transform.localPosition}, " + $"plank width {num:F2}m.")); } private static float ResolvePlankWidth(Transform visualRoot) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) float value = ModConfig.PlankWidthOverride.Value; if (value > 0.01f) { return value; } Bounds? val = ComputeLocalBounds(visualRoot); float num; if (!val.HasValue) { num = 3f; } else { Bounds valueOrDefault = val.GetValueOrDefault(); num = ((Bounds)(ref valueOrDefault)).size.z; } return num * 0.34f; } private static Transform ResolveVisualRoot(Boat boat) { if ((Object)(object)boat.VisualBoat != (Object)null) { return boat.VisualBoat; } if ((Object)(object)boat.VisualPhysicsRig != (Object)null) { return ((Component)boat.VisualPhysicsRig).transform; } return ((Component)boat).transform; } private static Placement? FindBowPlacement(Boat boat, Transform visualRoot) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00d5: 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) //IL_00d9: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: 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_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_0115: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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_00b8: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: 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) //IL_01be: Unknown result type (might be due to invalid IL or missing references) Bounds? val = ComputeLocalBounds(visualRoot); if (!val.HasValue) { return null; } Bounds value = val.Value; bool flag = ((Bounds)(ref value)).size.x >= ((Bounds)(ref value)).size.z; Vector3 val2 = (flag ? Vector3.right : Vector3.forward); float num = (flag ? ((Bounds)(ref value)).size.x : ((Bounds)(ref value)).size.z) * 0.5f; Transform val3 = FindPropeller(boat) ?? boat.DriverPos; if ((Object)(object)val3 != (Object)null) { if (Vector3.Dot(visualRoot.InverseTransformPoint(val3.position) - ((Bounds)(ref value)).center, val2) > 0f) { val2 = -val2; } } else { Plugin.Log.LogWarning((object)"No propeller or driver position found; bow direction is a guess."); } Vector3 up = Vector3.up; Vector3 val4 = Vector3.Cross(up, val2); Vector3 normalized = ((Vector3)(ref val4)).normalized; Vector3 val5 = ((Bounds)(ref value)).center + val2 * (num + ModConfig.OffsetForward.Value) + normalized * ModConfig.OffsetSide.Value; val5.y = ((Bounds)(ref value)).min.y + ((Bounds)(ref value)).size.y * ModConfig.PlankHeightFraction.Value; Plugin.Log.LogInfo((object)($"Boat bounds center={((Bounds)(ref value)).center} size={((Bounds)(ref value)).size}; forward={val2} " + string.Format("(long axis {0}); props at {1}.", flag ? "X" : "Z", val5))); return new Placement { Position = val5, Rotation = Quaternion.LookRotation(val2, up) }; } private static Transform FindPropeller(Boat boat) { if (!(AccessTools.Field(typeof(Boat), "_motors")?.GetValue(boat) is IEnumerable enumerable)) { return null; } foreach (object item in enumerable) { BoatMotor val = (BoatMotor)((item is BoatMotor) ? item : null); if (val != null && (Object)(object)val.Propeller != (Object)null) { return val.Propeller; } } return null; } private static Bounds? ComputeLocalBounds(Transform visualRoot) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_00a2: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00ff: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) MeshRenderer[] array = (from r in ((Component)visualRoot).GetComponentsInChildren() where (Object)(object)r != (Object)null && ((Renderer)r).enabled select r).ToArray(); if (array.Length == 0) { return null; } bool flag = false; Bounds value = default(Bounds); MeshRenderer[] array2 = array; foreach (MeshRenderer val in array2) { MeshFilter component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null) { continue; } Matrix4x4 val2 = visualRoot.worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix; Bounds bounds = component.sharedMesh.bounds; for (int num2 = 0; num2 < 8; num2++) { Vector3 val3 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(((num2 & 1) == 0) ? (-1f) : 1f, ((num2 & 2) == 0) ? (-1f) : 1f, ((num2 & 4) == 0) ? (-1f) : 1f)); Vector3 val4 = ((Matrix4x4)(ref val2)).MultiplyPoint3x4(val3); if (!flag) { ((Bounds)(ref value))..ctor(val4, Vector3.zero); flag = true; } else { ((Bounds)(ref value)).Encapsulate(val4); } } } if (!flag) { return null; } return value; } private static void BuildPlank(Transform parent, float width, Material material) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) GameObject obj = MakePrimitive((PrimitiveType)3, parent, material, Vector3.one, new Vector3(0f, 0.045f, 0f)); ((Object)obj).name = "Plank"; ShapePlank(obj, width, width, ModConfig.PlankLength.Value); obj.AddComponent(); } public static void ShapePlank(GameObject plank, float widthAft, float widthFore, float depth) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) MeshFilter component = plank.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh == (Object)null)) { if ((Object)(object)_cubeTemplate == (Object)null) { _cubeTemplate = component.sharedMesh; } Mesh val = Object.Instantiate(_cubeTemplate); ((Object)val).name = "BlirUBoat_Thwart"; Vector3[] vertices = val.vertices; for (int i = 0; i < vertices.Length; i++) { Vector3 val2 = vertices[i]; float num = Mathf.Clamp01(val2.z + 0.5f); float num2 = Mathf.Lerp(widthAft, widthFore, num); vertices[i] = new Vector3(val2.x * num2, val2.y * 0.09f, val2.z * depth); } val.vertices = vertices; val.RecalculateNormals(); val.RecalculateBounds(); Mesh sharedMesh = component.sharedMesh; component.sharedMesh = val; if ((Object)(object)sharedMesh != (Object)null && ((Object)sharedMesh).name == "BlirUBoat_Thwart") { Object.Destroy((Object)(object)sharedMesh); } } } private static Transform BuildButton(Transform parent, float plankWidth, float scale, Material plateMat, Material capMat) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_002f: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Button"); val.transform.SetParent(parent, false); val.transform.localPosition = new Vector3(plankWidth * 0.28f, 0f, 0f); ((Object)MakePrimitive((PrimitiveType)3, val.transform, plateMat, new Vector3(0.095f, 0.018f, 0.095f) * scale, new Vector3(0f, PlankTopY + 0.009f * scale, 0f))).name = "ButtonBase"; GameObject obj = MakePrimitive((PrimitiveType)2, val.transform, capMat, new Vector3(0.055f, 0.006f, 0.055f) * scale, new Vector3(0f, PlankTopY + 0.024f * scale, 0f)); ((Object)obj).name = "ButtonCap"; return obj.transform; } private static void BuildPack(Transform parent, float plankWidth, float scale, Material white, Material red, Material tobacco, Transform buttonCap) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00ec: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Expected O, but got Unknown //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CigarettePack"); val.SetActive(false); val.transform.SetParent(parent, false); val.layer = LayerMask.NameToLayer("Interactable"); val.tag = "Interactable"; val.transform.localPosition = new Vector3(plankWidth * -0.26f, PlankTopY, 0f); val.transform.localRotation = Quaternion.Euler(0f, ModConfig.PackYaw.Value, 0f); float num = 0.049300004f; float num2 = 0.0357f; GameObject val2 = MakePrimitive((PrimitiveType)3, val.transform, white, new Vector3(0.055f, num, 0.024f) * scale, new Vector3(0f, num * 0.5f * scale, 0f)); ((Object)val2).name = "PackBody"; GameObject val3 = MakePrimitive((PrimitiveType)3, val.transform, red, new Vector3(0.055549998f, num2, 0.02424f) * scale, new Vector3(0f, (num + num2 * 0.5f) * scale, 0f)); ((Object)val3).name = "PackLid"; GameObject val4 = MakePrimitive((PrimitiveType)3, val.transform, red, new Vector3(0.055f, 0.024f, 0.004f) * scale, new Vector3(0f, 0.09508f * scale, -0.01056f * scale)); ((Object)val4).name = "PackFlap"; val4.transform.localRotation = Quaternion.Euler(-32f, 0f, 0f); List list = new List { val2, val3, val4 }; for (int i = 0; i < 3; i++) { float num3 = (float)(i - 1) * 0.0135f; GameObject val5 = MakePrimitive((PrimitiveType)2, val.transform, (i == 1) ? tobacco : white, new Vector3(0.0072f, 0.006f, 0.0072f) * scale, new Vector3(num3 * scale, 0.089f * scale, 0f)); ((Object)val5).name = $"Cigarette{i}"; val5.transform.localRotation = Quaternion.Euler(5f * (float)(i - 1), 0f, 4f * (float)(i - 1)); list.Add(val5); } BoxCollider val6 = val.AddComponent(); ((Collider)val6).isTrigger = true; val6.size = new Vector3(0.16f, 0.16f, 0.16f) * scale; val6.center = new Vector3(0f, 0.0425f * scale, 0f); GameObject val7 = new GameObject("HoverTextAnchor"); val7.transform.SetParent(parent, false); val7.transform.localPosition = val.transform.localPosition + new Vector3(ModConfig.HoverTextSide.Value, ModConfig.HoverTextUp.Value, 0f); val.AddComponent().Configure((Collider)(object)val6, list.ToArray(), val7.transform, buttonCap); val.SetActive(true); } private static void BuildLighter(Transform parent, float plankWidth, float scale) { //IL_003c: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) Transform val = VanillaAssets.FindLighter(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogInfo((object)"No lighter found inside the Dynamite prefab; skipping it."); return; } GameObject val2 = VanillaAssets.CloneVisual(val, parent, "Lighter"); if (!((Object)(object)val2 == (Object)null)) { val2.transform.localScale = Vector3.one * scale; val2.transform.localPosition = new Vector3(plankWidth * -0.09f, PlankTopY, 0.03f * scale); val2.transform.localRotation = Quaternion.Euler(0f, 68f, 0f); SeatOnParentSurface(val2); } } private static void SeatOnParentSurface(GameObject go) { //IL_000f: 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_0026: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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) Renderer[] componentsInChildren = go.GetComponentsInChildren(); if (componentsInChildren.Length != 0) { Bounds bounds = componentsInChildren[0].bounds; Renderer[] array = componentsInChildren; foreach (Renderer val in array) { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } float num = go.transform.position.y - ((Bounds)(ref bounds)).min.y; Transform transform = go.transform; transform.localPosition += new Vector3(0f, num, 0f); } } private static GameObject MakePrimitive(PrimitiveType type, Transform parent, Material material, Vector3 scale, Vector3 localPosition) { //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_0026: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive(type); obj.transform.SetParent(parent, false); obj.transform.localPosition = localPosition; obj.transform.localScale = scale; Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Renderer component2 = obj.GetComponent(); if ((Object)(object)component2 != (Object)null && (Object)(object)material != (Object)null) { component2.material = material; } return obj; } private static Material MakeMaterial(Color tint) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) Shader val = ResolveShader(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"No usable shader found; props will render with the fallback material."); return null; } Material val2 = new Material(val); int num = 0; for (int i = 0; i < val.GetPropertyCount(); i++) { if ((int)val.GetPropertyType(i) == 0) { val2.SetColor(val.GetPropertyNameId(i), tint); num++; } } if (num == 0) { Plugin.Log.LogWarning((object)("Shader '" + ((Object)val).name + "' exposes no colour property; props will render untinted. Try a different Props.ShaderName — F9 lists the candidates.")); } if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", 0.15f); } if (val2.HasProperty("_Glossiness")) { val2.SetFloat("_Glossiness", 0.15f); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", 0f); } return val2; } private static Shader ResolveShader() { if ((Object)(object)_cachedShader != (Object)null) { return _cachedShader; } string[] array = new string[5] { ModConfig.ShaderName.Value, "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit", "Universal Render Pipeline/Unlit", "Sprites/Default" }; foreach (string text in array) { if (string.IsNullOrEmpty(text)) { continue; } Shader val = Shader.Find(text); if (!((Object)(object)val == (Object)null)) { if (HasBaseColour(val)) { Plugin.Log.LogInfo((object)("Prop shader: '" + text + "'.")); _cachedShader = val; return val; } Plugin.Log.LogInfo((object)("Shader '" + text + "' has no base colour property; trying the next candidate.")); } } return null; } private static bool HasBaseColour(Shader shader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < shader.GetPropertyCount(); i++) { if ((int)shader.GetPropertyType(i) == 0) { string propertyName = shader.GetPropertyName(i); if (propertyName == "_BaseColor" || propertyName == "_Color") { return true; } } } return false; } } internal class CigarettePackInteractable : Interactable { private Transform _buttonCap; private Vector3 _capRestPosition; private string _lastHoverText; private bool _pressing; public override bool InteractableWhenHoldingItem => false; private static bool CanUse { get { if (DiveController.CanTrigger && (Object)(object)BoatManager.Boat != (Object)null) { return BoatManager.Boat.BoatUnlocked; } return false; } } public void Configure(Collider interactCollider, GameObject[] outlineTargets, Transform textAnchor, Transform buttonCap) { //IL_007f: 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_0084: Unknown result type (might be due to invalid IL or missing references) AccessTools.Field(typeof(Interactable), "_interactCol")?.SetValue(this, interactCollider); AccessTools.Field(typeof(Interactable), "_modelsToOutline")?.SetValue(this, outlineTargets); AccessTools.Field(typeof(Interactable), "_textTarget")?.SetValue(this, textAnchor); _buttonCap = buttonCap; _capRestPosition = (((Object)(object)buttonCap != (Object)null) ? buttonCap.localPosition : Vector3.zero); } public override void Hover() { //IL_0018: 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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) string text = BuildHoverText(); if (!base._isHovering) { PlayerUI.SetLookAtText(text, ((Interactable)this).TextTarget, default(Vector3), false); PlayerUI.SetLookAtColor(CanUse ? Color.white : GameInfo.RedColor); } else if (text != _lastHoverText) { PlayerUI.UpdateLookAtText(text); } _lastHoverText = text; ((Interactable)this).Hover(); } public override void UnHover() { PlayerUI.HideLookAtText(_lastHoverText); ((Interactable)this).UnHover(); } public override void Interact(Player player) { if (CanUse) { ((Interactable)this).Interact(player); if (!_pressing) { ((MonoBehaviour)this).StartCoroutine(PressAnimation()); } DiveNet.RequestDive(); } } private string BuildHoverText() { if ((Object)(object)BoatManager.Boat == (Object)null || !BoatManager.Boat.BoatUnlocked) { return "Not your boat yet"; } if (!DiveController.CanTrigger) { if (DiveController.Phase != DivePhase.Cooldown) { return "Too late"; } return "Maybe don't"; } return "Röökille\n" + SpriteManager.GetPickUpInput(); } private IEnumerator PressAnimation() { _pressing = true; if ((Object)(object)_buttonCap != (Object)null) { for (float t = 0f; t < 0.06f; t += Time.deltaTime) { float num = Mathf.Clamp01(t / 0.06f); _buttonCap.localPosition = _capRestPosition - new Vector3(0f, 0.02f * num, 0f); yield return null; } _buttonCap.localPosition = _capRestPosition - new Vector3(0f, 0.02f, 0f); yield return (object)new WaitForSeconds(0.5f); for (float t = 0f; t < 0.25f; t += Time.deltaTime) { float num2 = Mathf.Clamp01(t / 0.25f); _buttonCap.localPosition = _capRestPosition - new Vector3(0f, 0.02f * (1f - num2), 0f); yield return null; } _buttonCap.localPosition = _capRestPosition; } _pressing = false; } } internal static class Diagnostics { public static void Dump() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("BlirUBoat diagnostics — " + Application.productName + " / Unity " + Application.unityVersion); stringBuilder.AppendLine(); Section(stringBuilder, "AUDIO CLIPS (AudioManager._realClips)", DumpAudioClips); Section(stringBuilder, "ITEM PREFABS (Resources.LoadAll(\"Items\"))", DumpItems); Section(stringBuilder, "BOAT GEOMETRY + BOW/TRIGGER CHECK", DumpBoat); Section(stringBuilder, "SHADER AVAILABILITY", DumpShaders); Section(stringBuilder, "BOAT MATERIALS + TEXTURES", DumpBoatMaterials); string text = Path.Combine(Paths.ConfigPath, "BlirUBoat-diagnostics.txt"); File.WriteAllText(text, stringBuilder.ToString()); Plugin.Log.LogInfo((object)("Diagnostics written to " + text)); } private static void Section(StringBuilder sb, string title, Action body) { sb.AppendLine(new string('=', 78)); sb.AppendLine(title); sb.AppendLine(new string('=', 78)); try { body(sb); } catch (Exception ex) { sb.AppendLine($" FAILED: {ex}"); Plugin.Log.LogWarning((object)("Diagnostics section '" + title + "' failed: " + ex.Message)); } sb.AppendLine(); } private static void DumpAudioClips(StringBuilder sb) { FieldInfo fieldInfo = AccessTools.Field(typeof(AudioManager), "_realClips"); if (fieldInfo == null) { sb.AppendLine(" AudioManager._realClips not found — field renamed?"); return; } if (!(fieldInfo.GetValue(null) is IDictionary dictionary)) { sb.AppendLine(" _realClips was null or not a dictionary."); return; } IOrderedEnumerable orderedEnumerable = (from object k in dictionary.Keys select k?.ToString() ?? "").OrderBy((string n) => n, StringComparer.OrdinalIgnoreCase); sb.AppendLine($" {dictionary.Count} clips:"); foreach (string item in orderedEnumerable) { sb.AppendLine(" " + item); } } private static void DumpItems(StringBuilder sb) { Item[] array = Resources.LoadAll("Items"); sb.AppendLine($" {array.Length} item prefabs:"); foreach (string item in array.Select((Item i) => ((Object)i).name).OrderBy((string n) => n, StringComparer.OrdinalIgnoreCase)) { sb.AppendLine(" " + item); } } private static void DumpBoat(StringBuilder sb) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: 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_023c: Unknown result type (might be due to invalid IL or missing references) Boat boat = BoatManager.Boat; if ((Object)(object)boat == (Object)null) { sb.AppendLine(" No boat spawned yet — load into a save with the boat present and press the key again."); return; } sb.AppendLine($" Boat: {((Object)boat).name} BoatUnlocked={boat.BoatUnlocked}"); sb.AppendLine(" VisualBoat: " + Describe(boat.VisualBoat)); sb.AppendLine(" DriverPos: " + Describe(boat.DriverPos)); sb.AppendLine(); if (!(AccessTools.Field(typeof(Boat), "_forcePoints")?.GetValue(boat) is Transform[] array) || array.Length == 0) { sb.AppendLine(" _forcePoints missing or empty — bow anchoring strategy needs rethinking."); return; } Transform transform = ((Component)boat).transform; sb.AppendLine($" {array.Length} force points (boat-local space, +Z assumed forward):"); Transform val = null; float num = float.NegativeInfinity; Transform[] array2 = array; foreach (Transform val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = transform.InverseTransformPoint(val2.position); sb.AppendLine($" {((Object)val2).name,-24} local={Fmt(val3)}"); if (val3.z > num) { num = val3.z; val = val2; } } } if ((Object)(object)val == (Object)null) { sb.AppendLine(" No usable force point found."); return; } sb.AppendLine(); sb.AppendLine($" Bow candidate (max local Z): {((Object)val).name} at local Z {num:F3}"); BoatTrigger boatTrigger = boat.BoatTrigger; if ((Object)(object)boatTrigger == (Object)null) { sb.AppendLine(" BoatTrigger is null — cannot evaluate coverage."); return; } Collider[] componentsInChildren = ((Component)boatTrigger).GetComponentsInChildren(); sb.AppendLine($" BoatTrigger '{((Object)boatTrigger).name}' has {componentsInChildren.Length} collider(s):"); bool flag = false; Collider[] array3 = componentsInChildren; foreach (Collider val4 in array3) { Bounds bounds = val4.bounds; bool flag2 = ((Bounds)(ref bounds)).Contains(val.position); flag = flag || flag2; sb.AppendLine($" {((object)val4).GetType().Name,-18} isTrigger={val4.isTrigger,-5} " + $"center={Fmt(((Bounds)(ref bounds)).center)} extents={Fmt(((Bounds)(ref bounds)).extents)} containsBow={flag2}"); } sb.AppendLine(); sb.AppendLine(flag ? " RESULT: bow IS inside the boat trigger — vanilla OnBoat covers the button position." : " RESULT: bow is OUTSIDE the boat trigger — drown immunity needs its own occupancy test at the bow (see RESEARCH.md §6.7)."); sb.AppendLine(" NOTE: bounds are axis-aligned approximations; confirm by standing on the bow and checking PlayerMovement.OnBoat."); } private static void DumpShaders(StringBuilder sb) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) foreach (string item in new string[8] { ModConfig.ShaderName.Value, "Shader Graphs/DefaultShader", "Shader Graphs/LevelShader", "Shader Graphs/CharacterShader", "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit", "Universal Render Pipeline/Unlit", "Sprites/Default" }.Distinct()) { if (string.IsNullOrEmpty(item)) { continue; } Shader val = Shader.Find(item); if ((Object)(object)val == (Object)null) { sb.AppendLine(" MISSING " + item); continue; } List list = new List(); for (int i = 0; i < val.GetPropertyCount(); i++) { if ((int)val.GetPropertyType(i) == 0) { list.Add(val.GetPropertyName(i)); } } sb.AppendLine(" OK " + item + " colour properties: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "")); } } private static void DumpBoatMaterials(StringBuilder sb) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Invalid comparison between Unknown and I4 //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) Boat boat = BoatManager.Boat; if ((Object)(object)boat == (Object)null) { sb.AppendLine(" No boat spawned yet."); return; } Transform val = (((Object)(object)boat.VisualBoat != (Object)null) ? boat.VisualBoat : ((Component)boat).transform); MeshRenderer[] componentsInChildren = ((Component)val).GetComponentsInChildren(); sb.AppendLine($" {componentsInChildren.Length} renderer(s) under '{((Object)val).name}':"); MeshRenderer[] array = componentsInChildren; foreach (MeshRenderer val2 in array) { Material sharedMaterial = ((Renderer)val2).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null) { continue; } MeshFilter component = ((Component)val2).GetComponent(); float num; if (!((Object)(object)component != (Object)null) || !((Object)(object)component.sharedMesh != (Object)null)) { num = 0f; } else { Bounds bounds = component.sharedMesh.bounds; Vector3 size = ((Bounds)(ref bounds)).size; num = ((Vector3)(ref size)).magnitude; } float num2 = num; sb.AppendLine($" {((Object)val2).name,-22} mat='{((Object)sharedMaterial).name}' shader='{((Object)sharedMaterial.shader).name}' meshSize={num2:F2}"); Shader shader = sharedMaterial.shader; for (int j = 0; j < shader.GetPropertyCount(); j++) { int propertyNameId = shader.GetPropertyNameId(j); ShaderPropertyType propertyType = shader.GetPropertyType(j); if ((int)propertyType == 4) { Texture texture = sharedMaterial.GetTexture(propertyNameId); if ((Object)(object)texture != (Object)null) { sb.AppendLine(" tex " + shader.GetPropertyName(j) + " = '" + ((Object)texture).name + "' " + $"({texture.width}x{texture.height})"); } } else if ((int)propertyType == 0) { sb.AppendLine($" col {shader.GetPropertyName(j)} = {sharedMaterial.GetColor(propertyNameId)}"); } } } } private static string Describe(Transform t) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)t == (Object)null)) { return ((Object)t).name + " world=" + Fmt(t.position); } return ""; } private static string Fmt(Vector3 v) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return $"({v.x:F2}, {v.y:F2}, {v.z:F2})"; } } internal enum DivePhase { Idle, Descending, Submerged, Ascending, Cooldown } internal static class DiveController { private const float ProportionalGain = 9f; private const float DerivativeGain = 5f; private const float MaxAcceleration = 28f; private static float _phaseStart; private static float _submergedDuration; private static float _currentTargetDepth; public static DivePhase Phase { get; private set; } private static float PhaseElapsed => Time.time - _phaseStart; public static bool Diving { get { if (Phase != DivePhase.Descending && Phase != DivePhase.Submerged) { return Phase == DivePhase.Ascending; } return true; } } public static bool CanTrigger => Phase == DivePhase.Idle; public static void ServerBeginDive() { if (!CanTrigger) { Plugin.Log.LogInfo((object)$"Dive requested but phase is {Phase}; ignoring."); return; } _submergedDuration = Random.Range(ModConfig.SubmergedSecondsMin.Value, Mathf.Max(ModConfig.SubmergedSecondsMin.Value, ModConfig.SubmergedSecondsMax.Value)); EnterPhase(DivePhase.Descending); Plugin.Log.LogInfo((object)($"Dive started — descending over {ModConfig.DescentSeconds.Value:F1}s, " + $"holding {_submergedDuration:F1}s.")); DiveNet.ServerAnnounceDiveStarted(); } public static void Abort(string reason) { if (Phase != DivePhase.Idle) { Plugin.Log.LogInfo((object)("Dive aborted: " + reason)); Phase = DivePhase.Idle; _currentTargetDepth = 0f; Klaxon.Stop(); } } private static void EnterPhase(DivePhase phase) { Phase = phase; _phaseStart = Time.time; } public static bool ServerTick(Boat boat) { AdvancePhase(); if (!Diving) { return false; } if ((Object)(object)boat == (Object)null) { return false; } Rigidbody hiddenPhysicsRig = boat.HiddenPhysicsRig; if ((Object)(object)hiddenPhysicsRig == (Object)null) { return false; } ApplyDepthControl(boat, hiddenPhysicsRig); KeepUpright(boat, hiddenPhysicsRig); ApplyUnderwaterDamping(boat, hiddenPhysicsRig); return true; } private static void AdvancePhase() { switch (Phase) { case DivePhase.Descending: if (PhaseElapsed >= ModConfig.DescentSeconds.Value) { EnterPhase(DivePhase.Submerged); Klaxon.Stop(); } break; case DivePhase.Submerged: if (PhaseElapsed >= _submergedDuration) { EnterPhase(DivePhase.Ascending); Plugin.Log.LogInfo((object)"Surfacing."); } break; case DivePhase.Ascending: if (PhaseElapsed >= ModConfig.AscentSeconds.Value) { EnterPhase(DivePhase.Cooldown); } break; case DivePhase.Cooldown: if (PhaseElapsed >= ModConfig.CooldownSeconds.Value) { Phase = DivePhase.Idle; Plugin.Log.LogInfo((object)"Boat ready to dive again."); } break; } } private static float TargetDepth() { float value = ModConfig.DiveDepth.Value; return Phase switch { DivePhase.Descending => value * Smooth(Mathf.Clamp01(PhaseElapsed / Mathf.Max(0.01f, ModConfig.DescentSeconds.Value))), DivePhase.Submerged => value, DivePhase.Ascending => value * (1f - Smooth(Mathf.Clamp01(PhaseElapsed / Mathf.Max(0.01f, ModConfig.AscentSeconds.Value)))), _ => 0f, }; } private static float Smooth(float t) { return t * t * (3f - 2f * t); } private static void ApplyDepthControl(Boat boat, Rigidbody rig) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0055: Unknown result type (might be due to invalid IL or missing references) _currentTargetDepth = TargetDepth(); Vector3 position = rig.position; float num = WaterManager.GetWaterHeight(position) - _currentTargetDepth - position.y; float y = rig.linearVelocity.y; float num2 = Mathf.Clamp(num * 9f - y * 5f, -28f, 28f); rig.AddForce(Vector3.up * num2, (ForceMode)5); } private static void KeepUpright(Boat boat, Rigidbody rig) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) //IL_005b: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (AccessTools.Field(typeof(Boat), "_antiRollOverForce")?.GetValue(boat) is float num) { Quaternion val = Quaternion.Euler(0f, ((Component)rig).transform.eulerAngles.y, 0f); Vector3 angularVelocityToTarget = DazedUtils.GetAngularVelocityToTarget(rig.rotation, val); rig.AddTorque(angularVelocityToTarget * num); } } private static void ApplyUnderwaterDamping(Boat boat, Rigidbody rig) { object obj = AccessTools.Field(typeof(Boat), "_underwaterLinearDamp")?.GetValue(boat); object obj2 = AccessTools.Field(typeof(Boat), "_underwaterAngularDamp")?.GetValue(boat); if (obj is float linearDamping) { rig.linearDamping = linearDamping; } if (obj2 is float angularDamping) { rig.angularDamping = angularDamping; } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct DiveRequestBroadcast : IBroadcast { } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct DiveStartedBroadcast : IBroadcast { } internal static class DiveNet { private static bool _serializersInstalled; private static bool _serverRegistered; private static bool _clientRegistered; public static bool Available { get; private set; } public static void RequestDive() { if (InstanceFinder.IsServerStarted) { DiveController.ServerBeginDive(); return; } if (!Available) { Plugin.Log.LogWarning((object)"Dive requested but this client cannot reach the host (broadcast unavailable). The host must have the mod installed."); return; } try { InstanceFinder.ClientManager.Broadcast(default(DiveRequestBroadcast), (Channel)0); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to send dive request: " + ex.Message)); } } public static void ServerAnnounceDiveStarted() { if (!InstanceFinder.IsServerStarted) { return; } Klaxon.Play(((Object)(object)BoatManager.Boat != (Object)null) ? BoatManager.Boat.VisualBoat : null); if (!Available) { return; } try { InstanceFinder.ServerManager.Broadcast(default(DiveStartedBroadcast), true, (Channel)0); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to announce dive to clients: " + ex.Message)); } } public static void EnsureRegistered(bool asServer) { InstallSerializers(); try { if (asServer && !_serverRegistered && (Object)(object)InstanceFinder.ServerManager != (Object)null) { InstanceFinder.ServerManager.RegisterBroadcast((Action)OnServerDiveRequest, true); _serverRegistered = true; Plugin.Log.LogInfo((object)"Registered server broadcast handler."); } if (!asServer && !_clientRegistered && (Object)(object)InstanceFinder.ClientManager != (Object)null) { InstanceFinder.ClientManager.RegisterBroadcast((Action)OnClientDiveStarted); _clientRegistered = true; Plugin.Log.LogInfo((object)"Registered client broadcast handler."); } } catch (Exception ex) { Available = false; Plugin.Log.LogWarning((object)("Broadcast registration failed (" + ex.Message + "). Falling back to host-only triggering.")); } } public static void Reset() { _serverRegistered = false; _clientRegistered = false; } private static void InstallSerializers() { if (_serializersInstalled) { return; } try { InstallSerializer(); InstallSerializer(); _serializersInstalled = true; Available = true; Plugin.Log.LogInfo((object)"Installed FishNet broadcast serializers."); } catch (Exception ex) { Available = false; Plugin.Log.LogWarning((object)("Could not install broadcast serializers (" + ex.Message + "). Host-only triggering will still work.")); } } private static void InstallSerializer() where T : struct, IBroadcast { Action value = delegate { }; Func value2 = (Reader reader) => default(T); SetStaticMember(typeof(GenericWriter), "k__BackingField", value); SetStaticMember(typeof(GenericReader), "k__BackingField", value2); SetStaticMember(typeof(GenericWriter), "HasCustomSerializer", true, optional: true); SetStaticMember(typeof(GenericReader), "HasCustomSerializer", true, optional: true); } private static void SetStaticMember(Type type, string name, object value, bool optional = false) { FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo != null) { fieldInfo.SetValue(null, value); return; } PropertyInfo propertyInfo = AccessTools.Property(type, name); if (propertyInfo != null && propertyInfo.CanWrite) { propertyInfo.SetValue(null, value, null); } else if (!optional) { throw new MissingFieldException(type.FullName, name); } } private static void OnServerDiveRequest(NetworkConnection sender, DiveRequestBroadcast msg, Channel channel) { Plugin.Log.LogInfo((object)$"Dive requested by client {sender?.ClientId}."); DiveController.ServerBeginDive(); } private static void OnClientDiveStarted(DiveStartedBroadcast msg, Channel channel) { if (!InstanceFinder.IsServerStarted) { Klaxon.Play(((Object)(object)BoatManager.Boat != (Object)null) ? BoatManager.Boat.VisualBoat : null); } } } internal static class Klaxon { private const int SampleRate = 44100; private const float ToneSeconds = 0.45f; private static readonly float[] Tones = new float[2] { 620f, 440f }; private static AudioClip _clip; private static AudioSource _source; public static void Play(Transform parent) { if (!ModConfig.AlarmEnabled.Value) { return; } try { EnsureSource(parent); if (!((Object)(object)_source == (Object)null)) { _source.volume = ModConfig.AlarmVolume.Value; if (!_source.isPlaying) { _source.Play(); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not start alarm: " + ex.Message)); } } public static void Stop() { if ((Object)(object)_source != (Object)null && _source.isPlaying) { _source.Stop(); } } private static void EnsureSource(Transform parent) { //IL_003e: 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)_clip == (Object)null) { _clip = Generate(); } if ((Object)(object)_source == (Object)null || (Object)(object)((Component)_source).gameObject == (Object)null) { _source = new GameObject("BlirUBoat_Alarm").AddComponent(); _source.clip = _clip; _source.loop = true; _source.playOnAwake = false; _source.spatialBlend = 1f; _source.rolloffMode = (AudioRolloffMode)1; _source.minDistance = 4f; _source.maxDistance = 90f; _source.outputAudioMixerGroup = ResolveFxMixerGroup(); } if ((Object)(object)parent != (Object)null) { ((Component)_source).transform.SetParent(parent, false); ((Component)_source).transform.localPosition = Vector3.zero; } } private static AudioMixerGroup ResolveFxMixerGroup() { object obj = AccessTools.Field(typeof(AudioManager), "_instance")?.GetValue(null); if (obj == null) { Plugin.Log.LogWarning((object)"AudioManager instance not found; alarm will bypass the FX mixer."); return null; } object? obj2 = AccessTools.Field(typeof(AudioManager), "_fxGroup")?.GetValue(obj); return (AudioMixerGroup)((obj2 is AudioMixerGroup) ? obj2 : null); } private static AudioClip Generate() { int num = Mathf.RoundToInt(19845f); float[] array = new float[num * Tones.Length]; for (int i = 0; i < Tones.Length; i++) { float num2 = Tones[i]; for (int j = 0; j < num; j++) { float num3 = (float)j / 44100f; float num4 = MathF.PI * 2f * num2 * num3; float num5 = Mathf.Sin(num4) + 0.45f * Mathf.Sin(2f * num4) + 0.22f * Mathf.Sin(3f * num4); num5 /= 1.67f; array[i * num + j] = num5 * Envelope((float)j / (float)num) * 0.8f; } } AudioClip obj = AudioClip.Create("BlirUBoat_Klaxon", array.Length, 1, 44100, false); obj.SetData(array, 0); return obj; } private static float Envelope(float t) { if (t < 0.03f) { return t / 0.03f; } if (t > 0.9f) { return (1f - t) / 0.1f; } return 1f; } } internal static class ModConfig { public static ConfigEntry DiveDepth; public static ConfigEntry DescentSeconds; public static ConfigEntry SubmergedSecondsMin; public static ConfigEntry SubmergedSecondsMax; public static ConfigEntry AscentSeconds; public static ConfigEntry CooldownSeconds; public static ConfigEntry AlarmEnabled; public static ConfigEntry AlarmVolume; public static ConfigEntry PropScale; public static ConfigEntry PlankWidthOverride; public static ConfigEntry PlankInset; public static ConfigEntry PlankLength; public static ConfigEntry OffsetForward; public static ConfigEntry OffsetSide; public static ConfigEntry PlankHeightFraction; public static ConfigEntry PackYaw; public static ConfigEntry HoverTextSide; public static ConfigEntry HoverTextUp; public static ConfigEntry ShaderName; public static ConfigEntry DiagnosticsKey; public static void Bind(ConfigFile cfg) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Expected O, but got Unknown //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Expected O, but got Unknown //IL_02f4: Unknown result type (might be due to invalid IL or missing references) DiveDepth = cfg.Bind("Dive", "Depth", 4f, "How far below the water surface the boat settles, in metres."); DescentSeconds = cfg.Bind("Dive", "DescentSeconds", 7f, new ConfigDescription("How long the boat takes to go under.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 30f), Array.Empty())); SubmergedSecondsMin = cfg.Bind("Dive", "SubmergedSecondsMin", 15f, "Minimum time held underwater before surfacing."); SubmergedSecondsMax = cfg.Bind("Dive", "SubmergedSecondsMax", 25f, "Maximum time held underwater before surfacing."); AscentSeconds = cfg.Bind("Dive", "AscentSeconds", 4f, new ConfigDescription("How long the boat takes to come back up.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 30f), Array.Empty())); CooldownSeconds = cfg.Bind("Dive", "CooldownSeconds", 10f, "Delay after surfacing before the button can be used again."); AlarmEnabled = cfg.Bind("Audio", "AlarmEnabled", true, "Play the alarm while the boat is going under."); AlarmVolume = cfg.Bind("Audio", "AlarmVolume", 0.55f, new ConfigDescription("Alarm loudness. Still scaled by the game's own FX volume slider.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); PropScale = cfg.Bind("Props", "Scale", 1f, "Size multiplier for the cigarette pack and button."); PlankWidthOverride = cfg.Bind("Props", "PlankWidthOverride", 0f, "Fixed plank width across the boat, in metres. Leave at 0 to measure the hull and fit the plank automatically — the hull tapers, so a fixed width punches through the sides near the bow."); PlankInset = cfg.Bind("Props", "PlankInset", -0.22f, new ConfigDescription("How far short of the measured hull each end of the plank stops, in metres. Small values fit snugly; negative values push the ends into the hull sides.", (AcceptableValueBase)(object)new AcceptableValueRange(-0.5f, 0.5f), Array.Empty())); PlankLength = cfg.Bind("Props", "PlankDepth", 0.5f, "Plank depth along the boat, in metres."); OffsetForward = cfg.Bind("Props", "OffsetForward", -1f, "Distance from the very tip of the bow, in metres. Negative moves inboard."); OffsetSide = cfg.Bind("Props", "OffsetSide", 0f, "Sideways offset across the boat, in metres."); PackYaw = cfg.Bind("Props", "PackYaw", 204f, new ConfigDescription("Which way the cigarette pack faces, in degrees around the vertical axis.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 360f), Array.Empty())); HoverTextSide = cfg.Bind("Props", "HoverTextSide", 0.32f, "How far to the side the 'Röökille' prompt sits from the pack, in metres, so the label doesn't cover the model."); HoverTextUp = cfg.Bind("Props", "HoverTextUp", 0.06f, "How far above the plank the prompt sits, in metres."); PlankHeightFraction = cfg.Bind("Props", "HeightFraction", 0.5f, new ConfigDescription("How far up the hull the plank sits, as a fraction of the boat's height. 0 is the keel, 1 is the gunwale, 0.5 is halfway up.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1.2f), Array.Empty())); ShaderName = cfg.Bind("Props", "ShaderName", "Universal Render Pipeline/Lit", "Shader used for the props. URP/Lit by default: it is the only candidate confirmed to expose _BaseColor. \"Shader Graphs/DefaultShader\" matches the game art style more closely but exposes no standard colour property. Never use the boat shader."); DiagnosticsKey = cfg.Bind("Diagnostics", "DumpKey", new KeyboardShortcut((KeyCode)290, Array.Empty()), "Writes a report of audio clips, item prefabs and boat geometry to the config folder."); } } [HarmonyPatch(typeof(Boat), "ApplyWaterForce")] internal static class BoatApplyWaterForcePatch { private static bool Prefix(Boat __instance) { return !DiveController.ServerTick(__instance); } } [HarmonyPatch(typeof(Boat), "OnStartClient")] internal static class BoatOnStartClientPatch { private static void Postfix(Boat __instance) { try { DiveNet.EnsureRegistered(asServer: false); BoatProps.Attach(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to set up boat props: {arg}"); } } } [HarmonyPatch(typeof(Boat), "OnStartServer")] internal static class BoatOnStartServerPatch { private static void Postfix() { DiveNet.EnsureRegistered(asServer: true); } } [HarmonyPatch(typeof(Boat), "OnStopClient")] internal static class BoatOnStopClientPatch { private static void Postfix() { DiveController.Abort("boat despawned"); DiveNet.Reset(); } } [HarmonyPatch(typeof(BoatManager), "TryMoveBoat")] internal static class BoatManagerTryMoveBoatPatch { private static void Prefix() { DiveController.Abort("boat relocated"); } } [HarmonyPatch(typeof(BoatManager), "RemoveBoat")] internal static class BoatManagerRemoveBoatPatch { private static void Prefix() { DiveController.Abort("boat removed"); } } [HarmonyPatch(typeof(PlayerMovement), "UnderWaterCheck")] internal static class PlayerMovementUnderWaterCheckPatch { private static readonly FieldRef TimeWentUnderWater = AccessTools.FieldRefAccess("_timeWentUnderWater"); private static void Prefix(PlayerMovement __instance) { if (!((Object)(object)__instance == (Object)null) && __instance.OnBoat && BoatIsSubmerged()) { TimeWentUnderWater.Invoke(__instance) = Time.time; } } private static bool BoatIsSubmerged() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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: Unknown result type (might be due to invalid IL or missing references) Boat boat = BoatManager.Boat; if ((Object)(object)boat == (Object)null) { return false; } Vector3 val = BoatVisualPosition(boat); return val.y < WaterManager.GetWaterHeight(val) - 0.15f; } private static Vector3 BoatVisualPosition(Boat boat) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)boat.VisualPhysicsRig != (Object)null) { return boat.VisualPhysicsRig.position; } if ((Object)(object)boat.VisualBoat != (Object)null) { return boat.VisualBoat.position; } return ((Component)boat).transform.position; } } internal class PlankFitter : MonoBehaviour { private const float MaxReach = 3f; private const float GiveUpAfterSeconds = 5f; private float _elapsed; private bool _done; private void FixedUpdate() { if (!_done) { _elapsed += Time.fixedDeltaTime; if (TryFit()) { _done = true; Object.Destroy((Object)(object)this); } else if (_elapsed >= 5f) { Plugin.Log.LogInfo((object)"Could not probe the hull for the plank's shape; leaving it rectangular. Set Props.PlankWidthOverride if it doesn't fit."); _done = true; Object.Destroy((Object)(object)this); } } } private bool TryFit() { float value = ModConfig.PlankLength.Value; float value2 = ModConfig.PlankInset.Value; if (!TryMeasureAt(value * 0.45f, out var width)) { return false; } if (!TryMeasureAt((0f - value) * 0.45f, out var width2)) { return false; } float num = Mathf.Max(0.15f, width - value2 * 2f); float num2 = Mathf.Max(0.15f, width2 - value2 * 2f); BoatProps.ShapePlank(((Component)this).gameObject, num2, num, value); Plugin.Log.LogInfo((object)($"Plank fitted to hull: {num2:F2}m aft, {num:F2}m fore " + $"(hull spans {width2:F2}m / {width:F2}m).")); return true; } private bool TryMeasureAt(float alongBoat, out float width) { //IL_000d: 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: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0048: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) width = 0f; Vector3 origin = ((Component)this).transform.position + ((Component)this).transform.forward * alongBoat + ((Component)this).transform.up * (BoatProps.PlankThicknessMetres * 0.5f); if (!TryCast(origin, ((Component)this).transform.right, out var distance)) { return false; } if (!TryCast(origin, -((Component)this).transform.right, out var distance2)) { return false; } width = distance + distance2; return width > 0.05f; } private bool TryCast(Vector3 origin, Vector3 direction, out float distance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) distance = 0f; RaycastHit[] array = Physics.RaycastAll(origin, direction, 3f, -1, (QueryTriggerInteraction)1); if (array.Length == 0) { return false; } Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); RaycastHit[] array2 = array; for (int num = 0; num < array2.Length; num++) { RaycastHit val = array2[num]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && BoatManager.ColToBoat.ContainsKey(((RaycastHit)(ref val)).collider)) { distance = ((RaycastHit)(ref val)).distance; return true; } } return false; } } [BepInPlugin("Stendari.BlirUBoat", "BlirUBoat", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "Stendari.BlirUBoat"; public const string PluginName = "BlirUBoat"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("Stendari.BlirUBoat"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"BlirUBoat 1.0.0 loaded. Press the button."); } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = ModConfig.DiagnosticsKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Diagnostics.Dump(); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class VanillaAssets { private static Item[] _items; private static Item[] Items => _items ?? (_items = Resources.LoadAll("Items")); private static Item FindItem(string nameFragment) { return ((IEnumerable)Items).FirstOrDefault((Func)((Item i) => (Object)(object)i != (Object)null && ((Object)i).name.IndexOf(nameFragment, StringComparison.OrdinalIgnoreCase) >= 0)); } public static Transform FindLighter() { Item val = FindItem("Dynamite"); if ((Object)(object)val == (Object)null) { return null; } Explosive val2 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInChildren(true); if ((Object)(object)val2 == (Object)null) { return null; } object? obj = AccessTools.Field(typeof(Explosive), "_lighter")?.GetValue(val2); return (Transform)((obj is Transform) ? obj : null); } public static GameObject CloneVisual(Transform source, Transform parent, string name) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00af: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_00db: 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_00f2: 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) if ((Object)(object)source == (Object)null) { return null; } MeshRenderer[] array = ((Component)source).GetComponentsInChildren(true).Where(delegate(MeshRenderer r) { if ((Object)(object)r != (Object)null) { MeshFilter component = ((Component)r).GetComponent(); return (Object)(object)((component != null) ? component.sharedMesh : null) != (Object)null; } return false; }).ToArray(); if (array.Length == 0) { Plugin.Log.LogWarning((object)("'" + ((Object)source).name + "' has no mesh renderers to clone.")); return null; } GameObject val = new GameObject(name); val.transform.SetParent(parent, false); MeshRenderer[] array2 = array; foreach (MeshRenderer val2 in array2) { GameObject val3 = new GameObject(((Object)val2).name); val3.transform.SetParent(val.transform, false); val3.transform.localPosition = source.InverseTransformPoint(((Component)val2).transform.position); val3.transform.localRotation = Quaternion.Inverse(source.rotation) * ((Component)val2).transform.rotation; val3.transform.localScale = ((Component)val2).transform.lossyScale; val3.AddComponent().sharedMesh = ((Component)val2).GetComponent().sharedMesh; ((Renderer)val3.AddComponent()).sharedMaterials = ((Renderer)val2).sharedMaterials; } Plugin.Log.LogInfo((object)$"Cloned visual from '{((Object)source).name}' ({array.Length} renderer(s))."); return val; } } }