chore: address E1.M3 docs review (Slice 3 anchor, Linear pointers)
parent
5bb70efd6c
commit
7f9fc8e429
|
|
@ -64,6 +64,8 @@ Deliver the foundational client runtime: movement, locked isometric camera, worl
|
|||
- **Orbit not exposed in UX** for players in prototype (`allow_yaw` default off); zoom stays within configured bands; player remains readable during motion; occlusion policy active; `CameraState` contract documented for dependents.
|
||||
- Telemetry hooks: `camera_zoom_changed` (optional throttled), occluder stress markers if needed for perf.
|
||||
|
||||
<a id="epic-1-slice-3"></a>
|
||||
|
||||
### Slice 3 - Interaction, targeting, and ability input wiring
|
||||
|
||||
- Scope: E1.M3 + E1.M4 with stub or live combat consumer for `AbilityCastRequest`.
|
||||
|
|
|
|||
|
|
@ -8,40 +8,128 @@
|
|||
| **Epic** | [Epic 1 — Core Player Runtime](../epics/epic_01_core_player_runtime.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | Planned (see [dependency register](module_dependency_register.md)) |
|
||||
| **Linear** | [NEO-23](https://linear.app/neon-sprawl/issue/NEO-23) through [NEO-27](https://linear.app/neon-sprawl/issue/NEO-27) ([backlog](../../plans/E1M3-prototype-backlog.md)); label **`E1.M3`** per [decomposition README — Linear alignment](../README.md#linear-alignment) |
|
||||
|
||||
## Purpose
|
||||
|
||||
World object selection, target lock, focus swap rules, and hover/highlight feedback. Bridges movement/position ([E1.M1](E1_M1_InputAndMovementRuntime.md)) into combat, gathering, and ability activation ([E1.M4](E1_M4_AbilityInputScaffold.md), [E5.M1](E5_M1_CombatRulesEngine.md)).
|
||||
**Authority:** [Client vs server — E1.M3](client_server_authority.md#e1m3-interactionandtargetinglayer) — the server decides valid **targets** and **interactable** eligibility (range, faction, phase, alive/dead, etc.); the client sends selection or use **intent** and may show hover/soft focus until the server confirms.
|
||||
|
||||
Bridges authoritative **position** and reach policy from [E1.M1 — InputAndMovementRuntime](E1_M1_InputAndMovementRuntime.md) into combat, gathering, abilities, and social flows: **who** is locked for gameplay, **what** in the world is selectable, and **when** selection changes—without treating camera pose as gameplay truth (see [E1.M2 — world-anchored semantics](E1_M2_IsometricCameraController.md#mid-project-rotation-policy-practical-compromise)).
|
||||
|
||||
## Boundary: E1.M1 vs E1.M3
|
||||
|
||||
| Topic | E1.M1 (today) | E1.M3 (this module) |
|
||||
|--------|----------------|---------------------|
|
||||
| **Primary job** | Locomotion + authoritative **“use interactable *now*”** attempt via `InteractionRequest` | **Selection model**: lock, swap rules, descriptors, and **change events** for UI and downstream systems |
|
||||
| **Wire / HTTP** | `POST …/interact` + `InteractionResponse` with `reasonCode` ([NEO-9](../../plans/NEO-9-implementation-plan.md)) | Future **selection/target** intents and authoritative **`TargetState`** sync (wire shape TBD; see [contracts.md](contracts.md)) |
|
||||
| **Registry / catalog** | Static prototype interactable table for range checks | **`InteractableDescriptor`** (and combat target projection) as the **stable** description surface for clients and validators |
|
||||
| **Client UX** | Minimal **E** key + proximity **preview** glow (authoritative outcome still from server) | Tab-target, focus cycling, sticky lock, rich hover, prompts—**prototype scope** grows here |
|
||||
|
||||
**Shared policy (not owned exclusively by either module):** horizontal reach on **X/Z only**, **Y ignored** for threshold distance, inclusive radius boundary—same rule as [NEO-9](../../plans/NEO-9-implementation-plan.md). Both E1.M1 interact validation and E1.M3 eligibility checks **reuse** the same math and content anchors so client preview and server denial stay aligned.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
- Resolve `TargetState` for tab-target and interaction flows.
|
||||
- Describe interactables via `InteractableDescriptor` and emit `SelectionEvent` for UI and systems.
|
||||
- **Combat target lock:** maintain and expose **`TargetState`** for tab-target flow; integrate with [E1.M4 — AbilityInputScaffold](E1_M4_AbilityInputScaffold.md) and [E5.M1 — CombatRulesEngine](E5_M1_CombatRulesEngine.md) for cast validity (server re-validates; client is a view + intent).
|
||||
- **World interactables:** discovery, selection, and use flow beyond the NEO-9 smoke path—multiple kinds, prompts, and data-driven defs when scoped; still grounded in **`InteractableDescriptor`** projections.
|
||||
- **Focus swap rules:** **Prototype:** explicit input to cycle targets / interactables in a deterministic order (e.g. tab through eligible list); **Deferred:** nearest-in-cone heuristics, soft-target stickiness under latency—decide with first multiplayer combat vertical slice ([authority doc — lag compensation](client_server_authority.md#what-this-doc-does-not-fix-yet)).
|
||||
- **Presentation:** highlights, outlines, and optional **wrong** local preview until server ack; reconcile on snapshot or denial ([authority](client_server_authority.md#e1m3-interactionandtargetinglayer)).
|
||||
- **E1.M2 adjacency:** targeting and line-of-interest for **gameplay** use **world geometry + server-known positions**, not `CameraState` or client-only aim ([E1.M2](E1_M2_IsometricCameraController.md)).
|
||||
|
||||
## Key contracts
|
||||
|
||||
| Contract | Role |
|
||||
|----------|------|
|
||||
| `TargetState` | Current locked target, validity, and swap rules. |
|
||||
| `InteractableDescriptor` | What can be selected or used in range. |
|
||||
| `SelectionEvent` | Selection changes for feedback and downstream consumers. |
|
||||
| `TargetState` | Authoritative (server) view: locked combat/interaction target, validity, swap/clear reasons. |
|
||||
| `InteractableDescriptor` | Stable description of a selectable world entity (id, kind, anchor, radius / flags). |
|
||||
| `SelectionEvent` | Selection changes for UI and consumers (who/when/cause). |
|
||||
|
||||
### Contract notes ([contracts.md](contracts.md))
|
||||
|
||||
- **Kind:** these are **wire** contracts once the game channel exists (Protobuf preferred per [tech stack](../../architecture/tech_stack.md)); until then, versioned **JSON** spikes must be explicitly throwaway and tracked for migration—same discipline as early `MoveCommand` / `InteractionRequest`.
|
||||
- **Denials:** reuse the **stable `reasonCode` string** pattern from `InteractionResponse` (NEO-9): whenever the server rejects a selection or use intent, return a **non-empty** machine-oriented code; document new codes alongside E5.M1 / E3.M1 consumers.
|
||||
|
||||
**Logical fields (illustrative, not a frozen schema):**
|
||||
|
||||
| Contract | Illustrative fields |
|
||||
|----------|---------------------|
|
||||
| `TargetState` | Target entity id (or empty); validity (`ok` / `out_of_range` / `invalid_target` / …); optional server tick or sequence for reconciliation |
|
||||
| `InteractableDescriptor` | `interactableId`; category/kind; world position or entity ref; `interactionRadius`; flags (e.g. usable-in-combat) |
|
||||
| `SelectionEvent` | Previous id, new id, cause (`tab`, `clear`, `target_died`, `zone_change`, `server_correction`, …) |
|
||||
|
||||
### Intent vs state (reference)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph clientLayer [Client]
|
||||
Hover[HoverHighlight]
|
||||
Intent[SelectionIntent]
|
||||
end
|
||||
subgraph serverLayer [Server]
|
||||
Pos[PositionState_E1M1]
|
||||
Val[EligibilityValidation]
|
||||
TS[TargetState_truth]
|
||||
end
|
||||
Pos --> Val
|
||||
Intent --> Val
|
||||
Val --> TS
|
||||
TS --> clientLayer
|
||||
Hover -.->|"preview_only"| clientLayer
|
||||
```
|
||||
|
||||
## Module dependencies
|
||||
|
||||
- **E1.M1** — InputAndMovementRuntime: range and position source of truth.
|
||||
- **E1.M1** — InputAndMovementRuntime: authoritative **`PositionState`** and horizontal reach policy; existing **`InteractionRequest`** path for “use now” attempts.
|
||||
|
||||
**Logical (not in register “Depends On” column):** [E5.M1](E5_M1_CombatRulesEngine.md) resolves hostile actions and may **deny** casts for reasons beyond selection; [E6.M1](E6_M1_PvPEligibilityAndFlagState.md) gates player targets ([pvp_combat_integration.md](pvp_combat_integration.md)). E1.M3 does **not** list E5.M1 as a hard module dependency in the register so that targeting can ship and test against stubs per [Epic 1 Slice 3](../epics/epic_01_core_player_runtime.md#epic-1-slice-3).
|
||||
|
||||
## Dependents (by design)
|
||||
|
||||
- **E1.M4** — AbilityInputScaffold.
|
||||
- **E3.M1** — ResourceNodeAndGatherLoop.
|
||||
- **E5.M1** — CombatRulesEngine.
|
||||
- **E8.M1** — PartyAndMatchAssembly (player/party context hooks per epic).
|
||||
- **E1.M4** — AbilityInputScaffold: reads **`TargetState`** (and related validity) to build **`AbilityCastRequest`**.
|
||||
- **E3.M1** — ResourceNodeAndGatherLoop: gather **interact** flows use range + selection aligned with **`InteractableDescriptor`**.
|
||||
- **E5.M1** — CombatRulesEngine: tab-target resolution, valid actions against locked target; re-validates client intent.
|
||||
- **E8.M1** — PartyAndMatchAssembly: party-scoped selection / invite context hooks anchored on player identity from targeting layer.
|
||||
|
||||
## Related implementation slices
|
||||
|
||||
Epic 1 **Slice 3** — interaction, targeting, and ability input wiring with [E5.M1](E5_M1_CombatRulesEngine.md).
|
||||
[Epic 1 — Slice 3: Interaction, targeting, and ability input wiring](../epics/epic_01_core_player_runtime.md#epic-1-slice-3)
|
||||
|
||||
- **Scope:** E1.M3 + E1.M4 with stub or live combat consumer for `AbilityCastRequest`.
|
||||
- **Dependencies:** E1.M2, E5.M1 (minimum combat hook).
|
||||
- **Acceptance criteria:** Players can select targets/interactables and fire hotbar-bound abilities when the combat module accepts casts.
|
||||
- **Telemetry hooks:** `target_changed`, `ability_cast_requested`, `ability_cast_denied` with reason codes (align with [E9.M1](E9_M1_TelemetryEventSchema.md) when catalog exists).
|
||||
|
||||
## Implementation snapshot
|
||||
|
||||
- **E1.M3 contracts (`TargetState`, `InteractableDescriptor`, `SelectionEvent`):** not implemented as named, versioned surfaces yet; register status remains **Planned** until wire/proto or agreed JSON v1 lands with a minimal consumer. **Story backlog:** [E1M3 prototype backlog](../../plans/E1M3-prototype-backlog.md).
|
||||
- **Precursor under E1.M1 (NEO-9):** server **`POST /game/players/{id}/interact`** with **`InteractionRequest`** / **`InteractionResponse`**; **`HorizontalReach`** (`server/NeonSprawl.Server/Game/World/HorizontalReach.cs`); prototype registry and API under `server/NeonSprawl.Server/Game/Interaction/`; client **`interaction_request_client.gd`**, **`interaction_radius_indicators.gd`**, **`prototype_interaction_constants.gd`** — see [NEO-9](../../plans/NEO-9-implementation-plan.md) and [server README — Interaction](../../../server/README.md#interaction-neo-9).
|
||||
- **Alignment:** [Documentation and implementation alignment](documentation_and_implementation_alignment.md).
|
||||
|
||||
## Risks and telemetry
|
||||
|
||||
- **Soft vs authoritative target:** client may highlight a target the server will deny; mitigate with visible deny reasons, fast reconciliation, and logging material mismatch ([authority — debugging](client_server_authority.md#telemetry-and-debugging)).
|
||||
- **Occluders vs truth:** camera occlusion and pick-through ([NEO-20](https://linear.app/neon-sprawl/issue/NEO-20)) affect **presentation** and click-to-move; **server** eligibility must not depend on “what the camera showed.”
|
||||
- **Lag compensation** for tab-target: deferred per [client vs server authority](client_server_authority.md#what-this-doc-does-not-fix-yet); decide with combat design when E5.M1 networking MVP is scoped.
|
||||
|
||||
**Telemetry (Slice 3):** `target_changed`, `ability_cast_requested`, `ability_cast_denied` — **TODO(E9.M1)** until event schema exists.
|
||||
|
||||
## Linear backlog
|
||||
|
||||
Parent epic: [Epic 1 — Core Player Runtime](https://linear.app/neon-sprawl/project/epic-1-core-player-runtime-client-controls-character-loop-66bd590cd016).
|
||||
|
||||
**Stories:** full acceptance checklists in **[`docs/plans/E1M3-prototype-backlog.md`](../../plans/E1M3-prototype-backlog.md)**.
|
||||
|
||||
| Slug | Linear |
|
||||
|------|--------|
|
||||
| **E1M3-01** | [NEO-23](https://linear.app/neon-sprawl/issue/NEO-23) — TargetState v1 + server selection intent |
|
||||
| **E1M3-02** | [NEO-24](https://linear.app/neon-sprawl/issue/NEO-24) — Client tab-target + lock UI synced to server |
|
||||
| **E1M3-03** | [NEO-25](https://linear.app/neon-sprawl/issue/NEO-25) — InteractableDescriptor list / projection |
|
||||
| **E1M3-04** | [NEO-26](https://linear.app/neon-sprawl/issue/NEO-26) — SelectionEvent surface + consumer |
|
||||
| **E1M3-05** | [NEO-27](https://linear.app/neon-sprawl/issue/NEO-27) — Slice 3 telemetry hook sites |
|
||||
|
||||
Issues should carry label **`E1.M3`** per [Linear alignment](../README.md#linear-alignment).
|
||||
|
||||
## Source anchors
|
||||
|
||||
- Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Epic 1.
|
||||
- Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Epic 1, E1.M3 module line.
|
||||
- [Module dependency register](module_dependency_register.md)
|
||||
- [PvP and the combat engine](pvp_combat_integration.md) — E5.M1 + E6.M1 gating for player targets
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ Rows appear when work starts; default for unlisted modules is **Planned** / not
|
|||
|--------|-----------------|----------|-------------------|
|
||||
| E1.M1 | Ready | Prototype milestone **Done** ([E1.M1](https://linear.app/neon-sprawl/project/e1m1-inputandmovementruntime-20eb28dd3db4)). Authoritative `PositionState` + **MoveCommand** over HTTP (JSON v1 snap); **in-memory** store by default, **Postgres** when configured ([NEO-8](../../plans/NEO-8-implementation-plan.md)); shared **NpgsqlDataSource** disposed on host shutdown ([NEO-13](../../plans/NEO-13-implementation-plan.md)); Godot sync + path-follow ([NEO-7](../../plans/NEO-7-implementation-plan.md), [NEO-11](../../plans/NEO-11-implementation-plan.md)); **InteractionRequest** + horizontal range ([NEO-9](../../plans/NEO-9-implementation-plan.md)). Follow-on: prediction/reconciliation, Slice 1 telemetry, Protobuf wire per [contracts.md](contracts.md). | [NEO-6](../../plans/NEO-6-implementation-plan.md), [NEO-7](../../plans/NEO-7-implementation-plan.md), [NEO-8](../../plans/NEO-8-implementation-plan.md), [NEO-9](../../plans/NEO-9-implementation-plan.md), [NEO-10](../../plans/NEO-10-implementation-plan.md), [NEO-11](../../plans/NEO-11-implementation-plan.md), [NEO-13](../../plans/NEO-13-implementation-plan.md); `server/NeonSprawl.Server/Game/PositionState/`, `Game/Interaction/`; [server README](../../../server/README.md) |
|
||||
| E1.M2 | Ready | **Slice 2 prototype slice closed:** follow + `CameraState` ([NEO-15](https://linear.app/neon-sprawl/issue/NEO-15)); zoom bands ([NEO-16](https://linear.app/neon-sprawl/issue/NEO-16)); occlusion ([NEO-17](https://linear.app/neon-sprawl/issue/NEO-17)); occluder pick-through ([NEO-20](https://linear.app/neon-sprawl/issue/NEO-20)); contracts + hardening ([NEO-18](https://linear.app/neon-sprawl/issue/NEO-18)). Client-local; no server use of camera pose. | [NEO-15](../../plans/NEO-15-implementation-plan.md), [NEO-16](../../plans/NEO-16-implementation-plan.md), [NEO-17](../../plans/NEO-17-implementation-plan.md), [NEO-18](../../plans/NEO-18-implementation-plan.md), [NEO-20](../../plans/NEO-20-implementation-plan.md); `client/scripts/isometric_follow_camera.gd`, `camera_state.gd`, `zoom_band_config.gd`, `occlusion_policy.gd`, `ground_pick.gd`; [E1_M2_IsometricCameraController.md](E1_M2_IsometricCameraController.md) |
|
||||
| E1.M3 | Planned | **No E1.M3-named contracts in code yet** (`TargetState`, `InteractableDescriptor`, `SelectionEvent`). **Precursor** for interact range + `POST …/interact` is **E1.M1** ([NEO-9](../../plans/NEO-9-implementation-plan.md)); see [E1_M3_InteractionAndTargetingLayer.md](E1_M3_InteractionAndTargetingLayer.md#boundary-e1m1-vs-e1m3). Register stays **Planned** until first E1.M3-tagged implementation merges. | Linear [NEO-23](https://linear.app/neon-sprawl/issue/NEO-23) through [NEO-27](https://linear.app/neon-sprawl/issue/NEO-27) (Todo); register unchanged until code merges. [E1_M3_InteractionAndTargetingLayer.md](E1_M3_InteractionAndTargetingLayer.md); [E1M3 prototype backlog](../../plans/E1M3-prototype-backlog.md); [NEO-9](../../plans/NEO-9-implementation-plan.md); [server README — Interaction](../../../server/README.md#interaction-neo-9) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ Fleshed-out scope, contracts, and integration notes live in **per-module documen
|
|||
|
||||
**E1.M2 note:** Prototype slice **Ready**: `client/scripts/isometric_follow_camera.gd` + child `Camera3D` on **`World/IsometricFollowCamera`** in `client/scenes/main.tscn`; per-tick **`CameraState`** (`client/scripts/camera_state.gd`); **`ZoomBandConfig`** ([NEO-16](https://linear.app/neon-sprawl/issue/NEO-16)); **`OcclusionPolicy`** ([NEO-17](https://linear.app/neon-sprawl/issue/NEO-17)); pick-through **`"occluder"`** convention ([NEO-20](https://linear.app/neon-sprawl/issue/NEO-20)); consumer contract + occluder lifecycle hardening ([NEO-18](https://linear.app/neon-sprawl/issue/NEO-18)). See [E1_M2_IsometricCameraController.md](E1_M2_IsometricCameraController.md).
|
||||
|
||||
**E1.M3 note:** Decomposition expanded in [E1_M3_InteractionAndTargetingLayer.md](E1_M3_InteractionAndTargetingLayer.md) (E1.M1 vs E1.M3 boundary, contract sketches, Slice 3 alignment). **`TargetState` / `InteractableDescriptor` / `SelectionEvent`** are still **Planned** until published wire (Protobuf or agreed JSON v1) plus a minimal consumer. **Precursor:** authoritative **`InteractionRequest`** + horizontal reach lives under **E1.M1** ([NEO-9](../../plans/NEO-9-implementation-plan.md); `Game/Interaction/`, `Game/World/HorizontalReach.cs`).
|
||||
|
||||
### Epic 2 — Skills and Progression Framework
|
||||
|
||||
| Module ID | Module Name | Depends On | Contract Needed | Phase Required | Status |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
# E1.M3 — Prototype story backlog (InteractionAndTargetingLayer)
|
||||
|
||||
Working backlog for **Epic 1 — Slice 3** ([interaction, targeting, ability input](../decomposition/epics/epic_01_core_player_runtime.md#epic-1-slice-3)). Decomposition and contracts: [E1.M3 — InteractionAndTargetingLayer](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md).
|
||||
|
||||
**Labels (Linear):** every issue **`E1.M3`**; add **`Epic 1`** / Slice 3 as your team prefers.
|
||||
|
||||
**Precursor (do not re-scope):** authoritative **`InteractionRequest`** + horizontal reach = **E1.M1** / [NEO-9](NEO-9-implementation-plan.md). These stories **build on** that policy; they do not replace NEO-9 acceptance.
|
||||
|
||||
**Linear issues (created):** attach **`docs/plans/NEO-*-implementation-plan.md`** on the same branch as code when each story starts (same pattern as [NEO-18](NEO-18-implementation-plan.md)).
|
||||
|
||||
| Slug | Linear |
|
||||
|------|--------|
|
||||
| E1M3-01 | [NEO-23](https://linear.app/neon-sprawl/issue/NEO-23) |
|
||||
| E1M3-02 | [NEO-24](https://linear.app/neon-sprawl/issue/NEO-24) |
|
||||
| E1M3-03 | [NEO-25](https://linear.app/neon-sprawl/issue/NEO-25) |
|
||||
| E1M3-04 | [NEO-26](https://linear.app/neon-sprawl/issue/NEO-26) |
|
||||
| E1M3-05 | [NEO-27](https://linear.app/neon-sprawl/issue/NEO-27) |
|
||||
|
||||
**Dependency graph in Linear:** NEO-24, NEO-25 → blocked by **NEO-23**. NEO-26 → blocked by **NEO-23**, **NEO-24**. NEO-27 → blocked by **NEO-24**.
|
||||
|
||||
---
|
||||
|
||||
## Story order (recommended)
|
||||
|
||||
| Order | Slug | Linear | Depends on |
|
||||
|-------|------|--------|------------|
|
||||
| 1 | **E1M3-01** | [NEO-23](https://linear.app/neon-sprawl/issue/NEO-23) | E1.M1 (PositionState, HorizontalReach), NEO-9 patterns |
|
||||
| 2 | **E1M3-02** | [NEO-24](https://linear.app/neon-sprawl/issue/NEO-24) | E1M3-01, E1.M2 (presentation only) |
|
||||
| 3 | **E1M3-03** | [NEO-25](https://linear.app/neon-sprawl/issue/NEO-25) | E1M3-01 or parallel after NEO-9 registry read |
|
||||
| 4 | **E1M3-04** | [NEO-26](https://linear.app/neon-sprawl/issue/NEO-26) | E1M3-01, E1M3-02 |
|
||||
| 5 | **E1M3-05** | [NEO-27](https://linear.app/neon-sprawl/issue/NEO-27) | E1M3-02; [E9.M1](../decomposition/modules/E9_M1_TelemetryEventSchema.md) when schema exists |
|
||||
|
||||
---
|
||||
|
||||
### E1M3-01 — `TargetState` v1 + server selection intent
|
||||
|
||||
**Goal:** Publish a **versioned** server-owned **`TargetState`** and a client→server **selection intent** (name TBD: e.g. `TargetSelectRequest`) over the **existing HTTP JSON** prototype channel, with **`reasonCode`** denials consistent with [NEO-9](NEO-9-implementation-plan.md) / `InteractionResponse`.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Server: validate intent using authoritative **`PositionState`** + shared **horizontal XZ** reach rules; stub **eligible target** set (static ids or scene-registered entities) until sim entities exist.
|
||||
- Response: current **`TargetState`** (locked id or empty, validity, optional sequence).
|
||||
- Unit + integration tests; [server README](../../server/README.md) section for the new route(s).
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Protobuf wire; full entity sim; PvP gating ([E6.M1](../decomposition/modules/E6_M1_PvPEligibilityAndFlagState.md)) beyond a single **stub** deny path if needed for API shape.
|
||||
- Replacing **`POST …/interact`** — remain separate surface ([E1.M3 boundary](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md#boundary-e1m1-vs-e1m3)).
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Client cannot commit a locked target the server would reject: **server** is source of truth for `TargetState`.
|
||||
- [ ] Denied selection returns **200** (or agreed status) + body with **required** `reasonCode` when selection is rejected (match project conventions from NEO-9).
|
||||
- [ ] Moving the player with the existing move APIs changes **in range / out of range** outcomes for target lock where range applies.
|
||||
- [ ] Documented field list matches the **illustrative** table in [E1_M3 — Key contracts](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md#key-contracts) (refine names in the plan PR, not silently in code only).
|
||||
|
||||
---
|
||||
|
||||
### E1M3-02 — Client tab-target + lock UI synced to server
|
||||
|
||||
**Goal:** **Tab** (or agreed binding) cycles **server-eligible** targets in deterministic order; **visual** lock state matches last acknowledged **`TargetState`**.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Godot: input → emit selection intent → apply server response; optional **optimistic** highlight **must** reconcile on deny ([authority](../decomposition/modules/client_server_authority.md#e1m3-interactionandtargetinglayer)).
|
||||
- Clear target (e.g. Esc) if product agrees; document binding in `client/project.godot` or module README.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Nearest-in-cone auto-target; sticky soft-target under latency ([deferred in E1.M3](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md#responsibilities)).
|
||||
- Hotbar / `AbilityCastRequest` — that is [E1.M4](../decomposition/modules/E1_M4_AbilityInputScaffold.md) once E1M3-01/02 are in place.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] With **≥2** stub targets, tab cycles order **defined in module or plan** (e.g. ascending id) and wraps.
|
||||
- [ ] UI or debug overlay shows **server** lock id (or “none”) after each successful round-trip.
|
||||
- [ ] Server denial updates UI to **previous** or **cleared** state per documented policy.
|
||||
|
||||
---
|
||||
|
||||
### E1M3-03 — `InteractableDescriptor` list / projection (multi-entity)
|
||||
|
||||
**Goal:** Server exposes a **small, versioned** list (or per-zone snapshot) of **`InteractableDescriptor`** records so the client can tab/hover **interactables** beyond the single NEO-9 row **without** duplicating registry truth in GDScript long-term.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Extend or complement `PrototypeInteractableRegistry` (or successor) so **multiple** entries are addressable; single JSON **GET** or embedded list on an existing player/session endpoint — pick one and document.
|
||||
- Client: consume list for proximity preview (optional) or selection id set; **constants in GDScript** must stay **documented** as duplicate-until-codegen ([NEO-9](NEO-9-implementation-plan.md) precedent) or generated from server in this story if you add a trivial export path.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Full data-driven content pipeline ([contracts.md](../decomposition/modules/contracts.md) content kind).
|
||||
- Gather loop rewards ([E3.M1](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md)).
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] **≥2** interactables in server registry; client can issue **`InteractionRequest`** against each id when in range (NEO-9 path still works).
|
||||
- [ ] Descriptor payload includes at least: **id**, **world anchor**, **`interactionRadius`**, **kind** string or enum for future branching.
|
||||
|
||||
---
|
||||
|
||||
### E1M3-04 — `SelectionEvent` surface + consumer
|
||||
|
||||
**Goal:** Any **change** to effective target or primary interactable selection is observable as a **`SelectionEvent`** (wire or client-local mirror of server events for prototype).
|
||||
|
||||
**In scope**
|
||||
|
||||
- Define minimal event shape: **previous**, **next**, **cause** (`tab`, `clear`, `server_correction`, …).
|
||||
- One **consumer**: debug panel, `print`, or thin HUD widget listing last N events.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Full UI prompt system; party flows ([E8.M1](../decomposition/modules/E8_M1_PartyAndMatchAssembly.md)).
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Tab, clear, and forced server correction (if testable) each emit distinguishable **cause** values.
|
||||
- [ ] [E1.M4](../decomposition/modules/E1_M4_AbilityInputScaffold.md) can subscribe to the same bus in a follow-up without reshaping the event (document subscription point).
|
||||
|
||||
---
|
||||
|
||||
### E1M3-05 — Slice 3 telemetry hook sites
|
||||
|
||||
**Goal:** Align with Epic 1 Slice 3 hooks: **`target_changed`**, and reserve call sites for **`ability_cast_requested`** / **`ability_cast_denied`** (latter two may no-op until E1.M4 + E5.M1).
|
||||
|
||||
**In scope**
|
||||
|
||||
- Document function names or signals in client/server where events **will** emit; **`TODO(E9.M1)`** until [telemetry schema](../decomposition/modules/E9_M1_TelemetryEventSchema.md) exists.
|
||||
- Optional: dev-only logging when flag enabled.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Production ingest, dashboards, OpenTelemetry wiring.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Code or doc comment references **event names** matching [epic_01 Slice 3](../decomposition/epics/epic_01_core_player_runtime.md#epic-1-slice-3).
|
||||
- [ ] [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) updated when **NEO-23** merges (status **In Progress** / contract partial per team rule).
|
||||
|
||||
---
|
||||
|
||||
## After this backlog
|
||||
|
||||
- **E1.M4** stories: hotbar + **`AbilityCastRequest`** to [E5.M1](../decomposition/modules/E5_M1_CombatRulesEngine.md) stub or live consumer (separate plans).
|
||||
- **Register:** when E1M3-01 merges exported **`TargetState`** (+ intent), update [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) **E1.M3** row / note and E1.M3 **Status** per [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md).
|
||||
|
||||
---
|
||||
|
||||
## Related docs
|
||||
|
||||
- [E1_M3_InteractionAndTargetingLayer.md](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md)
|
||||
- [NEO-9 — InteractionRequest](NEO-9-implementation-plan.md)
|
||||
- [client_server_authority.md — E1.M3](../decomposition/modules/client_server_authority.md#e1m3-interactionandtargetinglayer)
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
**Date:** 2026-04-18
|
||||
**Scope:** E1.M3 decomposition expansion, prototype backlog, register/alignment touchpoints, and Linear story linkage (NEO-23–NEO-27).
|
||||
**Base:** `main` / repo as of review (no full `docs/game-design/**` pass).
|
||||
**Base:** `main` / repo as of review (no full `docs/game-design/**` pass). **Follow-up:** Suggestions in this file are **Done.** (strikethrough under **Suggestions**); epic Slice 3 anchor + alignment pointers landed in the same doc pass.
|
||||
|
||||
## Verdict
|
||||
|
||||
**Approve with suggestions** — Content is coherent, implementation-ready, and consistent with authority + NEO-9 boundary. Address anchor fragility on Epic 1 Slice 3 and optionally tighten alignment-table wording vs Linear issues.
|
||||
**Approve with suggestions** — Content is coherent, implementation-ready, and consistent with authority + NEO-9 boundary. ~~Address anchor fragility on Epic 1 Slice 3 and optionally tighten alignment-table wording vs Linear issues.~~ **Done.** (2026-04-18 follow-up commit.)
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
@ -19,8 +19,8 @@ The E1.M3 module doc, `E1M3-prototype-backlog.md`, and supporting register/align
|
|||
| `docs/decomposition/modules/E1_M3_InteractionAndTargetingLayer.md` | **Matches** — Purpose, boundary table, contracts, deps/dependents, snapshot, risks, Linear table align with register and NEO-9 out-of-scope. |
|
||||
| `docs/plans/E1M3-prototype-backlog.md` | **Matches** — Story order, AC blocks, dependency graph vs Linear blockers; precursor warning preserved. |
|
||||
| `docs/decomposition/modules/module_dependency_register.md` (E1.M3 note) | **Matches** — Note matches module doc and Planned status. |
|
||||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` (E1.M3 row) | **Partially matches** — Register **Planned** + “until first … implementation merges” is consistent with policy; readers might still expect **In Progress** now that NEO-23–27 exist (see Suggestions). |
|
||||
| `docs/decomposition/epics/epic_01_core_player_runtime.md` (Slice 3) | **Partially matches** — Prose matches E1_M3 / backlog; **no explicit HTML `id`** on Slice 3 (see Suggestions vs `epic_03` pattern). |
|
||||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` (E1.M3 row) | **Matches** (post–follow-up) — Plans cell now cites Linear NEO-23–NEO-27 + backlog; register **Planned** rule unchanged. |
|
||||
| `docs/decomposition/epics/epic_01_core_player_runtime.md` (Slice 3) | **Matches** (post–follow-up) — Stable `#epic-1-slice-3` anchor added. |
|
||||
| `docs/decomposition/modules/client_server_authority.md` (E1.M3, deferred networking) | **Matches** — Cited behavior matches E1_M3 responsibilities. |
|
||||
| `docs/plans/NEO-9-implementation-plan.md` | **Matches** — Boundary “do not re-scope NEO-9” is respected in E1.M3 + backlog. |
|
||||
| `docs/decomposition/modules/contracts.md` | **N/A** (spot-check) — E1.M3 references kinds/versioning at high level; no conflict. |
|
||||
|
|
@ -32,15 +32,15 @@ None — no broken relative paths were found for files targeted by this change s
|
|||
|
||||
## Suggestions
|
||||
|
||||
1. **Stable anchor for Epic 1 Slice 3** — `epic_01_core_player_runtime.md` is deep-linked as `#slice-3---interaction-targeting-and-ability-input-wiring` from `E1_M3_InteractionAndTargetingLayer.md` and `E1M3-prototype-backlog.md`. Slug behavior varies by renderer; **`epic_03_crafting_economy.md` already uses explicit `<a id="epic-3-slice-…">`** (see `docs/reviews/2026-04-03-game-design-branch-docs-review.md`). Add a similar **`id="epic-1-slice-3"`** (or equivalent) before the Slice 3 heading in `epic_01_core_player_runtime.md` and point links at it to avoid anchor drift.
|
||||
1. ~~**Stable anchor for Epic 1 Slice 3** — `epic_01_core_player_runtime.md` is deep-linked as `#slice-3---interaction-targeting-and-ability-input-wiring` from `E1_M3_InteractionAndTargetingLayer.md` and `E1M3-prototype-backlog.md`. Slug behavior varies by renderer; **`epic_03_crafting_economy.md` already uses explicit `<a id="epic-3-slice-…">`** (see `docs/reviews/2026-04-03-game-design-branch-docs-review.md`). Add a similar **`id="epic-1-slice-3"`** (or equivalent) before the Slice 3 heading in `epic_01_core_player_runtime.md` and point links at it to avoid anchor drift.~~ **Done.** — `<a id="epic-1-slice-3"></a>` in `epic_01_core_player_runtime.md`; links updated to `#epic-1-slice-3` in `E1_M3_InteractionAndTargetingLayer.md` and `E1M3-prototype-backlog.md`.
|
||||
|
||||
2. **Alignment table vs Linear** — The E1.M3 row says register stays **Planned** until first implementation **merges**; that is correct per `documentation_and_implementation_alignment.md` rules. Consider one clause in the **Plans / pointers** cell: e.g. “Linear **NEO-23–NEO-27** (Todo); register unchanged until code merges” so the table reflects ticketed work without bumping status early.
|
||||
2. ~~**Alignment table vs Linear** — The E1.M3 row says register stays **Planned** until first implementation **merges**; that is correct per `documentation_and_implementation_alignment.md` rules. Consider one clause in the **Plans / pointers** cell: e.g. “Linear **NEO-23–NEO-27** (Todo); register unchanged until code merges” so the table reflects ticketed work without bumping status early.~~ **Done.** — E1.M3 row Plans cell updated.
|
||||
|
||||
3. **E1M3-05 acceptance line** — In `E1M3-prototype-backlog.md`, the checkbox “update … when E1M3-01 merges” could say **NEO-23** for parity with Linear keys used elsewhere in the same file.
|
||||
3. ~~**E1M3-05 acceptance line** — In `E1M3-prototype-backlog.md`, the checkbox “update … when E1M3-01 merges” could say **NEO-23** for parity with Linear keys used elsewhere in the same file.~~ **Done.**
|
||||
|
||||
## Nits
|
||||
|
||||
- Summary table Linear cell uses an en-dash between NEO-23 and NEO-27 links; fine for humans; optional to spell out “through” for clarity in prose contexts outside the table.
|
||||
- ~~Summary table Linear cell uses an en-dash between NEO-23 and NEO-27 links; fine for humans; optional to spell out “through” for clarity in prose contexts outside the table.~~ **Done.** — `E1_M3_InteractionAndTargetingLayer.md` Summary **Linear** row now says “through”.
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue