use bevy::app::{App, Plugin, Update};
use bevy::ecs::component::{Component, Mutable};
use bevy::ecs::entity::Entity;
use bevy::ecs::message::MessageReader;
use bevy::ecs::schedule::IntoScheduleConfigs;
use bevy::ecs::system::{Commands, Query, Res};
use bevy::input::gamepad::{Gamepad, GamepadConnection, GamepadConnectionEvent};
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::gamepad::binding::GamepadBinding;
use crate::relation::Controlling;
use crate::shared::ControllersSet;
pub struct GamepadPlugin<ActionKey, ValueKey, Binding>
where
Binding: GamepadBinding<ActionKey, ValueKey>,
{
_action_key: std::marker::PhantomData<ActionKey>,
_value_key: std::marker::PhantomData<ValueKey>,
_binding_key: std::marker::PhantomData<Binding>,
}
impl<ActionKey, ValueKey, Binding> GamepadPlugin<ActionKey, ValueKey, Binding>
where
Binding: GamepadBinding<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 GamepadPlugin<ActionKey, ValueKey, Binding>
where
ActionKey: Sync + Send + 'static,
ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
Binding: GamepadBinding<ActionKey, ValueKey>
+ Sync
+ Send
+ 'static
+ Component<Mutability = Mutable>,
{
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
gamepad_connections::<ActionKey, ValueKey, Binding>,
gamepad_system::<ActionKey, ValueKey, Binding>,
)
.in_set(ControllersSet),
);
}
}
fn gamepad_system<ActionKey, ValueKey, Binding>(
time: Res<Time>,
mut commands: Commands,
mut gamepads: Query<(
Entity,
&Gamepad,
&mut Binding,
&mut ControllerValues<ValueKey>,
Option<&Controlling>,
)>,
) where
ActionKey: Sync + Send + 'static,
ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
Binding: GamepadBinding<ActionKey, ValueKey> + Component<Mutability = Mutable>,
{
for (controller_id, gamepad, mut binding, mut controller_values, controlling) in
gamepads.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(gamepad, &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(gamepad, &time.delta(), value);
} else {
let mut new_value = VariantValue::None;
value_type.update_value(gamepad, &time.delta(), &mut new_value);
controller_values.values.insert(value_key, new_value);
};
}
}
}
fn gamepad_connections<ActionKey, ValueKey, Binding>(
mut commands: Commands,
mut connection_events: MessageReader<GamepadConnectionEvent>,
) where
ActionKey: Sync + Send + 'static,
ValueKey: std::hash::Hash + PartialEq + Eq + Sync + Send + 'static,
Binding: GamepadBinding<ActionKey, ValueKey>,
{
for connection_event in connection_events.read() {
match &connection_event.connection {
GamepadConnection::Connected {
name,
vendor_id,
product_id,
} => {
println!(
"New gamepad connected: {:?}, name: {}, vendor_id: {:?}, product_id: {:?}",
connection_event.gamepad, name, vendor_id, product_id,
);
commands.entity(connection_event.gamepad).insert_if_new((
Binding::default(),
ControllerInterface::new(ControllerLayout::UnknownGamepad),
ControllerValues::<ValueKey>::new(),
));
},
GamepadConnection::Disconnected => {},
}
}
}