Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CrowsNest v0.1.6
BepInEx/plugins/CrowsNest/CrowsNest.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace CrowsNest; internal static class Artwork { private sealed class Part { internal string Name; internal int Kind; internal Color Color; internal Mesh Mesh; } private static readonly List<Part> parts = new List<Part>(); internal static void Load() { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown using (Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("CrowsNest.patched.cnb")) { using BinaryReader binaryReader = new BinaryReader(stream); if (Encoding.ASCII.GetString(binaryReader.ReadBytes(4)) != "CNB1") { throw new InvalidDataException("Unknown mesh format"); } int num = binaryReader.ReadInt32(); if (num < 1 || num > 100) { throw new InvalidDataException("Invalid mesh count"); } for (int i = 0; i < num; i++) { int num2 = binaryReader.ReadInt32(); if (num2 < 1 || num2 > 256) { throw new InvalidDataException("Invalid name"); } Part part = new Part(); part.Name = Encoding.UTF8.GetString(binaryReader.ReadBytes(num2)); part.Kind = binaryReader.ReadInt32(); part.Color = new Color(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); Part part2 = part; int num3 = binaryReader.ReadInt32(); if (num3 < 3 || num3 > 65000) { throw new InvalidDataException("Invalid vertex count"); } Vector3[] array = (Vector3[])(object)new Vector3[num3]; Vector3[] array2 = (Vector3[])(object)new Vector3[num3]; Vector2[] array3 = (Vector2[])(object)new Vector2[num3]; for (int j = 0; j < num3; j++) { ref Vector3 reference = ref array[j]; reference = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); ref Vector3 reference2 = ref array2[j]; reference2 = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); ref Vector2 reference3 = ref array3[j]; reference3 = new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle()); } int num4 = binaryReader.ReadInt32(); if (num4 < 3 || num4 > 200000 || num4 % 3 != 0) { throw new InvalidDataException("Invalid triangle count"); } int[] array4 = new int[num4]; for (int k = 0; k < num4; k++) { array4[k] = binaryReader.ReadInt32(); if (array4[k] < 0 || array4[k] >= num3) { throw new InvalidDataException("Invalid triangle index"); } } Mesh val = new Mesh(); ((Object)val).name = "CN_" + part2.Name; part2.Mesh = val; part2.Mesh.vertices = array; part2.Mesh.normals = array2; part2.Mesh.uv = array3; part2.Mesh.triangles = array4; part2.Mesh.RecalculateBounds(); parts.Add(part2); } if (stream.Position != stream.Length) { throw new InvalidDataException("Unexpected mesh data"); } } Plugin.Log.LogInfo((object)("Loaded approved C geometry: " + parts.Count + " parts.")); } internal static void Create(Transform root, Ship ship, List<Object> resources) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown //IL_015b: 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_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) if ((int)SystemInfo.graphicsDeviceType == 4) { return; } Material val = null; GameObject val2 = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab("fermenter") : null); if (Object.op_Implicit((Object)(object)val2)) { MeshRenderer[] componentsInChildren = val2.GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val3 in componentsInChildren) { Material[] sharedMaterials = ((Renderer)val3).sharedMaterials; foreach (Material val4 in sharedMaterials) { if (Object.op_Implicit((Object)(object)val4) && ((Object)val4).name.StartsWith("fermenter", StringComparison.OrdinalIgnoreCase)) { val = val4; break; } } } } if (!Object.op_Implicit((Object)(object)val)) { throw new InvalidOperationException("Native fermenter material not available; keeping vanilla mast interaction."); } foreach (Part part in parts) { GameObject val5 = new GameObject(part.Name); val5.transform.SetParent(root, false); val5.AddComponent<MeshFilter>().sharedMesh = part.Mesh; MeshRenderer val6 = val5.AddComponent<MeshRenderer>(); if (part.Kind == 1) { ((Renderer)val6).sharedMaterial = val; continue; } Material val7 = new Material(val); ((Object)val7).name = "CN_" + part.Name; Texture2D val8 = new Texture2D(2, 2, (TextureFormat)4, false); ((Object)val8).name = "CN_solid"; val8.SetPixels((Color[])(object)new Color[4] { part.Color, part.Color, part.Color, part.Color }); val8.Apply(); val7.mainTexture = (Texture)(object)val8; if (val7.HasProperty("_Color")) { val7.SetColor("_Color", Color.white); } if (val7.HasProperty("_BumpMap")) { val7.SetTexture("_BumpMap", (Texture)null); } if (val7.HasProperty("_MetallicTex")) { val7.SetTexture("_MetallicTex", (Texture)null); } ((Renderer)val6).sharedMaterial = val7; resources.Add((Object)(object)val7); resources.Add((Object)(object)val8); } } } [DefaultExecutionOrder(9990)] public sealed class ClimbAnimation : MonoBehaviour { private const string ClipPath = "Assets/Characters/Player/model/old_PlayerCharacter/PlayerCharacter_01_3@Climbing Up Wall.fbx"; internal Nest Owner; private static AnimationClip clip; private static readonly int state = Animator.StringToHash("Base Layer.HoldMast"); private Player player; private Animator animator; private RuntimeAnimatorController original; private AnimatorOverrideController temporary; private bool rootMotion; private bool failed; private Transform hips; private Vector3 visualPosition; private float hipHeight; private bool offsetApplied; private void Awake() { player = ((Component)this).GetComponent<Player>(); } private static AnimationClip LoadClip() { if (Object.op_Implicit((Object)(object)clip)) { return clip; } try { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if (allLoadedAssetBundle.Contains("Assets/Characters/Player/model/old_PlayerCharacter/PlayerCharacter_01_3@Climbing Up Wall.fbx")) { clip = allLoadedAssetBundle.LoadAsset<AnimationClip>("Assets/Characters/Player/model/old_PlayerCharacter/PlayerCharacter_01_3@Climbing Up Wall.fbx"); if (Object.op_Implicit((Object)(object)clip)) { break; } } } if (!Object.op_Implicit((Object)(object)clip) || clip.legacy || !clip.humanMotion) { Plugin.Log.LogWarning((object)"Native climbing asset is missing or not humanoid."); clip = null; } else { Plugin.Log.LogInfo((object)("Loaded native climb: " + ((Object)clip).name + " (" + clip.length + " seconds).")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cannot load native climbing animation: " + ex.Message)); } return clip; } internal bool Prepare() { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_018b: 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) if (Object.op_Implicit((Object)(object)temporary)) { return true; } if (failed) { return false; } try { animator = ((Component)this).GetComponentInChildren<Animator>(); AnimationClip val = LoadClip(); if (!Object.op_Implicit((Object)(object)animator) || !animator.isHuman || !Object.op_Implicit((Object)(object)animator.runtimeAnimatorController) || !Object.op_Implicit((Object)(object)val) || !animator.HasState(0, state)) { throw new InvalidOperationException("Climb clip/current humanoid mast state unavailable"); } original = animator.runtimeAnimatorController; temporary = new AnimatorOverrideController(original); List<KeyValuePair<AnimationClip, AnimationClip>> list = new List<KeyValuePair<AnimationClip, AnimationClip>>(temporary.overridesCount); temporary.GetOverrides(list); bool flag = false; for (int i = 0; i < list.Count; i++) { if (Object.op_Implicit((Object)(object)list[i].Key) && ((Object)list[i].Key).name == "Hold The Mast") { list[i] = new KeyValuePair<AnimationClip, AnimationClip>(list[i].Key, val); flag = true; } } if (!flag) { throw new InvalidOperationException("Native Hold The Mast clip not found in player controller"); } temporary.ApplyOverrides((IList<KeyValuePair<AnimationClip, AnimationClip>>)list); rootMotion = animator.applyRootMotion; hips = animator.GetBoneTransform((HumanBodyBones)0); visualPosition = ((Component)animator).transform.localPosition; hipHeight = ((Component)player).transform.InverseTransformPoint(hips.position).y; return true; } catch (Exception ex) { failed = true; Stop(); Plugin.Log.LogWarning((object)("Climb animation unavailable: " + ex.Message)); return false; } } private void Update() { RestoreOffset(); if (!Object.op_Implicit((Object)(object)Owner) || !Owner.ClimbState(player, out var down, out var elapsed)) { Stop(); } else { if (!Prepare()) { return; } if ((Object)(object)animator.runtimeAnimatorController != (Object)(object)temporary) { if ((Object)(object)animator.runtimeAnimatorController != (Object)(object)original) { Stop(); failed = true; return; } animator.applyRootMotion = false; animator.runtimeAnimatorController = (RuntimeAnimatorController)(object)temporary; } float num = Mathf.Repeat(elapsed / Mathf.Max(0.1f, clip.length), 1f); animator.Play(state, 0, down ? (1f - num) : num); } } private void LateUpdate() { //IL_0051: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //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) if (Object.op_Implicit((Object)(object)animator) && Object.op_Implicit((Object)(object)temporary) && !((Object)(object)animator.runtimeAnimatorController != (Object)(object)temporary) && Object.op_Implicit((Object)(object)hips)) { float y = ((Component)player).transform.InverseTransformPoint(hips.position).y; Transform transform = ((Component)animator).transform; transform.position += ((Component)player).transform.up * (hipHeight - y); offsetApplied = true; } } private void RestoreOffset() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (offsetApplied && Object.op_Implicit((Object)(object)animator)) { ((Component)animator).transform.localPosition = visualPosition; } offsetApplied = false; } internal void Stop() { RestoreOffset(); if (Object.op_Implicit((Object)(object)animator) && Object.op_Implicit((Object)(object)temporary) && (Object)(object)animator.runtimeAnimatorController == (Object)(object)temporary) { bool flag = animator.GetBool("attach_mast"); animator.runtimeAnimatorController = original; animator.applyRootMotion = rootMotion; if (flag && ((Behaviour)animator).isActiveAndEnabled) { animator.SetBool("attach_mast", true); animator.Play(state, 0, 0f); animator.Update(0f); } } if (Object.op_Implicit((Object)(object)temporary)) { Object.Destroy((Object)(object)temporary); } temporary = null; original = null; } internal static void ReleaseClip() { clip = null; } private void OnDisable() { Stop(); } private void OnDestroy() { Stop(); } } internal static class ClimbRules { internal static float Progress(float elapsed, float duration) { if (duration <= 0f) { return 1f; } return Math.Max(0f, Math.Min(1f, elapsed / duration)); } internal static float RouteFraction(float progress, bool down) { if (!down) { return progress; } return 1f - progress; } internal static bool InCorridor(float x, float y, float z, float deckY) { if (Math.Abs(x) < 3f && z > -3f && z < 4f && y > deckY - 2f) { return y < 3f; } return false; } } internal static class LeaseRules { internal static readonly long Duration = TimeSpan.FromSeconds(10.0).Ticks; internal static readonly long ClockTolerance = TimeSpan.FromSeconds(5.0).Ticks; internal static bool Live(long deadline, long now) { if (deadline > now) { return deadline - now <= Duration + ClockTolerance; } return false; } internal static long Renew(long now) { return now + Duration; } } internal static class Native { private static readonly FieldInfo detach = AccessTools.Field(typeof(Player), "m_detachOffset"); private static readonly FieldInfo altitude = AccessTools.Field(typeof(Character), "m_maxAirAltitude"); private static readonly FieldInfo lastTouch = AccessTools.Field(typeof(Character), "m_lastGroundTouch"); private static readonly FieldInfo lastCollider = AccessTools.Field(typeof(Character), "m_lastGroundCollider"); private static readonly FieldInfo lastBody = AccessTools.Field(typeof(Character), "m_lastGroundBody"); private static readonly FieldInfo fallTimer = AccessTools.Field(typeof(Character), "m_fallTimer"); private static readonly FieldInfo animationSync = AccessTools.Field(typeof(Character), "m_zanim"); private static readonly FieldInfo syncAnimator = AccessTools.Field(typeof(ZSyncAnimation), "m_animator"); private static readonly Action<Character> resetGround = (Action<Character>)Delegate.CreateDelegate(typeof(Action<Character>), AccessTools.Method(typeof(Character), "ResetGroundContact", (Type[])null, (Type[])null)); private static Player transferring; private static float transferUntil; internal static void Validate() { if (detach == null || detach.FieldType != typeof(Vector3) || altitude == null || altitude.FieldType != typeof(float) || lastTouch == null || lastCollider == null || lastBody == null || fallTimer == null || animationSync == null || syncAnimator == null) { throw new MissingFieldException("Player attachment API changed"); } } internal static void SetExit(Player p, Vector3 offset) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) detach.SetValue(p, offset); } internal static void BeginTransfer(Player p) { transferring = p; transferUntil = Time.time + 0.5f; ClearGround(p); } internal static bool ProtectFall(Player p) { if (Object.op_Implicit((Object)(object)p)) { return TransferRules.Protect((Object)(object)p == (Object)(object)Player.m_localPlayer, (Object)(object)Nest.LocalRider(p) != (Object)null, (Object)(object)p == (Object)(object)transferring, Time.time, transferUntil); } return false; } internal static void ClearGround(Player p) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) resetGround((Character)(object)p); lastTouch.SetValue(p, 1f); lastCollider.SetValue(p, null); lastBody.SetValue(p, null); fallTimer.SetValue(p, 0f); altitude.SetValue(p, ((Component)p).transform.position.y); } internal static void SyncAttached(Player p) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0068: Unknown result type (might be due to invalid IL or missing references) Transform attachPoint = p.GetAttachPoint(); if (!Object.op_Implicit((Object)(object)attachPoint)) { return; } Rigidbody component = ((Component)p).GetComponent<Rigidbody>(); Rigidbody componentInParent = ((Component)attachPoint).GetComponentInParent<Rigidbody>(); if (Object.op_Implicit((Object)(object)component)) { component.position = attachPoint.position; component.rotation = attachPoint.rotation; if (!component.isKinematic) { component.linearVelocity = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.GetPointVelocity(attachPoint.position) : Vector3.zero); component.angularVelocity = Vector3.zero; } } ClearGround(p); } internal static void ResetFall(Player p, Vector3 velocity) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) BeginTransfer(p); Rigidbody component = ((Component)p).GetComponent<Rigidbody>(); if (Object.op_Implicit((Object)(object)component)) { component.position = ((Component)p).transform.position; if (!component.isKinematic) { component.linearVelocity = velocity; component.angularVelocity = Vector3.zero; } } ((Character)p).ResetCloth(); } internal static void ReleaseMastAnimation(Player p) { if (!Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)Player.m_localPlayer || ((Character)p).IsAttached()) { return; } object? value = animationSync.GetValue(p); ZSyncAnimation val = (ZSyncAnimation)((value is ZSyncAnimation) ? value : null); if (Object.op_Implicit((Object)(object)val)) { object? value2 = syncAnimator.GetValue(val); Animator val2 = (Animator)((value2 is Animator) ? value2 : null); if (Object.op_Implicit((Object)(object)val2)) { val2.SetBool("attach_mast", true); val.SetBool("attach_mast", false); Plugin.Log.LogInfo((object)"Released local and network mast animation."); } } } } public sealed class Nest : MonoBehaviour { private const string UserKey = "gomisan.cn.user"; private const string TokenKey = "gomisan.cn.token"; private const string LeaseKey = "gomisan.cn.lease"; private const string Request = "CN_Request"; private const string Reply = "CN_Reply"; private const string Pulse = "CN_Pulse"; private const string Release = "CN_Release"; private const string ClimbKey = "gomisan.cn.climb"; private const string ClimbTimeKey = "gomisan.cn.climbTime"; private static readonly Vector3 Stand = new Vector3(0f, 0.06f, 0.67f); private static Nest active; private static Nest pending; private static int nextToken = Environment.TickCount; private readonly List<Object> resources = new List<Object>(); private Ship ship; private ZNetView view; private Chair mast; private GameObject root; private Transform attach; private Vector3 deckPoint; private BoxCollider onboard; private Vector3 originalSize; private Vector3 originalCenter; private bool ready; private bool registered; private int token; private ZDOID localId; private float requestedAt; private float lastPulse; private float attachedAt; private Vector3 returnLocal; private float lastPoseCheck; private Vector3[] route; private float routeLength; private float travelStarted; private float travelDuration; private int travel; private Quaternion deckRotation; private readonly List<Collider> ignored = new List<Collider>(); private Collider riderCollider; private string detachReason; private static long Now { get { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return 0L; } return ZNet.instance.GetTime().Ticks; } } private bool Valid { get { if (ready && Object.op_Implicit((Object)(object)view)) { return view.IsValid(); } return false; } } private bool Occupied { get { //IL_0018: 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) if (Valid && view.GetZDO().GetZDOID("gomisan.cn.user") != ZDOID.None) { return LeaseRules.Live(view.GetZDO().GetLong("gomisan.cn.lease", 0L), Now); } return false; } } internal Vector3 RimForward => root.transform.forward; internal Vector3 RimUp => root.transform.up; internal static bool Supported(Ship s) { ZNetView component = ((Component)s).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { return false; } int prefab = component.GetZDO().GetPrefab(); if (prefab != StringExtensionMethods.GetStableHashCode("VikingShip")) { return prefab == StringExtensionMethods.GetStableHashCode("VikingShip_Ashlands"); } return true; } internal static Nest ForMast(Chair chair) { Nest componentInParent = ((Component)chair).GetComponentInParent<Nest>(); if (!Object.op_Implicit((Object)(object)componentInParent) || !componentInParent.ready || !((Object)(object)componentInParent.mast == (Object)(object)chair)) { return null; } return componentInParent; } internal static Nest LocalRider(Player player) { if (!Object.op_Implicit((Object)(object)player) || !((Object)(object)player == (Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)active) || !((Object)(object)player.GetAttachPoint() == (Object)(object)active.attach)) { return null; } return active; } internal static void LeaveLocal() { Player localPlayer = Player.m_localPlayer; Nest nest = LocalRider(localPlayer); if (Object.op_Implicit((Object)(object)nest)) { nest.Detach(localPlayer, "logout/plugin shutdown"); } if (Object.op_Implicit((Object)(object)pending)) { pending.ReleaseLocal(pending.token); pending = null; } } internal void Initialize(Ship target) { //IL_00ba: 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_00bf: 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_00dc: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: 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_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0234: 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) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_02af: 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_02c0: 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_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) ship = target; view = ((Component)target).GetComponent<ZNetView>(); Chair[] componentsInChildren = ((Component)target).GetComponentsInChildren<Chair>(true); foreach (Chair val in componentsInChildren) { if (val.m_attachAnimation == "attach_mast" && ((Component)val).gameObject.activeInHierarchy) { mast = val; break; } } if (!Object.op_Implicit((Object)(object)mast)) { throw new InvalidOperationException("No native mast holdfast was found."); } bool flag = view.GetZDO().GetPrefab() == StringExtensionMethods.GetStableHashCode("VikingShip_Ashlands"); Vector3 localPosition = (flag ? new Vector3(0f, 15.75f, 0.03f) : new Vector3(0f, 9.35f, 0.4f)); deckPoint = ((Component)ship).transform.InverseTransformPoint(mast.m_attachPoint.position); root = new GameObject("CN_PatchedNest"); root.transform.SetParent(((Component)ship).transform, false); root.transform.localPosition = localPosition; Artwork.Create(root.transform, ship, resources); attach = new GameObject("LookoutAttach").transform; attach.SetParent(root.transform, false); attach.localPosition = Stand; attach.localRotation = Quaternion.identity; Rigidbody component = ((Component)ship).GetComponent<Rigidbody>(); Vector3 centerOfMass = component.centerOfMass; Vector3 inertiaTensor = component.inertiaTensor; Quaternion inertiaTensorRotation = component.inertiaTensorRotation; int num = LayerMask.NameToLayer("piece"); root.layer = ((num >= 0) ? num : ((Component)ship).gameObject.layer); BoxCollider val2 = root.AddComponent<BoxCollider>(); val2.center = new Vector3(0f, 0.55f, 0f); val2.size = new Vector3(2.12f, 1.12f, 2.12f); root.AddComponent<NestInteraction>().Owner = this; component.centerOfMass = centerOfMass; component.inertiaTensor = inertiaTensor; component.inertiaTensorRotation = inertiaTensorRotation; Transform val3 = ((Component)ship).transform.Find("OnboardTrigger"); if (!Object.op_Implicit((Object)(object)val3)) { throw new InvalidOperationException("No onboard trigger found."); } onboard = ((Component)val3).GetComponent<BoxCollider>(); if (!Object.op_Implicit((Object)(object)onboard) || !((Collider)onboard).isTrigger) { throw new InvalidOperationException("Unexpected onboard trigger layout."); } originalSize = onboard.size; originalCenter = onboard.center; float num2 = originalCenter.y - originalSize.y * 0.5f; float y = ((Component)onboard).transform.InverseTransformPoint(root.transform.TransformPoint(new Vector3(0f, 2.2f, 0f))).y; Vector3 size = originalSize; size.y = Mathf.Max(originalSize.y, y - num2); Vector3 center = originalCenter; center.y = num2 + size.y * 0.5f; onboard.size = size; onboard.center = center; view.Register<ZDOID, int>("CN_Request", (Action<long, ZDOID, int>)OnRequest); view.Register<int, bool>("CN_Reply", (Action<long, int, bool>)OnReply); view.Register<ZDOID, int>("CN_Pulse", (Action<long, ZDOID, int>)OnPulse); view.Register<ZDOID, int>("CN_Release", (Action<long, ZDOID, int>)OnRelease); registered = true; ready = true; Plugin.Log.LogInfo((object)("Added patched crow's nest to " + (flag ? "Drakkar" : "Longship") + " " + view.GetZDO().m_uid)); } private bool Match(ZDOID id, int n) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (view.GetZDO().GetZDOID("gomisan.cn.user") == id) { return view.GetZDO().GetInt("gomisan.cn.token", 0) == n; } return false; } private Player Authenticate(long sender, ZDOID id) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNetScene.instance)) { return null; } GameObject val = ZNetScene.instance.FindInstance(id); if (!Object.op_Implicit((Object)(object)val)) { return null; } ZNetView component = val.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || component.GetZDO().GetOwner() != sender) { return null; } return val.GetComponent<Player>(); } private bool NearDeck(Player p, float distance) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)p) && !((Character)p).IsDead() && !((Character)p).IsTeleporting()) { return Vector3.Distance(((Component)p).transform.position, ((Component)ship).transform.TransformPoint(deckPoint)) <= distance; } return false; } private bool CanEnter(Player p) { if (Valid && Object.op_Implicit((Object)(object)p) && (Object)(object)p == (Object)(object)Player.m_localPlayer && !((Character)p).IsAttached() && !((Character)p).IsEncumbered() && p.GetDoodadController() == null && NearDeck(p, 3f)) { return (Object)(object)((Character)p).GetStandingOnShip() == (Object)(object)ship; } return false; } internal string Hover(bool top) { if (!ready) { return ""; } Player localPlayer = Player.m_localPlayer; if (top) { if (!((Object)(object)LocalRider(localPlayer) == (Object)(object)this)) { return "Crow's nest"; } if (travel == 0) { return Localization.instance.Localize("Crow's nest\n[<color=yellow><b>$KEY_Use</b></color>] Climb down"); } return "Crow's nest\nClimbing..."; } if ((Object)(object)pending == (Object)(object)this) { return "Crow's nest\nWaiting for ship..."; } if (Occupied) { return "Crow's nest\n<color=#aaaaaa>Occupied</color>"; } return Localization.instance.Localize("Crow's nest\n[<color=yellow><b>$KEY_Use</b></color>] Climb to crow's nest"); } internal void Enter(Player p) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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) //IL_00bb: 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_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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) if (!CanEnter(p) || Object.op_Implicit((Object)(object)active) || Object.op_Implicit((Object)(object)pending)) { return; } if (Occupied) { Say(p, "Crow's nest is occupied."); return; } localId = ((Component)p).GetComponent<ZNetView>().GetZDO().m_uid; token = ++nextToken; if (token == 0) { token = ++nextToken; } returnLocal = ((Component)ship).transform.InverseTransformPoint(((Component)p).transform.position) + Vector3.up * 0.1f; deckRotation = Quaternion.Inverse(root.transform.rotation) * ((Component)p).transform.rotation; requestedAt = Time.time; pending = this; view.InvokeRPC("CN_Request", new object[2] { localId, token }); } private void OnRequest(long sender, ZDOID id, int n) { //IL_0018: 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_0041: Unknown result type (might be due to invalid IL or missing references) if (Valid && view.IsOwner()) { Player val = Authenticate(sender, id); bool flag = n != 0 && NearDeck(val, 4f) && !((Character)val).IsAttached() && (!Occupied || Match(id, n)); if (flag) { ZDO zDO = view.GetZDO(); zDO.Set("gomisan.cn.user", id); zDO.Set("gomisan.cn.token", n); zDO.Set("gomisan.cn.lease", LeaseRules.Renew(Now)); } view.InvokeRPC(sender, "CN_Reply", new object[2] { n, flag }); } } private void OnReply(long sender, int n, bool allowed) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (!Valid || sender != view.GetZDO().GetOwner()) { return; } if ((Object)(object)pending != (Object)(object)this || n != token) { if (allowed) { ReleaseLocal(n); } return; } pending = null; Player localPlayer = Player.m_localPlayer; if (!allowed) { Say(localPlayer, "Crow's nest is occupied or unavailable."); return; } if (!CanEnter(localPlayer)) { ReleaseLocal(n); return; } ClimbAnimation climbAnimation = ((Component)localPlayer).GetComponent<ClimbAnimation>(); if (!Object.op_Implicit((Object)(object)climbAnimation)) { climbAnimation = ((Component)localPlayer).gameObject.AddComponent<ClimbAnimation>(); } climbAnimation.Owner = this; if (!climbAnimation.Prepare()) { ReleaseLocal(n); Say(localPlayer, "Climbing animation unavailable; see BepInEx log."); return; } active = this; attachedAt = (lastPulse = Time.time); try { BuildRoute(); StartTravel(localPlayer, 1); Native.BeginTransfer(localPlayer); ((Character)localPlayer).AttachStart(attach, root, true, false, true, "attach_mast", attach.InverseTransformPoint(((Component)ship).transform.TransformPoint(returnLocal)), (Transform)null); if ((Object)(object)localPlayer.GetAttachPoint() != (Object)(object)attach) { StopTravel(localPlayer); active = null; ReleaseLocal(n); } else { IgnoreShipSolids(localPlayer); EnsurePose(localPlayer); Say(localPlayer, "Crow's nest: look down at the basket and Use to climb down."); Plugin.Log.LogInfo((object)("Lookout attached: " + localId)); } } catch { if (Object.op_Implicit((Object)(object)localPlayer) && (Object)(object)localPlayer.GetAttachPoint() == (Object)(object)attach) { ((Character)localPlayer).AttachStop(); } StopTravel(localPlayer); RestoreShipSolids(); active = null; ReleaseLocal(n); throw; } } private void OnPulse(long sender, ZDOID id, int n) { //IL_0016: 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) if (Valid && view.IsOwner() && Match(id, n)) { Player val = Authenticate(sender, id); if (Object.op_Implicit((Object)(object)val) && !((Character)val).IsDead() && InClimbCorridor(val)) { view.GetZDO().Set("gomisan.cn.lease", LeaseRules.Renew(Now)); } } } private void OnRelease(long sender, ZDOID id, int n) { //IL_0016: 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_0040: Unknown result type (might be due to invalid IL or missing references) if (Valid && view.IsOwner() && Match(id, n) && Object.op_Implicit((Object)(object)Authenticate(sender, id))) { view.GetZDO().Set("gomisan.cn.user", ZDOID.None); view.GetZDO().Set("gomisan.cn.lease", 0L); view.GetZDO().Set("gomisan.cn.token", 0); } } private void ReleaseLocal(int n) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)view) && view.IsValid() && localId != ZDOID.None) { view.InvokeRPC("CN_Release", new object[2] { localId, n }); } } private void Update() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_022b: 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) if (Valid && Time.time - lastPoseCheck > 0.5f) { lastPoseCheck = Time.time; if (Occupied && Object.op_Implicit((Object)(object)ZNetScene.instance)) { GameObject val = ZNetScene.instance.FindInstance(view.GetZDO().GetZDOID("gomisan.cn.user")); if (Object.op_Implicit((Object)(object)val)) { EnsurePose(val.GetComponent<Player>()); } } } if ((Object)(object)pending == (Object)(object)this && Time.time - requestedAt > 5f) { ReleaseLocal(token); pending = null; Say(Player.m_localPlayer, "No reply from ship owner. The peer controlling this ship needs Crow's Nest."); } if ((Object)(object)active != (Object)(object)this) { return; } Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer)) { RestoreShipSolids(); ReleaseLocal(token); active = null; return; } if ((Object)(object)localPlayer.GetAttachPoint() != (Object)(object)attach) { StopTravel(localPlayer); ReleasePose(localPlayer); RestoreShipSolids(); ReleaseLocal(token); active = null; return; } if (!Valid || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting()) { Detach(localPlayer, (!Valid) ? "ship unavailable" : (((Character)localPlayer).IsDead() ? "player dead" : "teleport")); return; } if (Time.time - lastPulse >= 2f) { lastPulse = Time.time; view.InvokeRPC("CN_Pulse", new object[2] { localId, token }); } if (Time.time - attachedAt > 4f && (!Occupied || !Match(localId, token))) { ZDO zDO = view.GetZDO(); Plugin.Log.LogWarning((object)string.Concat("Reservation invalid: owner=", zDO.GetOwner(), " user=", zDO.GetZDOID("gomisan.cn.user"), " token=", zDO.GetInt("gomisan.cn.token", 0), " remaining=", ((double)(zDO.GetLong("gomisan.cn.lease", 0L) - Now) / 10000000.0).ToString("F3"), "s pulseAge=", (Time.time - lastPulse).ToString("F3"), "s")); Detach(localPlayer, "reservation invalid"); Say(localPlayer, "Lookout reservation ended; returned to deck."); } } private void EnsurePose(Player p) { if (Object.op_Implicit((Object)(object)p) && IsOccupant(p)) { RimPose rimPose = ((Component)p).GetComponent<RimPose>(); if (!Object.op_Implicit((Object)(object)rimPose)) { rimPose = ((Component)p).gameObject.AddComponent<RimPose>(); } rimPose.Owner = this; ClimbAnimation climbAnimation = ((Component)p).GetComponent<ClimbAnimation>(); if (!Object.op_Implicit((Object)(object)climbAnimation)) { climbAnimation = ((Component)p).gameObject.AddComponent<ClimbAnimation>(); } climbAnimation.Owner = this; } } internal bool CanPose(Player p) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!IsOccupant(p) || Vector3.Distance(((Component)p).transform.position, root.transform.TransformPoint(Stand)) > 0.65f) { return false; } ZNetView component = ((Component)p).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { return component.GetZDO().GetInt("gomisan.cn.climb", 0) == 0; } return false; } private bool IsOccupant(Player p) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (!Valid || !Object.op_Implicit((Object)(object)p) || ((Character)p).IsDead() || !Object.op_Implicit((Object)(object)attach)) { return false; } if ((Object)(object)p == (Object)(object)Player.m_localPlayer) { return (Object)(object)LocalRider(p) == (Object)(object)this; } ZNetView component = ((Component)p).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && Occupied) { return view.GetZDO().GetZDOID("gomisan.cn.user") == component.GetZDO().m_uid; } return false; } internal Vector3 RimTarget(bool left) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) return root.transform.TransformPoint(new Vector3(left ? (-0.32f) : 0.32f, 1.085f, 0.9575f)); } internal Vector3 ElbowHint(bool left) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) return root.transform.TransformPoint(new Vector3(left ? (-0.55f) : 0.55f, 1.12f, 0.32f)); } internal void PrepareDescent(Player p) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (detachReason == null) { Plugin.Log.LogWarning((object)("External nest detach: " + new StackTrace(2, fNeedFileInfo: false))); } else { Plugin.Log.LogInfo((object)("Nest detach reason: " + detachReason)); } if (Object.op_Implicit((Object)(object)attach) && Object.op_Implicit((Object)(object)ship)) { Native.SetExit(p, attach.InverseTransformPoint(((Component)ship).transform.TransformPoint(returnLocal))); } } internal void FinishDescent(Player p) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)p.GetAttachPoint() == (Object)(object)attach)) { StopTravel(p); ReleasePose(p); RestoreShipSolids(); attach.localPosition = Stand; attach.localRotation = Quaternion.identity; Rigidbody val = (Object.op_Implicit((Object)(object)ship) ? ((Component)ship).GetComponent<Rigidbody>() : null); Native.ResetFall(p, Object.op_Implicit((Object)(object)val) ? val.GetPointVelocity(((Component)p).transform.position) : Vector3.zero); ReleaseLocal(token); if ((Object)(object)active == (Object)(object)this) { active = null; } detachReason = null; Plugin.Log.LogInfo((object)("Lookout returned to deck: " + localId)); } } private bool InClimbCorridor(Player p) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) Vector3 val = root.transform.InverseTransformPoint(((Component)p).transform.position); float y = root.transform.InverseTransformPoint(((Component)ship).transform.TransformPoint(deckPoint)).y; return ClimbRules.InCorridor(val.x, val.y, val.z, y); } internal bool ClimbState(Player p, out bool down, out float elapsed) { down = false; elapsed = 0f; if (!IsOccupant(p)) { return false; } ZNetView component = ((Component)p).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { return false; } int num = component.GetZDO().GetInt("gomisan.cn.climb", 0); if (num != 1 && num != 2) { return false; } down = num == 2; elapsed = Mathf.Max(0f, (float)((double)(Now - component.GetZDO().GetLong("gomisan.cn.climbTime", Now)) / 10000000.0)); return true; } private void BuildRoute() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) //IL_0079: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = root.transform.InverseTransformPoint(((Component)ship).transform.TransformPoint(returnLocal)); route = (Vector3[])(object)new Vector3[7] { val, new Vector3(0f, val.y, 0.4f), new Vector3(0f, -3f, 0.4f), new Vector3(0f, -2.75f, 1.48f), new Vector3(0f, 1.25f, 1.48f), new Vector3(0f, 1.25f, Stand.z), Stand }; routeLength = 0f; for (int i = 1; i < route.Length; i++) { routeLength += Vector3.Distance(route[i - 1], route[i]); } travelDuration = Mathf.Max(2f, routeLength / 1.55f); } private void StartTravel(Player p, int direction) { travel = direction; travelStarted = Time.time; ZDO zDO = ((Component)p).GetComponent<ZNetView>().GetZDO(); zDO.Set("gomisan.cn.climbTime", Now); zDO.Set("gomisan.cn.climb", direction); PositionOnRoute((direction != 1) ? 1 : 0); Plugin.Log.LogInfo((object)("Lookout climbing " + ((direction == 1) ? "up" : "down") + ": " + travelDuration.ToString("F1") + " seconds.")); } internal void Advance(Player p) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (travel == 0 || route == null) { return; } float num = ClimbRules.Progress(Time.time - travelStarted, travelDuration); PositionOnRoute(ClimbRules.RouteFraction(num, travel == 2)); if (!(num < 1f)) { bool flag = travel == 2; StopTravel(p); if (flag) { Detach(p, "completed climb down"); return; } attach.localPosition = Stand; attach.localRotation = Quaternion.identity; Plugin.Log.LogInfo((object)"Lookout reached basket."); } } private void PositionOnRoute(float fraction) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_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_00fd: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01(fraction) * routeLength; for (int i = 1; i < route.Length; i++) { float num2 = Vector3.Distance(route[i - 1], route[i]); if (num <= num2 || i == route.Length - 1) { float num3 = ((num2 > 0.001f) ? Mathf.Clamp01(num / num2) : 1f); attach.localPosition = Vector3.Lerp(route[i - 1], route[i], num3); Quaternion val = Quaternion.Euler(0f, 180f, 0f); attach.localRotation = ((i == 1) ? Quaternion.Slerp(deckRotation, val, num3) : ((i >= route.Length - 2) ? Quaternion.Slerp(val, Quaternion.identity, ((float)(i - (route.Length - 2)) + num3) * 0.5f) : val)); break; } num -= num2; } } internal void RequestDescent(Player p) { if (!((Object)(object)LocalRider(p) != (Object)(object)this) && travel == 0) { ClimbAnimation component = ((Component)p).GetComponent<ClimbAnimation>(); if (!Object.op_Implicit((Object)(object)component) || !component.Prepare()) { Say(p, "Climb animation unavailable; returning safely to deck."); Detach(p, "descent animation unavailable"); } else { StartTravel(p, 2); } } } private void StopTravel(Player p) { travel = 0; if (Object.op_Implicit((Object)(object)p)) { ZNetView component = ((Component)p).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { component.GetZDO().Set("gomisan.cn.climb", 0); } ClimbAnimation component2 = ((Component)p).GetComponent<ClimbAnimation>(); if (Object.op_Implicit((Object)(object)component2)) { component2.Stop(); } } } private static void ReleasePose(Player p) { RimPose component = ((Component)p).GetComponent<RimPose>(); if (Object.op_Implicit((Object)(object)component)) { component.Release(); } Native.ReleaseMastAnimation(p); } private void Detach(Player p, string reason) { detachReason = reason; ((Character)p).AttachStop(); } private void IgnoreShipSolids(Player p) { riderCollider = (Collider)(object)((Component)p).GetComponent<CapsuleCollider>(); if (!Object.op_Implicit((Object)(object)riderCollider)) { return; } Collider[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Collider>(); foreach (Collider val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val) && !val.isTrigger && (Object)(object)val != (Object)(object)riderCollider && !Physics.GetIgnoreCollision(riderCollider, val)) { Physics.IgnoreCollision(riderCollider, val, true); ignored.Add(val); } } } private void RestoreShipSolids() { if (Object.op_Implicit((Object)(object)riderCollider)) { foreach (Collider item in ignored) { if (Object.op_Implicit((Object)(object)item)) { Physics.IgnoreCollision(riderCollider, item, false); } } } ignored.Clear(); riderCollider = null; } private static void Say(Player p, string text) { if (Object.op_Implicit((Object)(object)p)) { ((Character)p).Message((MessageType)2, text, 0, (Sprite)null, false); } } private void OnDestroy() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)active == (Object)(object)this && Object.op_Implicit((Object)(object)localPlayer) && (Object)(object)localPlayer.GetAttachPoint() == (Object)(object)attach) { Detach(localPlayer, "ship object destroyed/unloaded"); } if ((Object)(object)pending == (Object)(object)this) { ReleaseLocal(token); pending = null; } if ((Object)(object)active == (Object)(object)this) { active = null; } if (Object.op_Implicit((Object)(object)onboard)) { onboard.size = originalSize; onboard.center = originalCenter; } if (registered && Object.op_Implicit((Object)(object)view)) { view.Unregister("CN_Request"); view.Unregister("CN_Reply"); view.Unregister("CN_Pulse"); view.Unregister("CN_Release"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Crow's nest cleanup: " + ex.Message)); } RestoreShipSolids(); if (Object.op_Implicit((Object)(object)root)) { Object.Destroy((Object)(object)root); } foreach (Object resource in resources) { if (Object.op_Implicit(resource)) { Object.Destroy(resource); } } } } public sealed class NestInteraction : MonoBehaviour, Hoverable, Interactable { internal Nest Owner; public string GetHoverText() { if (!Object.op_Implicit((Object)(object)Owner)) { return ""; } return Owner.Hover(top: true); } public string GetHoverName() { return "Crow's nest"; } public float GetHoverOffset() { return 0f; } public bool Interact(Humanoid user, bool hold, bool alt) { Player val = (Player)(object)((user is Player) ? user : null); if (!hold && Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)Owner) && (Object)(object)Nest.LocalRider(val) == (Object)(object)Owner) { Owner.RequestDescent(val); } return false; } public bool UseItem(Humanoid user, ItemData item) { return false; } } [BepInPlugin("gomisan.CrowsNest", "Crow's Nest", "0.1.6")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "gomisan.CrowsNest"; public const string Version = "0.1.6"; internal static ManualLogSource Log; private Harmony harmony; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; try { Native.Validate(); Artwork.Load(); harmony = new Harmony("gomisan.CrowsNest"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Crow's Nest 0.1.6 ready: patched barrel / Longship + Drakkar."); } catch (Exception ex) { if (harmony != null) { harmony.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Crow's Nest disabled during initialization: " + ex)); ((Behaviour)this).enabled = false; } } private void OnDestroy() { Nest.LeaveLocal(); ClimbAnimation.ReleaseClip(); if (harmony != null) { harmony.UnpatchSelf(); } } } [HarmonyPatch(typeof(Ship), "Start")] internal static class ShipStartPatch { private static void Postfix(Ship __instance) { if (!Nest.Supported(__instance) || Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<Nest>())) { return; } try { ((Component)__instance).gameObject.AddComponent<Nest>().Initialize(__instance); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not add crow's nest to " + ((Object)__instance).name + ": " + ex)); Nest component = ((Component)__instance).GetComponent<Nest>(); if (Object.op_Implicit((Object)(object)component)) { Object.Destroy((Object)(object)component); } } } } [HarmonyPatch(typeof(Chair), "GetHoverText")] internal static class MastHoverPatch { private static bool Prefix(Chair __instance, ref string __result) { Nest nest = Nest.ForMast(__instance); if (!Object.op_Implicit((Object)(object)nest)) { return true; } __result = nest.Hover(top: false); return false; } } [HarmonyPatch(typeof(Chair), "Interact")] internal static class MastInteractPatch { private static bool Prefix(Chair __instance, Humanoid __0, bool __1, ref bool __result) { Nest nest = Nest.ForMast(__instance); if (!Object.op_Implicit((Object)(object)nest)) { return true; } if (!__1) { nest.Enter((Player)(object)((__0 is Player) ? __0 : null)); } __result = false; return false; } } [HarmonyPatch(typeof(Player), "AttachStop")] internal static class DetachPatch { private static void Prefix(Player __instance, out Nest __state) { __state = Nest.LocalRider(__instance); if (Object.op_Implicit((Object)(object)__state)) { __state.PrepareDescent(__instance); } } private static void Postfix(Player __instance, Nest __state) { if (Object.op_Implicit((Object)(object)__state)) { __state.FinishDescent(__instance); } } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class LookoutControlsPatch { private static void Prefix(Player __instance, ref bool ___m_autoRun, ref Vector3 __0, ref bool __1, ref bool __2, ref bool __3, ref bool __4, ref bool __5, ref bool __6, ref bool __7, ref bool __8, ref bool __9, ref bool __10, ref bool __11) { //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) if (Object.op_Implicit((Object)(object)Nest.LocalRider(__instance))) { ___m_autoRun = false; __0 = Vector3.zero; __1 = (__2 = (__3 = (__4 = (__5 = (__6 = (__7 = (__8 = (__9 = (__10 = (__11 = false)))))))))); } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class DeathPatch { private static void Prefix(Player __instance) { if (Object.op_Implicit((Object)(object)Nest.LocalRider(__instance))) { Plugin.Log.LogError((object)"Unexpected death while attached to crow's nest. Inspect preceding damage diagnostics."); ((Character)__instance).AttachStop(); } } } [HarmonyPatch(typeof(Player), "UpdateAttach")] internal static class AttachmentPhysicsPatch { private static void Prefix(Player __instance) { Nest nest = Nest.LocalRider(__instance); if (Object.op_Implicit((Object)(object)nest)) { nest.Advance(__instance); } } private static void Postfix(Player __instance) { if (Object.op_Implicit((Object)(object)Nest.LocalRider(__instance))) { Native.SyncAttached(__instance); } } } [HarmonyPatch(typeof(Character), "AddRootMotion")] internal static class ClimbRootMotionPatch { private static bool Prefix(Character __instance) { return !Object.op_Implicit((Object)(object)Nest.LocalRider((Player)(object)((__instance is Player) ? __instance : null))); } } [HarmonyPatch(typeof(Character), "UpdateGroundContact")] internal static class AttachedGroundPatch { private static void Prefix(Character __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (Object.op_Implicit((Object)(object)Nest.LocalRider(val))) { Native.ClearGround(val); } } } [HarmonyPatch(typeof(Character), "Damage")] internal static class TransferDamagePatch { private static bool Prefix(Character __instance, HitData __0) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_005d: Unknown result type (might be due to invalid IL or missing references) Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!Object.op_Implicit((Object)(object)val) || __0 == null || !Native.ProtectFall(val)) { return true; } if ((int)__0.m_hitType == 3) { Plugin.Log.LogWarning((object)("Suppressed artificial fall damage during crow's nest attachment/transfer: " + __0.GetTotalDamage())); return false; } Plugin.Log.LogWarning((object)string.Concat("Damage during crow's nest attachment/transfer (not suppressed): ", __0.m_hitType, " amount=", __0.GetTotalDamage())); return true; } } [HarmonyPatch(typeof(Game), "Logout")] internal static class LogoutPatch { private static void Prefix() { Nest.LeaveLocal(); } } [HarmonyPatch(typeof(Game), "OnApplicationQuit")] internal static class QuitPatch { private static void Prefix() { Nest.LeaveLocal(); } } [DefaultExecutionOrder(10000)] public sealed class RimPose : MonoBehaviour { private sealed class Arm { internal Transform Upper; internal Transform Lower; internal Transform Hand; internal Transform Middle; internal Transform Index; internal Transform Pinky; internal Quaternion U; internal Quaternion L; internal Quaternion H; internal bool Saved; internal void Restore() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (Saved) { if (Object.op_Implicit((Object)(object)Upper)) { Upper.localRotation = U; } if (Object.op_Implicit((Object)(object)Lower)) { Lower.localRotation = L; } if (Object.op_Implicit((Object)(object)Hand)) { Hand.localRotation = H; } Saved = false; } } } internal Nest Owner; private Player player; private Arm left; private Arm right; private bool attempted; private bool reachReported; private void Awake() { player = ((Component)this).GetComponent<Player>(); } private Arm FindArm(string side) { Transform val = ((Component)this).transform.Find("Visual/Armature/Hips/Spine/Spine1/Spine2/" + side + "Shoulder/" + side + "Arm"); if (!Object.op_Implicit((Object)(object)val)) { return null; } Transform val2 = val.Find(side + "ForeArm"); if (!Object.op_Implicit((Object)(object)val2)) { return null; } Transform val3 = val2.Find(side + "Hand"); if (!Object.op_Implicit((Object)(object)val3)) { return null; } Arm arm = new Arm(); arm.Upper = val; arm.Lower = val2; arm.Hand = val3; arm.Middle = val3.Find(side + "HandMiddle1"); arm.Index = val3.Find(side + "HandIndex1"); arm.Pinky = val3.Find(side + "HandPinky1"); return arm; } private void Update() { Restore(); } private void Restore() { if (left != null) { left.Restore(); } if (right != null) { right.Restore(); } } internal void Release() { Restore(); Owner = null; } private void LateUpdate() { if (!Object.op_Implicit((Object)(object)Owner) || !Owner.CanPose(player)) { Restore(); return; } if (!attempted) { attempted = true; left = FindArm("Left"); right = FindArm("Right"); if (left == null || right == null) { Plugin.Log.LogWarning((object)"Crow's nest arm pose unavailable for this player rig."); } } Apply(left, isLeft: true); Apply(right, isLeft: false); } private void Apply(Arm a, bool isLeft) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_019e: 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_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: 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_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01de: 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_00ad: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00ca: 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_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_029d: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: 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_033c: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03d5: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: 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_02fd: 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_011a: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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_014a: 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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: 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_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: 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_0185: 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_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) if (a == null || !Object.op_Implicit((Object)(object)a.Upper) || !Object.op_Implicit((Object)(object)a.Lower) || !Object.op_Implicit((Object)(object)a.Hand)) { return; } a.U = a.Upper.localRotation; a.L = a.Lower.localRotation; a.H = a.Hand.localRotation; a.Saved = true; Quaternion rotation = a.Hand.rotation; Vector3 val = Vector3.zero; if (Object.op_Implicit((Object)(object)a.Middle) && Object.op_Implicit((Object)(object)a.Index) && Object.op_Implicit((Object)(object)a.Pinky)) { Vector3 val2 = a.Middle.position - a.Hand.position; Vector3 val3 = Vector3.Cross(val2, a.Index.position - a.Pinky.position) * (float)(isLeft ? 1 : (-1)); if (((Vector3)(ref val2)).sqrMagnitude > 1E-05f && ((Vector3)(ref val3)).sqrMagnitude > 1E-08f) { Quaternion val4 = Quaternion.LookRotation(Owner.RimForward - Owner.RimUp * 0.15f, Owner.RimUp) * Quaternion.Inverse(Quaternion.LookRotation(val2, val3)); val = val4 * (val2 * 0.5f - ((Vector3)(ref val3)).normalized * 0.025f); rotation = val4 * a.Hand.rotation; } } Vector3 position = a.Upper.position; Vector3 val5 = Owner.RimTarget(isLeft) - val; Vector3 val6 = val5 - position; float num = Vector3.Distance(position, a.Lower.position); float num2 = Vector3.Distance(a.Lower.position, a.Hand.position); if (!(num < 0.001f) && !(num2 < 0.001f) && !(((Vector3)(ref val6)).sqrMagnitude < 0.0001f)) { if (!reachReported && ((Vector3)(ref val6)).magnitude > num + num2 + 0.025f) { reachReported = true; Plugin.Log.LogWarning((object)("Rim contact beyond arm reach by " + (((Vector3)(ref val6)).magnitude - num - num2).ToString("F3") + "m; report the player pose screenshot.")); } float num3 = Mathf.Clamp(((Vector3)(ref val6)).magnitude, Mathf.Abs(num - num2) + 0.001f, num + num2 - 0.001f); ((Vector3)(ref val6)).Normalize(); val5 = position + val6 * num3; Vector3 val7 = Vector3.ProjectOnPlane(Owner.ElbowHint(isLeft) - position, val6); Vector3 normalized = ((Vector3)(ref val7)).normalized; if (((Vector3)(ref normalized)).sqrMagnitude < 0.1f) { Vector3 val8 = Vector3.ProjectOnPlane(-Owner.RimUp, val6); normalized = ((Vector3)(ref val8)).normalized; } float num4 = (num * num - num2 * num2 + num3 * num3) / (2f * num3); float num5 = Mathf.Sqrt(Mathf.Max(0f, num * num - num4 * num4)); Vector3 val9 = position + val6 * num4 + normalized * num5; a.Upper.rotation = Quaternion.FromToRotation(a.Lower.position - position, val9 - position) * a.Upper.rotation; a.Lower.rotation = Quaternion.FromToRotation(a.Hand.position - a.Lower.position, val5 - a.Lower.position) * a.Lower.rotation; a.Hand.rotation = rotation; } } private void OnDisable() { Restore(); } private void OnDestroy() { Restore(); } } internal static class TransferRules { internal static bool Protect(bool local, bool attached, bool sameTransferPlayer, float now, float until) { if (local) { if (!attached) { if (sameTransferPlayer) { return now < until; } return false; } return true; } return false; } }