use core::hash::Hash;
use core::marker::PhantomData;
use std::fmt::Debug;
use bevy::app::{App, FixedPostUpdate, Plugin, RunFixedMainLoop};
use bevy::input::InputSystem;
use bevy::prelude::*;
use bevy::reflect::TypePath;
use bevy::time::run_fixed_main_schedule;
#[cfg(feature = "ui")]
use bevy::ui::UiSystem;
use updating::CentralInputStore;
use crate::action_state::{ActionState, ButtonData};
use crate::clashing_inputs::ClashStrategy;
use crate::input_map::InputMap;
use crate::input_processing::*;
#[cfg(feature = "mouse")]
use crate::systems::{accumulate_mouse_movement, accumulate_mouse_scroll};
#[cfg(feature = "timing")]
use crate::timing::Timing;
use crate::user_input::*;
use crate::Actionlike;
pub struct InputManagerPlugin<A: Actionlike> {
_phantom: PhantomData<A>,
machine: Machine,
}
impl<A: Actionlike> Default for InputManagerPlugin<A> {
fn default() -> Self {
Self {
_phantom: PhantomData,
machine: Machine::Client,
}
}
}
impl<A: Actionlike> InputManagerPlugin<A> {
#[must_use]
pub fn server() -> Self {
Self {
_phantom: PhantomData,
machine: Machine::Server,
}
}
}
enum Machine {
Server,
Client,
}
impl<A: Actionlike + TypePath + bevy::reflect::GetTypeRegistration> Plugin
for InputManagerPlugin<A>
{
fn build(&self, app: &mut App) {
use crate::systems::*;
match self.machine {
Machine::Client => {
if !app.is_plugin_added::<AccumulatorPlugin>() {
app.add_plugins((AccumulatorPlugin, CentralInputStorePlugin));
}
if !app.is_plugin_added::<CentralInputStorePlugin>() {
app.add_plugins(CentralInputStorePlugin);
}
app.add_systems(
PreUpdate,
(
tick_action_state::<A>.in_set(TickActionStateSystem::<A>::new()),
clear_central_input_store,
)
.in_set(InputManagerSystem::Tick)
.before(InputManagerSystem::Update),
)
.add_systems(PostUpdate, release_on_input_map_removed::<A>);
app.add_systems(
PreUpdate,
update_action_state::<A>.in_set(InputManagerSystem::Update),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::ManualControl.after(InputManagerSystem::Update),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::Unify.after(InputManagerSystem::Filter),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::Update
.after(InputSystem)
.after(InputManagerSystem::Unify),
);
#[cfg(any(feature = "egui", feature = "ui"))]
app.add_systems(
PreUpdate,
filter_captured_input
.before(update_action_state::<A>)
.in_set(InputManagerSystem::Filter),
);
#[cfg(feature = "egui")]
app.configure_sets(
PreUpdate,
InputManagerSystem::Filter.after(bevy_egui::EguiSet::ProcessInput),
);
#[cfg(feature = "ui")]
app.configure_sets(PreUpdate, InputManagerSystem::Filter.after(UiSystem::Focus));
#[cfg(feature = "ui")]
app.configure_sets(
PreUpdate,
InputManagerSystem::ManualControl
.after(InputManagerSystem::Tick)
.after(InputManagerSystem::Update)
.after(UiSystem::Focus)
.after(InputSystem),
);
app.add_systems(
RunFixedMainLoop,
(
swap_to_fixed_update::<A>,
update_action_state::<A>,
)
.chain()
.before(run_fixed_main_schedule),
);
app.add_systems(FixedPostUpdate, release_on_input_map_removed::<A>);
app.add_systems(
FixedPostUpdate,
tick_action_state::<A>
.in_set(TickActionStateSystem::<A>::new())
.in_set(InputManagerSystem::Tick)
.before(InputManagerSystem::Update),
);
app.add_systems(
RunFixedMainLoop,
swap_to_update::<A>.after(run_fixed_main_schedule),
);
}
Machine::Server => {
app.add_systems(
PreUpdate,
tick_action_state::<A>
.in_set(TickActionStateSystem::<A>::new())
.in_set(InputManagerSystem::Tick),
);
}
};
#[cfg(feature = "mouse")]
app.register_type::<AccumulatedMouseMovement>()
.register_type::<AccumulatedMouseScroll>()
.register_buttonlike_input::<MouseMoveDirection>()
.register_axislike_input::<MouseMoveAxis>()
.register_dual_axislike_input::<MouseMove>()
.register_buttonlike_input::<MouseScrollDirection>()
.register_axislike_input::<MouseScrollAxis>()
.register_dual_axislike_input::<MouseScroll>();
#[cfg(feature = "keyboard")]
app.register_buttonlike_input::<KeyCode>()
.register_buttonlike_input::<ModifierKey>()
.register_axislike_input::<KeyboardVirtualAxis>()
.register_dual_axislike_input::<KeyboardVirtualDPad>()
.register_triple_axislike_input::<KeyboardVirtualDPad3D>();
#[cfg(feature = "gamepad")]
app.register_buttonlike_input::<GamepadControlDirection>()
.register_axislike_input::<GamepadControlAxis>()
.register_dual_axislike_input::<GamepadStick>()
.register_buttonlike_input::<GamepadButtonType>()
.register_axislike_input::<GamepadVirtualAxis>()
.register_dual_axislike_input::<GamepadVirtualDPad>();
app.register_buttonlike_input::<ButtonlikeChord>()
.register_axislike_input::<AxislikeChord>()
.register_dual_axislike_input::<DualAxislikeChord>()
.register_triple_axislike_input::<TripleAxislikeChord>();
app.register_type::<ActionState<A>>()
.register_type::<InputMap<A>>()
.register_type::<ButtonData>()
.register_type::<ActionState<A>>()
.register_type::<CentralInputStore>();
app.register_type::<AxisProcessor>()
.register_type::<AxisBounds>()
.register_type::<AxisExclusion>()
.register_type::<AxisDeadZone>()
.register_type::<DualAxisProcessor>()
.register_type::<DualAxisInverted>()
.register_type::<DualAxisSensitivity>()
.register_type::<DualAxisBounds>()
.register_type::<DualAxisExclusion>()
.register_type::<DualAxisDeadZone>()
.register_type::<CircleBounds>()
.register_type::<CircleExclusion>()
.register_type::<CircleDeadZone>();
app.init_resource::<ClashStrategy>();
#[cfg(feature = "timing")]
app.register_type::<Timing>();
}
}
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
pub enum InputManagerSystem {
Tick,
Accumulate,
Filter,
Unify,
Update,
ManualControl,
}
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
pub struct TickActionStateSystem<A: Actionlike> {
phantom_data: PhantomData<A>,
}
impl<A: Actionlike> TickActionStateSystem<A> {
pub fn new() -> Self {
Self {
phantom_data: PhantomData,
}
}
}
impl<A: Actionlike> Default for TickActionStateSystem<A> {
fn default() -> Self {
Self::new()
}
}
pub struct AccumulatorPlugin;
impl Plugin for AccumulatorPlugin {
#[allow(unused_variables)]
fn build(&self, app: &mut App) {
#[cfg(feature = "mouse")]
{
app.init_resource::<AccumulatedMouseMovement>();
app.init_resource::<AccumulatedMouseScroll>();
app.add_systems(
PreUpdate,
(accumulate_mouse_movement, accumulate_mouse_scroll)
.in_set(InputManagerSystem::Accumulate),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::Accumulate
.after(InputSystem)
.before(InputManagerSystem::Unify),
);
}
}
}
pub struct CentralInputStorePlugin;
impl Plugin for CentralInputStorePlugin {
fn build(&self, app: &mut App) {
let mut central_input_store = CentralInputStore::default();
central_input_store.register_standard_input_kinds(app);
app.insert_resource(central_input_store);
}
}