use crate::action_state::{ActionData, ActionState};
use crate::axislike::{
AxisType, DeadZoneShape, DualAxis, DualAxisData, MouseMotionAxisType, MouseWheelAxisType,
SingleAxis, VirtualAxis, VirtualDPad,
};
use crate::buttonlike::{MouseMotionDirection, MouseWheelDirection};
use crate::clashing_inputs::ClashStrategy;
use crate::input_map::InputMap;
use crate::timing::Timing;
use crate::user_input::{InputKind, Modifier, UserInput};
use crate::Actionlike;
use core::hash::Hash;
use core::marker::PhantomData;
use std::fmt::Debug;
use bevy::app::{App, Plugin};
use bevy::ecs::prelude::*;
use bevy::input::{ButtonState, InputSystem};
use bevy::prelude::{PostUpdate, PreUpdate};
use bevy::reflect::TypePath;
#[cfg(feature = "ui")]
use bevy::ui::UiSystem;
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> Plugin for InputManagerPlugin<A> {
fn build(&self, app: &mut App) {
use crate::systems::*;
match self.machine {
Machine::Client => {
app.add_systems(
PreUpdate,
tick_action_state::<A>
.run_if(run_if_enabled::<A>)
.in_set(InputManagerSystem::Tick)
.before(InputManagerSystem::Update),
)
.add_systems(
PreUpdate,
release_on_disable::<A>
.in_set(InputManagerSystem::ReleaseOnDisable)
.after(InputManagerSystem::Update),
)
.add_systems(PostUpdate, release_on_input_map_removed::<A>);
app.add_systems(
PreUpdate,
update_action_state::<A>
.run_if(run_if_enabled::<A>)
.in_set(InputManagerSystem::Update),
);
app.configure_sets(PreUpdate, InputManagerSystem::Update.after(InputSystem));
#[cfg(feature = "egui")]
app.configure_sets(
PreUpdate,
InputManagerSystem::Update.after(bevy_egui::EguiSet::ProcessInput),
);
#[cfg(feature = "ui")]
app.configure_sets(PreUpdate, InputManagerSystem::Update.after(UiSystem::Focus));
#[cfg(feature = "ui")]
app.configure_sets(
PreUpdate,
InputManagerSystem::ManualControl
.before(InputManagerSystem::ReleaseOnDisable)
.after(InputManagerSystem::Tick)
.after(InputManagerSystem::Update)
.after(UiSystem::Focus)
.after(InputSystem),
);
#[cfg(feature = "ui")]
app.add_systems(
PreUpdate,
update_action_state_from_interaction::<A>
.run_if(run_if_enabled::<A>)
.in_set(InputManagerSystem::ManualControl),
);
}
Machine::Server => {
app.add_systems(
PreUpdate,
tick_action_state::<A>
.run_if(run_if_enabled::<A>)
.in_set(InputManagerSystem::Tick),
);
}
};
app.register_type::<ActionState<A>>()
.register_type::<InputMap<A>>()
.register_type::<UserInput>()
.register_type::<InputKind>()
.register_type::<ActionData>()
.register_type::<Modifier>()
.register_type::<ActionState<A>>()
.register_type::<Timing>()
.register_type::<VirtualDPad>()
.register_type::<VirtualAxis>()
.register_type::<SingleAxis>()
.register_type::<DualAxis>()
.register_type::<AxisType>()
.register_type::<MouseWheelAxisType>()
.register_type::<MouseMotionAxisType>()
.register_type::<DualAxisData>()
.register_type::<DeadZoneShape>()
.register_type::<ButtonState>()
.register_type::<MouseWheelDirection>()
.register_type::<MouseMotionDirection>()
.init_resource::<ToggleActions<A>>()
.init_resource::<ClashStrategy>();
}
}
#[derive(Resource)]
pub struct ToggleActions<A: Actionlike> {
pub enabled: bool,
pub phantom: PhantomData<A>,
}
impl<A: Actionlike> ToggleActions<A> {
pub const ENABLED: ToggleActions<A> = ToggleActions::<A> {
enabled: true,
phantom: PhantomData::<A>,
};
pub const DISABLED: ToggleActions<A> = ToggleActions::<A> {
enabled: false,
phantom: PhantomData::<A>,
};
}
impl<A: Actionlike> Default for ToggleActions<A> {
fn default() -> Self {
Self {
enabled: true,
phantom: PhantomData::<A>,
}
}
}
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
pub enum InputManagerSystem {
Tick,
Update,
ReleaseOnDisable,
ManualControl,
}