acdream/docs/superpowers/plans
Erik e0d5d271f3 fix(retail): rotation rate, useability gate, retail toast strings
Two retail divergences fixed from the 2026-05-16 faithfulness audit
(Commit A of the plan at docs/superpowers/plans/2026-05-16-retail-faithfulness-fixes.md).

1. Rotation rate ignored HoldKey.Run. Retail's CMotionInterp::
   apply_run_to_command (decomp 0x00527be0 line 305098) multiplies
   turn_speed by run_turn_factor (1.5, PDB-named symbol at 0x007c8914)
   when input is TurnRight/TurnLeft under HoldKey.Run. Effective
   running rotation is 50% faster (~135°/s vs walking ~90°/s).
   Our keyboard A/D and ApplyAutoWalkOverlay used a fixed walking
   rate.

   New: RemoteMoveToDriver.TurnRateFor(running) helper. Keyboard
   path passes input.Run; auto-walk overlay passes
   _autoWalkInitiallyRunning. The walking-rate base
   (BaseTurnRateRadPerSec = π/2) is unchanged; TurnRateRadPerSec
   constant is preserved as the walking-rate alias for callers
   that don't have run/walk state (NPC remotes).

2. IsUseableTarget gated on `useability & USEABLE_REMOTE (0x20)`,
   which was stricter than retail. Per ItemUses::IsUseable
   (acclient_2013_pseudo_c.txt:256455) cross-referenced with 4
   call sites, retail's IsUseable() semantic is `_useability != 0`.
   But visually retail's USEABLE_NO (1) entities don't approach
   either, because ACE never broadcasts MovementType=6 for them.
   Our client installs a speculative auto-walk BEFORE the server
   responds, so we'd visibly approach + face signs before the
   wire packet was rejected.

   Pragmatic fix: block USEABLE_UNDEF (0) AND USEABLE_NO (1) in
   IsUseableTarget — slightly stricter than retail's
   IsUseable but matches retail's user-visible behaviour
   ("R on sign does nothing"). Documented in the doc-comment so
   a future implementer knows the gap.

