12 KiB
E1.M3 — InteractionAndTargetingLayer
Summary
| Field | Value |
|---|---|
| Module ID | E1.M3 |
| Epic | Epic 1 — Core Player Runtime |
| Stage target | Prototype |
| Status | In Progress (see dependency register) |
| Linear | NEO-23 through NEO-27 (backlog); label E1.M3 per decomposition README — Linear alignment |
Purpose
Authority: Client vs server — E1.M3 — 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 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).
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) |
Future selection/target intents and authoritative TargetState sync (wire shape TBD; see 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. 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
- Combat target lock: maintain and expose
TargetStatefor tab-target flow; integrate with E1.M4 — AbilityInputScaffold and E5.M1 — CombatRulesEngine 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
InteractableDescriptorprojections. - 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).
- Presentation: highlights, outlines, and optional wrong local preview until server ack; reconcile on snapshot or denial (authority).
- E1.M2 adjacency: targeting and line-of-interest for gameplay use world geometry + server-known positions, not
CameraStateor client-only aim (E1.M2).
Key contracts
| Contract | Role |
|---|---|
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)
- Kind: these are wire contracts once the game channel exists (Protobuf preferred per tech stack); until then, versioned JSON spikes must be explicitly throwaway and tracked for migration—same discipline as early
MoveCommand/InteractionRequest. - Denials: reuse the stable
reasonCodestring pattern fromInteractionResponse(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)
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: authoritative
PositionStateand horizontal reach policy; existingInteractionRequestpath for “use now” attempts.
Logical (not in register “Depends On” column): E5.M1 resolves hostile actions and may deny casts for reasons beyond selection; E6.M1 gates player targets (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.
Dependents (by design)
- E1.M4 — AbilityInputScaffold: reads
TargetState(and related validity) to buildAbilityCastRequest. - 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
- 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_deniedwith reason codes (align with E9.M1 when catalog exists).
Implementation snapshot
TargetState(prototype JSON v1 — NEO-23): serverGET /game/players/{id}/targetandPOST /game/players/{id}/target/selectwith versionedPlayerTargetStateResponse/TargetSelectRequest/TargetSelectResponse; stub registry + in-memory lock underserver/NeonSprawl.Server/Game/Targeting/; horizontal reach + soft lock semantics — NEO-23, server README — Targeting. Godot tab-target: NEO-24.InteractableDescriptorlist:GET /game/world/interactables(NEO-25).SelectionEvent(prototype): GodotTargetSelectionClient.selection_eventwhenlockedTargetIdchanges — NEO-26. Story backlog: E1M3 prototype backlog.- Precursor under E1.M1 (NEO-9 + NEO-25): server
POST /game/players/{id}/interactwithInteractionRequest/InteractionResponse;GET /game/world/interactables(versioned descriptor list);HorizontalReach(server/NeonSprawl.Server/Game/World/HorizontalReach.cs); prototype registry underserver/NeonSprawl.Server/Game/Interaction/; clientinteractables_catalog_client.gd,interactable_world_builder.gd,interaction_request_client.gd,interaction_radius_indicators.gd— see NEO-9, NEO-25, and server README — Interaction. - Alignment: Documentation and implementation alignment.
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).
- Occluders vs truth: camera occlusion and pick-through (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; 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.
Stories: full acceptance checklists in docs/plans/E1M3-prototype-backlog.md.
| Slug | Linear |
|---|---|
| E1M3-01 | NEO-23 — TargetState v1 + server selection intent |
| E1M3-02 | NEO-24 — Client tab-target + lock UI synced to server |
| E1M3-03 | NEO-25 — InteractableDescriptor list / projection |
| E1M3-04 | NEO-26 — SelectionEvent surface + consumer |
| E1M3-05 | NEO-27 — Slice 3 telemetry hook sites |
Issues should carry label E1.M3 per Linear alignment.
Source anchors
- Master plan:
neon_sprawl_vision.plan.md— Epic 1, E1.M3 module line. - Module dependency register
- PvP and the combat engine — E5.M1 + E6.M1 gating for player targets