feat(selection): port retail polygon picking and vivid marker
Replace the projected Setup-sphere rectangle and independent physics-wall ray with retail's render-coupled picker: only visible server-object parts participate, each exact drawing sphere broad-phases the camera-eye ray, and first-in-DAT-order visual polygon hits globally outrank sphere fallbacks. Replace the devtools-only procedural triangles with the retained gameplay VividTargetIndicator using retail client-enum surfaces 1..4, radar-blip colorization, Setup selection-sphere framing, and the exact eight-pixel viewport clamp. Release build succeeds with zero warnings and all 5,886 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0f82a08f0a
commit
146a963aeb
26 changed files with 1302 additions and 1340 deletions
|
|
@ -4077,11 +4077,23 @@ or +small fix if different. Not blocking M1.
|
|||
|
||||
## #71 — WorldPicker Stage B — polygon refine for retail-accurate clicks
|
||||
|
||||
**Status:** OPEN
|
||||
**Status:** DONE 2026-07-17
|
||||
**Severity:** MEDIUM (Stage A now causes real play mis-picks through open doors/windows)
|
||||
**Filed:** 2026-05-16
|
||||
**Component:** selection / picker
|
||||
|
||||
**Resolution:** Replaced the projected `Setup.SelectionSphere` rectangle and
|
||||
independent collision-polygon wall ray with the retail render-coupled path.
|
||||
`WbDrawDispatcher` now publishes only server-object parts which survive the
|
||||
normal visible draw and each part's drawing-sphere view-cone check.
|
||||
`RetailWorldPicker` transforms the ray into each part,
|
||||
uses `GfxObj.DrawingBSP.Root.BoundingSphere` as broadphase, scans visual
|
||||
polygons in DAT order, keeps only the first polygon hit per part, and gives
|
||||
every polygon hit global priority over sphere-only fallbacks. The obsolete
|
||||
picker overloads and cell occluder were deleted. Conformance tests pin
|
||||
single-sided rejection, first-polygon ordering, affine scale, fallback, and
|
||||
global arbitration.
|
||||
|
||||
**Description:** Retail's mouse picker does two-tier sphere-then-polygon
|
||||
selection (`acclient_2013_pseudo_c.txt:0x0054c740`
|
||||
`Render::GfxObjUnderSelectionRay`):
|
||||
|
|
@ -4124,11 +4136,21 @@ play, so this should be scheduled soon after A8 rather than left as polish.
|
|||
|
||||
## #70 — Triangle apex/size — final retail-feel UX pass
|
||||
|
||||
**Status:** OPEN
|
||||
**Status:** DONE 2026-07-17
|
||||
**Severity:** LOW (cosmetic — indicator already retail-anchored, this is final-feel polish)
|
||||
**Filed:** 2026-05-16
|
||||
**Component:** ui / target indicator
|
||||
|
||||
**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.
|
||||
|
||||
**Description:** Per 2026-05-16 user feedback during the
|
||||
`SelectionSphere` indicator ship, the triangle apex direction
|
||||
(flipped to point inward at the target) and sprite size (currently
|
||||
|
|
@ -4141,8 +4163,8 @@ shape and size come from the dat, not constants.
|
|||
either (a) blit the exact bitmap, or (b) pick a procedural size +
|
||||
shape that matches it pixel-for-pixel at standard zoom.
|
||||
|
||||
**Files:** [TargetIndicatorPanel.cs](src/AcDream.App/UI/TargetIndicatorPanel.cs)
|
||||
— `TriangleSize` constant + the four `AddTriangleFilled` calls.
|
||||
**Files:** [VividTargetIndicatorController.cs](src/AcDream.App/UI/Layout/VividTargetIndicatorController.cs)
|
||||
— exact retained-UI DAT surface resolution, colorization, and placement.
|
||||
|
||||
**Estimated scope:** Small (~1-2 hours, mostly dat exploration).
|
||||
Not blocking M1.
|
||||
|
|
@ -7798,6 +7820,13 @@ cell structs. Entities behind walls from the camera's perspective are no
|
|||
longer selectable. Screen-rect occlusion tests verify the filter across
|
||||
several hit/miss scenarios.
|
||||
|
||||
**Superseded 2026-07-17:** The full retail render-coupled polygon picker
|
||||
(#71) removed this independent collision-BSP occluder. Retail inherits
|
||||
occlusion from the normal portal/viewcone draw traversal; maintaining a second
|
||||
ray against physics polygons could both hide drawn targets and admit undrawn
|
||||
ones. The original commits remain useful history, but this is no longer the
|
||||
runtime mechanism.
|
||||
|
||||
---
|
||||
|
||||
## #77 — [DONE 2026-05-18 · 3be7000] Auto-walk doesn't engage at walking range; pickup at walking range overshoots and snaps back
|
||||
|
|
|
|||
|
|
@ -137,7 +137,6 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| AP-26 | DDD interrogation answered with an empty dat-version list (count=0); retail reports actual dat iteration state | `src/AcDream.Core.Net/Messages/DddInterrogationResponse.cs:18` | ACE is satisfied by the empty ack; pattern from holtburger | A dat-patching-enabled server could push a full patch or reject on version mismatch — the lie is harmless only while the server never acts on it | DDD flow 0xF7E5/0xF7E6 |
|
||||
| AP-27 | PlayerDescription trailer: GameplayOptions skipped by a 4-byte-aligned heuristic scan for a valid inventory parse; options blob captured opaque, never decoded (retail decodes + applies UI options) | `src/AcDream.Core.Net/Messages/PlayerDescriptionParser.cs:69` | Variable-length opaque blobs; mirrors holtburger's heuristics; follow-up issue extends when panels consume those sections | An options blob that coincidentally parses as a valid inventory (or inventory not landing at EOF) yields wrong/empty inventory+equipped at login; retail-persisted UI options silently ignored | ACE GameEventPlayerDescription.WriteEventBody; holtburger events.rs:195-218 |
|
||||
| AP-28 | 3D audio falloff via OpenAL InverseDistanceClamped with picked constants (ref 2 m, max 1000 m, rolloff 1); voice pool/eviction IS cited to retail | `src/AcDream.App/Audio/OpenAlAudioEngine.cs:146` | Stands in for retail's DirectSound-era attenuation; r05 §5.3 documents inverse-square behavior but the three AL params were picked, not ported | Sounds attenuate at a different rate — too loud/quiet at range side-by-side; gain-driven eviction comparisons inherit the skew | FUN_00550ad0 (voice pool only); r05 §5.3 |
|
||||
| AP-29 | Target-indicator fallback for entities with no baked selection sphere: invented 1.5 m × scale box + 16/12 px screen floors (primary path is a faithful `GetObjectBoundingBox` port) | `src/AcDream.App/UI/TargetIndicatorPanel.cs:86` | Fallback only fires when the Setup didn't bake a selection sphere — rare in practice | Sphere-less entities get a non-retail indicator size/placement; the pixel floors prevent retail's far-distance collapse | `SmartBox::GetObjectBoundingBox` 0x00452e20; `GetSelectionSphere` |
|
||||
| AP-30 | AutonomousPosition diff cadence compares with epsilons (1 mm pos, 1e-4 normal, 1 mm dist); retail's `Frame::is_equal` is an exact float compare | `src/AcDream.App/Input/PlayerMovementController.cs:1110` | Sub-millimeter epsilon is well below any movement worth suppressing; comparisons are against last-SENT state so drift accumulates past the epsilon | Sub-epsilon drift suppresses an AP send retail would have made — negligible today; a consumer expecting retail's exact send-on-any-change cadence sees fewer packets | `Frame::is_equal` pc:700263 |
|
||||
| AP-31 | Scenery placement drift + the 0xA9B1 road-edge tree — WB-upstream divergences from retail, ACCEPTED (**#49/#50**, 2026-05-11) | `src/AcDream.Core/World/SceneryGenerator.cs` (via `WbSceneryAdapter`) | Piecemeal patching against WB upstream is net-negative (the `e279c46` road-check attempt over-suppressed scenery elsewhere, reverted `677a726`); visible impact = a handful of trees a few meters off | The same WB-upstream class could hide a *larger* placement divergence elsewhere; revisit only via a coherent ACME-style per-vertex filter port | `CLandBlock::get_land_scenes`; ACME GameScene.cs:1074 per-vertex road filter |
|
||||
| AP-32 | Cell shells DRAW +0.02 m above the dat EnvCell origin (`ShellDrawLiftZ`, z-fight vs coplanar terrain); retail draws at the origin verbatim. Split invariant: PHYSICS + visibility graph UNLIFTED (f35cb8b, **#119**-residual), every DRAW-space consumer of portal/cell geometry LIFTED (OutsideView color gate via `Build(drawLiftZ)`, seal/punch fans — **#130**) | `src/AcDream.App/Rendering/GameWindow.cs:5604` (const at `PortalVisibilityBuilder.ShellDrawLiftZ`) | Shell floors coplanar with terrain z-fight in our z-buffered frame; the 2 cm lift is the documented stand-in | A new draw-space consumer of portal/cell polygons that forgets the lift re-opens a 2 cm seam at horizontal aperture edges (the #130 top-edge strip, ~7 px at 2.4 m); a visibility consumer that picks up the LIFTED transform re-opens the #119-residual horizontal-portal side-cull | retail draws cell geometry at the dat EnvCell origin (no lift) |
|
||||
|
|
@ -230,7 +229,6 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| TS-9 | MP3 (0x55) and MS-ADPCM (0x02) waves undecoded — affected sounds skipped; retail decoded both via winmm ACM | `src/AcDream.Core/Audio/WaveDecoder.cs:33` | Managed decoder (NAudio or similar) deferred; PCM covers the vast majority of ~3500 waves | Any MP3 (common for music-ish clips) or ADPCM cue plays as silence where retail plays it | winmm ACM path (r05 §2.1) |
|
||||
| TS-14 | Setup `Flatten` ignores ParentIndex part hierarchy (treats every placement as root-local); still in production use (GameWindow hydration, SkyRenderer) | `src/AcDream.Core/Meshing/SetupMesh.cs:15` | Most Setups are flat single-level rigs where root-local equals composed; hierarchical composition deferred ("Phase 3") | Any Setup with genuinely nested parts renders them at wrong offsets — mis-assembled multi-part objects in the Flatten paths | retail Setup ParentIndex chain composition |
|
||||
| TS-15 | No distance-driven degrade (LOD): always close-detail slot 0; plus the **#47** static `Degrades[0]` swap for 34-part humanoids only (structural sentinel detector) | `src/AcDream.Core/Meshing/GfxObjDegradeResolver.cs:57` (+ `src/AcDream.App/Rendering/GameWindow.cs:2608`) | LOD plumbing doesn't exist; slot 0 is correct for player + nearby NPCs; #47 closed the visible low-detail-arms bug without porting UpdateViewerDistance | Distant objects render max-detail (perf + wrong visuals where far meshes intentionally differ/hide parts); a future 34-part non-humanoid matching the sentinel gets the wrong mesh swap | `CPhysicsPart::UpdateViewerDistance` 0x0050E030; ::Draw 0x0050D7A0; ::LoadGfxObjArray 0x0050DCF0 |
|
||||
| TS-16 | Click picking is Stage A only: ray-vs-fixed-radius spheres (0.7–1.0 m) + screen rect matched to the indicator; retail's per-polygon refine deferred (**#71**); rect-over-circle is a user-approved UX divergence | `src/AcDream.Core/Selection/WorldPicker.cs:199` | Stage B only needed if visual testing surfaces Stage-A over-picks; sphere/rect + cell-BSP occlusion adequate so far | Clicks near (not on) an entity still select it; fixed radii can mis-prioritize overlapping candidates vs retail's polygon-accurate test | `CPolygon::polygon_hits_ray` 0x0054c889 |
|
||||
| TS-17 | AttackConditions suffix always empty in combat chat — formatting ported, wire bitflag not plumbed (Phase I.7 follow-up) | `src/AcDream.Core/Chat/CombatChatTranslator.cs:233` | Only the wire plumbing is missing; the holtburger-ported formatter is ready | Combat log omits "[Sneak Attack]"-style suffixes retail displays — hidden combat-mechanic feedback | holtburger chat.rs:588-595 |
|
||||
| TS-18 | `LandCell.BuildingCellId` (CSortCell building bridge) declared but never populated — always null in Stage 1 | `src/AcDream.Core/World/Cells/LandCell.cs:19` | Cell graph shipped in stages; population is explicitly membership Stage 2 (the outdoor→indoor entry path the physics digest flags as unvalidated) | Cell-graph paths that should discover a building's EnvCells from the outdoor cell silently find nothing — the doorway-entry bug class | CSortCell (acclient.h:31880) |
|
||||
| TS-19 | Legacy non-retail ChaseCamera (invented pitch/distance, K-fix12 airborne Z-pin) retained behind `ACDREAM_RETAIL_CHASE=0` / DebugPanel toggle; both update every frame | `src/AcDream.App/Rendering/ChaseCamera.cs:49` | Diagnostic before/after comparison path, "pending the follow-up deletion commit" | When toggled on, the eye diverges from retail's spring-arm — and the render roots at the VIEWER cell, so a non-retail eye changes the render root near doorways, masking or manufacturing flap symptoms during debugging | `CameraManager::UpdateCamera` (retail path in RetailChaseCamera.cs) |
|
||||
|
|
|
|||
|
|
@ -400,6 +400,11 @@ WorldBuilder is a dat editor; it does not have:
|
|||
- **Audio** — OpenAL pipeline, sound triggers (we have this)
|
||||
- **TurbineChat** + **slash commands** (we have this)
|
||||
- **Login + character selection flow** (we have this)
|
||||
- **World-object mouse selection** — WorldBuilder supplies mesh/DAT access but
|
||||
no retail client picker. Our narrow `RetailSelectionGeometryCache` reuses
|
||||
`DatCollection` to expose each GfxObj drawing-BSP root sphere and visual
|
||||
polygons; `WbDrawDispatcher` supplies the normal draw's current part
|
||||
transforms to the named-retail selection accumulator.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -524,6 +524,7 @@ behavior. Estimated 17–26 days focused work, 3–5 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 + vivid target indicator (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. 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.
|
||||
|
|
|
|||
145
docs/research/2026-07-17-retail-world-selection-pseudocode.md
Normal file
145
docs/research/2026-07-17-retail-world-selection-pseudocode.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Retail world selection and vivid target indicator
|
||||
|
||||
Oracle: September 2013 EoR named retail client. Cross-checks: the extracted
|
||||
WorldBuilder mesh path (DAT geometry and current part transforms) and ACE object
|
||||
identity/qualities. Neither reference implementation supplies the client picker;
|
||||
the named retail functions below are authoritative.
|
||||
|
||||
## Mouse selection
|
||||
|
||||
`SmartBox::find_object @ 0x00451C60`
|
||||
|
||||
```text
|
||||
remember the clicked viewport pixel
|
||||
clear the previous per-frame selection accumulator
|
||||
arm mouse selection for the next world draw
|
||||
```
|
||||
|
||||
`CPhysicsPart::Draw @ 0x0050D7A0`, `RenderDeviceD3D::DrawMesh @ 0x005A0860`,
|
||||
and `Render::GfxObjUnderSelectionRay @ 0x0054C740`
|
||||
|
||||
```text
|
||||
for each physics part which reaches the normal visible-mesh draw path:
|
||||
reject parts which do not belong to a server object
|
||||
transform the one world selection ray into this part's local coordinates
|
||||
divide by the part's GfxObj scale (do not change the ray parameter units)
|
||||
|
||||
sphere = gfxObj.drawing_sphere
|
||||
if sphere does not intersect the local ray:
|
||||
continue
|
||||
|
||||
if a polygon has already won globally and sphereDistance is farther:
|
||||
continue
|
||||
|
||||
if sphereDistance is nearer than the current sphere fallback:
|
||||
remember this object's id, part index, and sphereDistance
|
||||
|
||||
for polygons in the GfxObj's stored flat DAT order:
|
||||
if polygon_hits_ray(polygon, localRay, out t):
|
||||
remember only this FIRST polygon hit for this part
|
||||
if t is nearer than the current global polygon winner:
|
||||
remember this object's id, part index, and t
|
||||
break
|
||||
```
|
||||
|
||||
`CPolygon::polygon_hits_ray @ 0x005395E0`
|
||||
|
||||
```text
|
||||
if polygon.sides_type == ST_SINGLE (raw value 0)
|
||||
and dot(ray.direction, polygon.plane.normal) > 0:
|
||||
miss // back face
|
||||
|
||||
denominator = dot(ray.direction, polygon.plane.normal)
|
||||
if abs(denominator) < 0.0002:
|
||||
miss // parallel to the plane
|
||||
|
||||
t = plane/ray intersection time
|
||||
if t < 0:
|
||||
miss // behind the viewer
|
||||
|
||||
point = ray.origin + t * ray.direction
|
||||
normal = normalize(sum(cross(vertex[i] - vertex[0],
|
||||
vertex[i+1] - vertex[0])))
|
||||
for each ordered edge (previous -> current):
|
||||
inward = cross(normal, current - previous)
|
||||
if dot(point - previous, inward) < 0:
|
||||
miss
|
||||
return hit
|
||||
```
|
||||
|
||||
The broad-phase helper is retail `CSphere::sphere_intersects_ray @
|
||||
0x005377A0`: it rejects rays beginning in/on the sphere, requires squared ray
|
||||
direction length at least `0.0002`, solves the quadratic in double precision,
|
||||
and chooses the near root when non-negative or the far root otherwise.
|
||||
The ray origin is `Render::viewpoint` (the camera eye), not the unprojected
|
||||
near-plane point; `Render::pick_ray @ 0x0054B610` supplies only its direction.
|
||||
|
||||
`Render::GetMouseSelectionObjectID @ 0x0054C950`
|
||||
|
||||
```text
|
||||
if any polygon hit exists anywhere in the visible draw:
|
||||
return the closest polygon winner
|
||||
if any drawing-sphere hit exists:
|
||||
return the closest sphere fallback
|
||||
return no object
|
||||
```
|
||||
|
||||
Important consequences:
|
||||
|
||||
- The broad phase is each visible **GfxObj part's drawing sphere**, not a Setup
|
||||
selection sphere, physics collision shape, fixed creature hit box, or expanded
|
||||
screen rectangle.
|
||||
- A polygon hit on any visible part beats every sphere-only fallback.
|
||||
- Retail does not ray-test world-cell walls as a second, independent occluder.
|
||||
Occlusion is inherited from the same portal/viewcone draw traversal that decides
|
||||
which parts may call `GfxObjUnderSelectionRay`.
|
||||
- `BSPTREE::GetSphere @ 0x005397E0` returns the drawing BSP root sphere exactly;
|
||||
the DAT source is `GfxObj.DrawingBSP.Root.BoundingSphere`.
|
||||
|
||||
## Vivid target indicator
|
||||
|
||||
`VividTargetIndicator::SetSelected @ 0x004F5CE0`
|
||||
|
||||
```text
|
||||
if selected id is the player: clear it
|
||||
if object is player-owned or IN_CONTAINER: clear it
|
||||
publish the remaining id to SmartBox
|
||||
if enabled and an id remains:
|
||||
color = gmRadarUI::GetBlipColor(selected id)
|
||||
else:
|
||||
hide both indicator roots
|
||||
```
|
||||
|
||||
`VividTargetIndicator::Initialized @ 0x004F6C60`
|
||||
|
||||
```text
|
||||
onscreenRoot = child 0x10000038
|
||||
corners = children 0x10000039, 0x1000003A, 0x1000003B, 0x1000003C
|
||||
offscreenRoot = child 0x10000045
|
||||
```
|
||||
|
||||
The constructor resolves twelve source surfaces by calling `DBObj::GetByEnum`
|
||||
with RenderSurface DBO type `0x0C`, client-enum category `0x10000009`, and
|
||||
enum values `1..12`. Values `1..4` are the four on-screen corners.
|
||||
`CopyImage @ 0x004F5DD0`
|
||||
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)
|
||||
|
||||
```text
|
||||
if disabled, no selected id, or selected part was not reported by this world draw:
|
||||
hide indicator
|
||||
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
|
||||
```
|
||||
|
||||
There is no selected-world-mesh tint or luminosity mutation in
|
||||
`CPhysicsPart::Draw`, `ACCWeenieObject::SetSelected`, or the world mesh draw
|
||||
path. The visible notification is the colorized four-corner DAT indicator. A
|
||||
mesh shader tint would therefore be a new effect, not a retail port.
|
||||
Loading…
Add table
Add a link
Reference in a new issue