95 lines
4.0 KiB
Markdown
95 lines
4.0 KiB
Markdown
# Input Centralization Design
|
|
|
|
## Problem
|
|
|
|
Input handling is scattered across `PlayerController`, `CameraController`, and `TileHighlight`, each independently reading mouse state. There's no way to globally disable gameplay input during overlays or animations.
|
|
|
|
## Design
|
|
|
|
### PlayerController — single input authority
|
|
|
|
`PlayerController` becomes the only script that reads raw input. All other gameplay scripts react to its signals.
|
|
|
|
**Signals:**
|
|
|
|
| Signal | Emitted when |
|
|
|--------|-------------|
|
|
| `mouse_grid_changed(coords: Vector2i)` | Hovered grid cell changes (every frame check in `_process()`) |
|
|
| `camera_drag(delta: Vector2)` | Mouse is dragged (left-drag past 8px threshold, or any middle-drag) |
|
|
| `combat_requested(attacker, defender)` | Existing — click on enemy unit while friendly selected |
|
|
|
|
**Properties:**
|
|
|
|
| Property | Purpose |
|
|
|----------|---------|
|
|
| `input_disabled: bool` | When true, all input processing stops — no signals emitted, no click handling, no movement |
|
|
|
|
**Input handling (`_unhandled_input`):**
|
|
|
|
- Left click release (no drag): unit selection or movement target (existing logic)
|
|
- Left click release on enemy unit while friendly selected: emit `combat_requested` (existing logic)
|
|
- Left mouse drag: track press position, once delta exceeds 8px threshold, enter drag mode and emit `camera_drag(delta)` on each `InputEventMouseMotion`
|
|
- Middle mouse drag: immediately enter drag mode, emit `camera_drag(delta)` on each motion
|
|
- All of the above gated by `if input_disabled: return` at the top
|
|
|
|
**Mouse grid tracking (`_process`):**
|
|
|
|
- Read `get_global_mouse_position()`, snap to grid, convert to `Vector2i`
|
|
- If coords changed from previous frame, emit `mouse_grid_changed(coords)`
|
|
- Gated by `input_disabled`
|
|
|
|
**Left-drag vs left-click disambiguation:**
|
|
|
|
- On left button press: record press position, set `_drag_candidate = true`
|
|
- On mouse motion while `_drag_candidate`: if distance from press > 8px, enter drag mode (`_dragging = true`), stop treating this as a click
|
|
- On left button release: if `_dragging`, end drag and reset state; if not, treat as click (existing selection/movement logic)
|
|
- This matches the existing `CameraController` behavior
|
|
|
|
### CameraController — reactive
|
|
|
|
- Remove `_unhandled_input()` entirely
|
|
- Remove all drag detection state (`_dragging`, `_drag_start`, etc.)
|
|
- Expose a method (e.g., `apply_drag(delta: Vector2)`) or connect directly to `camera_drag` signal
|
|
- Apply delta to camera position, respecting any existing bounds/smoothing
|
|
|
|
### TileHighlight — reactive
|
|
|
|
- Remove `_process()` mouse tracking and `get_global_mouse_position()` calls
|
|
- Keep the pulse animation (can run in its own `_process` gated on visibility)
|
|
- Expose a method like `set_grid_coords(coords: Vector2i)` that positions the highlight
|
|
- Hide when input is disabled (connected to a signal or called directly)
|
|
- Keep `_notification` for `WM_MOUSE_EXIT` / `WM_MOUSE_ENTER` if still relevant, or remove if PlayerController handles this
|
|
|
|
### strategy_phase.gd — wiring
|
|
|
|
Connects signals in `_ready()`:
|
|
|
|
```
|
|
player_controller.mouse_grid_changed → tile_highlight.set_grid_coords (or similar)
|
|
player_controller.camera_drag → camera_controller.apply_drag
|
|
```
|
|
|
|
Overlays toggle input:
|
|
|
|
```
|
|
# When showing overlay:
|
|
player_controller.input_disabled = true
|
|
|
|
# When hiding overlay:
|
|
player_controller.input_disabled = false
|
|
```
|
|
|
|
## What doesn't change
|
|
|
|
- `CombatUI._unhandled_input()` for right-click dismiss — this is UI-layer input, not gameplay input
|
|
- Unit signals (`unit_died`, `unit_selected_changed`)
|
|
- CombatSystem logic
|
|
- All existing click behavior (selection, movement, combat requests) — just consolidated under the `input_disabled` gate
|
|
|
|
## Migration notes
|
|
|
|
- `CameraController` drag state variables and `_unhandled_input` are deleted, not left as dead code
|
|
- `TileHighlight._process` mouse reading is deleted
|
|
- `TileHighlight` keeps its `tile_size` export and grid-snapping math (used by `set_grid_coords`)
|
|
- PlayerController needs `dl_map` reference for grid coordinate conversion (already has it)
|