2023-12-28 03:53:58 +00:00
|
|
|
use std::{collections::HashMap, ops::Deref};
|
2023-11-04 15:34:27 +00:00
|
|
|
|
2024-01-06 20:40:13 +00:00
|
|
|
use lyra_ecs::world::World;
|
2023-11-04 15:34:27 +00:00
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
use crate::plugin::Plugin;
|
2023-11-04 15:34:27 +00:00
|
|
|
|
2023-11-06 03:50:57 +00:00
|
|
|
use super::{Button, KeyCode, InputButtons};
|
2023-11-04 15:34:27 +00:00
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[allow(dead_code)]
|
2023-11-04 15:34:27 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
2023-12-28 03:53:58 +00:00
|
|
|
pub enum GamepadFormat {
|
2023-11-04 15:34:27 +00:00
|
|
|
DualAxis,
|
|
|
|
Joystick,
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[allow(dead_code)]
|
2023-11-04 15:34:27 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
2023-12-28 03:53:58 +00:00
|
|
|
pub enum GamepadButton {
|
2023-11-04 15:34:27 +00:00
|
|
|
FaceBottom,
|
|
|
|
FaceLeft,
|
|
|
|
FaceRight,
|
|
|
|
FaceTop,
|
|
|
|
VirtualConfirm,
|
|
|
|
VirtualDeny,
|
|
|
|
LThumbstick,
|
|
|
|
RThumbstick,
|
|
|
|
DPadUp,
|
|
|
|
DPadDown,
|
|
|
|
DPadLeft,
|
|
|
|
DPadRight,
|
|
|
|
LShoulder,
|
|
|
|
RShoulder,
|
|
|
|
LTrigger,
|
|
|
|
RTrigger,
|
|
|
|
Special,
|
|
|
|
LSpecial,
|
|
|
|
RSpecial,
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[allow(dead_code)]
|
2023-11-04 15:34:27 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
2023-12-28 03:53:58 +00:00
|
|
|
pub enum GamepadAxis {
|
2023-11-04 15:34:27 +00:00
|
|
|
LThumbstickX,
|
|
|
|
LThumbstickY,
|
|
|
|
RThumbstickX,
|
|
|
|
RThumbstickY,
|
|
|
|
LTrigger,
|
|
|
|
RTrigger,
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[allow(dead_code)]
|
2023-11-04 15:34:27 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
2023-12-28 03:53:58 +00:00
|
|
|
pub enum GamepadInput {
|
2023-11-04 15:34:27 +00:00
|
|
|
Button(GamepadButton),
|
|
|
|
Axis(GamepadAxis),
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[allow(dead_code)]
|
2023-11-04 15:34:27 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
|
|
pub enum ActionSource {
|
|
|
|
Keyboard(KeyCode),
|
|
|
|
Gamepad(GamepadFormat, GamepadInput),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ActionSource {
|
|
|
|
/// Convert this Source into a Binding. Uses a default Binding modifier value of `1.0`.
|
|
|
|
pub fn into_binding(self) -> Binding {
|
|
|
|
Binding::from_source(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert this Source into a Binding, setting the modifier.
|
|
|
|
pub fn into_binding_modifier(self, modifier: f32) -> Binding {
|
|
|
|
Binding::from_source_modifier(self, modifier)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct Binding {
|
|
|
|
pub source: ActionSource,
|
|
|
|
pub modifier: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Binding {
|
|
|
|
/// Create a binding from a Source. Uses a default value of `1.0` as the modifier.
|
|
|
|
pub fn from_source(source: ActionSource) -> Self {
|
|
|
|
Self {
|
|
|
|
source,
|
|
|
|
modifier: 1.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a binding from a Source, with a modifier
|
|
|
|
pub fn from_source_modifier(source: ActionSource, modifier: f32) -> Self {
|
|
|
|
Self {
|
|
|
|
source,
|
|
|
|
modifier,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-06 03:50:57 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
pub enum ActionState {
|
|
|
|
/// No input is being given by the user for the button/axis.
|
|
|
|
Idle,
|
|
|
|
/// The button is pressed
|
|
|
|
Pressed(f32),
|
|
|
|
/// The button was just pressed
|
|
|
|
JustPressed(f32),
|
|
|
|
/// The button was just released
|
|
|
|
JustReleased,
|
|
|
|
|
|
|
|
//Released,
|
|
|
|
/// The axis moved
|
|
|
|
Axis(f32),
|
|
|
|
/// Some other state with a modifier
|
|
|
|
Other(f32),
|
|
|
|
}
|
|
|
|
|
2023-11-04 15:34:27 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
|
|
pub enum ActionKind {
|
|
|
|
Button,
|
|
|
|
Axis
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct Action {
|
2023-11-06 03:50:57 +00:00
|
|
|
pub kind: ActionKind,
|
|
|
|
pub state: ActionState,
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Action {
|
|
|
|
pub fn new(kind: ActionKind) -> Self {
|
|
|
|
Self {
|
|
|
|
kind,
|
2023-11-06 03:50:57 +00:00
|
|
|
state: ActionState::Idle,
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
2023-11-04 15:34:27 +00:00
|
|
|
pub struct LayoutId(u32);
|
|
|
|
|
|
|
|
impl From<u32> for LayoutId {
|
|
|
|
fn from(value: u32) -> Self {
|
|
|
|
Self(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[derive(Clone, Default)]
|
2023-11-04 15:34:27 +00:00
|
|
|
pub struct Layout {
|
|
|
|
mappings: HashMap<ActionMappingId, ActionMapping>,
|
2023-11-06 03:50:57 +00:00
|
|
|
active_mapping: ActionMappingId,
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Layout {
|
|
|
|
pub fn new() -> Self {
|
2023-12-28 03:53:58 +00:00
|
|
|
Self::default()
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_mapping(&mut self, mapping: ActionMapping) -> &mut Self {
|
|
|
|
self.mappings.insert(mapping.id, mapping);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
2023-11-04 15:34:27 +00:00
|
|
|
pub struct ActionMappingId(u32);
|
|
|
|
|
|
|
|
impl From<u32> for ActionMappingId {
|
|
|
|
fn from(value: u32) -> Self {
|
|
|
|
Self(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct ActionMapping {
|
|
|
|
layout: LayoutId,
|
|
|
|
id: ActionMappingId,
|
|
|
|
action_binds: HashMap<String, Vec<Binding>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ActionMapping {
|
|
|
|
pub fn new(layout: LayoutId, id: ActionMappingId) -> Self {
|
|
|
|
Self {
|
|
|
|
layout,
|
|
|
|
id,
|
|
|
|
action_binds: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a binding for the action.
|
|
|
|
///
|
|
|
|
/// If the action is not in this layout, this will panic!
|
|
|
|
///
|
|
|
|
/// Parameters:
|
|
|
|
/// * `action_label` - The label corresponding to the action in this Layout.
|
|
|
|
/// * `bind` - The Binding to add to the Action.
|
|
|
|
pub fn bind(mut self, action_label: &str, bindings: &[Binding]) -> Self {
|
|
|
|
let mut bindings = bindings.to_vec();
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
let action_binds = self.action_binds.entry(action_label.to_string()).or_default();
|
2023-11-04 15:34:27 +00:00
|
|
|
action_binds.append(&mut bindings);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates multiple binding for the action.
|
|
|
|
///
|
|
|
|
/// If the action is not in this layout, this will panic!
|
|
|
|
///
|
|
|
|
/// Parameters:
|
|
|
|
/// * `action_label` - The label corresponding to the action in this Layout.
|
|
|
|
/// * `bindings` - The list of Bindings to add to the Action.
|
|
|
|
/* pub fn add_bindings(&mut self, action_label: String, bindings: &[Binding]) -> &mut Self {
|
|
|
|
let mut bindings = bindings.to_vec();
|
|
|
|
let action_binds = self.action_binds.entry(action_label)
|
2023-12-28 03:53:58 +00:00
|
|
|
.or_insert_with(Vec::new);
|
2023-11-04 15:34:27 +00:00
|
|
|
action_binds.append(&mut bindings);
|
|
|
|
|
|
|
|
self
|
|
|
|
} */
|
|
|
|
|
|
|
|
pub fn finish(self) -> Self {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
#[derive(Clone, Default)]
|
2023-11-04 15:34:27 +00:00
|
|
|
pub struct ActionHandler {
|
2023-11-06 03:50:57 +00:00
|
|
|
pub actions: HashMap<String, Action>,
|
|
|
|
pub layouts: HashMap<LayoutId, Layout>,
|
|
|
|
pub current_layout: LayoutId,
|
|
|
|
pub current_mapping: ActionMappingId,
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ActionHandler {
|
|
|
|
pub fn new() -> Self {
|
2023-12-28 03:53:58 +00:00
|
|
|
Self::default()
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|
|
|
|
|
2023-11-06 03:50:57 +00:00
|
|
|
pub fn add_layout(mut self, id: LayoutId) -> Self {
|
2023-11-04 15:34:27 +00:00
|
|
|
self.layouts.insert(id, Layout::new());
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2023-11-06 03:50:57 +00:00
|
|
|
pub fn add_action(mut self, label: &str, action: Action) -> Self {
|
2023-11-04 15:34:27 +00:00
|
|
|
self.actions.insert(label.to_string(), action);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2023-11-06 03:50:57 +00:00
|
|
|
pub fn add_mapping(mut self, mapping: ActionMapping) -> Self {
|
2023-11-04 15:34:27 +00:00
|
|
|
let layout = self.layouts.get_mut(&mapping.layout).unwrap();
|
|
|
|
layout.add_mapping(mapping);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
2023-11-06 03:50:57 +00:00
|
|
|
|
|
|
|
/// Returns true if the action is pressed (or was just pressed).
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn is_action_pressed(&self, action_name: &str) -> bool {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
matches!(action.state, ActionState::Pressed(_) | ActionState::JustPressed(_))
|
2023-11-06 03:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the action was just pressed.
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn was_action_just_pressed(&self, action_name: &str) -> bool {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
matches!(action.state, ActionState::JustPressed(_))
|
2023-11-06 03:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the action was just released.
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn was_action_just_released(&self, action_name: &str) -> bool {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
2023-12-28 03:53:58 +00:00
|
|
|
matches!(action.state, ActionState::JustReleased)
|
2023-11-06 03:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns an action's state.
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn get_action_state(&self, action_name: &str) -> ActionState {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
|
|
|
action.state
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the action's modifier if it is pressed (or was just pressed).
|
|
|
|
/// Returns `None` if the action's state is not `ActionState::Pressed` or `ActionState::JustPressed`.
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn get_pressed_modifier(&self, action_name: &str) -> Option<f32> {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
|
|
|
match action.state {
|
|
|
|
ActionState::Pressed(v) | ActionState::JustPressed(v) => Some(v),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the action's modifier if it was just pressed.
|
|
|
|
/// Returns `None` if the action's state is not `ActionState::JustPressed`.
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn get_just_pressed_modifier(&self, action_name: &str) -> Option<f32> {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
|
|
|
match action.state {
|
|
|
|
ActionState::JustPressed(v) => Some(v),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the action's modifier if its an updated axis.
|
|
|
|
/// Returns `None` if the action's state is not `ActionState::Axis`.
|
|
|
|
///
|
|
|
|
/// This will panic if the action name does not correspond to an action.
|
|
|
|
pub fn get_axis_modifier(&self, action_name: &str) -> Option<f32> {
|
|
|
|
let action = self.actions.get(action_name)
|
2023-12-28 03:53:58 +00:00
|
|
|
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
|
2023-11-06 03:50:57 +00:00
|
|
|
|
|
|
|
match action.state {
|
|
|
|
ActionState::Axis(v) => Some(v),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn actions_system(world: &mut World) -> anyhow::Result<()> {
|
2023-12-26 19:12:53 +00:00
|
|
|
let keys = world.try_get_resource::<InputButtons<KeyCode>>()
|
2023-11-06 03:50:57 +00:00
|
|
|
.map(|r| r.deref().clone());
|
|
|
|
|
|
|
|
if let Some(keys) = keys {
|
2023-12-26 19:12:53 +00:00
|
|
|
let mut handler = world.try_get_resource_mut::<ActionHandler>()
|
2023-11-06 03:50:57 +00:00
|
|
|
.expect("No Input Action handler was created in the world!");
|
|
|
|
|
|
|
|
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
|
|
|
|
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
|
|
|
|
|
|
|
|
for (action_name, binds) in mapping.action_binds.clone().iter() { // TODO: dont clone
|
|
|
|
let mut new_state = None;
|
|
|
|
|
|
|
|
for bind in binds.iter() {
|
|
|
|
match bind.source {
|
|
|
|
ActionSource::Keyboard(key) => {
|
|
|
|
// JustPressed needs to be first, since is_pressed includes buttons that
|
|
|
|
// were just pressed.
|
|
|
|
if keys.was_just_pressed(key) {
|
|
|
|
new_state = Some(ActionState::JustPressed(bind.modifier));
|
|
|
|
} else if keys.is_pressed(key) {
|
|
|
|
new_state = Some(ActionState::Pressed(bind.modifier));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ActionSource::Gamepad(_, _) => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let action = handler.actions.get_mut(action_name).expect("Action name for binding is invalid!");
|
|
|
|
|
|
|
|
if let Some(new_state) = new_state {
|
|
|
|
action.state = new_state;
|
|
|
|
} else {
|
|
|
|
match action.state {
|
|
|
|
ActionState::Idle => {},
|
|
|
|
ActionState::JustReleased => action.state = ActionState::Idle,
|
|
|
|
_ => action.state = ActionState::JustReleased,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, Clone, Copy)]
|
|
|
|
pub struct InputActionPlugin;
|
|
|
|
|
|
|
|
impl Plugin for InputActionPlugin {
|
|
|
|
fn setup(&self, game: &mut crate::game::Game) {
|
2024-01-06 20:40:13 +00:00
|
|
|
game.with_system("input_actions", actions_system, &[]);
|
2023-11-06 03:50:57 +00:00
|
|
|
}
|
2023-11-04 15:34:27 +00:00
|
|
|
}
|