acdream/src/AcDream.App
Erik f532f28c5b feat(ui): Campaign CT slice CT5 — attribute/skill row geometry + selection media
Aligns the hand-built attribute/skill rows in CharacterStatController with
the authored shared row template 0x10000248 (LayoutDesc 0x21000045,
InfoRegion::InfoRegion @0x004F1450 template index 0 — the same template
gmAttributeUI and gmSkillUI both instantiate):

- Row geometry replaced with AUTHORED PIXEL VALUES instead of derived
  fractions: icon flush left 20x20 (was 16x16 at X=4, vertically
  centered), name column X=25 W=150 fixed (was RowPadX+IconSize+IconGap
  offset with a width*0.60 fraction), value column X=175 W=100
  right-justified (its right edge sits 7px short of the row's 282px
  right edge — the authored gutter the owner reported). Row width itself
  now clamps to the authored 282px template width (RowContentWidth)
  rather than the ListBox's raw 300px container width. Attribute-row
  height fixed at 20px (was 22px, no dat basis); SkillRowHeight folded
  into the same RowHeight constant since both row kinds share H=20.

- RowHighlightSprite corrected from 0x06001397 to 0x06000F93 — CT1's
  ground-truth research sealed the verdict that gmAttributeUI::
  UpdateSelection @0x0049DEE0 (SetState(6) -> InfoRegion::SetState
  @0x004F0EE0) swaps the row's Highlight-state media (0x06000F93), a
  full-row background swap. 0x06001397 belongs to a different mechanism
  entirely (the spellbook row's UIElement_UIItem::SetSelectedState
  overlay child) and SpellbookRowStyle.cs is untouched.

- UiClickablePanel.UseSelectionBars/SelectionBarHeight retired outright
  (UiPanel.cs): they existed only to emulate 0x06001397's dark-bars art;
  the correct retail rendering is the full-panel sprite stretch the base
  UiPanel.OnDraw already performs, so the override is dead code once the
  correct sprite is used. No consumer existed outside
  CharacterStatController.

- Per-attribute/per-vital icon DIDs now resolve through the live
  DBObj::GetDIDByEnum chain (RetailDataIdResolver.Resolve, AP-235's
  unification seam) when a resolver is supplied — RetailUiRuntime.
  MountCharacter wires one under the shared DatLock — falling back to
  the hardcoded AttrRows/VitalRows column otherwise (tests, no dat).
  gmAttributeUI::PostInit @0x0049DB70 read verbatim: attributes resolve
  via category 0x10000002 (statId order 1,2,4,3,5,6, matching AttrRows'
  authored display order exactly); vitals via category 0x10000003.
  Live-DAT-verified: every hardcoded fallback value already matched the
  resolved DID byte-exact (new InstalledDat pin
  AttributeAndVitalIconDids_MatchTheRetailEnumMapperChain).

- RetailAppraisalNameResolver.ResolveHeritage's independent
  re-implementation of the 2/5/13 heritage overrides deleted; it now
  delegates straight to CharacterIdentityText.HeritageGroupDisplayName
  (which already bakes in the same overrides) — one owner, byte-identical
  behavior. AP-235's register row updated to reflect the single-owner fix
  (the underlying hardcoded-vs-live-DAT mechanism divergence itself
  stays open — out of CT5's scope).

Hand-built-vs-template ruling: rows stay HAND-BUILT rather than
converting to UiTemplateListBox instantiation. The hand-built path hits
every authored number byte-exact (proven by the CT1 InstalledDat pin
AttributeRowTemplate_IconIsFlushLeftTwentyPixels_NameAndValueAreFixedColumns),
while conversion would touch ~15 call sites (raise-button affordability,
footer State A/B, per-row tooltip, section bucketing, live-refresh,
selection-highlight) for a geometry-only slice — smaller-risk path per
the task's own judgment-call guidance.

Tests: CharacterStatControllerTests' sprite/UseSelectionBars assertions
corrected to the authored geometry; new InstalledDat pin for the icon-DID
chain. Full hermetic solution suite green (App/Core/Runtime/Headless/
Launcher/Content/etc., 0 failures) and the full InstalledDat lane green
(203 App.Tests pins, TowerAscentReplayTests' known Status=KnownFailure
case excluded per the acceptance filter).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 01:15:32 +02:00
..
Audio fix #417: world ambience kept playing at character select after the in-world logoff 2026-08-17 18:46:04 +02:00
Combat fix #436: combat no-target refusal reaches the SpewBox with retail's exact text 2026-08-24 12:53:11 +02:00
Composition feat(ui): Campaign CT slice CT4 — header identity block retail-exact 2026-08-25 00:02:11 +02:00
Configuration feat(runtime): share chat commands and run login sequence 2026-08-14 20:27:45 +02:00
Credentials fix(launcher): close Campaign LA LA1 review findings 2026-08-14 17:00:09 +02:00
Diagnostics test(gates): sequence the two-client remote-shadow gate with harness signals 2026-08-22 17:44:13 +02:00
Input fix #435 (part 1): delete 17 probes that outlived their closed investigations 2026-08-24 11:41:20 +02:00
Interaction fix #435 (part 2, closes it): attribute the unowned probes — delete 7, reclassify 8, restore 1 2026-08-24 12:32:42 +02:00
Net feat(CT): CT2 — Runtime character-title ownership + wire 2026-08-24 22:03:22 +02:00
Physics fix #435 (part 1): delete 17 probes that outlived their closed investigations 2026-08-24 11:41:20 +02:00
Platform docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1 2026-08-14 16:04:32 +02:00
Plugins feat(render): implement Campaign AR and terrain fidelity 2026-08-22 13:13:29 +02:00
Rendering fix #435 (part 2, closes it): attribute the unowned probes — delete 7, reclassify 8, restore 1 2026-08-24 12:32:42 +02:00
Runtime fix(CT): CT2 fix round — dedupe client-side title add, drop retail-inexact send guard 2026-08-24 22:27:07 +02:00
Settings feat(render): implement Campaign AR and terrain fidelity 2026-08-22 13:13:29 +02:00
Spells Revert "Campaign V slice V4a" - it lost world multisampling 2026-07-27 18:29:28 +02:00
Streaming fix #435 (part 1): delete 17 probes that outlived their closed investigations 2026-08-24 11:41:20 +02:00
UI feat(ui): Campaign CT slice CT5 — attribute/skill row geometry + selection media 2026-08-25 01:15:32 +02:00
Update feat(audio): Campaign A slice A5 — retail's region ambient soundscape 2026-08-08 22:53:41 +02:00
World fix #435 (part 2, closes it): attribute the unowned probes — delete 7, reclassify 8, restore 1 2026-08-24 12:32:42 +02:00
AcDream.App.csproj feat(mosstank): cast back-to-back, and a settings view for the thresholds 2026-08-20 18:51:15 +02:00
GlobalUsings.cs feat(runtime): share chat commands and run login sequence 2026-08-14 20:27:45 +02:00
packages.linux-x64.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.neutral.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.win-x64.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
Program.cs feat(render): implement Campaign AR and terrain fidelity 2026-08-22 13:13:29 +02:00
RuntimeOptions.cs fix #435 (part 2, closes it): attribute the unowned probes — delete 7, reclassify 8, restore 1 2026-08-24 12:32:42 +02:00