use avian3d::math::{Scalar, Vector2};
use bevy::{
app::{Plugin, Update},
ecs::{
component::Component,
entity::Entity,
event::EventWriter,
query::With,
resource::Resource,
schedule::IntoScheduleConfigs,
system::{Query, Res},
},
input::{ButtonInput, keyboard::KeyCode, mouse::AccumulatedMouseMotion},
platform::collections::HashSet,
};
use crate::{
kcc::Kcc,
movement_events::{
IchunFloatEvent, IchunJumpEvent, IchunMoveEvent, IchunRotateEvent, IchunRunEvent,
KccMovementEventsConfig,
},
system_sets::IchunSystemSet,
};
#[derive(Component)]
#[require(Kcc, KccMovementEventsConfig)]
pub struct KccInputConfig {
pub forward: HashSet<KeyCode>,
pub backward: HashSet<KeyCode>,
pub left: HashSet<KeyCode>,
pub right: HashSet<KeyCode>,
pub run_conditions: RunConditions,
pub is_running_toggled: bool,
pub run: HashSet<KeyCode>,
pub jump: HashSet<KeyCode>,
pub is_floating_toggled: bool,
pub float: HashSet<KeyCode>,
pub rotation_activation: Option<HashSet<KeyCode>>,
pub input_handling_active: bool,
}
impl Default for KccInputConfig {
fn default() -> Self {
let mut forward = HashSet::new();
forward.insert(KeyCode::KeyW);
let mut backward = HashSet::new();
backward.insert(KeyCode::KeyS);
let mut left = HashSet::new();
left.insert(KeyCode::KeyA);
let mut right = HashSet::new();
right.insert(KeyCode::KeyD);
let mut run = HashSet::new();
run.insert(KeyCode::ShiftLeft);
let mut jump = HashSet::new();
jump.insert(KeyCode::Space);
let mut float = HashSet::new();
float.insert(KeyCode::Space);
Self {
forward,
backward,
left,
right,
run_conditions: RunConditions { forward_only: true },
is_running_toggled: true,
run,
jump,
is_floating_toggled: false,
float,
rotation_activation: None,
input_handling_active: true,
}
}
}
impl KccInputConfig {
pub fn new(
forward: impl IntoIterator<Item = KeyCode>,
backward: impl IntoIterator<Item = KeyCode>,
left: impl IntoIterator<Item = KeyCode>,
right: impl IntoIterator<Item = KeyCode>,
run_conditions: RunConditions,
is_running_toggled: bool,
run: impl IntoIterator<Item = KeyCode>,
jump: impl IntoIterator<Item = KeyCode>,
is_floating_toggled: bool,
float: impl IntoIterator<Item = KeyCode>,
rotation_activation: Option<impl IntoIterator<Item = KeyCode>>,
input_handling_active: bool,
) -> Self {
Self {
forward: forward.into_iter().collect(),
backward: backward.into_iter().collect(),
left: left.into_iter().collect(),
right: right.into_iter().collect(),
run_conditions,
is_running_toggled,
run: run.into_iter().collect(),
jump: jump.into_iter().collect(),
is_floating_toggled,
float: float.into_iter().collect(),
rotation_activation: rotation_activation.map(|keys| keys.into_iter().collect()),
input_handling_active,
}
}
}
#[derive(Default)]
pub struct RunConditions {
pub forward_only: bool,
}
#[derive(Resource)]
struct InputHandling {
input_handling_active: bool,
}
pub struct IchunInputPlugin;
impl Plugin for IchunInputPlugin {
fn build(&self, app: &mut bevy::app::App) {
app.insert_resource(InputHandling {
input_handling_active: true, })
.add_systems(
Update,
(
rotation_input_sys,
jump_input_sys,
run_input_sys,
movement_input_sys,
float_input_sys,
)
.chain()
.in_set(IchunSystemSet::InputSet),
);
}
}
fn movement_input_sys(
mut movement_event_writer: EventWriter<IchunMoveEvent>,
keyboard_input_res: Res<ButtonInput<KeyCode>>,
input_handling_res: Res<InputHandling>,
kcc_qry: Query<(Entity, &KccInputConfig), With<Kcc>>,
) {
if !input_handling_res.input_handling_active {
return;
}
for (entity, input) in kcc_qry.iter() {
if !input.input_handling_active {
continue;
}
let right_pressed = input
.right
.iter()
.any(|&key| keyboard_input_res.pressed(key));
let left_pressed = input
.left
.iter()
.any(|&key| keyboard_input_res.pressed(key));
let forward_pressed = input
.forward
.iter()
.any(|&key| keyboard_input_res.pressed(key));
let backward_pressed = input
.backward
.iter()
.any(|&key| keyboard_input_res.pressed(key));
let horizontal = right_pressed as i8 - left_pressed as i8;
let vertical = forward_pressed as i8 - backward_pressed as i8;
let direction =
Vector2::new(horizontal as Scalar, -vertical as Scalar).clamp_length_max(1.0);
if direction != Vector2::ZERO {
movement_event_writer.write(IchunMoveEvent { entity, direction });
}
}
}
fn run_input_sys(
mut run_event_writer: EventWriter<IchunRunEvent>,
keyboard_input_res: Res<ButtonInput<KeyCode>>,
input_handling_res: Res<InputHandling>,
kcc_qry: Query<(Entity, &KccInputConfig, &KccMovementEventsConfig), With<Kcc>>,
) {
if !input_handling_res.input_handling_active {
return;
}
for (entity, input, movement) in kcc_qry.iter() {
if !input.input_handling_active {
continue;
}
let run_conditions_met = !input.run_conditions.forward_only
|| input
.forward
.iter()
.any(|&key| keyboard_input_res.pressed(key));
if !run_conditions_met {
if movement.is_running {
run_event_writer.write(IchunRunEvent {
entity,
is_running: Some(false),
});
}
continue;
}
if input.is_running_toggled {
if input
.run
.iter()
.any(|&key| keyboard_input_res.just_pressed(key))
{
run_event_writer.write(IchunRunEvent {
entity,
is_running: None, });
}
continue;
}
let run_key_pressed = input.run.iter().any(|&key| keyboard_input_res.pressed(key));
if movement.is_running != run_key_pressed {
run_event_writer.write(IchunRunEvent {
entity,
is_running: Some(run_key_pressed),
});
}
}
}
fn jump_input_sys(
mut jump_event_writer: EventWriter<IchunJumpEvent>,
keyboard_input_res: Res<ButtonInput<KeyCode>>,
input_handling_res: Res<InputHandling>,
kcc_qry: Query<(Entity, &KccInputConfig), With<Kcc>>,
) {
if !input_handling_res.input_handling_active {
return;
}
for (entity, input) in kcc_qry.iter() {
if !input.input_handling_active {
continue;
}
let jump_pressed = input
.jump
.iter()
.any(|&key| keyboard_input_res.just_pressed(key));
if jump_pressed {
jump_event_writer.write(IchunJumpEvent { entity });
}
}
}
fn float_input_sys(
mut float_event_writer: EventWriter<IchunFloatEvent>,
keyboard_input_res: Res<ButtonInput<KeyCode>>,
input_handling_res: Res<InputHandling>,
kcc_qry: Query<(Entity, &KccInputConfig, &KccMovementEventsConfig, &Kcc)>,
) {
if !input_handling_res.input_handling_active {
return;
}
for (entity, input, movement, kcc) in kcc_qry.iter() {
if !input.input_handling_active {
continue;
}
if kcc.is_grounded {
if movement.is_floating {
float_event_writer.write(IchunFloatEvent {
entity,
is_floating: Some(false),
});
}
continue;
}
if input.is_floating_toggled {
if input
.float
.iter()
.any(|&key| keyboard_input_res.just_pressed(key))
{
float_event_writer.write(IchunFloatEvent {
entity,
is_floating: None, });
}
continue;
}
let float_key_pressed = input
.float
.iter()
.any(|&key| keyboard_input_res.pressed(key));
if movement.is_floating != float_key_pressed {
float_event_writer.write(IchunFloatEvent {
entity,
is_floating: Some(float_key_pressed),
});
}
}
}
fn rotation_input_sys(
mut look_event_writer: EventWriter<IchunRotateEvent>,
keyboard_input_res: Res<ButtonInput<KeyCode>>,
input_handling_res: Res<InputHandling>,
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
kcc_qry: Query<(Entity, &KccInputConfig), With<Kcc>>,
) {
if !input_handling_res.input_handling_active {
return;
}
let mouse_delta = accumulated_mouse_motion.delta;
if mouse_delta == Vector2::ZERO {
return;
}
for (entity, input) in kcc_qry.iter() {
if !input.input_handling_active {
continue;
}
let should_rotate = match &input.rotation_activation {
Some(activation_keys) => activation_keys
.iter()
.any(|&key| keyboard_input_res.pressed(key)),
None => true,
};
if should_rotate {
look_event_writer.write(IchunRotateEvent {
entity,
rotation: mouse_delta,
});
}
}
}