Lock On: observe "beyond its sight" in the sim's own sensing, pin the hostile's facing, stage off axis, read the lock's Strength
The Update(0) checks could not fail and the 40..200 tick window was tuning. The test now asks the hostile's PassiveInfo for the player's signature against the detection threshold after the first real tick, asserts the hostile has no track then, pins the facing, and asserts the Track paint's Strength rises. The hostile is staged at (1500, 3700) facing the player. The unsound comment about the player's sight is corrected. Builds: not yet verified (verify job follows). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
05b17ba43a
commit
038628e914
2 changed files with 31 additions and 16 deletions
|
|
@ -10,10 +10,11 @@ public sealed class LockOn : Scenario
|
|||
public override uint Seed => 113;
|
||||
public override bool Ambient => false;
|
||||
|
||||
// The hostile starts far beyond the player's sight and the hostile's own, 4000 m ahead facing the player. Stepping the
|
||||
// arena headlessly, it closes to about 3000 m, sees the player after some eight seconds, and locks two ticks later;
|
||||
// at Duel's 900 m (and at 600 m) it sees and locks within a second.
|
||||
public static readonly CultMath.float2 HostileAt = float2(0, 4000);
|
||||
// The hostile starts about 4000 m off, to the side of the player's axis, facing the player. At that range its passive
|
||||
// sensors hold no track of the player (the test asks the sim's own PassiveInfo against the detection threshold); the
|
||||
// player sees the hostile a little sooner, at about tick 20. The hostile closes, sees the player some eight seconds in,
|
||||
// and locks two ticks later. At Duel's 900 m (and at 600 m) it sees and locks within a second.
|
||||
public static readonly CultMath.float2 HostileAt = float2(1500, 3700);
|
||||
|
||||
public override Galaxy Generate(GalaxyStage stage) => stage.Prelude(DemoTerminus.Cast);
|
||||
|
||||
|
|
@ -38,6 +39,6 @@ public sealed class LockOn : Scenario
|
|||
("Store-All Plus", int2(2, 8), None));
|
||||
|
||||
stage.Player(fighter, float2(0, 0), facing: float2(0, 1));
|
||||
stage.Place(fighter, HostileAt, facing: float2(0, -1), stance: ScenarioStance.Hostile, piloted: true);
|
||||
stage.Place(fighter, HostileAt, facing: normalize(-HostileAt), stance: ScenarioStance.Hostile, piloted: true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using static CultMath.math;
|
||||
|
|
@ -6,9 +7,9 @@ using static CultMath.math;
|
|||
// test launches the scenario the way New Game does and steps what it staged.
|
||||
public sealed partial class RunStartTests
|
||||
{
|
||||
// Lock On: the hostile starts piloted, beyond its sight of the player, and the player has no incoming paint. Stepped at
|
||||
// .1 s, it closes, finds the player and locks: a Track paint reaches the player bearing toward the hostile, and not at
|
||||
// once, because the hostile had to find the player first. The tick is the lock's, at seed 113 it is 83 (8.4 s).
|
||||
// Lock On: the hostile starts piloted, facing the player, beyond its own sight of the player, and the player has no
|
||||
// incoming paint. Stepped at .1 s, it closes, finds the player and locks: a Track paint reaches the player bearing
|
||||
// toward the hostile, and its Strength rises from nothing as the lock tightens.
|
||||
[Fact]
|
||||
public void LockOnSituation()
|
||||
{
|
||||
|
|
@ -19,25 +20,38 @@ public sealed partial class RunStartTests
|
|||
Assert.True(player.IsHostileTo(hostile) && hostile.IsHostileTo(player), "the hostile is hostile both ways");
|
||||
Assert.Equal(1, AgentsOf(arena, hostile));
|
||||
|
||||
arena.Update(0f);
|
||||
Assert.DoesNotContain(hostile, player.VisibleEnemies);
|
||||
var toPlayer = normalize(player.Position.xz - hostile.Position.xz);
|
||||
Assert.True(dot(normalize(hostile.Direction), toPlayer) >= cos(radians(5f)), "the hostile faces the player");
|
||||
|
||||
// The first real sensing tick: the hostile holds no track of the player, and the sim's own passive sensing, asked for
|
||||
// the player's signature at the staged place, settles below the detection threshold: the staging is out of its sight.
|
||||
arena.Update(.1f);
|
||||
Assert.DoesNotContain(player, hostile.VisibleEnemies);
|
||||
Assert.DoesNotContain(player.IncomingPaints, paint => paint.Mode == PaintMode.Track);
|
||||
Assert.DoesNotContain(player, hostile.VisibleEnemies); // it starts without a track of the player
|
||||
Assert.True(player.Visibility > 0f, "the player shows a signature to be seen by");
|
||||
var threshold = player.ItemManager.GameplaySettings.TargetDetectionInfoThreshold;
|
||||
var settled = hostile.PassiveInfo(player.Position.xz, player.Visibility);
|
||||
Assert.True(settled <= threshold, $"the hostile would settle at {settled} on the player, above the detection threshold {threshold}: staged inside its sight");
|
||||
|
||||
var lockedAt = -1;
|
||||
var bearingDot = 0f;
|
||||
for (var tick = 0; tick < 600 && lockedAt < 0; tick++)
|
||||
var strengths = new List<float>();
|
||||
for (var tick = 0; tick < 600 && (lockedAt < 0 || tick < lockedAt + 40); tick++)
|
||||
{
|
||||
arena.Update(.1f);
|
||||
foreach (var paint in player.IncomingPaints.Where(paint => paint.Mode == PaintMode.Track))
|
||||
{
|
||||
lockedAt = tick;
|
||||
bearingDot = dot(normalize(paint.Bearing), normalize(hostile.Position.xz - player.Position.xz));
|
||||
if (lockedAt < 0)
|
||||
{
|
||||
lockedAt = tick;
|
||||
bearingDot = dot(normalize(paint.Bearing), toPlayer * -1f);
|
||||
}
|
||||
strengths.Add(paint.Strength);
|
||||
}
|
||||
}
|
||||
Assert.True(lockedAt >= 0, "the hostile never locked the player within 600 ticks of .1 s");
|
||||
Assert.True(bearingDot >= cos(radians(5f)), "the warning points at the hostile");
|
||||
// Not at the staging: the hostile had to close and find the player first (a hostile staged inside sight locks within 10 ticks).
|
||||
Assert.InRange(lockedAt, 40, 200);
|
||||
Assert.True(strengths.First() < strengths.Last(), $"the lock tightens: Strength went {strengths.First()} to {strengths.Last()}");
|
||||
Assert.True(strengths.Zip(strengths.Skip(1), (a, b) => b >= a).All(rising => rising), "the lock never loosens while it holds");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue