using Unity.Mathematics; namespace ProjectM.Simulation { /// /// Pure, deterministic Husk AI math — no RNG, no wall-clock — so server simulation stays reproducible and /// the helpers are EditMode-unit-testable without an ECS world (mirrors / /// StatMath). /// public static class EnemyAIMath { /// /// Planar (XZ) seek velocity from toward at /// . Y is forced to 0 (top-down plane). Returns zero once within /// (so the Husk halts at strike range instead of jittering through the /// target) or when the two points coincide. /// public static float3 SeekVelocity(float3 from, float3 to, float speed, float stopDistance) { float3 d = to - from; d.y = 0f; float distSq = math.lengthsq(d); if (distSq <= stopDistance * stopDistance || distSq < 1e-8f) return float3.zero; return math.normalize(d) * speed; } /// /// True when is within of on the /// XZ plane. /// public static bool InAttackRange(float3 from, float3 to, float range) { float3 d = to - from; d.y = 0f; return math.lengthsq(d) <= range * range; } /// /// Projects a planar movement onto a wall plane defined by /// (collide-and-slide): removes the component of that pushes into the surface so the /// mover glances along the wall instead of stopping dead. Both inputs are flattened to the XZ plane (top-down). /// Returns unchanged when the normal is degenerate. /// public static float3 SlideVelocity(float3 vel, float3 surfaceNormal) { surfaceNormal.y = 0f; float len = math.length(surfaceNormal); if (len < 1e-6f) return vel; float3 n = surfaceNormal / len; float3 slid = vel - math.dot(vel, n) * n; slid.y = 0f; return slid; } /// /// Deterministic planar ring position around for spawn /// : evenly spaced over angles at /// . Stable per index so a replayed spawn lands identically. /// public static float3 RingPosition(float3 center, int index, int slots, float radius) { if (slots < 1) slots = 1; int slot = ((index % slots) + slots) % slots; float angle = (2f * math.PI * slot) / slots; math.sincos(angle, out float s, out float c); return center + new float3(c * radius, 0f, s * radius); } } }