3. New IsPickupableTarget gate for F-key path — requires
   USEABLE_REMOTE (0x20) bit. Null-useability fallback for
   BF_CORPSE + small-item ItemTypes (preserves M1 ground-item
   pickup flow when ACE seed DB doesn't publish useability).

4. R-key (UseCurrentSelection) upfront gate now ALWAYS uses
   IsUseableTarget. R is conceptually "use" with smart-routing
   to pickup as a downstream optimization. F-key (SendPickUp)
   uses IsPickupableTarget directly.

5. Retail toast strings on block, centralised in new
   src/AcDream.Core/Ui/RetailMessages.cs:
   - "The X cannot be used" (data 0x007e2a70, sprintf 0x00588ea4)
     fires on UseCurrentSelection / SendUse gate block.
   - "The X can't be picked up!" (sprintf 0x00587353) fires on
     SendPickUp non-pickupable block.
   - "You cannot pick up creatures!" (data 0x007e22b4) fires on
     SendPickUp creature block (was previously silent).
   - Plus 4 inactive retail strings ready for future call sites:
     CannotBeUsedWith (two-target Use), CannotBePickedUp (formal
     pickup variant), CannotBeUsedWhileOnHook_HooksOff +
     CannotBeUsedWhileOnHook_NotOwner (housing). All cite their
     retail data addresses + runtime sprintf addresses.

6. ProbeUseabilityFallbackEnabled diagnostic (env var
   ACDREAM_PROBE_USEABILITY_FALLBACK=1) logs every time the
   null-useability fallback fires. Settles whether the
   fallback for creature + BF_DOOR/LIFESTONE/PORTAL/CORPSE
   entries in ACE's seed DB without useability is hot code
   or theoretical defense.

Test coverage:
- +3 RemoteMoveToDriverTests cover TurnRateFor walking/running/back-compat.
- +7 RetailMessagesTests cover each retail string with retail anchor.
- +1 CreateObjectTests TryParse_WeenieFlagsUsable_ReadsUseableNoValue
  pins parser correctness for USEABLE_NO=1.
- 294/294 Core.Net pass; 24/24 new+touched Core tests pass.
- Pre-existing baseline of 8 Physics test failures unchanged
  (BSPStepUp + MotionInterpreter regression noise from prior
  sessions; out of scope here).

Deferred to a separate session per user direction:
- Click area = indicator-rect retail fidelity. Retail's picker
  uses per-part CGfxObj.drawing_sphere + polygon refine
  (0x0054c740); ours uses single Setup.SelectionSphere ray-
  intersect. The rect corners are dead zones today. Three fix
  options analyzed: screen-space rectangle hit-test, sqrt(2)
  sphere inflation, polygon refine Stage B.

Plan: docs/superpowers/plans/2026-05-16-retail-faithfulness-fixes.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:17:54 +02:00
..
2026-04-11-foundation-a1-streaming.md fix(app): Phase A.1 — separate Visible from Resident in StreamingRegion 2026-04-11 22:08:17 +02:00
2026-04-12-b3-complete-movement.md docs(plans): Phase B.3 Complete — movement + world navigation plan 2026-04-12 15:59:29 +02:00
2026-04-12-physics-collision-engine.md docs(plans): Phase B.3 physics collision engine implementation plan 2026-04-12 09:48:06 +02:00
2026-04-12-player-movement.md docs(plans): Phase B.2 player movement implementation plan 2026-04-12 14:10:13 +02:00
2026-04-13-movement-completion.md docs: movement completion implementation plan (7 tasks) 2026-04-13 23:08:48 +02:00
2026-05-02-l3-1-interpolation-manager.md docs(plan): Phase L.3.1 — InterpolationManager core implementation plan 2026-05-02 18:26:02 +02:00
2026-05-02-l3-positionmanager-jump.md docs(plan): Phase L.3.1+L.3.2 PositionManager + retail-faithful jump plan 2026-05-03 10:10:16 +02:00
2026-05-08-phase-n1-scenery-via-wb-helpers.md plan(N.1): scenery via WorldBuilder helpers — implementation plan 2026-05-08 09:05:53 +02:00
2026-05-08-phase-n3-texture-decode-via-wb.md docs(N.3): mark Phase N.3 shipped + commit implementation plan 2026-05-08 11:37:52 +02:00
2026-05-08-phase-n4-rendering-foundation.md phase(N.4): SHIP — flag default-on + finalize plan + roadmap 2026-05-08 18:01:23 +02:00
2026-05-08-phase-n5-modern-rendering.md docs: defer per-instance highlight to open backlog (no scheduled phase) 2026-05-08 22:22:23 +02:00
2026-05-09-phase-a5-two-tier-streaming.md docs(A.5 T27): spec + plan amendments for T22.5 + ship 2026-05-10 10:06:26 +02:00
2026-05-09-phase-n5b-terrain-modern.md phase(N.5b): SHIP — terrain on modern rendering path 2026-05-09 13:05:12 +02:00
2026-05-10-issue-13-pd-trailer.md refactor(net): #13 Parsed.TrailerTruncated + diag logging 2026-05-10 08:26:08 +02:00
2026-05-10-issue-53-tier1-cache.md docs(post-A.5 #53): Tier 1 cache — implementation plan (writing-plans) 2026-05-10 17:06:42 +02:00
2026-05-11-phase-n6-slice1.md docs(perf): Phase N.6 slice 1 — implementation plan 2026-05-11 11:12:26 +02:00
2026-05-12-phase-c1.5a-portals.md docs(vfx #C.1.5a): implementation plan + spec wiring-location fixes 2026-05-11 13:36:18 +02:00
2026-05-12-phase-l2g-slice1.md docs(phys L.2g): slice 1 implementation plan 2026-05-12 21:52:31 +02:00
2026-05-13-phase-b4b-plan.md docs(B.4b): implementation plan — 6 tasks, TDD picker + handler wiring 2026-05-13 17:37:09 +02:00
2026-05-13-phase-b4c-plan.md docs(B.4c): implementation plan — 4 tasks, door spawn-time sequencer + UM diagnostic 2026-05-14 06:50:51 +02:00
2026-05-13-phase-c1.5b.md docs(vfx #C.1.5b): design + plan for issue #56 + EnvCell DefaultScript 2026-05-11 23:51:44 +02:00
2026-05-14-phase-b5-pickup.md docs(B.5): implementation plan from writing-plans skill 2026-05-14 15:10:23 +02:00
2026-05-16-retail-faithfulness-fixes.md fix(retail): rotation rate, useability gate, retail toast strings 2026-05-16 12:17:54 +02:00