Phase B.4b shipped end-to-end 2026-05-13. Holtburg inn doorway double-click verified: pick -> BuildUse -> ACE SetState reply -> ID-translated registry update -> CollisionExemption exempts -> player walks through. M1 demo target "open the inn door" met. 9 commits on this branch: - Tasks 1-4 per plan (BuildRay, Pick, rename, handler wiring) - 4 bonus visual-test discoveries: * InputDispatcher double-click detection (was dead code) * DoubleClick activation gate fix in OnInputAction * L.2g slice 1b: CollisionExemption widened to ETHEREAL alone * L.2g slice 1c: ServerGuid -> entity.Id translation (silent blocker) Closes #57. Files #58 for door swing animation (UpdateMotion routing for non-creature entities, M1 deferred polish). Updates roadmap and CLAUDE.md Phase L.2 paragraph. Memory file project_interaction_pipeline.md updated outside the repo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
08be296dcd
commit
2c9bdb512b
4 changed files with 501 additions and 92 deletions
417
docs/research/2026-05-13-b4b-shipped-handoff.md
Normal file
417
docs/research/2026-05-13-b4b-shipped-handoff.md
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
# Phase B.4b shipped — handoff (visual-verified 2026-05-13)
|
||||
|
||||
**Date:** 2026-05-13.
|
||||
**Branch:** `claude/compassionate-wilson-23ff99` (ready to merge to main; do NOT merge here — controller handles that after code review).
|
||||
**Predecessors:**
|
||||
- [docs/research/2026-05-12-l2g-slice1-shipped-handoff.md](2026-05-12-l2g-slice1-shipped-handoff.md) — L.2g slice 1 ship handoff that discovered the B.4 handler gap and deferred the Holtburg visual test to B.4b.
|
||||
- [docs/superpowers/specs/2026-05-13-phase-b4b-design.md](../superpowers/specs/2026-05-13-phase-b4b-design.md) — B.4b design spec.
|
||||
- [docs/superpowers/plans/2026-05-13-phase-b4b-plan.md](../superpowers/plans/2026-05-13-phase-b4b-plan.md) — B.4b implementation plan (6 tasks; Tasks 1-4 per plan + 2 bonus sets beyond the plan).
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Phase B.4b **shipped end-to-end and is visual-verified 2026-05-13.** The M1
|
||||
demo target *"open the inn door"* is met. 9 commits on this branch implement
|
||||
and fix the complete round-trip: double-click door → `WorldPicker.Pick` →
|
||||
`InteractRequests.BuildUse` → ACE broadcasts `SetState (0xF74B)` with
|
||||
`ETHEREAL` bit → `ShadowObjectRegistry.UpdatePhysicsState` (L.2g slice 1)
|
||||
mutates cached state → `CollisionExemption.ShouldSkip` exempts the door →
|
||||
player walks through.
|
||||
|
||||
The plan estimated "30-50 LOC, 1-2 subagent dispatches, ~30 min."
|
||||
Visual testing surfaced **four bonus discoveries** beyond the plan's
|
||||
Tasks 1-4:
|
||||
|
||||
1. `InputDispatcher` had no double-click detection (the `SelectDblLeft`
|
||||
binding was dead code — the dispatcher never produced `DoubleClick`
|
||||
activations).
|
||||
2. `OnInputAction`'s early-return gate discarded `DoubleClick` activations
|
||||
before the switch reached the `SelectDblLeft` case.
|
||||
3. L.2g `CollisionExemption.ShouldSkip` required **both** `ETHEREAL` +
|
||||
`IGNORE_COLLISIONS` bits, but ACE's `Door.Open()` sends only `ETHEREAL`
|
||||
(`state=0x0001000C`).
|
||||
4. `OnLiveStateUpdated` passed a server GUID to `ShadowObjectRegistry` which
|
||||
is keyed by local entity ID — the registry lookup always missed → no-op
|
||||
→ the door never became passable. **This was the actual blocker the user
|
||||
reported.**
|
||||
|
||||
Fixes 1-4 were shipped as bonus commits 5-9 beyond the plan's Tasks 1-4.
|
||||
L.2g slice 1 and B.4b are now both fully verified by the same visual test.
|
||||
Issue #57 is closed. Issue #58 (door swing animation) is filed as M1-deferred
|
||||
polish.
|
||||
|
||||
---
|
||||
|
||||
## What shipped on this branch
|
||||
|
||||
| # | Commit | Subject | Task |
|
||||
|---|---|---|---|
|
||||
| 1 | `f0b3bd9` | `feat(B.4b): WorldPicker.BuildRay — mouse-to-world ray unprojection` | Task 1 |
|
||||
| 2 | `221b641` | `feat(B.4b): WorldPicker.Pick — ray-sphere entity pick` | Task 2 |
|
||||
| 3 | `5821bdc` | `fix(B.4b): WorldPicker.Pick — handle inside-sphere origin + document normalize contract` | Task 2 review fix |
|
||||
| 4 | `7b4aff2` | `refactor(B.4b): unify _selectedTargetGuid -> _selectedGuid` | Task 3 |
|
||||
| 5 | `89d82e1` | `feat(B.4b): GameWindow wires Select/Use handlers via WorldPicker` | Task 4 |
|
||||
| 6 | `242ce70` | `feat(B.4b): InputDispatcher detects double-clicks` | Bonus: Task 4b |
|
||||
| 7 | `58b95bc` | `fix(B.4b): let DoubleClick activation pass the OnInputAction gate` | Bonus: Task 4c |
|
||||
| 8 | `a6e4b57` | `fix(phys L.2g slice 1b): widen CollisionExemption to ETHEREAL alone` | L.2g slice 1b |
|
||||
| 9 | `08be296` | `fix(phys L.2g slice 1c): translate ServerGuid -> entity.Id for ShadowObjectRegistry` | L.2g slice 1c |
|
||||
|
||||
Plus plan/spec commits earlier in the branch session:
|
||||
- `4a1c594` — B.4b design spec.
|
||||
- `ffa404d` — corrected file paths in spec (WorldPicker is in `AcDream.Core.Selection`, not `AcDream.App/Rendering`).
|
||||
- `179e441` — B.4b implementation plan (6 tasks).
|
||||
|
||||
**Build:** clean. **Tests:** 4 new double-click detection tests (commit `242ce70`, all pass). Full suite: builds green, no regressions. L.2g slice 1's 6 tests continue to pass.
|
||||
|
||||
---
|
||||
|
||||
## What the code does end-to-end
|
||||
|
||||
When the user double-left-clicks a door entity in the Holtburg inn doorway,
|
||||
the following chain fires:
|
||||
|
||||
1. **Double-click detection** — `InputDispatcher.OnMouseDown` checks the
|
||||
elapsed time since the previous `MouseLeft` press. If ≤500ms, the
|
||||
activation kind is `DoubleClick`; otherwise `Press`. This is new as
|
||||
of commit `242ce70`; prior to this the `SelectDblLeft` binding was dead
|
||||
code (the dispatcher never produced `DoubleClick` activations).
|
||||
|
||||
2. **Action dispatch** — `InputDispatcher` resolves the chord
|
||||
`[MouseLeft, DoubleClick]` → `InputAction.SelectDblLeft` + activation
|
||||
`DoubleClick`. The multicast `InputAction` event fires, logged as:
|
||||
`[input] SelectDblLeft DoubleClick`.
|
||||
|
||||
3. **OnInputAction gate** — `GameWindow.OnInputAction` receives the event.
|
||||
Prior to commit `58b95bc`, an early-return guard (`if (activation != Press) return;`)
|
||||
discarded all `DoubleClick` events. The fix widens the gate to
|
||||
`if (activation != Press && activation != DoubleClick) return;`.
|
||||
The switch now reaches the `SelectDblLeft` case.
|
||||
|
||||
4. **Ray construction** — `WorldPicker.BuildRay(mousePos, viewport, viewMatrix, projMatrix)`
|
||||
unprojects the cursor pixel into a world-space ray origin + direction,
|
||||
using standard NDC→view→world unprojection. Numerically: the mouse pixel
|
||||
is mapped to `[-1,+1]` NDC, transformed through `inverse(proj)` to get
|
||||
a view-space direction, then through `inverse(view)` for world-space.
|
||||
|
||||
5. **Entity pick** — `WorldPicker.Pick(ray, entities, maxDist=50m)` iterates
|
||||
all entities in `_gpuWorldState.GetAllEntities()`, tests each against a
|
||||
ray-sphere intersection with the entity's bounding radius, and returns
|
||||
the closest hit. A special-case inside-sphere origin guard (commit `5821bdc`)
|
||||
ensures the pick works even when the cursor origin is already inside an
|
||||
entity's bounding sphere (common for large portals or doors at close range).
|
||||
`[B.4b] pick guid=0x7A9B4015 name=Door` logged on hit.
|
||||
|
||||
6. **Use message** — `GameWindow` stores `_selectedGuid = picked.Guid` and
|
||||
calls `InteractRequests.BuildUse(seq, guid)`. The resulting `0xF7B1 / 0x0036`
|
||||
game message is sent to ACE via `_liveSession.SendGameMessage(body)`.
|
||||
`[B.4b] use guid=0x7A9B4015 seq=N` logged.
|
||||
|
||||
7. **ACE processes the Use** — ACE's `Door.Open()` flips the door's physics
|
||||
flags to `ETHEREAL | ...` and broadcasts `SetState (0xF74B)` with the
|
||||
new state value.
|
||||
|
||||
8. **SetState arrives** — `WorldSession.OnSetState` parses the 12-byte
|
||||
payload (Guid + PhysicsState + InstanceSeq + StateSeq) and fires
|
||||
`WorldSession.StateUpdated`. `GameWindow.OnLiveStateUpdated` handles it.
|
||||
**New as of commit `08be296` (slice 1c):** the handler translates
|
||||
`parsed.Guid` (server GUID `0x7A9B4015`) to `entity.Id` (local entity ID
|
||||
`0x000F4245`) via `_entitiesByServerGuid` before calling
|
||||
`ShadowObjectRegistry.UpdatePhysicsState`. Without this translation the
|
||||
registry lookup always returned "not found" — a silent no-op.
|
||||
Log: `[setstate] guid=0x7A9B4015 entityId=0x000F4245 state=0x0001000C`.
|
||||
|
||||
9. **Collision exemption** — next physics tick, `FindObjCollisions` calls
|
||||
`CollisionExemption.ShouldSkip(entry.State, entry.Flags, moverState)`.
|
||||
**New as of commit `a6e4b57` (slice 1b):** the check fires on
|
||||
`(state & ETHEREAL_PS) != 0` alone (widened from the original `ETHEREAL &&
|
||||
IGNORE_COLLISIONS` conjunction). Because ACE broadcasts only `ETHEREAL`
|
||||
in the low bits (`state=0x0001000C`), the original conjunction never fired;
|
||||
the door stayed solid.
|
||||
|
||||
10. **Player walks through** — the resolver produces no wall-contact response
|
||||
for the door's collision geometry. User confirms: "Now I can walk through."
|
||||
|
||||
### Observed log evidence
|
||||
|
||||
```
|
||||
[input] SelectDblLeft DoubleClick
|
||||
[B.4b] pick guid=0x7A9B4015 name=Door
|
||||
[B.4b] use guid=0x7A9B4015 seq=N
|
||||
[setstate] guid=0x7A9B4015 entityId=0x000F4245 state=0x0001000C
|
||||
```
|
||||
|
||||
Player walks through the closed door after the `setstate` line.
|
||||
|
||||
---
|
||||
|
||||
## The four bonus discoveries
|
||||
|
||||
### 1. InputDispatcher had no double-click detection (`242ce70`)
|
||||
|
||||
**Root cause:** `InputDispatcher.OnMouseDown` only looked up `Press` and
|
||||
`Hold` activations in the binding table. The `SelectDblLeft` binding was
|
||||
wired to the chord `[MouseLeft, DoubleClick]` in `KeyBindings.cs:300-320`
|
||||
(shipped in B.4, 2026-04-28), but the dispatcher's mouse-down handler
|
||||
never set activation to `DoubleClick` — it always produced `Press`.
|
||||
So `SelectDblLeft` was literally unreachable: the chord required
|
||||
`DoubleClick` to match, but the dispatcher never generated it.
|
||||
|
||||
**Fix:** Added a `_lastMouseDownTime` (and `_lastMouseDownButton`) tracker
|
||||
to `InputDispatcher`. In `OnMouseDown`, if the same button fires within
|
||||
500ms of its last press, activation is `DoubleClick`; otherwise `Press`.
|
||||
500ms matches the standard Windows/macOS double-click threshold.
|
||||
|
||||
**Rationale:** The fix is minimal and correct. A more faithful retail
|
||||
implementation might read the OS's configured double-click interval, but
|
||||
500ms is the retail default and was the right call for now. 4 new unit
|
||||
tests cover the timing logic: first click = Press, second click within
|
||||
500ms = DoubleClick, third click = Press again (resets the window), and
|
||||
button mismatch = Press.
|
||||
|
||||
### 2. OnInputAction gate discarded DoubleClick activations (`58b95bc`)
|
||||
|
||||
**Root cause:** Even after discovery #1 was fixed and `SelectDblLeft DoubleClick`
|
||||
fired from the dispatcher, the event handler had an early-return guard at
|
||||
the top of `GameWindow.OnInputAction`:
|
||||
|
||||
```csharp
|
||||
if (activation != InputActivation.Press) return;
|
||||
```
|
||||
|
||||
This guard was introduced to prevent `Hold` repetition from triggering
|
||||
switch cases intended for one-shot actions. It correctly blocked `Hold`
|
||||
but also blocked `DoubleClick` — so the `SelectDblLeft` case was still
|
||||
unreachable even after the dispatcher started generating `DoubleClick`.
|
||||
|
||||
**Fix:** Widened the guard to let both `Press` and `DoubleClick` through:
|
||||
|
||||
```csharp
|
||||
if (activation != InputActivation.Press && activation != InputActivation.DoubleClick) return;
|
||||
```
|
||||
|
||||
**Rationale:** `DoubleClick` is semantically a one-shot activation (fires
|
||||
once per double-click gesture), so it belongs in the same pass-through
|
||||
group as `Press`. `Hold` repetition remains blocked.
|
||||
|
||||
### 3. CollisionExemption required both ETHEREAL + IGNORE_COLLISIONS (`a6e4b57`)
|
||||
|
||||
**Root cause:** The original `CollisionExemption.ShouldSkip` check was
|
||||
ported faithfully from `acclient_2013_pseudo_c.txt:276782`, which requires
|
||||
**both** `ETHEREAL_PS (0x4)` and `IGNORE_COLLISIONS_PS (0x10)` to be set
|
||||
simultaneously before short-circuiting collision detection. Retail servers
|
||||
send both bits when opening a door, so retail clients see `state ≥ 0x14`.
|
||||
|
||||
However, ACE's `Door.Open()` broadcasts only the `ETHEREAL` bit in the
|
||||
low portion of the state word. The observed wire value was
|
||||
`state=0x0001000C`: bit `0x4` (ETHEREAL) is set, bit `0x10`
|
||||
(IGNORE_COLLISIONS) is not. The `&&` conjunction in `ShouldSkip` evaluated
|
||||
to false → door stayed solid even after the registry update.
|
||||
|
||||
This was the exact scenario the L.2g slice 1 Important review note warned
|
||||
about (see L.2g handoff §"One Important review note"): *"ACE's
|
||||
`PhysicsObj.cs:787-791` may set both bits... but this is not verified by
|
||||
the test suite. The B.4b visual test will settle this definitively."*
|
||||
It settled as: ACE sends `0x4` alone, not `0x14`.
|
||||
|
||||
**Fix:** Widened the short-circuit to fire on `ETHEREAL` alone:
|
||||
|
||||
```csharp
|
||||
// Widened from (ETHEREAL && IGNORE_COLLISIONS) — ACE Door.Open() sends
|
||||
// ETHEREAL alone (state=0x0001000C); retail servers send both.
|
||||
// Pragmatic choice: exempt on ETHEREAL-bit-alone until full retail
|
||||
// obstruction_ethereal flag path is ported.
|
||||
if ((state & ETHEREAL_PS) != 0) return true;
|
||||
```
|
||||
|
||||
**Rationale:** The deeper retail path (pseudo-C line 276795 sets
|
||||
`obstruction_ethereal=1` and routes through downstream movement handling)
|
||||
was not ported — that's a more invasive change requiring more testing. The
|
||||
pragmatic widening to ETHEREAL alone is correct for ACE's Door behavior and
|
||||
matches the spirit of the retail check (ETHEREAL means "pass through me").
|
||||
If a future retail-server emulator sends both bits, the widened check still
|
||||
fires (ETHEREAL is a subset of ETHEREAL+IGNORE_COLLISIONS).
|
||||
|
||||
### 4. ServerGuid → entity.Id translation missing in OnLiveStateUpdated (`08be296`) — THE actual blocker
|
||||
|
||||
**Root cause:** `ShadowObjectRegistry` is keyed by local `entity.Id` (the
|
||||
per-session integer ID assigned by `GpuWorldState` at entity registration,
|
||||
e.g. `0x000F4245`). The `GameWindow.OnLiveStateUpdated` handler was passing
|
||||
`parsed.Guid` — the **server GUID** broadcasted in the `SetState` packet
|
||||
(e.g. `0x7A9B4015`) — directly to `UpdatePhysicsState`. Because the registry
|
||||
has no entry keyed by server GUID, the lookup always returned "not found"
|
||||
and the state mutation was silently dropped. The registry stayed at
|
||||
`state=0x00000000` (closed, solid) regardless of how many times the door
|
||||
was clicked.
|
||||
|
||||
This is why discoveries 1-3 alone were insufficient: even with double-click
|
||||
detection working, the correct gate firing, and `CollisionExemption`
|
||||
widened, the registry still held the stale closed state and the door
|
||||
stayed solid.
|
||||
|
||||
**Fix:** Added a `_entitiesByServerGuid` reverse-lookup dictionary to
|
||||
`GameWindow` (populated at entity registration in `OnLiveCreateObject`).
|
||||
`OnLiveStateUpdated` now does:
|
||||
|
||||
```csharp
|
||||
if (_entitiesByServerGuid.TryGetValue(parsed.Guid, out var entity))
|
||||
_physicsEngine.ShadowObjects.UpdatePhysicsState(entity.Id, parsed.PhysicsState);
|
||||
```
|
||||
|
||||
The `entityId=` field was added to the `[setstate]` diagnostic log line
|
||||
specifically to make this translation visible and greppable:
|
||||
`[setstate] guid=0x7A9B4015 entityId=0x000F4245 state=0x0001000C`.
|
||||
|
||||
**Why this was missed:** L.2g slice 1's unit tests operated at the
|
||||
`ShadowObjectRegistry` level directly, calling `UpdatePhysicsState` with
|
||||
an `entity.Id` (not a server GUID). The integration was never exercised
|
||||
end-to-end before B.4b's visual test. The two tests `UpdatePhysicsState_FlipsEthereal_*`
|
||||
were correct in isolation; the broken layer was one level above them
|
||||
(the handler → registry call site).
|
||||
|
||||
**Why the "multiple doors" misdiagnosis occurred:** Before slice 1c was
|
||||
identified, the `[resolve]` probes showed wall hits attributed to
|
||||
`obj=0x000F4245` while the clicked door's ServerGuid was `0x7A9B4015`.
|
||||
Initial read: "these are two different entities blocking the threshold."
|
||||
Slice 1c clarified: both IDs refer to the same door — `0x000F4245` is
|
||||
the local entity ID, `0x7A9B4015` is the server GUID for the same entity.
|
||||
The ID-space mismatch was the cause of both the collision-not-clearing
|
||||
AND the "different object" misread.
|
||||
|
||||
---
|
||||
|
||||
## Open notes / follow-ups
|
||||
|
||||
### Door swing animation (#58)
|
||||
|
||||
When ACE opens a door it broadcasts **two** packets, not one:
|
||||
|
||||
1. `SetState (0xF74B)` — the collision-bit flip. **Handled by L.2g slice 1.**
|
||||
2. `UpdateMotion (0xF74D)` with stance/command `(NonCombat, On)` — the
|
||||
swing animation cycle. **NOT handled.**
|
||||
|
||||
acdream's `UpdateMotion` pipeline is currently scoped to player + creature
|
||||
animation (Phase L.3). Non-creature entities like doors do not receive
|
||||
cycle commands. The door therefore opens (becomes passable) but has no
|
||||
visible swing animation.
|
||||
|
||||
Filed as **issue #58**. Scope is unknown — routing `UpdateMotion` to
|
||||
non-creature `WorldEntity` instances could be quick (few lines), or the
|
||||
`AnimationSequencer` may have creature-specific assumptions that require
|
||||
audit first. Filed as M1-deferred polish; it does not block the demo
|
||||
scenario.
|
||||
|
||||
### Door toggle behavior
|
||||
|
||||
ACE doors toggle on each Use: first double-click opens, subsequent
|
||||
double-click closes (re-sends `SetState` with `state=0x00000000`, restoring
|
||||
collision). This is correct ACE behavior and matches retail. No issue to file.
|
||||
|
||||
Rapid double-clicks (faster than ACE's server-tick processing) will open
|
||||
then close in quick succession — each Use lands as a distinct game action.
|
||||
Expected behavior; no fix needed.
|
||||
|
||||
### Multiple-door misdiagnosis (historical note)
|
||||
|
||||
While slice 1c was still unidentified, the `[resolve]` diagnostic showed:
|
||||
|
||||
```
|
||||
[resolve] ... obj=0x000F4245 wall hit
|
||||
[B.4b] use guid=0x7A9B4015 ...
|
||||
[setstate] guid=0x7A9B4015 state=0x0001000C
|
||||
[resolve] ... obj=0x000F4245 wall hit (unchanged!)
|
||||
```
|
||||
|
||||
Initial misdiagnosis: there must be a *different* door entity (`0x000F4245`)
|
||||
blocking the threshold whose state was never updated. Slice 1c revealed:
|
||||
both IDs refer to the same door — one is the server GUID (network space),
|
||||
the other is the local entity ID (registry space). The registry update was
|
||||
targeting the server GUID (which missed), so the local-ID-keyed entry
|
||||
stayed solid.
|
||||
|
||||
### Selection HUD / hover-highlight / brackets
|
||||
|
||||
Out of B.4b scope per design spec §Non-goals. The `_selectedGuid` field on
|
||||
`GameWindow` is populated (stores the last-picked entity's server GUID), but
|
||||
nothing renders a selection bracket, hover highlight, or target nameplate.
|
||||
That is M2/M3 HUD work (Phase D.6).
|
||||
|
||||
### BuildPickUp (F key) + UseWithTarget UX
|
||||
|
||||
`InteractRequests.BuildPickUp` exists (as an alias of `BuildUse`). The
|
||||
`SelectionPickUp` input action and the F-key binding exist. But
|
||||
`OnInputAction` has no case for `SelectionPickUp` — pick-up-by-F-key is
|
||||
still unimplemented. Same for `UseWithTarget` (requires a secondary target
|
||||
selection UX). Both deferred to a follow-up phase; not M1-blocking.
|
||||
|
||||
---
|
||||
|
||||
## Next session
|
||||
|
||||
**M1 demo progress as of this branch:**
|
||||
- ✅ "walk through Holtburg without getting stuck" — Phase L.2 in progress (outdoor collision works; CBuildingObj interior still deferred to L.2d).
|
||||
- ✅ "open the inn door" — **done** (B.4b, this branch).
|
||||
- ⬜ "click an NPC" — pick + Use wiring exists now; depends on ACE NPC handler responding to Use.
|
||||
- ⬜ "pick up an item" — `BuildPickUp` + F-key wiring not yet in `OnInputAction`.
|
||||
|
||||
**Recommended next steps (in M1 critical-path order):**
|
||||
|
||||
1. **Door swing animation (#58)** — cosmetic M1 polish. Route
|
||||
`UpdateMotion (0xF74D)` to non-creature entities so the door visually
|
||||
swings. Could be quick (30 min) or moderate (2 hrs with AnimationSequencer
|
||||
audit). Worth a spike before committing to an estimate.
|
||||
|
||||
2. **Chronic open-issue triage** — #2 (lightning), #4 (horizon-glow), #28
|
||||
(aurora), #29 (cloud thinness), #37 (humanoid coat), #41 (remote-motion
|
||||
blips) have been deferred since April/early-May. Link each to a future
|
||||
phase or downgrade. ~1 hour. Not M1-blocking but surfaces the real backlog.
|
||||
|
||||
3. **More Phase C visual-fidelity** — C.2 (dynamic point lights), C.3
|
||||
(palette tuning), C.4 (double-sided translucent polys). World still reads
|
||||
"old" without local lighting on fireplaces/lamps.
|
||||
|
||||
---
|
||||
|
||||
## Reproducibility
|
||||
|
||||
Same launch recipe as before. For reproducing the visual test:
|
||||
|
||||
```powershell
|
||||
$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"
|
||||
$env:ACDREAM_PROBE_BUILDING = "1"
|
||||
$env:ACDREAM_PROBE_RESOLVE = "1"
|
||||
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Debug 2>&1 |
|
||||
Tee-Object -FilePath "launch-b4b.log"
|
||||
```
|
||||
|
||||
Walk to the Holtburg inn doorway. Double-left-click the closed Door. Walk
|
||||
through. Subsequent double-clicks will close and re-open (ACE toggle).
|
||||
|
||||
After closing the client, grep for:
|
||||
|
||||
```powershell
|
||||
Select-String -Path launch-b4b.log -Pattern "SelectDblLeft|pick guid|use guid|setstate.*entityId"
|
||||
```
|
||||
|
||||
Expected:
|
||||
- `[input] SelectDblLeft DoubleClick` — dispatcher fires on second click within 500ms.
|
||||
- `[B.4b] pick guid=0x7A9B4015 name=Door` — ray hits the door.
|
||||
- `[B.4b] use guid=0x7A9B4015 seq=N` — Use message sent.
|
||||
- `[setstate] guid=0x7A9B4015 entityId=0x000F4245 state=0x0001000C` — ACE reply processed, translation confirmed.
|
||||
|
||||
---
|
||||
|
||||
## Worktree state at handoff
|
||||
|
||||
- Branch `claude/compassionate-wilson-23ff99`.
|
||||
- 9 implementation commits + 3 plan/spec commits ahead of `eea9b4d`
|
||||
(the L.2g slice 1 merge from the previous session).
|
||||
- Controller should run a code review, then merge to main.
|
||||
- Do NOT rebase or squash — each commit tells a diagnostic story that
|
||||
the next phase's debugging may need.
|
||||
Loading…
Add table
Add a link
Reference in a new issue