bevy_controls 1.3.0

Bevy controls library
use bevy::{
    input::{
        gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent},
        mouse::{MouseMotion, MouseWheel},
    },
    prelude::*,
    state::state::FreelyMutableState,
};
use std::marker::PhantomData;

use crate::{
    accumulative::{
        accumulative_control::AccumulativeControl,
        accumulative_input::AccumulativeInputs,
        accumulative_sensitivity::{GamepadSensitivity, MouseSensitivity},
    },
    read_input_systems::ReadInputSystems,
};

pub struct AccumulativePlugin<TContext, TAction>(PhantomData<TContext>, PhantomData<TAction>);

impl<TC, TA> Default for AccumulativePlugin<TC, TA> {
    fn default() -> Self {
        Self(Default::default(), Default::default())
    }
}

impl<TContext: FreelyMutableState + FromWorld, TAction: Sync + Send + 'static> Plugin
    for AccumulativePlugin<TContext, TAction>
{
    fn build(&self, app: &mut App) {
        app.init_state::<TContext>()
            .init_resource::<MouseSensitivity>()
            .init_resource::<GamepadSensitivity>()
            .add_systems(Update, Self::check_controls.in_set(ReadInputSystems));
    }
}

impl<TContext: States, TAction: Sync + Send + 'static> AccumulativePlugin<TContext, TAction> {
    fn check_controls(
        mut controls: Query<&mut AccumulativeControl<TContext, TAction>>,
        context: Res<State<TContext>>,
        mut gamepad_button_input: MessageReader<GamepadButtonChangedEvent>,
        mut gamepad_axis_input: MessageReader<GamepadAxisChangedEvent>,
        mut mouse_input: MessageReader<MouseMotion>,
        mut wheel_events: MessageReader<MouseWheel>,
        mouse_sensitivity: Res<MouseSensitivity>,
        gamepad_sensitivity: Res<GamepadSensitivity>,
    ) {
        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 analog_inputs = gamepad_inputs
            .iter()
            .map(|g| {
                (
                    AccumulativeInputs::Gamepad(g.button),
                    g.value * **gamepad_sensitivity,
                )
            })
            .chain(gamepad_axis_input.read().map(|ga| {
                (
                    AccumulativeInputs::GamepadAxis(ga.axis),
                    ga.value * **gamepad_sensitivity,
                )
            }))
            .chain(wheel_events.read().flat_map(|we| {
                [
                    (AccumulativeInputs::MouseWheelVertical, we.y),
                    (AccumulativeInputs::MouseWheelHorizontal, we.x),
                ]
            }))
            .chain(mouse_input.read().flat_map(|me| {
                [
                    (AccumulativeInputs::MouseX, me.delta.x * **mouse_sensitivity),
                    (AccumulativeInputs::MouseY, me.delta.y * **mouse_sensitivity),
                ]
            }));

        for (inputs, value) in analog_inputs {
            let interacted_controls = active_controls
                .iter_mut()
                .map(|c| (c.mappings.iter().find(|(m, _)| m == &inputs).cloned(), c));

            for (mapping, control) in interacted_controls {
                let Some((input_type, sensitivity)) = mapping else {
                    continue;
                };
                match input_type {
                    AccumulativeInputs::Gamepad(_) => {
                        control.set_gamepade_input(value * sensitivity)
                    }
                    AccumulativeInputs::GamepadAxis(_) => {
                        control.set_gamepade_input(value * sensitivity)
                    }
                    _ => control.add_input(value * sensitivity),
                }
            }
        }

        for control in active_controls.iter_mut() {
            control.update_value();
        }
    }
}