feat(selection): port retail offscreen target indicator

This commit is contained in:
Erik 2026-07-18 07:41:12 +02:00
parent ea4f52ec51
commit ec1bb19609
9 changed files with 404 additions and 97 deletions

View file

@ -4143,13 +4143,15 @@ play, so this should be scheduled soon after A8 rather than left as polish.
**Resolution:** Deleted the procedural ImGui triangles and mounted a retained
gameplay-UI `VividTargetIndicatorController`. It resolves retail client-enum
category `0x10000009`, values `1..4`, to the four installed 12×12 RenderSurface
corners; colorizes their grayscale masks with `gmRadarUI::GetBlipColor`
equivalent colors; places them outside the exact Setup selection-sphere screen
rectangle; and ports retail's separate-edge 8-pixel viewport clamp. The marker
is available without devtools and follows only targets reported by the normal
world draw. Installed-DAT and layout conformance tests pin the assets and
placement.
category `0x10000009`, values `1..12`, to the four installed 12×12 corner
surfaces and eight 24×24 off-screen direction surfaces; colorizes their
grayscale masks with `gmRadarUI::GetBlipColor` equivalent colors; places them
outside the exact Setup selection-sphere screen rectangle; and ports retail's
8-pixel viewport clamp. The marker is available without devtools, remains
visible through walls, has no marker-side distance cutoff, and uses the larger
authored direction image when the live target leaves the viewport.
Installed-DAT and layout conformance tests pin the assets, direction sectors,
and placement.
**Description:** Per 2026-05-16 user feedback during the
`SelectionSphere` indicator ship, the triangle apex direction

View file

