174 lines
5.2 KiB
Plaintext
174 lines
5.2 KiB
Plaintext
use bevy_app::{App, Plugin, PreUpdate};
|
|
use bevy_derive::{Deref, DerefMut};
|
|
use bevy_ecs::system::{In, IntoSystem};
|
|
use bevy_ecs::{
|
|
event::EventReader,
|
|
system::{Res, ResMut, Resource},
|
|
};
|
|
use bevy_input::{
|
|
keyboard::KeyCode,
|
|
mouse::{MouseButton, MouseMotion, MouseScrollUnit, MouseWheel},
|
|
ButtonInput,
|
|
};
|
|
use bevy_utils::HashMap;
|
|
|
|
use crate::{
|
|
contract::{Action, GameState, InputsContainer},
|
|
resource::*,
|
|
};
|
|
|
|
pub struct ControlsPlugin<A: Action, Ic: InputsContainer<A>, Gs: GameState> {
|
|
_action: std::marker::PhantomData<A>,
|
|
_inputs_container: std::marker::PhantomData<Ic>,
|
|
_game_state: std::marker::PhantomData<Gs>,
|
|
controls: Controls<A, Gs>,
|
|
}
|
|
|
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Default for ControlsPlugin<A, Ic, Gs> {
|
|
fn default() -> Self {
|
|
Self {
|
|
_action: std::marker::PhantomData,
|
|
_inputs_container: std::marker::PhantomData,
|
|
_game_state: std::marker::PhantomData,
|
|
controls: Controls::<A, Gs>::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
|
pub fn new(controls: Controls<A, Gs>) -> Self {
|
|
Self {
|
|
_action: std::marker::PhantomData,
|
|
_inputs_container: std::marker::PhantomData,
|
|
_game_state: std::marker::PhantomData,
|
|
controls,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Resource, Default, Debug, Deref, DerefMut)]
|
|
struct TrigeredInputs(HashMap<InputType, InputValue>);
|
|
|
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin<A, Ic, Gs> {
|
|
fn build(&self, app: &mut App) {
|
|
app
|
|
.insert_resource(self.controls.clone())
|
|
.init_resource::<Ic>()
|
|
.init_resource::<TrigeredInputs>()
|
|
.insert_state::<Gs>(Gs::default())
|
|
.add_systems(
|
|
PreUpdate,
|
|
Self::collect_inputs.pipe(Self::fill_players_inputs),
|
|
);
|
|
|
|
#[cfg(feature = "reflect")]
|
|
app.register_type::<Controls<A, Gs>>();
|
|
}
|
|
}
|
|
|
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
|
// Separate collecting and filling is necessary becouse order of complicate imputs in future
|
|
fn collect_inputs(
|
|
keyboard_button: Res<ButtonInput<KeyCode>>,
|
|
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
|
mut scroll_evr: EventReader<MouseWheel>,
|
|
mut motion_evr: EventReader<MouseMotion>,
|
|
// mut collected_inputs: ResMut<TrigeredInputs>,
|
|
) -> TrigeredInputs {
|
|
let mut collected_inputs = TrigeredInputs(HashMap::new());
|
|
for key in keyboard_button.get_pressed() {
|
|
collected_inputs.insert(InputType::Keyboard(*key), InputValue::Boolean(true));
|
|
}
|
|
|
|
// buttons.get_just_released()
|
|
|
|
for button in mouse_buttons.get_pressed() {
|
|
collected_inputs.insert(
|
|
InputType::Mouse(MouseInput::Button(*button)),
|
|
InputValue::Boolean(true),
|
|
);
|
|
}
|
|
|
|
for ev in motion_evr.read() {
|
|
collected_inputs.insert(
|
|
InputType::Mouse(MouseInput::Axis(AxisName::Horizontal)),
|
|
InputValue::Float(ev.delta.x),
|
|
);
|
|
collected_inputs.insert(
|
|
InputType::Mouse(MouseInput::Axis(AxisName::Vertical)),
|
|
InputValue::Float(ev.delta.y),
|
|
);
|
|
}
|
|
|
|
for ev in scroll_evr.read() {
|
|
match ev.unit {
|
|
MouseScrollUnit::Line => {
|
|
collected_inputs.insert(
|
|
InputType::Mouse(MouseInput::Wheel(AxisName::Horizontal)),
|
|
InputValue::Float(ev.x),
|
|
);
|
|
collected_inputs.insert(
|
|
InputType::Mouse(MouseInput::Wheel(AxisName::Vertical)),
|
|
InputValue::Float(ev.y),
|
|
);
|
|
}
|
|
MouseScrollUnit::Pixel => {
|
|
panic!("I'm do not accept you to use touch pad in my game");
|
|
}
|
|
}
|
|
}
|
|
|
|
collected_inputs
|
|
}
|
|
|
|
// TODO: ! Zero out values that did not arrive
|
|
fn fill_players_inputs(
|
|
In(collected_inputs): In<TrigeredInputs>,
|
|
mut inputs_container: ResMut<Ic>,
|
|
controls: Res<Controls<A, Gs>>,
|
|
game_state: Res<State<Gs>>,
|
|
) {
|
|
if let Some(inputs) = inputs_container.me_mut() {
|
|
// TODO: only on controls change
|
|
let mut action_binding_pairs: Vec<(&A, &BindingConfig<Gs>, &Binding<Gs>)> = Vec::new();
|
|
for (action, config) in controls.iter() {
|
|
for binding in config.bindings.iter() {
|
|
action_binding_pairs.push((action, config, binding));
|
|
}
|
|
}
|
|
|
|
action_binding_pairs.sort_by(|a, b| b.2.input.len().cmp(&a.2.input.len()));
|
|
|
|
'bindings_loop: for (action, _config, binding) in action_binding_pairs.iter() {
|
|
// check binding conditions
|
|
for condition in &binding.conditions {
|
|
match condition {
|
|
BindingCondition::InGameState(state) => {
|
|
if *state != *game_state.get() {
|
|
continue 'bindings_loop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//log::info!("{:#?}", collected_inputs);
|
|
//log::info!("{:#?}", action_binding_pairs[0]);
|
|
|
|
match &binding.input {
|
|
ButtonCombination::Single(input_type) => {
|
|
// TODO: exclude if used
|
|
if let Some(value) = collected_inputs.get(&*input_type) {
|
|
inputs.forced_set(**action, *value);
|
|
} else {
|
|
inputs.forced_set(**action, InputValue::Empty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// TODO: print onece
|
|
log::warn!("cannot find me in inputs container");
|
|
}
|
|
}
|
|
}
|