1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Input resources.

use bones_schema::HasSchema;

use self::prelude::{GamepadInputs, KeyboardInputs};

pub mod gamepad;
pub mod gilrs;
pub mod keyboard;
pub mod mouse;
pub mod window;

/// The state of a button, ether pressed or released.
#[derive(HasSchema, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(u8)]
pub enum ButtonState {
    /// The button is pressed.
    Pressed,
    #[default]
    /// The button is released.
    Released,
}

impl ButtonState {
    /// Get whether or not the button is pressed.
    pub fn pressed(&self) -> bool {
        matches!(self, ButtonState::Pressed)
    }
}

/// Module prelude.
pub mod prelude {
    pub use super::{gamepad::*, keyboard::*, mouse::*, window::*, ButtonState};
    pub use crate::input::{InputCollector, PlayerControls};
}

/// Maps raw inputs to game controls and exposes controls for respective player and their control source.
///
/// [`InputCollector::apply_inputs`] maps raw input to game controls and updates them.
///
/// [`InputCollector::update_just_pressed`] computes any changes in pressed buttons that may be stored on control.
///
/// [`InputCollector::advance_frame`] is  used to mark that the input has been consumed, and update the prev frame inputs to current, to compute changes next frame.
///
/// Generic type param ControlMapping is HasSchema because it is expected to be a Resource retrievable on world.
pub trait InputCollector<'a, ControlMapping: HasSchema, ControlSource, Control>:
    Send + Sync
{
    /// Update the internal state with new inputs. This must be called every render frame with the
    /// input events. This updates which buttons are pressed, but does not compute what buttons were "just_pressed".
    /// use [`InputCollector::update_just_pressed`] to do this.
    fn apply_inputs(
        &mut self,
        mapping: &ControlMapping,
        keyboard: &KeyboardInputs,
        gamepad: &GamepadInputs,
    );

    /// Indicate input for this frame has been consumed. An implementation of [`InputCollector`] that is
    /// used with a fixed simulation step may track what keys are currently pressed, and what keys were "just pressed",
    /// (changing from previous state).
    ///
    /// This saves current inputs as previous frame's inputs, allowing for determining what is "just pressed" next frame.
    fn advance_frame(&mut self);

    /// Update which buttons have been "just pressed", when input has changed from last frame and current input state.
    ///
    /// This does not modify previous frame's input, to do this use [`InputCollector::advance_frame`].
    fn update_just_pressed(&mut self);

    /// Get control for player based on provided `ControlSource`.
    fn get_control(&self, player_idx: usize, control_source: ControlSource) -> &Control;
}

/// Trait that tracks player control state. Provides associated types for other input trait implementations.
pub trait PlayerControls<'a, Control> {
    /// InputCollector used to update controls.
    type InputCollector: InputCollector<'a, Self::ControlMapping, Self::ControlSource, Control>;

    /// Control mapping from raw input, expected to be able to be retrieved as `Resource` from world.
    type ControlMapping: HasSchema;

    /// Type used to map source of input to control.
    type ControlSource;

    /// Update control state from input collector.
    fn update_controls(&mut self, collector: &mut Self::InputCollector);

    /// Get `ControlSource` for player (only present for local player).
    fn get_control_source(&self, local_player_idx: usize) -> Option<Self::ControlSource>;

    /// Get control for player.
    fn get_control(&self, player_idx: usize) -> &Control;

    /// Get mutable control for player.
    fn get_control_mut(&mut self, player_idx: usize) -> &mut Control;
}