using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; namespace LLib.Fishing; public static class WaterSurfaceDetector { public static bool IsFishable(Vector3 position, float rotation) { Vector3? hitPoint; return CheckFishableAtRotation(position, rotation, out hitPoint); } public static (float Rotation, Vector3 HitPoint)? FindFishableRotation(Vector3 position, int searchSteps = 36) { float num = (float)Math.PI * 2f / (float)searchSteps; List list = new List(); for (int i = 0; i < searchSteps; i++) { float rotation = (float)i * num; if (CheckFishableAtRotation(position, rotation, out var _)) { list.Add(i); } } if (list.Count == 0) { return null; } List list2 = (from g in FindContiguousGroups(list, searchSteps) orderby g.Count descending select g).First(); float num2 = ((float)list2[0] + (float)(list2.Count - 1) / 2f) % (float)searchSteps * num; if (CheckFishableAtRotation(position, num2, out var hitPoint2) && hitPoint2.HasValue) { return (num2, hitPoint2.Value); } return null; } private unsafe static bool CheckFishableAtRotation(Vector3 position, float rotation, out Vector3? hitPoint) { hitPoint = null; Framework* ptr = Framework.Instance(); if (ptr == null || ptr->BGCollisionModule == null) { return false; } BGCollisionModule* bGCollisionModule = ptr->BGCollisionModule; float num = MathF.Cos(rotation); float num2 = MathF.Sin(rotation); Matrix4x4 matrix = new Matrix4x4 { M11 = num, M13 = 0f - num2, M22 = 1f, M31 = num2, M33 = num }; Vector3 vector = Vector3.Transform(new Vector3(0f, 0f, 2f), matrix) + position; vector.Y += 2f; Vector3 vector2 = new Vector3(position.X, position.Y + 0.87f, position.Z); Vector3 vector3 = Vector3.Normalize(vector - vector2); float maxDistance = Vector3.Distance(vector2, vector); byte* intPtr = stackalloc byte[16]; ReadOnlySpan readOnlySpan = new int[4] { 16384, 0, 16384, 0 }; // IL cpblk instruction Unsafe.CopyBlockUnaligned(intPtr, ref readOnlySpan[0], 16); int* flags = (int*)intPtr; RaycastHit raycastHit = default(RaycastHit); if (bGCollisionModule->RaycastMaterialFilter(&raycastHit, &vector2, &vector3, maxDistance, 1, flags)) { return false; } Vector3 vector4 = Vector3.Transform(new Vector3(0f, -80f, 40f), matrix); float num3 = vector4.Length(); Vector3 vector5 = vector4 / num3; byte* intPtr2 = stackalloc byte[16]; readOnlySpan = new int[4] { 32768, 0, 32768, 0 }; // IL cpblk instruction Unsafe.CopyBlockUnaligned(intPtr2, ref readOnlySpan[0], 16); int* flags2 = (int*)intPtr2; RaycastHit raycastHit2 = default(RaycastHit); if (bGCollisionModule->RaycastMaterialFilter(&raycastHit2, &vector, &vector5, num3, 1, flags2)) { hitPoint = raycastHit2.Point; return true; } return false; } private static List> FindContiguousGroups(List angles, int maxSteps) { if (angles.Count == 0) { return new List>(); } List list = angles.OrderBy((int a) => a).ToList(); List> list2 = new List>(); List list3 = new List { list[0] }; for (int num = 1; num < list.Count; num++) { if (list[num] - list[num - 1] == 1) { list3.Add(list[num]); continue; } list2.Add(list3); int num2 = 1; List list4 = new List(num2); CollectionsMarshal.SetCount(list4, num2); CollectionsMarshal.AsSpan(list4)[0] = list[num]; list3 = list4; } list2.Add(list3); if (list2.Count > 1 && list[0] == 0) { if (list[list.Count - 1] == maxSteps - 1) { List item = list2[list2.Count - 1].Concat(list2[0]).ToList(); list2.RemoveAt(list2.Count - 1); list2.RemoveAt(0); list2.Add(item); } } return list2; } }