Commit graph

20 commits

Author SHA1 Message Date
Erik
edc9be3008 fix(paperdoll): port quest-gated Aetheria slots
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>
2026-07-13 09:55:20 +02:00
Erik
1be18cc4db fix(paperdoll): restore authored slot backgrounds
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>
2026-07-13 09:43:21 +02:00
Erik
609ed8bfc2 fix(ui): select items before drag mesh
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>
2026-07-13 09:28:28 +02:00
Erik
ace5880fed fix(ui): port retail item drag visuals
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>
2026-07-13 09:14:24 +02:00
Erik
0b74d19475 fix(ui): port exact paperdoll body selection
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>
2026-07-11 11:56:26 +02:00
Erik
e65119f0c6 feat(ui): complete retail quick-slot input
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>
2026-07-11 08:59:49 +02:00
Erik
05f6222865 fix(ui): complete live targeted healing flow
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.
2026-07-11 07:58:59 +02:00
Erik
eb6229394a fix(ui): unify target-mode primary clicks 2026-07-11 01:36:43 +02:00
Erik
7983309d23 feat(ui): centralize retail selection state 2026-07-11 00:51:20 +02:00
Erik
5d9e98c118 refactor(ui): own retained controller lifetimes 2026-07-10 23:35:26 +02:00
Erik
769ebef30d fix(ui): retail target-cursor shape (pending over UI, world-object driven) + visible UseDone refusals
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>
2026-07-03 10:27:33 +02:00
Erik
047410ccc9 fix(ui): retail-faithful use-target compat gate + paperdoll doll self-target
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>
2026-07-03 10:09:13 +02:00
Erik
b7dc91a053 feat(ui): D.2b item interaction + retail cursors + live character sheet
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>
2026-07-03 09:18:43 +02:00
Erik
8fa66c23d5 fix(D.2b): Slice 2 — retail-exact doll pose, camera + heading (visual gate)
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>
2026-06-25 13:16:27 +02:00
Erik
594942f127 fix(D.2b): Slice 2 — Slots caption white + left-aligned (visual gate)
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>
2026-06-24 22:31:44 +02:00
Erik
fe319bd2aa fix(D.2b): Slice 2 visual gate — frame the whole doll + caption the Slots toggle
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>
2026-06-23 09:58:22 +02:00
Erik
c5604ff6ad feat(D.2b): Slice 2 — paperdoll armor/non-armor partition + Slots toggle state
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>
2026-06-23 08:57:50 +02:00
Erik
a4c68520ea fix(D.2b): paperdoll empty slots show a visible frame (not transparent)
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>
2026-06-23 00:02:11 +02:00
Erik
3b8a39c49e fix(D.2b): PaperdollController review fixes — player-scope Concerns, loop clarity
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>
2026-06-22 22:32:34 +02:00
Erik
9f187c3e31 feat(D.2b): PaperdollController — equip slots bind + wield drag handler (Slice 1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 22:24:52 +02:00