3409c53148
Melee combo (left-click / pad-West) becomes the player's primary verb; the ranged projectile is demoted to right-click / pad-left-trigger. Predicted, owner-replicated combo Step (path-dependent -> [GhostField] anchor + absolute-write idempotency, NOT derived like the dash), server-only cleave mirroring ProjectileDamageSystem (SourceTick-stamped DamageEvent + KnockbackState), dash-cancellable movement-commit, 9 live TuningConfig knobs, and swing juice scaling with the combo step. The MC-6 archetype byte is deferred (the melee is its own verb). See DR-030. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
40 lines
2.0 KiB
C#
40 lines
2.0 KiB
C#
using Unity.Mathematics;
|
|
|
|
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Pure, deterministic cone hit-test for the MC-4 melee cleave. Shares the EXACT range + bearing predicate with
|
|
/// <see cref="AutoTarget"/> (planar XZ distance gate + <c>dot(bearing, facing) >= cos(halfAngle)</c>) but as a
|
|
/// per-candidate boolean, so the cleave can collect ALL enemies in the cone — <see cref="AutoTarget.Resolve"/> is a
|
|
/// single-winner reducer and cannot be reused for collect-all (MC-4 review REUSE-1). Burst-safe, allocation-free,
|
|
/// no wall-clock / no randomness; intended to run server-side inside the predicted <c>MeleeComboSystem</c>.
|
|
/// </summary>
|
|
public static class MeleeConeMath
|
|
{
|
|
/// <summary>
|
|
/// True iff <paramref name="targetPos"/> lies within <paramref name="range"/> (planar XZ) of
|
|
/// <paramref name="from"/> AND its bearing from <paramref name="from"/> is within the cone half-angle of
|
|
/// <paramref name="facingDir"/> (<paramref name="cosHalfAngle"/> = cos(halfAngle)). A coincident
|
|
/// (zero-distance) target is excluded (undefined bearing). <paramref name="facingDir"/> must be
|
|
/// caller-normalized; a zero-length facing or a non-positive range returns false.
|
|
/// </summary>
|
|
public static bool InCone(float3 from, float2 facingDir, float range, float cosHalfAngle, float3 targetPos)
|
|
{
|
|
if (range <= 0f || math.lengthsq(facingDir) < 1e-6f)
|
|
return false;
|
|
|
|
float3 offset = targetPos - from;
|
|
float2 planar = new float2(offset.x, offset.z);
|
|
float distSq = math.lengthsq(planar);
|
|
|
|
if (distSq < 1e-6f)
|
|
return false; // coincident -> undefined bearing
|
|
if (distSq > range * range)
|
|
return false; // out of range
|
|
|
|
float2 bearing = planar * math.rsqrt(distSq); // normalized planar bearing
|
|
return math.dot(bearing, facingDir) >= cosHalfAngle;
|
|
}
|
|
}
|
|
}
|