fix(combat): synchronize auto-target presentation

Make the retained toolbar consume retail's payload-free selection notice from canonical live state so a reentrant Auto Target replacement cannot be erased by the outer clear. Restrict automatic acquisition to the requested hostile non-player monster policy and record the intentional PK edge divergence.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-12 20:26:00 +02:00
parent 8be933fc94
commit 0f2d98c501
11 changed files with 225 additions and 7 deletions

View file

@ -171,6 +171,38 @@ public class SelectedObjectControllerTests
Assert.Empty(textChild.LinesProvider());
}
[Fact]
public void ReentrantAutoTargetNotice_RendersCurrentReplacementNotStaleClear()
{
const uint Dead = 0xAA00u;
const uint Replacement = 0xAA01u;
var (layout, nameEl, _, _) = FakeLayout();
var h = new Harness();
h.NameMap[Dead] = "Dead Drudge";
h.NameMap[Replacement] = "Drudge Prowler";
h.StackMap[Dead] = 1u;
h.StackMap[Replacement] = 1u;
h.Selection.Select(Dead, SelectionChangeSource.World);
// CombatTargetController is registered before the retained toolbar in
// production. Its clear handler selects a replacement reentrantly.
h.Selection.Changed += transition =>
{
if (transition.SelectedObjectId is null)
h.Selection.Select(Replacement, SelectionChangeSource.System);
};
h.Bind(layout);
h.Selection.Clear(
SelectionChangeSource.System,
SelectionChangeReason.CombatTargetDied);
Assert.Equal(Replacement, h.Selection.SelectedObjectId);
var lines = nameEl.Children.OfType<UiText>().Single().LinesProvider();
Assert.Single(lines);
Assert.Equal("Drudge Prowler", lines[0].Text);
}
// ── H1: Select a health target — meter does NOT show on select alone ─────
[Fact]