use bevy::app::{App, Plugin, Update};
use bevy::ecs::component::{Component, Mutable};
use bevy::ecs::entity::Entity;
use bevy::ecs::query::Added;
use bevy::ecs::schedule::IntoScheduleConfigs;
use bevy::ecs::system::{Commands, Query, Res};
use bevy::input::ButtonInput;
use bevy::input::keyboard::KeyCode;
use bevy::time::Time;
use crate::action_event::ActionEvent;
use crate::controller_interface::ControllerInterface;
use crate::controller_layout::ControllerLayout;
use crate::controller_values::{ControllerValues, VariantValue};
use crate::relation::Controlling;
use crate::shared::ControllersSet;
use super::binding::KeyboardBinding;
pub struct KeyboardPlugin<ActionKey, ValueKey, Binding>
where
Binding: KeyboardBinding<ActionKey, ValueKey>,
{
_action_key: std::marker::PhantomData<ActionKey>,
_value_key: std::marker::PhantomData<ValueKey>,
_binding_key: std::marker::PhantomData<Binding>,
}
impl<ActionKey, ValueKey, Binding> KeyboardPlugin<ActionKey, ValueKey, Binding>
where
Binding: KeyboardBinding<ActionKey, ValueKey>,
{
pub fn new() -> Self {
Self {
_action_key: Default::default(),
_value_key: Default::default(),
_binding_key: Default::default(),
}
}
}
impl<ActionKey, ValueKey, Binding> Plugin for KeyboardPlugin<ActionKey, ValueKey, Binding>
where
ActionKey: Sync + Send + 'static,
ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
Binding: KeyboardBinding<ActionKey, ValueKey>
+ Sync
+ Send
+ 'static
+ Component<Mutability = Mutable>,
{
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
on_add_binding::<ActionKey, ValueKey, Binding>,
keyboard_system::<ActionKey, ValueKey, Binding>,
)
.in_set(ControllersSet),
);
}
}
fn on_add_binding<
ActionKey: Sync + Send + 'static,
ValueKey,
Binding: KeyboardBinding<ActionKey, ValueKey>,
>(
mut commands: Commands,
new_bindings: Query<Entity, Added<Binding>>,
) where
ActionKey: Sync + Send + 'static,
ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
Binding: KeyboardBinding<ActionKey, ValueKey>,
{
for new_binding in new_bindings {
commands.entity(new_binding).insert_if_new((
ControllerInterface::new(ControllerLayout::Keyboard),
ControllerValues::<ValueKey>::new(),
));
}
}
fn keyboard_system<
ActionKey: Sync + Send + 'static,
ValueKey,
Binding: KeyboardBinding<ActionKey, ValueKey> + Component<Mutability = Mutable>,
>(
time: Res<Time>,
keys: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
mut keyboards: Query<(
Entity,
&mut Binding,
&mut ControllerValues<ValueKey>,
Option<&Controlling>,
)>,
) where
ActionKey: Sync + Send + 'static,
ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
Binding: KeyboardBinding<ActionKey, ValueKey>,
{
for (controller_id, mut binding, mut controller_values, controlling) in keyboards.iter_mut() {
let controlled_entities = std::cell::LazyCell::new(|| {
let Some(controlling) = controlling else {
return Vec::new();
};
return controlling.controlling().clone();
});
for (trigger, action_key) in binding.get_action_bindings() {
if trigger.is_triggered(&keys, &time.delta()) {
let action_event = ActionEvent {
controller: controller_id,
controlling: controlled_entities.clone(),
action_key,
};
commands.trigger(action_event);
}
}
for (value_type, value_key) in binding.get_value_bindings() {
if let Some(value) = controller_values.values.get_mut(&value_key) {
value_type.update_value(&keys, &time.delta(), value);
} else {
let mut new_value = VariantValue::None;
value_type.update_value(&keys, &time.delta(), &mut new_value);
controller_values.values.insert(value_key, new_value);
};
}
}
}