Add the three authored blue, yellow, and red sigil backgrounds and drive their visibility from player PropertyInt.AetheriaBitfield bits 1/2/4 at login and on live updates, matching gmPaperDollUI::UpdateAetheria. Include the sigil equip masks in the shared slot table and narrow AP-108.
Co-Authored-By: Codex <codex@openai.com>
Port the retail UIItem catalog path so every supported equipment location resolves its exact empty-state silhouette from live DAT. Share one definition table between equip behavior and presentation, wire runtime plus UI Studio, pin all 21 prototypes and surfaces, and retire AP-66.
Co-Authored-By: Codex <codex@openai.com>
Port ItemList_BeginDrag's select-before-waiting order so a direct press-drag shows the selection indicator on its first frame across inventory, paperdoll, and toolbar item lists.
Co-Authored-By: Codex <codex@openai.com>
Keep retail's underlay-free m_pDragIcon separate from the full cell icon and reveal the authored ghost mesh on physical source cells for the complete drag lifecycle. This removes the backpack backing from the cursor and retires AP-47.
Co-Authored-By: Codex <codex@openai.com>
Resolve and sample retail's authored nine-color paperdoll click map, preserve local click coordinates, and select the stable highest-priority worn item with the player fallback. Keep targeted-use body clicks routed to self and pin both synthetic and live-DAT conformance.
Co-Authored-By: Codex <codex@openai.com>
Port global toolbar use/select/create actions, migrate the collapsed Ctrl-number bindings, and route them through a focused retained-UI controller. Preserve shortcut aliases as aliases across inventory and paperdoll drops with retail's neutral/accept/reject drag states, preventing physical item moves such as unwielding an equipped helmet.
Co-Authored-By: Codex <codex@openai.com>
Route ACE server commands through the existing chat path, bind the retail paperdoll hit mask instead of its obscured viewport, and prefer authoritative private health vitals. Record the user-confirmed live gate and pin the production DAT widget type in tests.
Visual gate round 2 (user, retail oracle): the target cursor over UI must
be the 0x27 four-arrows PENDING cursor — retail's UpdateCursorState
(0x00564630) keys valid/invalid solely off the SmartBox found object,
i.e. the WORLD entity under the cursor. And "can't heal myself" turned
out to be TWO stacked causes, both fixed:
- Cursor: UI hover no longer forces TargetInvalid (that arm was a
non-retail invention) and the doll/status-bar hover providers are
gone (UiElement.UseTargetGuidProvider deleted). Valid/invalid now
come from (a) a hovered occupied item slot's own item, or (b) the
world entity under the cursor via a new worldTargetProvider — the
B.4b screen-rect picker extracted into GameWindow.PickWorldGuidAtCursor
and shared by click + hover.
- Self-heal: the world picker always skipped the local player
(skipServerGuid), so clicking your own toon in target mode could
never acquire self. Target-use picks now include self (retail lets
you kit-heal yourself by clicking your character); plain selection
keeps the exclusion. The doll click still self-targets.
- Silent refusals: the [use-target] log proved the UseWithTarget action
WAS sent (kit 0x00220008 / TargetType 0x10, target self) — ACE
refused with WeenieError.YouArentTrainedInHealing (0x04FC) and we
never parsed UseDone (0x01C7). Now dispatched + surfaced as a chat
line via WeenieErrorText (interim subset map, register AP-74, #166
ports the retail String-table lookup).
Full suite green (3,295).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Visual gate on the item-interaction slice found two target-use bugs:
the valid-target bullseye showed over inventory ITEMS (a coat is not a
valid healing-kit target) while the paperdoll doll showed blocked (it
should mean SELF).
- TargetCompatible is now a faithful port of
ItemHolder::IsTargetCompatibleWithTargetingObject (0x00588070):
a location constraint applies only when the least-limited target-use
bit (ItemUses::GetLeastLimitedTargetUse 0x004fcd50, new
ItemUseability.LeastLimitedTargetUse) is Contained/Wielded; the
player as target requires the Self bit (IsUseable_SelfTarget
0x004fcd30); and EVERY target passes retail's kind gate
source._targetType & target->InqType(). The previous hand-rolled
arms (Remote => accept anything, invented Viewed/Contained accepts,
self path skipping the kind gate) were the yellow-over-items bug.
Retail's tradeState refusal is skipped (no trade state yet).
- The paperdoll doll viewport now carries UseTargetGuidProvider =
player and Clicked -> AcquireSelfTarget (UiViewport gains an opt-in
Clicked handler), so hovering the doll resolves the self cursor and
clicking it applies the armed kit to yourself.
- AcquireTarget logs one [use-target] line (wire useability, TargetType
mask, target kind, decision) so live refusals are diagnosable from
the launch log.
- Test fixtures updated from the guessed kit useability 0x000A0008 to
the real USEABLE_SOURCE_CONTAINED_TARGET_REMOTE_OR_SELF (0x00220008,
ACE Usable.SourceContainedTargetRemoteOrSelf) + TargetType masks;
new coverage pins the kind gate, the Self-bit requirement, the
missing-mask-matches-nothing shape, and the Contained location rule.
Full suite green (3,294).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lands the codex-worktree D.2b stream plus the extraction the 2026-07-02
UI architecture review mandated before commit:
- ItemInteractionController: single owner of double-click use/equip/
container-open, targeted-use mode (health kits), drag-out drop;
toolbar shortcut drags don't drop the real item. ItemEquipRules for
multi-slot (coat) coverage via equip masks.
- Cursor phase: CursorFeedbackController (semantic priority chain:
drag > resize > window-move > target-mode > text) + RetailCursorCatalog
(enums 0x27/0x28/0x29, hotspot 14,14; ClientUISystem::UpdateCursorState
0x00564630) resolved through the portal EnumIDMap chain by
RetailCursorResolver; RetailCursorManager applies dat cursor art to the
OS cursor. Register row AP-72 covers the OS standard-cursor fallback.
- Character window goes live: CharacterSheetProvider owns sheet assembly,
XP-curve/raise-cost math and the raise flow — extracted out of
GameWindow per Code Structure Rule 1 instead of committing the ~430-line
feature body there. Optimistic XP/credit debits go through eventful
store APIs (new ClientObjectTable.UpdateInt64Property +
LocalPlayerState.DebitIntProperty/DebitInt64Property) instead of raw
property-dictionary writes; register row AP-73 covers the still-missing
raise ledger (#163).
- RetailWindowFrame: the shared nine-slice window mount recipe; the
character window uses it, remaining windows migrate via #164.
- Status-bar buttons toggle inventory/character windows; retail row-major
backpack ordering; WorldSession.SendUseWithTarget + raise/train sends.
GameWindow shrinks 14,214 -> 13,877 lines despite the new features; the
sheet/raise logic is unit-tested in CharacterSheetProviderTests instead
of trapped in the god object. Build green; full suite 3,286 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The paperdoll doll now matches retail: correct held pose, framing, and
facing. Three decomp-sourced fixes closed the visual gate.
Pose: cdb-confirmed m_didAnimation = 0x030003C0 (gmPaperDollUI), played
once + HELD (set_sequence_animation framerate=0, RedressCreature
0x004a3c22). Dumping the dat showed the 29-frame anim has only two
distinct keyframes — frame 0 (transitional, bent arm) and frames 1..28
(byte-identical: the settled stance, arms down + leg back) — so
ApplyPaperdollPose applies the LAST frame statically (no looping).
Camera: ported verbatim from UIElement_Viewport::SetCamera (decomp
0x004a5a39). position (0.12,-2.4,0.88); direction (0,0,0) => IDENTITY
view frame => look straight down +Y, ZERO yaw; FOV pi/4 (CreatureMode
ctor default 0x004543cf); ambient 0.3. The prior hand-tune aimed the
camera at mid-body, adding a ~2deg yaw that turned the doll's face away
— full-body framing comes from eye-height + FOV, not aiming.
Heading: retail Frame::set_heading(h) (0x00535e40) builds facing
(sin h, cos h); System.Numerics CreateFromAxisAngle(+Z, +h) rotates the
body's default +Y forward to (-sin h, cos h) — the X-lean was MIRRORED
(~22deg), the real cause of the turned-away face. Negate the angle to
land on retail's facing.
Wrap-up: stripped the temporary O/P pose-frame stepper + Slice2
diagnostics; divergence register AP-66 reworded, AP-67 (RTT doll render
vs in-cell CreatureMode::Render) + AP-68 (per-race UpdateForRace
unimpl) added; DollCameraTests pinned to the retail values + a zero-yaw
guard. tools/cdb/paperdoll-pose.cdb = the pose-DID capture script.
Build + full suite green (Core 1579 / Core.Net 343 / App 597 / UI 425).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
User gate: the "Slots" toggle caption was gold (should be white) and centered
(should sit at the left, before the slots). UiButton gains a LabelAlignment
(Center default / Left); the paperdoll caption is set white + Left-aligned.
The retail checkbox box (green-checked / circle-unchecked) + the exact pose
are the two remaining pieces, done next.
Build + full App suite green (596).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Visual gate 1 (user): the doll rendered but only the legs showed (camera
aimed at the model origin = the feet) and the Slots button was invisible.
- DollCamera: aim the look-at at mid-body (~0.95 m) and stand back ~3.7 m
so the whole ~1.9 m figure fits. (Size is a later retail-comparison
polish per the user.)
- PaperdollController: the Slots button (0x100005BE) is found + wired
(diagnostic confirmed armorSlots=9/9, viewport=UiViewport,
slotsButton=UiButton) but its dat element has no face sprite, so it drew
nothing. Give it a gold "Slots" caption (UiButton.Label, like chat Send)
via a new datFont param on Bind. Temporary [Slice2-paperdoll] diagnostic
logs the button rect + the found widgets (stripped at wrap-up).
Idle animation deferred to a focused follow-up (faithful idle needs a full
AnimatedEntity + Sequencer through the TickAnimations multi-branch path +
re-dress coordination — real integration risk vs the verified static doll).
Build + full App suite green (594).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds ArmorSlotElementIds (the exact 9 ids gmPaperDollUI::ListenToElementMessage
flips per decomp 175674-175706) and PaperdollViewState (SlotView/DollVisible/
ArmorSlotsVisible/Toggle) as a nested public class on PaperdollController.
Wires ApplyView() into the constructor so the Slots button (0x100005BE) drives
armor-slot Visible and the doll viewport Visible on every click. Initial state
is doll-view (armor slots hidden). The doll viewport element (0x100001D5) is
bound as UiElement? so this slice stays independent of Slice 3's IUiViewportRenderer
seam; _armorSlots collects whichever of the 9 ids were found in the live layout.
Three new unit tests verify the id set, the default state, and the round-trip
toggle against the public PaperdollViewState surface only.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
User correction at the visual gate: the green "figure" in the paperdoll is
the LIVE 3D character (the doll — can be naked), NOT per-slot silhouettes.
The default view (Slots button OFF) = the doll + non-armor slots; pressing
Slots hides the doll and shows the armor slots. So the doll + the Slots
toggle are Slice 2 (the UiViewport); there are no per-slot silhouette sprites
to chase.
For Slice 1 (no doll yet) the right empty-slot look is simply a VISIBLE FRAME
so every slot position can be seen + used — which fixes the "I see only slots
with equipment, no empty slots" report. The earlier transparent (EmptySprite=0)
came from the stale silhouette assumption. PaperdollController now takes an
emptySlotSprite; GameWindow passes the inventory grid's empty square
(0x06004D20) for a consistent visible frame.
App suite 580 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code-quality review on Task 5:
- I1: Concerns was unscoped (CurrentlyEquippedLocation != None) → an NPC's
wielded item (which also carries that wire field) triggered spurious full
repaints. Narrowed to (WielderId==p || ContainerId==p), matching
InventoryController; OnObjectMoved's from/to-player backstop still catches
unwield-into-a-side-bag. Populate's own scope already prevented wrong data;
this kills the wasted repaints.
- I2: replaced the dual-`index++` (assign-vs-skip) with a for-i loop;
SlotIndex = SlotMap position (= the drag payload's SourceSlot on unwield).
- M1/M2: comment that the cell's SpriteResolve + the discrete-slot accept/
reject ring (0x060011F9/F8, not the grid insert-arrow) are factory-provided.
- Added two behavioral tests: a live player wield repaints the slot
(ObjectMoved → Concerns → Populate); an NPC's wielded item never appears on
the doll (player-scoping).
- Synced the stale spec §4b/§6c/§8 to the Task-3 Option-1 reality (the
optimistic wield is ContainerId-based and does NOT write WielderId).
App suite 580 passed / 0 failed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>