docs(B.5): ship handoff + roadmap/CLAUDE update + file #63 #64

Phase B.5 (ground-item pickup, close-range path) shipped and
visual-verified 2026-05-14 at Holtburg. M1 demo target 4/4 ("pick up
an item") met.

New ship-handoff doc captures the 5-commit history including the
post-visual-test PickupEvent (0xF74A) wire-handler fix that closes
the local-despawn gap.

Roadmap and CLAUDE.md updated to reflect the ship + the new follow-up
issues:

  - #63 (MEDIUM) — server-initiated MoveToObject auto-walk not
    honored; blocks double-click pickup + out-of-range F. Filed as
    candidate Phase B.6. holtburger has the reference implementation.
  - #64 (LOW) — local-player pickup animation does not render
    (retail observers see it correctly). Likely a self-echo filter
    dropping UpdateMotion(Pickup) on the local player.

Carry-overs from B.4c (#61 link-cycle flash, #62 PARTSDIAG null-guard)
unchanged.
This commit is contained in:
Erik 2026-05-14 16:23:20 +02:00
parent f7636a9e78
commit d132fcccfb
4 changed files with 364 additions and 17 deletions

View file

@ -619,19 +619,26 @@ acdream's plan lives in two files committed to the repo:
**Currently in Phase L.2 (Movement & Collision Conformance).** L.2a slices
1+2+3 + L.2d slice 1+1.5 + L.2g slice 1 + L.2g slice 1b + L.2g slice 1c +
**Phase B.4b** + **Phase B.4c** all shipped and visual-verified 2026-05-13.
The M1 demo target *"open the inn door"* is met **with full visual feedback**:
double-click a door in the Holtburg inn doorway → `WorldPicker.Pick` finds
the door entity → `BuildUse` sends `0xF7B1/0x0036` to ACE → ACE broadcasts
`SetState (0xF74B)` with `ETHEREAL` bit → `ShadowObjectRegistry.UpdatePhysicsState`
(L.2g slice 1) mutates the cached state (via fixed ServerGuid→entity.Id
translation, L.2g slice 1c) → `CollisionExemption.ShouldSkip` exempts on
ETHEREAL-alone (L.2g slice 1b) → player walks through → door swing animation
plays (B.4c: spawn-time `AnimationSequencer` registration + `OnLiveMotionUpdated`
routing for door entities). Issue #57 (B.4 handler gap) is closed. Issue #58
(door swing animation) is closed by B.4c. Issues #61 (link→cycle boundary
flash) and #62 (PARTSDIAG null-guard) are filed as M1-deferred polish.
**Phase B.4b** + **Phase B.4c** all shipped and visual-verified 2026-05-13;
**Phase B.5** (ground-item pickup, F-key) shipped and visual-verified
2026-05-14. The M1 demo target *"pick up an item"* is met for the
close-range path — single-click a ground item to select, walk within
~0.6 m of it, press F, and the item is removed from the world and added
to the player's inventory. Wire chain: `InteractRequests.BuildPickUp`
sends `PutItemInContainer (0xF7B1/0x0019)`; ACE despawns the item with
`GameMessagePickupEvent (0xF74A)` (NOT `0xF747 DeleteObject` — the
distinction surfaced during visual testing and is fixed by the new
`PickupEvent.cs` parser routed through the shared `EntityDeleted`
event). The M1 demo target *"open the inn door"* remains met from B.4b
+ B.4c. Issue #57 (B.4 handler gap) is closed. Issue #58 (door swing
animation) is closed by B.4c. Issues #61 (link→cycle boundary flash),
#62 (PARTSDIAG null-guard), **#63 (server-initiated MoveToObject
auto-walk not honored — blocks out-of-range pickup / Use)**, and **#64
(local-player pickup animation does not render)** are filed as
M1-deferred follow-up.
**B.5 ship handoff:** [`docs/research/2026-05-14-b5-shipped-handoff.md`](docs/research/2026-05-14-b5-shipped-handoff.md)
— full evidence for the 5 commits across InteractRequests / GameWindow / WorldSession + the bonus `PickupEvent (0xF74A)` wire-handler fix that closes the despawn gap.
**B.4c ship handoff:** [`docs/research/2026-05-13-b4c-shipped-handoff.md`](docs/research/2026-05-13-b4c-shipped-handoff.md)
— full evidence for the 4 commits + 2 bonus discoveries (stance-value wrong
`0x01` vs `0x3D` causing underground doors; link→cycle boundary flash).
@ -725,11 +732,15 @@ project.
packets, wire the handlers; if it is silent, investigate ACE's NPC handler
configuration. ~30 min spike, outcome determines whether NPC interaction
needs a full phase or is a one-commit fix.
- **Phase B.5 — Ground item pickup (F key) (M1 critical path).** The
`SelectionPickUp` input action + F-key binding exist in `KeyBindings` but
`OnInputAction` has no case for it. `BuildUse` IS `BuildPickUp` (same wire
format). One-commit addition: add `SelectionPickUp` case to `GameWindow.
OnInputAction` → call `InteractRequests.BuildPickUp(seq, _selectedGuid)`. ~30 min.
- **Phase B.6 — Client-side MoveToObject auto-walk handling (closes #63).**
ACE auto-walks the player to out-of-range Use / Pickup targets via
`CreateMoveToChain` + `EnqueueBroadcastMotion(MoveToObject)`, but our client
doesn't honor the inbound motion broadcast — character drifts toward the
target and snaps back, ACE's chain times out. Reference implementation
exists in `references/holtburger/crates/holtburger-core/src/client/simulation.rs`
(the `approximate_move_to_object_projection_target` + `MoveToObject` case).
Unlocks double-click pickup, F-key pickup from any distance, Use on
out-of-range NPCs / corpses. Probably 1-2 commits + visual verification.
- **Triage the chronic open-issue list** in `docs/ISSUES.md`#2 (lightning),
#4 (sky horizon-glow), #28 (aurora), #29 (cloud thinness), #37 (humanoid
coat), #41 (remote-motion blips) have been open since April/early-May and

