d77649fe16
23 plain-Entities tests: cone predicate (range/bearing/coincident/planar), combo advance/chain-window/lockout/reset/idempotency, movement-commit + SwingStartTick rollback lower-bound, dash precedence (active window + same-tick tie + recovery tail), server cleave (cone select, finisher scaling, knockback, dead-exclusion, client-no-damage), comboLen=2 finisher, two-player co-op cleave, death-clears. 294/294 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
2.5 KiB
C#
69 lines
2.5 KiB
C#
using NUnit.Framework;
|
|
using ProjectM.Simulation;
|
|
using Unity.Mathematics;
|
|
|
|
namespace ProjectM.Tests
|
|
{
|
|
/// <summary>
|
|
/// MC-4 — pure cone hit-test (<see cref="MeleeConeMath.InCone"/>): the planar XZ range gate + the
|
|
/// dot(bearing, facing) >= cos(halfAngle) cone gate, plus the coincident / zero-facing / non-positive-range
|
|
/// guards. Shares the exact predicate AutoTarget uses, but as a per-candidate boolean for the collect-all cleave.
|
|
/// </summary>
|
|
public class MeleeConeMathTests
|
|
{
|
|
const float Cos60 = 0.5f; // cos(60deg): half-angle 60 -> 120deg full cone
|
|
|
|
[Test]
|
|
public void In_Range_And_Straight_Ahead_Hits()
|
|
{
|
|
Assert.IsTrue(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 3f, Cos60, new float3(0, 0, 2)));
|
|
}
|
|
|
|
[Test]
|
|
public void Out_Of_Range_Misses()
|
|
{
|
|
Assert.IsFalse(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 3f, Cos60, new float3(0, 0, 4)));
|
|
}
|
|
|
|
[Test]
|
|
public void At_90_Degrees_Outside_Cone_Misses()
|
|
{
|
|
// bearing +X vs facing +Z: dot = 0 < cos60 (0.5) -> outside the cone.
|
|
Assert.IsFalse(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 3f, Cos60, new float3(2, 0, 0)));
|
|
}
|
|
|
|
[Test]
|
|
public void At_45_Degrees_Inside_Cone_Hits()
|
|
{
|
|
// bearing ~45deg: dot ~0.707 > 0.5 -> inside.
|
|
Assert.IsTrue(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 5f, Cos60, new float3(2, 0, 2)));
|
|
}
|
|
|
|
[Test]
|
|
public void Behind_The_Player_Misses()
|
|
{
|
|
Assert.IsFalse(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 5f, Cos60, new float3(0, 0, -2)));
|
|
}
|
|
|
|
[Test]
|
|
public void Coincident_Target_Excluded()
|
|
{
|
|
Assert.IsFalse(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 3f, Cos60, float3.zero));
|
|
}
|
|
|
|
[Test]
|
|
public void Zero_Facing_Or_NonPositive_Range_Misses()
|
|
{
|
|
Assert.IsFalse(MeleeConeMath.InCone(float3.zero, float2.zero, 3f, Cos60, new float3(0, 0, 2)), "zero facing -> miss");
|
|
Assert.IsFalse(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 0f, Cos60, new float3(0, 0, 1)), "range 0 -> miss");
|
|
}
|
|
|
|
[Test]
|
|
public void Uses_Planar_XZ_And_Ignores_Y()
|
|
{
|
|
// straight ahead in XZ but far in Y: still a hit (Y ignored, top-down).
|
|
Assert.IsTrue(MeleeConeMath.InCone(float3.zero, new float2(0, 1), 3f, Cos60, new float3(0, 5, 2)));
|
|
}
|
|
}
|
|
}
|