4.0 KiB
4.0 KiB
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 eachInputEventMouseMotion - Middle mouse drag: immediately enter drag mode, emit
camera_drag(delta)on each motion - All of the above gated by
if input_disabled: returnat the top
Mouse grid tracking (_process):
- Read
get_global_mouse_position(), snap to grid, convert toVector2i - 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
CameraControllerbehavior
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 tocamera_dragsignal - Apply delta to camera position, respecting any existing bounds/smoothing
TileHighlight — reactive
- Remove
_process()mouse tracking andget_global_mouse_position()calls - Keep the pulse animation (can run in its own
_processgated 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
_notificationforWM_MOUSE_EXIT/WM_MOUSE_ENTERif 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_disabledgate
Migration notes
CameraControllerdrag state variables and_unhandled_inputare deleted, not left as dead codeTileHighlight._processmouse reading is deletedTileHighlightkeeps itstile_sizeexport and grid-snapping math (used byset_grid_coords)- PlayerController needs
dl_mapreference for grid coordinate conversion (already has it)