Applies all 11 items from the Opus dual-lens review of989f6652(0 blockers, 7 SHOULD-FIX, 4 NOTE): - S2: UiElement.DrawSelfAndChildren now pushes the ambient clip right after PushAlpha and wraps OnDraw + the children walk + OnDrawAfterChildren in ONE block — the literal UIRegion::DrawHere @0x0069FA30 shape, which clips an element's OWN DrawSelf too, not just its children (UIElement_Text::DrawSelf @0x00467AA0 locks glyph blits to its own clipped surface rect; UIRegion::DrawSelf @0x0069F1A0 blits per clip rect). Deleted the two now-redundant ad-hoc self-clips this supersedes: UiText.DrawText and UiField.DrawMultiLine both pushed their own (0,0,Width,Height) — exactly what the new ambient clip already provides one level up. Kept UiButton.DrawBlockLabel's clip: it clips to LabelBox/ValueBox, an authored INNER sub-rect that can be smaller than and offset from the button's own full rect — a genuine narrower viewport, not a redundant duplicate. - S3: deleted UiItemList's `ClipsChildren => CellWidth > 0f` override — correct under the old opt-in-false default, inverted under the new default-true (an unconfigured list would stop clipping instead of clipping like everything else). - S4: pinned the escaped-popup input path end to end. New UiAncestorClipTests test mounts a menu inside a short window on a real UiRoot, opens it, and proves a click in the escaped popup region reaches the menu through UiRoot.PopupHit (a plain top-down walk is proven to reject the same point first). UiRoot.WantsMouse now also checks PopupHit — it previously only checked Captured/ HitTestTopDown, so a game action could fire underneath an open dropdown's escaped region. OnMouseDown/OnScroll already routed through PopupHit first (#374); unchanged. - S5: strengthened the Titles-divider regression test's positive half. The old assertion only checked SOME quad's Y fell in a band — vacuously true given other same-band content. Now asserts the divider's exact rect (X and Y), then diffs against the same rect with the divider hidden (Visible=false) to prove the quad was actually attributable to it. - S1: added UiWindowDrawCaptureSweepTests — Character/Chat/Vendor/ Options mounted through their real production Bind entry points with a non-zero sprite resolver, drawn via RecordingGpuDevice, asserting a per-window vertex floor (~40-45% of this session's observed baseline: Character 588, Chat 162, Vendor 54, Options 240) plus one key sprite id read LIVE off the bound controller/element (never hardcoded). Character's key sprite (RetailChromeSprites. TopEdge) specifically exercises OnDrawAfterChildren, the exact path S2's caution note flagged. Inventory/Paperdoll/social/map-house skipped — no single fixture-driven top-level Bind entry point. - S6: added the CT-GF1 subsection to the campaign plan's ledger (989f6652+ this fix round; CT7 re-gate still owed). - S7: UiRenderContext.PushClipUnbounded now resets to the CANVAS rect (0,0,ScreenSize), not null — retail's own popup region is SCREEN-clipped (UIElement_Menu::MakePopup spawns a top-level region bounded by the screen), not truly unbounded. AD-113 amended. - N1: UiRoot overrides ClipsChildren => false — the root's own region IS the screen (the viewport already scissors it), so this is a safety net against a momentarily zero-sized root silently blanking the whole UI tree under the new ancestor-clip default. - N2: added the empty-clip subtree cull (retail's var_24 gate @0x0069FB8E) to DrawSelfAndChildren only — DrawOverlays is a wholly separate traversal untouched by this change. New test proves a menu inside a fully-clipped (zero-width) window still draws its open popup via the overlay pass while the main pass draws nothing. - N3: CT7 script §5 now names the collapsed-toolbar check and the four highest-overflow windows (combat/vitals bar, Options bottom-button row, map/house page, floaty chat) as explicit eyeball items for the re-gate. - N4: verification below covers both the working tree and the clean committed tree. Decomp anchors: UIRegion::DrawHere @0x0069FA30 (var_24 gate @0x0069FB8E); UIElement_Text::DrawSelf @0x00467AA0 (self-clip); UIRegion::DrawSelf @0x0069F1A0; UIElement_Menu::MakePopup (screen- clipped popup region). Verification (both runs green, --filter "Lane!=InstalledDat& Lane!=PreparedPackage&Lane!=Live&Lane!=Manual&Lane!=Timing& Lane!=Windows&Lane!=Linux&Lane!=SystemFont&Purpose!=Diagnostic& Status!=KnownFailure"): full Release solution build green; working tree 14,900+ tests across every project (one LandblockPresentation PipelineTests flake reproduced ONLY under full-solution parallel load, passes standalone and on rerun — unrelated to this change, streaming domain); InstalledDat lane green (ACDREAM_RUN_INSTALLED_DAT _TESTS=1, Status!=KnownFailure, 205+34+3+172 App/Content/Bake/Core tests). Clean committed tree (git stash push -u the uncommitted owner probe + docs files, rerun, stash pop) reported in the session summary. src/AcDream.App/UI/UiRoot.cs carries an unrelated, pre-existing uncommitted owner probe (ACDREAM_PROBE_UI_HOVER) — staged selectively (git add -p) so only this commit's own two hunks (ClipsChildren override, WantsMouse) landed; the probe hunk is untouched and stays uncommitted, same as before this fix round. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
179 lines
9.6 KiB
Markdown
179 lines
9.6 KiB
Markdown
# Campaign CT — connected gate script (CT7, user-driven)
|
||
|
||
**Purpose:** live verification of the character-panel parity campaign
|
||
(CT1–CT6) against ACE. Launch: the normal connected launch
|
||
(`ACDREAM_RETAIL_UI=1`, live ACE at `127.0.0.1:9000`,
|
||
`ACDREAM_PAK_PATH=<worktree>\artifacts\owner-gate\acdream-v5.pak`).
|
||
Open the character panel (F9 / toolbar). Retail side-by-side comparison
|
||
is the oracle for every visual item.
|
||
|
||
Useful ACE console helpers: title grants come from quests/admin — check
|
||
`@acecommands` for a title-grant command; `@grantxp` for levels.
|
||
|
||
---
|
||
|
||
## 1. Header identity block (CT4) — Attributes AND Skills tabs
|
||
|
||
1. **Name line**: the plain character name (rankless characters — the
|
||
allegiance rank prefix is a registered deferral, AP-109).
|
||
2. **Heritage line**: "<Gender> <Heritage> <DisplayTitle>" — e.g.
|
||
"Female Aluvian War Mage" when a display title is set; just
|
||
"Female Aluvian" when none. PASS: matches retail's composition and
|
||
spacing exactly; a title beginning with "The" shows unmangled.
|
||
3. **PK line**: "Non-Player Killer" (or "Player Killer" / "Player
|
||
Killer Lite" on a PK/PKL character) in PURE WHITE. PASS: correct
|
||
text + color on BOTH the Attributes and Skills tabs.
|
||
4. **Level number**: pale gold with outline (authored color — compare
|
||
against retail's level display side-by-side; the owner reported ours
|
||
was previously off).
|
||
5. **Luminance pair**: on a sub-200 character, NO luminance caption or
|
||
value renders. (A level ≥ 200 character with MaximumLuminance shows
|
||
"Luminance:" and "<available> / <maximum>" — verify only if such a
|
||
character is available.)
|
||
6. **Live update**: set a display title (see §2) — the heritage line
|
||
updates the moment the server confirms, with no relog and no panel
|
||
re-open.
|
||
|
||
## 2. Titles tab (CT3)
|
||
|
||
1. Click the **Titles** tab. PASS: the page shows "Current Display
|
||
Title:" + the current title (or "Unknown" only when the server's
|
||
title id fails to resolve — normally a real title or the authored
|
||
empty state), the "All Available Titles:" list, and the
|
||
"Set as Display Title" button.
|
||
2. **List content**: every earned title, alphabetically sorted,
|
||
readable rows. With few titles the scrollbar shows retail's
|
||
full-track thumb; with many (if available) the thumb sizes
|
||
proportionally and scrolls.
|
||
3. **Ghost rule**: with NOTHING selected the button is ghosted. Select
|
||
the title that IS the current display title — button stays ghosted.
|
||
Select a DIFFERENT title — button un-ghosts.
|
||
4. **Set round trip**: click Set as Display Title. PASS: the display
|
||
title text updates on the server's confirmation, the selection
|
||
CLEARS (row highlight goes dark — retail behavior), the button
|
||
re-ghosts, and the §1 heritage line updates live.
|
||
5. **Row selection visual**: the selected row highlights with retail's
|
||
row highlight art (full-row background swap), not a synthesized bar.
|
||
|
||
## 3. Attribute/skill rows (CT5)
|
||
|
||
1. **Icon alignment**: row icons sit flush left (20x20 at the row's
|
||
left edge), matching retail — the previous inset/smaller icons are
|
||
gone. Compare a few rows side-by-side against retail.
|
||
2. **Value gutter**: the numbers column ends with a visible margin
|
||
before the panel border (the scrollbar band) — retail's 7px gutter.
|
||
3. **Row height**: rows are retail-height (slightly tighter than
|
||
before); section headers (Trained/Untrained/Unusable) unchanged.
|
||
4. **Selection highlight**: clicking a row highlights with the retail
|
||
full-row art; the spellbook's selection visuals are UNCHANGED
|
||
(regression check — open the spellbook and select a spell).
|
||
5. **Raise buttons / tooltips / footer**: regression sweep — raise ×1
|
||
and ×10 still work with correct ghosting, skill tooltips still show
|
||
formula + description wrapped correctly, the footer numbers update.
|
||
|
||
## 4. Resize + scrollbar (CT6)
|
||
|
||
Ground truth (2026-08-25 live probe against layout `0x2100006E`, host
|
||
`0x100005FE` — `docs/research/2026-08-24-campaign-ct-dat-ground-truth.md`
|
||
§CT6, corrected by the CT6 fix round's BLOCKER B1): the resize clamp is
|
||
authored on the SHARED `gmPanelUI` host, not the character content
|
||
itself, and the host IS retail's own outer window frame — its authored
|
||
values are chrome-INCLUSIVE, not a content size our own chrome adds on
|
||
top of. Host authors **MinWidth=MaxWidth=310** (fixed width — no
|
||
horizontal Resizebar) and **MinHeight=372, MaxHeight=1000**. The
|
||
MOUNTED window's outer bounds are EXACTLY those same numbers: width
|
||
fixed **310px**, floor **372px**, ceiling **1000px** — no inset is
|
||
added on top (`RetailWindowFrame.Options.DatConstraintSourceIsOuterFrame`
|
||
now tells the mount path this source already includes the bevel).
|
||
**Starting height:** the window OPENS at retail's authored default,
|
||
**372px** — its own resize floor. It cannot open any shorter; it can
|
||
only be dragged taller.
|
||
|
||
1. **Grab the bottom edge and drag up (shrink).** PASS: the window
|
||
stops shrinking at its authored floor (372px outer / the point where
|
||
further dragging has no visible effect) — since the window already
|
||
OPENS at that floor, this step should show no shrink at all (there
|
||
is no room below the default to shrink into). It does NOT collapse
|
||
arbitrarily small. Retail comparison: drag retail's own Character/
|
||
Skills window down from its own default; it should likewise refuse
|
||
to shrink further immediately.
|
||
2. **Keep dragging down (grow).** PASS: the window keeps growing until
|
||
its authored ceiling (1000px outer) — same side-by-side comparison
|
||
against retail's own ceiling.
|
||
3. **Left/right edges do not resize.** Only the bottom edge (and top
|
||
Dragbar for moving, not resizing) responds — matches retail's
|
||
fixed-width authoring (no horizontal Resizebar).
|
||
4. **Scrollbar hand-off, Attributes tab.** At the default window size
|
||
(372px) the 9 attribute/vital rows (180px content) OVERFLOW the
|
||
160px list — the scrollbar is ACTIVE (visible + interactive)
|
||
IMMEDIATELY on open, not after shrinking. PASS: this is the owner's
|
||
item 2 fix — previously the scrollbar never appeared on Attributes
|
||
at all. Grow the window until the rows fit without scrolling: the
|
||
bar DISAPPEARS entirely (0x1000023E authors 0x79 hide-when-disabled
|
||
TRUE — a fitting list hides the bar, it does not leave a full-track
|
||
"disabled" thumb visible). Shrink back down and the bar reappears.
|
||
5. **Scrollbar hand-off, Skills tab.** Same immediate-overflow-at-
|
||
default check (a longer skills list only makes the overflow more
|
||
obvious); grow until it fits and confirm the bar disappears the same
|
||
way.
|
||
6. **Scrollbar hand-off, Titles tab.** With several earned titles, the
|
||
Titles list (authored 455px, inside the 575px page) is scroll-clipped
|
||
into the same 372px-default window and its own scrollbar
|
||
(`0x10000533`, also hide-when-disabled — fixture-verified) takes over
|
||
the same way: active when titles overflow, hidden when the window is
|
||
grown enough that they all fit.
|
||
7. **Footer stays bottom-docked.** While shrinking/growing on the
|
||
Attributes/Skills tabs, the footer (raise buttons / selected-stat
|
||
info) stays pinned to the bottom edge — it does not float mid-window
|
||
or get clipped early.
|
||
8. **Grow back restores.** Drag back down to the original default
|
||
(372px): the lists return to their default OVERFLOWING state
|
||
(scrollbar reactivates — this is the default, not "all rows fit")
|
||
and the window returns to its original proportions.
|
||
9. Other windows (chat, social) still clamp at their own authored
|
||
minimums — regression check (chat: min 300×100, max 2000×2000 per
|
||
`CharacterPanelLiveDatTests.ChatWindowRoot_AuthorsExplicitSizeConstraints`).
|
||
|
||
## 5. Regression sweep (5 minutes)
|
||
|
||
- Attributes/Skills tab switching unaffected; CA5 behaviors intact
|
||
(raise round trips, live run-speed update on Quickness).
|
||
- Logout/login: titles and display title persist; the header matches
|
||
PlayerDescription's values.
|
||
- Chat window: the CH-round fixes hold (input rails on focus, "Gen"
|
||
caption, button flick, no vibrating text while dragging).
|
||
- **CT-GF1 fix round (client-wide ancestor clip) — eyeball items.** The new
|
||
default clips every element to its own box by default; these are the
|
||
windows most likely to show a silent over-clip (content trimmed that
|
||
should be visible) if the port has an edge case the automated suite
|
||
didn't catch:
|
||
- **Collapsed toolbar**: collapse the combat/spell toolbar to its narrow
|
||
strip and back — confirm nothing inside it (icons, the collapse grip)
|
||
gets cut off or fails to reappear on expand.
|
||
- **Combat/vitals bar**: at its default size, confirm the health/
|
||
stamina/mana bars and their numeric overlays render in full, not
|
||
trimmed at an edge.
|
||
- **Options panel bottom-button row** (Gameplay tab): confirm all seven
|
||
buttons (Exit to Character Selection, Configure Keyboard, In-Game
|
||
Help, Urgent Assistance, Report Abuse, mouse-turning checkbox, Exit
|
||
Game) render completely, none clipped at the panel's bottom edge.
|
||
- **Map/house page**: confirm the map image and player/house icons
|
||
render in full across the page's own scroll/zoom range, not clipped
|
||
at the viewport edge.
|
||
- **Floaty chat** (a detached floating chat window, Alt+1..4): confirm
|
||
the transcript and input row render in full at both a small and a
|
||
resized-larger window size — the same class of symptom CT-GF1's own
|
||
`ChatLayoutConformanceTests` regression-pinned for the main chat
|
||
window's input row.
|
||
|
||
---
|
||
|
||
## Report back
|
||
|
||
Per section: PASS/FAIL plus anything odd. The three answers that matter
|
||
most:
|
||
1. §2.4 — does the set-title round trip clear the selection and update
|
||
the header live?
|
||
2. §3.1/§3.2 — do icons and the value gutter now match retail
|
||
side-by-side?
|
||
3. §1.3 — is the PK line present, white, and correct on both tabs?
|