use crate::action_state::ActionData;
use crate::buttonlike::ButtonState;
use crate::clashing_inputs::ClashStrategy;
use crate::input_streams::InputStreams;
use crate::user_input::{InputKind, Modifier, UserInput};
use crate::Actionlike;
use bevy::ecs::component::Component;
use bevy::ecs::system::Resource;
use bevy::input::gamepad::Gamepad;
use bevy::reflect::TypeUuid;
use core::fmt::Debug;
use petitset::PetitSet;
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use std::hash::Hash;
use std::marker::PhantomData;
#[derive(Resource, Component, Debug, Clone, PartialEq, Eq, TypeUuid)]
#[uuid = "D7DECC78-8573-42FF-851A-F0344C7D05C9"]
pub struct InputMap<A: Actionlike> {
map: Vec<PetitSet<UserInput, 16>>,
associated_gamepad: Option<Gamepad>,
marker: PhantomData<A>,
}
impl<A: Actionlike> Default for InputMap<A> {
fn default() -> Self {
InputMap {
map: A::variants().map(|_| PetitSet::default()).collect(),
associated_gamepad: None,
marker: PhantomData,
}
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
pub fn new(bindings: impl IntoIterator<Item = (impl Into<UserInput>, A)>) -> Self {
let mut input_map = InputMap::default();
input_map.insert_multiple(bindings);
input_map
}
#[inline]
#[must_use]
pub fn build(&mut self) -> Self {
self.clone()
}
}
impl<A: Actionlike> InputMap<A> {
pub fn insert(&mut self, input: impl Into<UserInput>, action: A) -> &mut Self {
let input = input.into();
self.map[action.index()].insert(input);
self
}
pub fn insert_at(&mut self, input: impl Into<UserInput>, action: A, index: usize) -> &mut Self {
let input = input.into();
self.map[action.index()].insert_at(input, index);
self
}
pub fn insert_multiple(
&mut self,
input_action_pairs: impl IntoIterator<Item = (impl Into<UserInput>, A)>,
) -> &mut Self {
for (action, input) in input_action_pairs {
self.insert(action, input);
}
self
}
pub fn insert_chord(
&mut self,
buttons: impl IntoIterator<Item = impl Into<InputKind>>,
action: A,
) -> &mut Self {
self.insert(UserInput::chord(buttons), action);
self
}
pub fn insert_modified(
&mut self,
modifier: Modifier,
input: impl Into<InputKind>,
action: A,
) -> &mut Self {
self.insert(UserInput::modified(modifier, input), action);
self
}
pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
let associated_gamepad = if self.associated_gamepad == other.associated_gamepad {
self.associated_gamepad
} else {
None
};
let mut new_map = InputMap {
associated_gamepad,
..Default::default()
};
for action in A::variants() {
for input in self.get(action.clone()).iter() {
new_map.insert(input.clone(), action.clone());
}
for input in other.get(action.clone()).iter() {
new_map.insert(input.clone(), action.clone());
}
}
*self = new_map;
self
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
pub fn gamepad(&self) -> Option<Gamepad> {
self.associated_gamepad
}
pub fn set_gamepad(&mut self, gamepad: Gamepad) -> &mut Self {
self.associated_gamepad = Some(gamepad);
self
}
pub fn clear_gamepad(&mut self) -> &mut Self {
self.associated_gamepad = None;
self
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
pub fn pressed(
&self,
action: A,
input_streams: &InputStreams,
clash_strategy: ClashStrategy,
) -> bool {
let action_data = self.which_pressed(input_streams, clash_strategy);
action_data[action.index()].state.pressed()
}
#[must_use]
pub fn which_pressed(
&self,
input_streams: &InputStreams,
clash_strategy: ClashStrategy,
) -> Vec<ActionData> {
let mut action_data = vec![ActionData::default(); A::N_VARIANTS];
for action in A::variants() {
let mut inputs = Vec::new();
for input in self.get(action.clone()).iter() {
let action = &mut action_data[action.index()];
let axis_pair = input_streams.input_axis_pair(input);
if let Some(axis_pair) = axis_pair {
if let Some(current_axis_pair) = &mut action.axis_pair {
*current_axis_pair = current_axis_pair.merged_with(axis_pair);
} else {
action.axis_pair = Some(axis_pair);
}
}
if input_streams.input_pressed(input) {
inputs.push(input.clone());
action.value += input_streams.input_value(input);
}
}
if !inputs.is_empty() {
action_data[action.index()].state = ButtonState::JustPressed;
}
}
self.handle_clashes(&mut action_data, input_streams, clash_strategy);
action_data
}
}
impl<A: Actionlike> InputMap<A> {
pub fn iter(&self) -> impl Iterator<Item = (&PetitSet<UserInput, 16>, A)> {
self.map
.iter()
.enumerate()
.map(|(action_index, inputs)| (inputs, A::get_at(action_index).unwrap()))
}
pub fn iter_inputs(&self) -> impl Iterator<Item = &PetitSet<UserInput, 16>> {
self.map.iter()
}
#[must_use]
pub fn get(&self, action: A) -> &PetitSet<UserInput, 16> {
&self.map[action.index()]
}
#[must_use]
pub fn len(&self) -> usize {
let mut i = 0;
for action in A::variants() {
i += self.get(action).len();
}
i
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<A: Actionlike> InputMap<A> {
pub fn clear_action(&mut self, action: A) {
self.map[action.index()].clear();
}
pub fn remove_at(&mut self, action: A, index: usize) -> bool {
self.map[action.index()].remove_at(index)
}
pub fn remove(&mut self, action: A, input: impl Into<UserInput>) -> Option<usize> {
self.map[action.index()].remove(&input.into())
}
}
impl<A: Actionlike> From<HashMap<A, Vec<UserInput>>> for InputMap<A> {
fn from(map: HashMap<A, Vec<UserInput>>) -> Self {
map.iter()
.flat_map(|(action, inputs)| inputs.iter().map(|input| (action.clone(), input.clone())))
.collect()
}
}
impl<A: Actionlike> FromIterator<(A, UserInput)> for InputMap<A> {
fn from_iter<T: IntoIterator<Item = (A, UserInput)>>(iter: T) -> Self {
InputMap::new(iter.into_iter().map(|(action, input)| (input, action)))
}
}
impl<A> Serialize for InputMap<A>
where
A: Actionlike + Serialize + Eq + Hash + Ord,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
use std::collections::BTreeMap;
let mut input_map = serializer.serialize_struct("InputMap", 1)?;
input_map.serialize_field(
"map",
&self
.iter()
.map(|(set, action)| (action, set.iter().collect()))
.collect::<BTreeMap<A, Vec<&UserInput>>>(),
)?;
input_map.end()
}
}
impl<'de, A> Deserialize<'de> for InputMap<A>
where
A: Actionlike + Deserialize<'de> + Eq + Hash,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Visitor;
#[derive(Deserialize, PartialEq)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Map,
}
struct InputMapVisitor<'de, A: Actionlike + Deserialize<'de>> {
marker: PhantomData<&'de A>,
}
impl<'de, A> Visitor<'de> for InputMapVisitor<'de, A>
where
A: Actionlike + Eq + Hash + Deserialize<'de>,
{
type Value = InputMap<A>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a struct with field 'map' of type map where key is `Actionlike` and value is sequents of `UserInput`")
}
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
where
S: serde::de::SeqAccess<'de>,
{
let map = seq.next_element::<HashMap<A, Vec<UserInput>>>()?;
map.ok_or_else(|| {
serde::de::Error::invalid_length(0, &"one argument with type `map`")
})
.map(InputMap::from)
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: serde::de::MapAccess<'de>,
{
map.next_key::<Field>()?
.filter(|key| *key == Field::Map)
.ok_or_else(|| serde::de::Error::missing_field("map"))?;
let value = map.next_value::<HashMap<A, Vec<UserInput>>>()?;
Ok(value.into())
}
}
let visitor = InputMapVisitor {
marker: PhantomData,
};
const FIELDS: &[&str] = &["map"];
deserializer.deserialize_struct("InputMap", FIELDS, visitor)
}
}
mod tests {
use serde::{Deserialize, Serialize};
use crate as leafwing_input_manager;
use crate::prelude::*;
#[derive(
Actionlike, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug,
)]
enum Action {
Run,
Jump,
Hide,
}
#[test]
fn insertion_idempotency() {
use bevy::input::keyboard::KeyCode;
use petitset::PetitSet;
let mut input_map = InputMap::<Action>::default();
input_map.insert(KeyCode::Space, Action::Run);
assert_eq!(
*input_map.get(Action::Run),
PetitSet::<UserInput, 16>::from_iter([KeyCode::Space.into()])
);
input_map.insert(KeyCode::Space, Action::Run);
assert_eq!(
*input_map.get(Action::Run),
PetitSet::<UserInput, 16>::from_iter([KeyCode::Space.into()])
);
}
#[test]
fn multiple_insertion() {
use crate::user_input::UserInput;
use bevy::input::keyboard::KeyCode;
use petitset::PetitSet;
let mut input_map_1 = InputMap::<Action>::default();
input_map_1.insert(KeyCode::Space, Action::Run);
input_map_1.insert(KeyCode::Return, Action::Run);
assert_eq!(
*input_map_1.get(Action::Run),
PetitSet::<UserInput, 16>::from_iter([KeyCode::Space.into(), KeyCode::Return.into()])
);
let input_map_2 = InputMap::<Action>::new([
(KeyCode::Space, Action::Run),
(KeyCode::Return, Action::Run),
]);
assert_eq!(input_map_1, input_map_2);
}
#[test]
fn chord_singleton_coercion() {
use crate::input_map::UserInput;
use bevy::input::keyboard::KeyCode;
let mut input_map_1 = InputMap::<Action>::default();
input_map_1.insert(KeyCode::Space, Action::Run);
let mut input_map_2 = InputMap::<Action>::default();
input_map_2.insert(UserInput::chord([KeyCode::Space]), Action::Run);
assert_eq!(input_map_1, input_map_2);
}
#[test]
fn input_clearing() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::<Action>::default();
input_map.insert(KeyCode::Space, Action::Run);
input_map.clear_action(Action::Run);
assert_eq!(input_map, InputMap::default());
input_map.insert(KeyCode::Space, Action::Run);
input_map.insert(KeyCode::LShift, Action::Run);
assert!(input_map.remove_at(Action::Run, 1));
assert!(
!input_map.remove_at(Action::Run, 1),
"Should return false on second removal at the same index"
);
assert!(input_map.remove_at(Action::Run, 0));
assert!(
!input_map.remove_at(Action::Run, 0),
"Should return false on second removal at the same index"
);
}
#[test]
fn merging() {
use bevy::input::{gamepad::GamepadButtonType, keyboard::KeyCode};
let mut input_map = InputMap::default();
let mut default_keyboard_map = InputMap::default();
default_keyboard_map.insert(KeyCode::LShift, Action::Run);
default_keyboard_map.insert_chord([KeyCode::LControl, KeyCode::H], Action::Hide);
let mut default_gamepad_map = InputMap::default();
default_gamepad_map.insert(GamepadButtonType::South, Action::Run);
default_gamepad_map.insert(GamepadButtonType::East, Action::Hide);
input_map.merge(&default_keyboard_map);
assert_eq!(input_map, default_keyboard_map);
input_map.merge(&default_keyboard_map);
assert_eq!(input_map, default_keyboard_map);
}
#[test]
fn gamepad_swapping() {
use bevy::input::gamepad::Gamepad;
let mut input_map = InputMap::<Action>::default();
assert_eq!(input_map.gamepad(), None);
input_map.set_gamepad(Gamepad { id: 0 });
assert_eq!(input_map.gamepad(), Some(Gamepad { id: 0 }));
input_map.clear_gamepad();
assert_eq!(input_map.gamepad(), None);
}
#[test]
fn from() {
use bevy::prelude::KeyCode;
use std::collections::HashMap;
let mut map: HashMap<Action, Vec<UserInput>> = HashMap::default();
map.insert(
Action::Hide,
vec![UserInput::chord(vec![KeyCode::R, KeyCode::E])],
);
map.insert(Action::Jump, vec![UserInput::from(KeyCode::Space)]);
map.insert(
Action::Run,
vec![KeyCode::LShift.into(), KeyCode::RShift.into()],
);
let mut input_map = InputMap::default();
input_map.insert_chord(vec![KeyCode::R, KeyCode::E], Action::Hide);
input_map.insert(KeyCode::Space, Action::Jump);
input_map.insert(KeyCode::LShift, Action::Run);
input_map.insert(KeyCode::RShift, Action::Run);
assert_eq!(input_map, map.into());
}
#[test]
fn serde() {
use bevy::prelude::KeyCode;
use serde_test::assert_tokens;
use serde_test::Token;
let mut input_map = InputMap::default();
input_map.insert_chord(vec![KeyCode::R, KeyCode::E], Action::Hide);
input_map.insert(KeyCode::Space, Action::Jump);
input_map.insert(KeyCode::LShift, Action::Run);
input_map.insert(KeyCode::RShift, Action::Run);
assert_tokens(
&input_map,
&[
Token::Struct {
name: "InputMap",
len: 1,
},
Token::Str("map"),
Token::Map { len: Some(3) },
Token::UnitVariant {
name: "Action",
variant: "Run",
},
Token::Seq { len: Some(2) },
Token::NewtypeVariant {
name: "UserInput",
variant: "Single",
},
Token::NewtypeVariant {
name: "InputKind",
variant: "Keyboard",
},
Token::UnitVariant {
name: "KeyCode",
variant: "LShift",
},
Token::NewtypeVariant {
name: "UserInput",
variant: "Single",
},
Token::NewtypeVariant {
name: "InputKind",
variant: "Keyboard",
},
Token::UnitVariant {
name: "KeyCode",
variant: "RShift",
},
Token::SeqEnd,
Token::UnitVariant {
name: "Action",
variant: "Jump",
},
Token::Seq { len: Some(1) },
Token::NewtypeVariant {
name: "UserInput",
variant: "Single",
},
Token::NewtypeVariant {
name: "InputKind",
variant: "Keyboard",
},
Token::UnitVariant {
name: "KeyCode",
variant: "Space",
},
Token::SeqEnd,
Token::UnitVariant {
name: "Action",
variant: "Hide",
},
Token::Seq { len: Some(1) },
Token::NewtypeVariant {
name: "UserInput",
variant: "Chord",
},
Token::Seq { len: Some(8) },
Token::Some,
Token::NewtypeVariant {
name: "InputKind",
variant: "Keyboard",
},
Token::UnitVariant {
name: "KeyCode",
variant: "R",
},
Token::Some,
Token::NewtypeVariant {
name: "InputKind",
variant: "Keyboard",
},
Token::UnitVariant {
name: "KeyCode",
variant: "E",
},
Token::None,
Token::None,
Token::None,
Token::None,
Token::None,
Token::None,
Token::SeqEnd,
Token::SeqEnd,
Token::MapEnd,
Token::StructEnd,
],
)
}
}