feat(D.5.3a): selected-object meter — Health bar + name on the action bar
Port of gmToolbarUI::HandleSelectionChanged (acclient_2013_pseudo_c.txt:198635). When the player selects a world object the action bar's bottom strip shows the object name + (for player/pet/attackable targets) a live Health meter; deselect clears it. Mana (#140) + stack slider deferred. - SelectedObjectController (new): clear-then-populate on selection change; sets name (UiText child, VitalsController pattern), overlay state (ObjectSelected / StackedItemSelected via UiDatElement.ActiveState), shows the health meter and sends QueryHealth for health targets. Subscribes via a delegate seam (no GameWindow coupling). - GameWindow: _selectedGuid field -> SelectedGuid property + SelectionChanged event (fires on actual change only); 3 write sites converted, reads untouched. All selection-write paths (LMB pick, Tab/Q, despawn-clear via Tick()) run on the render thread, so the event-driven UI mutation is single-threaded. - WorldSession.SendQueryHealth (0x01BF) — wraps SocialActions.BuildQueryHealth. - DatWidgetFactory.BuildMeter: handle the single-image toolbar meter shape (back-track on the element's own DirectState, fill on one Type-3 child). The sprites go in the TILE slot (DrawMode=Normal tiles to full bar geometry per UIElement_Meter::DrawChildren) — a left-cap assignment would gap/clamp a sub-140px sprite. Vitals 3-slice path unchanged. - ToolbarController.HiddenIds: A1 (health) now owned by SelectedObjectController; A2 (mana) + A4 (stack) stay hidden (deferred) so their dat back-tracks don't render as stray empty bars. Adversarial Opus review found + fixed: the mana-meter orphan (A2 left unhidden) and the meter tile-vs-cap render bug (C1). Divergence rows AP-46 (health gate approximation: IsLiveCreatureTarget vs IsPlayer||pet||attackable) + AP-47 (meter shown on select vs on UpdateHealth reply). Spec §5 corrected. Build + full test suite green (2,684 passed / 4 skipped). Health meter render fidelity (full-width fill + fraction mapping) pending the user's visual gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e8562fc4e2
commit
6636e50c2a
11 changed files with 851 additions and 30 deletions
|
|
@ -136,6 +136,47 @@ public class DatWidgetFactoryTests
|
|||
Assert.IsType<AcDream.App.UI.UiItemList>(w);
|
||||
}
|
||||
|
||||
// ── Test M1: Single-image meter (toolbar selected-object meters) ────────
|
||||
//
|
||||
// The toolbar health/mana meters (0x100001A1 / 0x100001A2) use a DIFFERENT
|
||||
// shape from the vitals 3-slice meters: the back-track sprite lives on the
|
||||
// meter ELEMENT's own DirectState ("" key), and there is exactly ONE Type-3
|
||||
// child whose own DirectState ("" key) carries the fill sprite. That child
|
||||
// has no image grandchildren, so SliceIds would return all-zero — the new
|
||||
// Count==1 branch reads the StateMedia entries directly instead.
|
||||
// The sprites go in the TILE slot (Back/FrontTile), NOT the cap slot: DrawMode=Normal
|
||||
// tiles at native width across the full bar geometry (UIElement_Meter::DrawChildren),
|
||||
// so the back spans all 140px and the fill clips to 140*fraction for any native width.
|
||||
// Back/FrontLeft + Back/FrontRight must be 0 (no caps on a single-image bar).
|
||||
|
||||
[Fact]
|
||||
public void BuildMeter_SingleImageShape_ReadsDirectStateFromElementAndFillChild()
|
||||
{
|
||||
const uint BackFile = 0x0600193Eu; // health back-track (from toolbar dump)
|
||||
const uint FillFile = 0x0600193Fu; // health fill (from toolbar dump)
|
||||
|
||||
// Meter element: Type 7, own DirectState = back-track sprite.
|
||||
var meter = new ElementInfo { Type = 7, Id = 0x100001A1u, Width = 140, Height = 31 };
|
||||
meter.StateMedia[""] = (BackFile, 1);
|
||||
|
||||
// Single Type-3 fill container: own DirectState = fill sprite, no grandchildren.
|
||||
var fillContainer = new ElementInfo { Type = 3, ReadOrder = 1 };
|
||||
fillContainer.StateMedia[""] = (FillFile, 1);
|
||||
meter.Children.Add(fillContainer);
|
||||
|
||||
var e = DatWidgetFactory.Create(meter, NoTex, null);
|
||||
|
||||
var m = Assert.IsType<UiMeter>(e);
|
||||
// Back-track on the meter element's own DirectState, fill on the single child —
|
||||
// both in the TILE slot so they tile across the full 140px bar (DrawMode=Normal).
|
||||
Assert.Equal(BackFile, m.BackTile);
|
||||
Assert.Equal(0u, m.BackLeft);
|
||||
Assert.Equal(0u, m.BackRight);
|
||||
Assert.Equal(FillFile, m.FrontTile);
|
||||
Assert.Equal(0u, m.FrontLeft);
|
||||
Assert.Equal(0u, m.FrontRight);
|
||||
}
|
||||
|
||||
// ── Test 6: Meter slice extraction (the important one) ───────────────────
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue