From 2f15c79176e126607a2a99da9256fd7597abdeb3 Mon Sep 17 00:00:00 2001 From: soul Date: Thu, 22 Oct 2026 08:39:15 +0200 Subject: [PATCH] Seam fix tests: the stance press resolves its target at the step, flips the belief, and the ping press pings once (red) Co-Authored-By: Claude Opus 5.5 --- .../ContactRevealTests.cs | 17 ++++++++++ .../IffAndCombatTests.cs | 17 ++++++++++ .../PilotSeamPlayerTests.cs | 31 ++++++++++++++----- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/tests/Aetheria.Shared.Tests/ContactRevealTests.cs b/tests/Aetheria.Shared.Tests/ContactRevealTests.cs index d23f166ea..1a8df874c 100644 --- a/tests/Aetheria.Shared.Tests/ContactRevealTests.cs +++ b/tests/Aetheria.Shared.Tests/ContactRevealTests.cs @@ -587,6 +587,23 @@ public sealed partial class ContactRevealTests : IDisposable return (light.ToArray(), Track(o, p)); } + // The pilot's ping is a press on its command: the sensor pings at the next step, once, and the press is not replayed. + [Fact] + public void APingPressPingsOnceAtTheStep() + { + var p = Add(float3(0, 0, 1000), new[] { "Eye" }); + var sensor = p.Equipment.SelectMany(i => i.Behaviors).OfType().Single(); + var pings = 0; + sensor.OnPingStart += () => pings++; + Tick(.1f); // a sensor's first tick opens its ping cooldown; Ping() is refused before it + p.Command.Ping(); + Assert.Equal(0, pings); // nothing lands before the step + Tick(.1f); + Assert.Equal(1, pings); + for (var tick = 0; tick < 60; tick++) Tick(.1f); // past the cooldown, so a replayed press would ping again + Assert.Equal(1, pings); + } + // Two items revealed at different times: the contact's bearing, while untracked, is the freshest reveal's. [Fact] public void TheFreshestRevealGivesTheBearing() diff --git a/tests/Aetheria.Shared.Tests/IffAndCombatTests.cs b/tests/Aetheria.Shared.Tests/IffAndCombatTests.cs index c9e184fa8..b2a01e356 100644 --- a/tests/Aetheria.Shared.Tests/IffAndCombatTests.cs +++ b/tests/Aetheria.Shared.Tests/IffAndCombatTests.cs @@ -284,6 +284,23 @@ public sealed class IffAndCombatTests : IDisposable Assert.DoesNotContain(stranger, patrol.VisibleFriendlies); } + // A stance press flips the stance as it stands, the override or else the belief: against a contact believed hostile with + // no override held, it declares the contact neutral. It lands on a target designated in the same step. + [Fact] + public void AStancePressFlipsTheBelief() + { + var (_, zone, patrol, stranger) = Owned(new Faction { Name = "X" }); + Detect(patrol, stranger); + Assert.True(patrol.IsHostileTo(stranger), "fixture: hostile by belief"); + Assert.Null(patrol.IffOverride(stranger)); // fixture: no override, which alone would read neutral + + patrol.Command.Designate(stranger); + patrol.Command.ToggleStance(); + zone.Update(.1f); + Assert.False(patrol.IffOverride(stranger)); + Assert.False(patrol.IsHostileTo(stranger)); + } + [Fact] public void AlliesAreKnownAsTruth() { diff --git a/tests/Aetheria.Shared.Tests/PilotSeamPlayerTests.cs b/tests/Aetheria.Shared.Tests/PilotSeamPlayerTests.cs index 7dfb2d339..c4f8227ca 100644 --- a/tests/Aetheria.Shared.Tests/PilotSeamPlayerTests.cs +++ b/tests/Aetheria.Shared.Tests/PilotSeamPlayerTests.cs @@ -221,27 +221,42 @@ public sealed partial class RunStartTests SeamAimEqual(selectThenCycle, east.ResolvedAim); } - // A stance press flips the stance as it stands when the press is applied, so two in a step cancel, as two flips in - // place did before the seam; none is replayed. + // A stance press flips the stance on the target as it stands when the press is applied, after the step's target verbs, + // so two in a step cancel, as two flips in place did before the seam; none is replayed. [Fact] public void StanceTogglesInAStepCompose() { - var (arena, east, west, _) = PlayerSeamTrio(); + var (arena, east, west, third) = PlayerSeamTrio(); + Assert.True(east.Target.Value.IsNone); Assert.False(east.IsHostileTo(west)); - east.Command.ToggleStance(west); + // No target is applied yet: the verb in the same step picks the nearest (west), and the press flips it. + east.Command.Verb(TargetVerb.Next); + east.Command.ToggleStance(); Assert.False(east.IsHostileTo(west), "nothing lands before the step"); arena.Update(SeamDt); - Assert.True(east.IsHostileTo(west)); + Assert.Same(west, east.Target.Value.Entity); + Assert.True(east.IsHostileTo(west), "the press lands on the target the step's verb picked"); + Assert.False(east.IsHostileTo(third)); east.Command.Iff(west, true); arena.Update(SeamDt); Assert.True(east.IsHostileTo(west), "a stance set is a set, not a flip"); - east.Command.ToggleStance(west); - east.Command.ToggleStance(west); + east.Command.ToggleStance(); + east.Command.ToggleStance(); arena.Update(SeamDt); Assert.True(east.IsHostileTo(west), "two flips in a step cancel"); - east.Command.ToggleStance(west); + // Next then the press in one step: the press flips the new target, not the one applied when it was pressed. + east.Command.Verb(TargetVerb.Next); + east.Command.ToggleStance(); + arena.Update(SeamDt); + Assert.Same(third, east.Target.Value.Entity); + Assert.True(east.IsHostileTo(third), "the press flips the new target"); + Assert.True(east.IsHostileTo(west), "the old target is untouched"); + east.Command.Designate(new TargetRef(west)); + arena.Update(SeamDt); + + east.Command.ToggleStance(); arena.Update(SeamDt); Assert.False(east.IsHostileTo(west)); arena.Update(SeamDt);