View file

@ -46,6 +46,89 @@ Copy this block when adding a new issue:
# Active issues
## #64 — Local-player pickup animation does not render
**Status:** OPEN
**Severity:** LOW (visual feedback only — pickup completes correctly)
**Filed:** 2026-05-14 (B.5 visual verification)
**Component:** motion / animation routing for local player
**Description:** When `+Acdream` picks up an item (B.5 close-range
path), retail observers see the character play the pickup animation
correctly, but the local view shows no pickup animation. The item
despawns, the inventory updates, but the character's own
bend-down-and-grab animation is missing.
**Root cause / hypothesis:** ACE broadcasts `Motion(MotionCommand.Pickup)`
via `Player_Inventory.AddPickupChainToMoveToChain` (line 711713,
`EnqueueBroadcastMotion(motion)`), which arrives as a normal
`UpdateMotion (0xF74D)` packet. Retail observers route it through
their remote-creature animation pipeline and render the pickup. For
the local player, our `OnLiveMotionUpdated` likely filters self-echoes
(local player drives its own motion via prediction, not server
echoes) and drops the pickup motion. The pickup is a one-shot
animation initiated by the server, so the prediction path has no
trigger — and the echo path is filtered.
**Acceptance:** When `+Acdream` picks up an item, the local view shows
the same pickup animation retail observers see. Probably resolved by
either (a) admitting server-initiated one-shot motions through the
local-player motion filter, or (b) generating the pickup animation
locally on send (mirroring retail's client behavior).
**Files:** `src/AcDream.App/Rendering/GameWindow.cs` `OnLiveMotionUpdated`
(motion routing); the self-echo filter is somewhere along this path.
**Estimated scope:** Small-to-medium. Mostly investigation +
12 commits.
---
## #63 — Server-initiated auto-walk (MoveToObject) not honored
**Status:** OPEN
**Severity:** MEDIUM (blocks out-of-range Use + Pickup; close-range
works fine)
**Filed:** 2026-05-14 (B.5 visual verification)
**Component:** motion / inbound MoveToObject handling
**Description:** When the player triggers a Use or PutItemInContainer
on a target outside ACE's `WithinUseRadius` (default 0.6 m), ACE
runs server-side auto-walk via `CreateMoveToChain`
`PhysicsObj.MoveToObject` + `EnqueueBroadcastMotion(Motion(MoveToObject, target))`.
Our client receives the `UpdateMotion(MoveToObject)` broadcast for
the player but doesn't honor it: the character either visually
drifts a bit toward the target and snaps back, or just stands still.
ACE's MoveToChain then times out, the `success: false` path
broadcasts `InventoryServerSaveFailed (ActionCancelled)`, and the
pickup/use never completes.
**User-visible symptom:** Double-click a ground item from any
distance, or F-key it from > 0.6 m: character partially walks toward
the item, then flips back to original position. No pickup.
**Reference:** [holtburger simulation.rs:3341 + 178191](references/holtburger/crates/holtburger-core/src/client/simulation.rs)
already implements client-side `MoveToObject` motion projection +
auto-walk handling. That's the shape of the fix.
**Root cause:** Our `OnLiveMotionUpdated` has no handler for the
`MoveToObject` motion type; the broadcast is silently dropped.
**Acceptance:** Double-click a ground item from 25 m away. Character
auto-walks to within use radius, ACE's MoveToChain confirms success,
pickup completes (including the existing PickupEvent despawn). Same
behavior for Use on out-of-range NPCs.
**Files:** `src/AcDream.App/Rendering/GameWindow.cs` `OnLiveMotionUpdated`
(routing); likely a new `MoveToObjectMotion` handler in the motion /
prediction layer + a server-acked position-update echo so ACE sees the
player has reached the target.
**Estimated scope:** Medium. Probably its own phase (B.6 or similar);
not a one-commit fix. Compose from holtburger's pattern.
---
## #62 — PARTSDIAG null-guard for sequencer-driven entities
**Status:** OPEN

View file

@ -67,6 +67,7 @@
| C.1.5a | Portal PES wiring — server-spawned `WorldEntity` entities now fire their `Setup.DefaultScript` through the already-shipped `PhysicsScriptRunner` on enter-world. New ~70-line [`EntityScriptActivator`](../../src/AcDream.App/Rendering/Vfx/EntityScriptActivator.cs) class wires into `GpuWorldState`'s spawn lifecycle (`AppendLiveEntity``OnCreate`, `RemoveEntityByServerGuid``OnRemove`). Resolver lambda in `GameWindow` hits `_dats.Get<Setup>(...)?.DefaultScript.DataId` with defensive try/catch returning `0u` on miss. Activator also seeds `_particleSink.SetEntityRotation` so hook offsets transform from entity-local to world space correctly. **Verified at the Holtburg Town network portal**: 10-hook portal script fires end-to-end with correct color, persistence, orientation, multi-emitter dispatch. **Known limitation surfaced and filed as issue #56**: `ParticleHookSink` ignores `CreateParticleHook.PartIndex`, so the 10 emitters collapse to one root position instead of distributing across the portal Setup's parts — visually produces a compressed, partly-ground-buried swirl. Mechanism is correct; per-part transform handling is the next vfx-pipeline work (blocks slice 2 visual delight; affects every multi-emitter PES). Spec: [`docs/superpowers/specs/2026-05-12-phase-c1.5a-portals-design.md`](../superpowers/specs/2026-05-12-phase-c1.5a-portals-design.md). Plan: [`docs/superpowers/plans/2026-05-12-phase-c1.5a-portals.md`](../superpowers/plans/2026-05-12-phase-c1.5a-portals.md). | Live ✓ (with #56) |
| B.4b | Outbound Use handler wiring + 4 bonus fixes (L.2g slices 1b+1c, double-click detection, DoubleClick gate fix). Shipped 2026-05-13 (branch `claude/compassionate-wilson-23ff99`, merge pending). Closes #57. Files #58 (door swing animation, M1-deferred). `WorldPicker.BuildRay` + `Pick` (ray-sphere entity pick with inside-sphere guard); `GameWindow.OnInputAction` switch cases for `SelectLeft` / `SelectDblLeft` / `UseSelected`; `_entitiesByServerGuid` reverse-lookup dict + ServerGuid→entity.Id translation in `OnLiveStateUpdated` (L.2g slice 1c — THE actual blocker); `InputDispatcher` double-click detection 500ms threshold (binding was dead code without it); `CollisionExemption.ShouldSkip` widened to ETHEREAL-alone (ACE Door.Open() sends `state=0x0001000C`, not `0x14`). M1 demo target "open the inn door" verified at Holtburg inn doorway. Plan: [`docs/superpowers/plans/2026-05-13-phase-b4b-plan.md`](../superpowers/plans/2026-05-13-phase-b4b-plan.md). Handoff: [`docs/research/2026-05-13-b4b-shipped-handoff.md`](../research/2026-05-13-b4b-shipped-handoff.md). | Live ✓ |
| B.4c | Door swing animation. Shipped 2026-05-13 (branch `claude/phase-b4c-door-anim`, merge pending). Closes #58. Files #61 (AnimationSequencer link→cycle boundary flash; low-severity polish) + #62 (PARTSDIAG null-guard; latent). Spawn-time `AnimationSequencer` registration for door entities in `GameWindow.OnLiveEntitySpawnedLocked`: initial cycle seeded from `spawn.PhysicsState` (Off for closed, On for open). Shared `IsDoorName` / `IsDoorSpawn` helpers. `[door-cycle]` diagnostic in `OnLiveMotionUpdated` (gated on `ACDREAM_PROBE_BUILDING`). Bonus stance-value fix: `NonCombat = 0x3D` not `0x01` (wrong value caused doors to render halfway underground via empty sequencer frames). Visual-verified 2026-05-13 at Holtburg inn doorway: swing-open + swing-close cycles both play. M1 demo target "open the inn door" now has full visual feedback. Plan: [`docs/superpowers/plans/2026-05-13-phase-b4c-plan.md`](../superpowers/plans/2026-05-13-phase-b4c-plan.md). Handoff: [`docs/research/2026-05-13-b4c-shipped-handoff.md`](../research/2026-05-13-b4c-shipped-handoff.md). | Live ✓ |
| B.5 | Ground-item pickup (F-key, close-range path). Shipped 2026-05-14 (branch `claude/phase-b5-pickup`, merge pending). Closes M1 demo target 4/4 *"pick up an item"*. New `InteractRequests.BuildPickUp(seq, itemGuid, containerGuid, placement)` builds the 24-byte `PutItemInContainer (0xF7B1/0x0019)` wire body verified against `references/ACE/Source/ACE.Server/Network/GameAction/Actions/GameActionPutItemInContainer.cs`. New private `GameWindow.SendPickUp(uint itemGuid)` helper mirrors `SendUse`'s gate-on-InWorld pattern; `case InputAction.SelectionPickUp` in `OnInputAction` switch routes the F-key through `_selectedGuid`. **Bonus wire-handler fix (Task 2b):** ACE despawns picked-up items via `GameMessagePickupEvent (0xF74A)`, not the `GameMessageDeleteObject (0xF747)` we already handled — surfaced during visual testing (item kept rendering on ground after successful server-side pickup). New `PickupEvent.cs` parser + `WorldSession` dispatch branch adapt to `DeleteObject.Parsed` and reuse the existing `EntityDeleted → OnLiveEntityDeleted → RemoveLiveEntityByServerGuid` chain. Files #63 (server-initiated `MoveToObject` auto-walk not honored — out-of-range pickup / double-click fails server-side timeout) + #64 (local-player pickup animation does not render). Visual-verified 2026-05-14 at Holtburg: 3 successful close-range pickups (Pink Taper + Violet Tapers), item despawns locally as ACE acks. Plan: [`docs/superpowers/plans/2026-05-14-phase-b5-pickup.md`](../superpowers/plans/2026-05-14-phase-b5-pickup.md). Handoff: [`docs/research/2026-05-14-b5-shipped-handoff.md`](../research/2026-05-14-b5-shipped-handoff.md). | Live ✓ |
| C.1.5b | Per-part PES transforms + dat-hydrated entity DefaultScript dispatch. Closes issue #56. Shipped 2026-05-12 across 5 commits (`1e3c33b` docs+plan, `f3bc15e` SetupPartTransforms helper, `11521f4` ParticleHookSink applies `CreateParticleHook.PartIndex`, `5ca5827` activator refactor + GameWindow resolver lambda, `8735c39` GpuWorldState 4 new fire-sites). **Slice A** — new [`SetupPartTransforms.Compute(setup)`](../../src/AcDream.Core/Meshing/SetupPartTransforms.cs) walks `PlacementFrames[Resting]``[Default]` → first-available (mirrors `SetupMesh.Flatten` priority) and returns `Matrix4x4` per part; new `ParticleHookSink.SetEntityPartTransforms(entityId, partTransforms)` mirrors the existing `_rotationByEntity` pattern; `SpawnFromHook` now transforms hook offset through `partTransforms[partIndex]` before applying entity rotation. **Slice B** — activator's `ServerGuid==0` guard relaxed: keys by `entity.ServerGuid` when non-zero, else `entity.Id` (collision-free with server guids in the `0x40xxxxxx` interior / `0x80xxxxxx` scenery / `0xC0xxxxxx` ranges). Resolver delegate refactored to return `ScriptActivationInfo(ScriptId, PartTransforms)` so one dat lookup yields both pieces. `GpuWorldState` fires the activator from 4 new sites: `AddLandblock` + `AddEntitiesToExistingLandblock` (Far→Near promotion) for OnCreate, `RemoveLandblock` + `RemoveEntitiesFromLandblock` (Near→Far demotion) for OnRemove. ServerGuid==0 filter on AddLandblock avoids double-firing pending-bucket merges. **Reality discovery folded into spec §3**: EnvCell `StaticObjects` are already hydrated as `WorldEntity` instances by `GameWindow.BuildInteriorEntitiesForStreaming` (with stable `entity.Id` in `0x40xxxxxx`) — no synthetic-ID scheme or separate walker class needed (handoff §4 Q1/Q2 mooted). **Visual verification 2026-05-12**: Holtburg Town network portal swirl distributes across the arch (no ground-burial), Inn fireplace flames render over the firebox, cottage chimney smoke columns render, spell-cast animation-hook particles all match retail. 18 new + 4 updated tests, all Vfx/Meshing/Streaming/Activator green. Spec: [`docs/superpowers/specs/2026-05-13-phase-c1.5b-design.md`](../superpowers/specs/2026-05-13-phase-c1.5b-design.md). Plan: [`docs/superpowers/plans/2026-05-13-phase-c1.5b.md`](../superpowers/plans/2026-05-13-phase-c1.5b.md). | Live ✓ |
Plus polish that doesn't get its own phase number:

View file

@ -0,0 +1,252 @@
# Phase B.5 shipped — handoff (visual-verified 2026-05-14)
**Date:** 2026-05-14.
**Branch:** `claude/phase-b5-pickup` (ready to merge to main; controller handles the merge after this doc lands).
**Predecessors:**
- [docs/research/2026-05-13-b4c-shipped-handoff.md](2026-05-13-b4c-shipped-handoff.md) — B.4c (door swing) shipped immediately before.
- [docs/research/2026-05-13-b5-pickup-handoff.md](2026-05-13-b5-pickup-handoff.md) — fresh-session handoff that scoped this phase.
- [docs/superpowers/plans/2026-05-14-phase-b5-pickup.md](../superpowers/plans/2026-05-14-phase-b5-pickup.md) — implementation plan (2 tasks).
---
## TL;DR
Phase B.5 **shipped end-to-end and is visual-verified 2026-05-14.** The
M1 demo target *"pick up an item"* is met for the close-range path —
single-click a ground item to select, walk within ~0.6 m of it, press
F, and the item is removed from the world and added to the player's
inventory.
The plan budgeted 2 implementation tasks (~50 LOC). Visual testing
surfaced **one wire-handler gap** that became Task 2b: ACE despawns
picked-up items via `GameMessagePickupEvent (0xF74A)`, not the
`GameMessageDeleteObject (0xF747)` we already handled — without that
fix the pickup succeeded server-side but the item kept rendering on
the ground locally. Caught and fixed in the same session.
Two known gaps remain, filed as issues for follow-up:
- **#63 (MEDIUM)** — Server-initiated auto-walk for out-of-range Use /
PickUp not honored. Double-click a ground item from > 0.6 m and the
character partially walks then snaps back; ACE's `MoveToChain` times
out. This is a separate motion-handling phase, not a B.5 regression.
- **#64 (LOW)** — Local-player pickup animation doesn't render
(retail observers see it correctly; local view is silent). Likely a
self-echo filter dropping `UpdateMotion(Pickup)` on the local player.
---
## What shipped on this branch
| # | Commit | Subject | Task |
|---|---|---|---|
| 1 | `e8a20f2` | `feat(B.5): InteractRequests.BuildPickUp — PutItemInContainer 0x0019` | Task 1 |
| 2 | `ced1b85` | `test(B.5): exercise i32 sign-correctness for BuildPickUp.placement` | Task 1 code-review fix |
| 3 | `54d9bb9` | `feat(B.5): SendPickUp helper + F-key SelectionPickUp wiring` | Task 2 |
| 4 | `5c24f6c` | `docs(B.5): implementation plan from writing-plans skill` | Plan doc |
| 5 | `f7636a9` | `fix(B.5): handle PickupEvent 0xF74A so picked-up items despawn locally` | Task 2b (post-visual-test fix) |
Plus the predecessor handoff (`86440ff`) that started the branch.
**Build:** clean.
**Tests:** `dotnet test -c Debug` shows AcDream.Core.Net.Tests 290/290
passing (was 287 at branch start; +3 from Task 2b's PickupEvent tests;
the two BuildPickUp tests landed inside the same project's existing
file). Failure count unchanged at 8 pre-existing baseline in
AcDream.Core.Tests.
---
## What the code does end-to-end
**Outbound (Tasks 1 & 2):**
1. User single-clicks a ground item near `+Acdream`.
`case InputAction.SelectLeft → PickAndStoreSelection(useImmediately: false)`
runs B.4b's `WorldPicker.Pick`, finds the item, sets `_selectedGuid`.
Log: `[B.4b] pick guid=0x… name=…`.
2. User presses F.
`case InputAction.SelectionPickUp → SendPickUp(_selectedGuid)` builds
the wire body via `InteractRequests.BuildPickUp(seq, itemGuid,
_playerServerGuid, placement: 0)` and posts it through
`_liveSession.SendGameAction`. Log: `[B.5] pickup item=… container=… seq=…`.
3. Wire layout (24 bytes): `0xF7B1 envelope | seq | 0x0019 opcode |
itemGuid u32 | containerGuid u32 | placement i32`. Verified against
`references/ACE/Source/ACE.Server/Network/GameAction/Actions/GameActionPutItemInContainer.cs`.
**Inbound (Task 2b — surfaced during visual test):**
4. ACE runs `HandleActionPutItemInContainer`. If the player is within
`WithinUseRadius` (~0.6 m), the close-range branch in
`CreateMoveToChain` skips the auto-walk and runs the pickup chain
directly: server-side `Landblock.RemoveWorldObject(item.Guid,
adjacencyMove: false, fromPickup: true)` → per-player
`Player_Tracking.RemoveTrackedObject(wo, fromPickup: true)`
broadcast `GameMessagePickupEvent (0xF74A)` to all observers.
5. Our `WorldSession.Dispatch` now routes `0xF74A` (in addition to
`0xF747 DeleteObject`) through the shared `EntityDeleted` event,
adapting the `PickupEvent.Parsed` to a `DeleteObject.Parsed` so
`OnLiveEntityDeleted → RemoveLiveEntityByServerGuid` runs unchanged.
The item disappears from the local view.
---
## Wire-handler gap (Task 2b)
ACE distinguishes two despawn opcodes:
- `0xF747 GameMessageDeleteObject` — "object is gone" (timeout / death /
out-of-LOS). Our existing handler.
- `0xF74A GameMessagePickupEvent` — "object was picked up by a player."
Sent by `Player_Tracking.RemoveTrackedObject(wo, fromPickup: true)`.
Both are functionally identical from the client's view (remove the
entity from the world), but only one was handled. Wire format adds
one `u16 objectPositionSequence` field over DeleteObject's layout, so
`PickupEvent.cs` is its own parser; the dispatcher adapts to
`DeleteObject.Parsed` for the downstream consumer.
This is exactly the kind of trap CLAUDE.md's reference-repo discipline
exists to prevent — the handoff spec said "the existing despawn path
removes the ground item from view," which was *almost* true. Took one
visual-verification round-trip to surface, ten minutes to fix with a
clean wire parser + 3 new unit tests.
---
## Visual verification — what was observed
**Test scenario:** ACE dropped a Pink Taper, then a Violet Taper, then
two more tapers near `+Acdream` at Holtburg. Player walked up close,
single-clicked, pressed F. Three pickups completed in the post-fix
log: items `0x80000725`, `0x8000072A`, `0x80000729`.
**Before Task 2b:** Server-side pickup succeeded — `[B.5] pickup …
seq=46` in log; retail observer saw item disappear from world. Local
view still rendered the item on the ground.
**After Task 2b:** Item disappears locally as soon as ACE acks the
pickup. Three successful close-range pickups recorded in the log.
**Door-interaction regression check (B.4c carry-forward):** Not
explicitly re-tested this session; no code path touched by B.5
affects door interaction.
**Click-NPC bonus (M1 demo target 3 verification):** Not visually
verified this session — log shows `[B.4b] use guid=… name=Novedion
the Gem Seller seq=…` from B.4c testing but ACE response not
re-confirmed here. Carry-forward to next session.
---
## What did NOT work (and why it's not B.5's bug)
1. **Double-click on a ground item from any distance, or F from > 0.6 m.**
ACE auto-walks the player toward the item (`CreateMoveToChain`
`PhysicsObj.MoveToObject` + `EnqueueBroadcastMotion(MoveToObject)`),
but our client doesn't handle inbound `MoveToObject` motion broadcasts.
ACE's MoveToChain times out, the chain's `success: false` path sends
`InventoryServerSaveFailed (ActionCancelled)`, and the pickup never
completes. Visible as "character drifts toward item then flips back."
**Filed as #63.** Out of B.5's stated scope (which was: select-first
+ F-key wire chain). holtburger's `simulation.rs` has the reference
implementation; would be its own phase (B.6 or similar).
2. **Local-player pickup animation doesn't render.** Retail observers
see `+Acdream` play the bend-down-and-grab animation; our local view
shows nothing. ACE broadcasts `Motion(MotionCommand.Pickup)` via
`EnqueueBroadcastMotion`, our motion routing probably filters
self-echoes for the local player (motion is normally predicted
locally, not echoed from server). Server-initiated one-shot motions
like Pickup have no local prediction trigger, so they're dropped.
**Filed as #64.** Visual feedback gap only; pickup completes
correctly.
Both are well-defined follow-up work; neither blocks M1.
---
## Carry-overs from B.4c
Both pre-existed B.5; neither was touched.
- **#61** — AnimationSequencer link→cycle boundary frame-0 flash on
door swing. Low severity polish.
- **#62** — PARTSDIAG null-guard for sequencer-driven entities.
Latent; not currently reachable for doors.
---
## M1 status after B.5
Demo targets:
1. Walk through Holtburg — met (L.2a-d + L.2g shipped earlier)
2. Open the inn door — met (B.4b + B.4c shipped 2026-05-13)
3. Click an NPC — chain wired (B.4b), not visually re-verified this
session
4. Pick up an item — met, close-range path (this phase)
Outstanding work for the M1 demo recording:
- Optionally re-verify target 3 (NPC click) once and either confirm
met or file a gap.
- Optionally resolve #63 if the demo wants to show double-click /
out-of-range pickup. The close-range path is sufficient for the
scripted demo scenario.
- Carry-overs #61, #62, #64 are polish; do before recording if
visible on tape.
---
## Reproducibility
```powershell
Get-Process -Name AcDream.App -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 20
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
$env:ACDREAM_LIVE = "1"
$env:ACDREAM_TEST_HOST = "127.0.0.1"
$env:ACDREAM_TEST_PORT = "9000"
$env:ACDREAM_TEST_USER = "testaccount"
$env:ACDREAM_TEST_PASS = "testpassword"
$env:ACDREAM_DEVTOOLS = "1"
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Debug 2>&1 |
Tee-Object -FilePath "launch-b5.log"
```
Log evidence:
```powershell
Get-Content launch-b5.log -Encoding Unicode |
Select-String -Pattern "\[B\.5\] pickup|\[B\.4b\] pick"
```
Expected: a `[B.5] pickup item=… container=0x5000000A seq=…` line for
each successful F-press, preceded by `[B.4b] pick guid=…` from the
single-click that set the selection.
---
## Files touched this session
- New: `src/AcDream.Core.Net/Messages/PickupEvent.cs`
- New: `tests/AcDream.Core.Net.Tests/Messages/PickupEventTests.cs`
- New: `docs/superpowers/plans/2026-05-14-phase-b5-pickup.md`
- New: `docs/research/2026-05-14-b5-shipped-handoff.md` (this file)
- Modified: `src/AcDream.Core.Net/Messages/InteractRequests.cs`
- Modified: `src/AcDream.Core.Net/WorldSession.cs`
- Modified: `src/AcDream.App/Rendering/GameWindow.cs`
- Modified: `tests/AcDream.Core.Net.Tests/Messages/InteractRequestsTests.cs`
- Modified: `docs/ISSUES.md` (added #63, #64)
---
## State at handoff
- **Branch:** `claude/phase-b5-pickup`, 6 commits ahead of `main`
(predecessor handoff + 5 implementation commits + this docs commit
land in the same merge).
- **Main HEAD before merge:** `e7842e0` — Merge B.4c.
- **Build state:** worktree compiles cleanly under `dotnet build -c Debug`.
- **Tests:** baseline + 3 new (PickupEvent) + 2 new (BuildPickUp +
sign-correctness) — failure count unchanged.
Ready for non-fast-forward merge into `main`.