use bevy::{
input::{
gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent},
keyboard::KeyboardInput,
mouse::MouseButtonInput,
},
prelude::*,
state::state::FreelyMutableState,
};
use std::marker::PhantomData;
use crate::{
analog::{analog_control::AnalogControl, analog_inputs::AnalogInputs},
button::buttons::Buttons,
};
pub struct AnalogPlugin<TContext, TAction>(PhantomData<TContext>, PhantomData<TAction>);
impl<TC, TA> Default for AnalogPlugin<TC, TA> {
fn default() -> Self {
Self(Default::default(), Default::default())
}
}
impl<TContext: FreelyMutableState + FromWorld, TAction: Sync + Send + 'static> Plugin
for AnalogPlugin<TContext, TAction>
{
fn build(&self, app: &mut App) {
app.init_state::<TContext>()
.add_systems(Update, Self::check_controls);
}
}
impl<TContext: States, TAction: Sync + Send + 'static> AnalogPlugin<TContext, TAction> {
fn check_controls(
mut controls: Query<&mut AnalogControl<TContext, TAction>>,
context: Res<State<TContext>>,
mut keyboard_input: MessageReader<KeyboardInput>,
mut mouse_button_input: MessageReader<MouseButtonInput>,
mut gamepad_button_input: MessageReader<GamepadButtonChangedEvent>,
mut gamepad_axis_input: MessageReader<GamepadAxisChangedEvent>,
) {
let mut active_controls = controls
.iter_mut()
.filter(|c| c.contexts.contains(&context.get()))
.collect::<Vec<_>>();
let gamepad_inputs = gamepad_button_input.read().collect::<Vec<_>>();
let button_inputs = keyboard_input
.read()
.filter(|k| !k.repeat)
.map(|k| (Buttons::Keyboard(k.key_code), k.state.is_pressed()))
.chain(
mouse_button_input
.read()
.map(|m| (Buttons::Mouse(m.button), m.state.is_pressed())),
)
.chain(
gamepad_inputs
.iter()
.map(|g| (Buttons::Gamepad(g.button), g.state.is_pressed())),
);
let analog_inputs = gamepad_inputs
.iter()
.map(|g| (AnalogInputs::Gamepad(g.button), g.value))
.chain(
gamepad_axis_input
.read()
.map(|ga| (AnalogInputs::GamepadAxis(ga.axis), ga.value)),
);
for (inputs, value) in analog_inputs {
let interacted_controls = active_controls
.iter_mut()
.filter(|c| c.mappings.contains(&inputs));
for control in interacted_controls {
control.analog_input(value);
}
}
for (button, is_pressed) in button_inputs {
let interacted_controls = active_controls.iter_mut().map(|c| {
(
c.mappings
.iter()
.find(|m| match m {
AnalogInputs::Button(button_mapping, _) => *button_mapping == button,
_ => false,
})
.cloned(),
c,
)
});
for (input, control) in interacted_controls {
let Some(AnalogInputs::Button(_, input_value)) = input else {
continue;
};
if is_pressed {
control.button_pressed(input_value);
} else {
control.button_released(input_value);
}
}
}
}
}