@ -524,7 +524,7 @@ behavior. Estimated 1726 days focused work, 35 weeks calendar.
- **✓ SHIPPED — UI Studio** (2026-06-26, branch `claude/hopeful-maxwell-214a12`, ~33693c6→HEAD). Standalone `AcDream.App ui-studio <datdir> [--layout 0xNNNN | --dump <slug>] [--screenshot <png>]` Silk tool that previews any panel through the **production renderer** (`RenderBootstrap.cs` + GameWindow untouched). Sources: 26-window retail dump (`docs/research/2026-06-25-retail-ui-layout-dump.json`) via `--dump`, or live dat import + fixtures via `--layout`. Interactive canvas (click-routing to `UiHost`, Interact/Inspect ImGui toggle), headless `--screenshot`. Architecture: `src/AcDream.App/Studio/` (`StudioWindow`, `PanelFbo`, `FixtureProvider`, `LayoutSource`) + `src/AcDream.App/Rendering/RenderBootstrap.cs`. ISSUES #156 (inventory all-black in studio) and #157 (GameWindow font resolver) filed.
- **✓ SHIPPED — Importer dat-fidelity** (Fix A/B/C, 2026-06-26, same branch). **Boundary established: look = importer (font/justification/color from dat); state/behavior = runtime controller logic.** Fix A: justification property `0x14`/`0x15``UiText` Centered/RightAligned/VerticalJustify. Fix B: FontColor property `0x1B``UiText.DefaultColor`; character colors proved RUNTIME (no importer fix). Fix C: FontDid → per-element dat font via a resolver in `DatWidgetFactory`**STUDIO path only**; GameWindow passes `null` (Issue #157). Fix 4 (UIState-group activation): INVESTIGATED — no importer fix exists; the dat has no Visible encoding; runtime `gm*UI` is correct.
- **✓ SHIPPED — Wave 3.3 unified primary-click router.** `ItemInteractionController.OfferPrimaryClick` returns `NotActive` / `ConsumedSuccess` / `ConsumedRejected`; inventory, bags, main pack, paperdoll, toolbar, radar, and world clicks all offer target mode before local fallback. Rejected targets cannot drift selection or open/use the clicked object. Issue #197 and AP-107 retired; retained matrix and live ACE-provisioned healing gate user-confirmed. The 3-D doll routes through authored hit mask `0x100001D6` (resolved `UiButton` above viewport `0x100001D5`), and local health prefers authoritative private-vital state.
- **✓ SHIPPED — retail world selection + complete SmartBox feedback (2026-07-17).** The projected Setup-sphere rectangle and independent physics-wall ray are deleted. The normal visible-mesh traversal now publishes server-object parts through their exact drawing-BSP spheres; `RetailWorldPicker` ports the camera-eye ray, per-part local transform, double-precision retail sphere solve, first visual-polygon hit in DAT order, one-sided/convex-edge rules, and global polygon-over-sphere arbitration. The retained gameplay UI resolves client-enum category `0x10000009` values `1..4` to the four installed 12×12 corner surfaces, colorizes them with the radar-blip color, and ports the exact Setup-sphere rectangle plus separate-edge 8-pixel viewport clamp. Successful select/use/targeted-use/drop clicks also run SmartBox's separate 0.8-second HIGH/LOW/HIGH/LOW material-lighting pulse with the exact retail luminosity/diffuse replacements before restoring authored rendering. Issues #70/#71 closed; AP-29/TS-16 retired. Research: `docs/research/2026-07-17-retail-world-selection-pseudocode.md`.
- **✓ SHIPPED — retail world selection + complete SmartBox feedback (2026-07-18).** The projected Setup-sphere rectangle and independent physics-wall ray are deleted. The normal visible-mesh traversal now publishes server-object parts through their exact drawing-BSP spheres; `RetailWorldPicker` ports the camera-eye ray, per-part local transform, double-precision retail sphere solve, first visual-polygon hit in DAT order, one-sided/convex-edge rules, and global polygon-over-sphere arbitration. The retained gameplay UI resolves client-enum category `0x10000009` values `1..12` to the four installed 12×12 corner surfaces and eight 24×24 off-screen direction surfaces, colorizes them with the radar-blip color, and ports the exact Setup-sphere rectangle, through-wall/no-distance SmartBox callback, direction sectors, and 8-pixel viewport-edge clamp. Successful select/use/targeted-use/drop clicks also run SmartBox's separate 0.8-second HIGH/LOW/HIGH/LOW material-lighting pulse with the exact retail luminosity/diffuse replacements before restoring authored rendering. Issues #70/#71 closed; AP-29/TS-16 retired. Research: `docs/research/2026-07-17-retail-world-selection-pseudocode.md`.
- **✓ SHIPPED — Wave 4.1 retail quick-slot input.** Bare `1..9` use toolbar slots 0..8, Ctrl+`1..9` selects them, Alt+`5..9` uses slots 13..17, and `0` creates a shortcut to the selected owned/eligible object in retail's first empty slot. `ToolbarInputController` is the focused semantic adapter; `ToolbarController` owns `UseShortcut` target-before-use/select ordering and auto-slot eligibility. Keybinding schema v2 migrates only the exact old default Ctrl+number chords, so custom bindings retain their chosen action. Full automated conformance and live user gate passed 2026-07-11.
- **✓ SHIPPED — Wave 4.2 exact shortcut records.** One Core-owned `ShortcutEntry` preserves retail's signed `index_`, unsigned `objectID_`, and raw 32-bit `spellID_` through PlayerDescription parse, the nullable 18-slot session store, drag snapshots/reindexing, WorldSession, and exact 12-byte AddShortcut packing. The visible `gmToolbarUI` remains object-only; `favorite_spells_[8]` remains a separate future spell-bar source. AP-103 retired. Full automated conformance and live mutate→clean-relog persistence gate passed 2026-07-11.
- **✓ SHIPPED — Wave 4.3a shortcut-alias boundary.** Drag feedback now preserves retail's neutral/accept/reject distinction. Inventory and paperdoll apply the `(DropItemFlags & 0xE) == 0` physical-item gate, so a toolbar alias can reorder within the toolbar but can never move, unwield, wield, or drop the object it references. Live equipped-helmet gate passed 2026-07-11: inventory drop removed only the shortcut.

View file

@ -125,23 +125,70 @@ enum values `1..12`. Values `1..4` are the four on-screen corners.
uses the active retail blit mode (`s_BlitMethod == 4`) to colorize the source
art with the same color as the object's radar blip.
`VividTargetIndicator::OnDraw @ 0x004F62B0` (on-screen branch)
`SmartBox::GetObjectBoundingBox @ 0x00452E20`
```text
if disabled, no selected id, or selected part was not reported by this world draw:
hide indicator
resolve the selected id directly through CObjectMaint::GetObjectA
if the object is absent:
return ObjectNotFound
reset to Render::set_default_view
push the object's position
obtain CPhysicsObj::GetSelectionSphere
if no sphere exists:
use an origin-centered sphere with radius 0.1 world units
if Render::viewconeCheck says the sphere intersects the full viewport cone:
project Render::GetViewerBBox's two corners
return ObjectOnscreen and the resulting screen rectangle
target = normalized selection-sphere center in viewer-local coordinates
if target is in front of the viewer:
angle = (450 - degrees(atan2(target.screenRight, target.screenUp))) mod 360
else:
colorize the four DAT corner images with the selected radar-blip color
obtain the selected object's SmartBox screen rectangle
use the actual DAT corner width and height
place the corner images just outside the rectangle
clamp the assembled indicator to an 8-pixel viewport margin
show the on-screen root and hide the off-screen root
angle = (450 - degrees(atan2(target.screenRight, 0))) mod 360
return ObjectOffscreen and angle
```
The indicator is persistent selection presentation. Retail also has a separate,
short-lived click confirmation owned by SmartBox; it is not implemented in
`CPhysicsPart::Draw` or `ACCWeenieObject::SetSelected`.
This path has no object-distance test and does not ask whether the selected
object participated in the visible world draw. `set_default_view` also removes
the per-portal view cone before the test. Consequently, a live selected object
keeps its four corners through walls and at any distance for which it remains in
the client object table.
`VividTargetIndicator::OnDraw @ 0x004F62B0`
```text
if disabled, display-off, or selected id is zero:
hide both roots
else if status is ObjectOnscreen:
colorize source images 1..4 with the selected radar-blip color
use the actual DAT corner width and height
place the corners just outside the SmartBox screen rectangle
clamp the assembled indicator to an 8-pixel viewport margin
show the on-screen root and hide the off-screen root
else if status is ObjectOffscreen:
choose one authored source image by angle:
[338,360) or [0,23) -> 6
[23,68) -> 7
[68,113) -> 9
[113,158) -> 12
[158,203) -> 11
[203,248) -> 10
[248,293) -> 8
[293,338) -> 5
colorize it with the selected radar-blip color
intersect the angle ray from viewport center with the screen edge
center the image on that intersection
clamp its top-left to the 8-pixel viewport margin
hide the on-screen root and show the off-screen image
```
Installed retail DAT verification pins images 1..4 at 12×12 pixels and images
5..12 at 24×24 pixels. The larger off-screen marker is authored art, not a
procedural arrow. The indicator is persistent selection presentation. Retail
also has a separate, short-lived click confirmation owned by SmartBox; it is not
implemented in `CPhysicsPart::Draw` or `ACCWeenieObject::SetSelected`.
## SmartBox click lighting pulse