#[cfg(feature = "pickup")]
use avian_pickup::input::{AvianPickupAction, AvianPickupInput};
use bevy_ecs::entity::MapEntities;
use bevy_time::Stopwatch;
use crate::CharacterControllerState;
use crate::kcc::{forward, right};
use crate::prelude::*;
use crate::fixed_update_utils::did_fixed_timestep_run_this_frame;
pub struct AhoyInputPlugin;
impl Plugin for AhoyInputPlugin {
fn build(&self, app: &mut App) {
app.add_observer(apply_movement)
.add_observer(apply_jump)
.add_observer(apply_global_movement)
.add_observer(apply_tac)
.add_observer(apply_crouch)
.add_observer(apply_swim_up)
.add_observer(apply_crane)
.add_observer(apply_mantle)
.add_observer(apply_climbdown)
.add_systems(
RunFixedMainLoop,
clear_accumulated_input
.run_if(did_fixed_timestep_run_this_frame)
.in_set(RunFixedMainLoopSystems::AfterFixedMainLoop),
)
.add_systems(PreUpdate, tick_timers.in_set(EnhancedInputSystems::Update));
#[cfg(feature = "pickup")]
app.add_observer(apply_drop)
.add_observer(apply_pull)
.add_observer(apply_throw);
}
}
#[derive(Debug, InputAction)]
#[action_output(Vec2)]
pub struct Movement;
#[derive(Debug, InputAction)]
#[action_output(Vec3)]
pub struct GlobalMovement;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct Jump;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct SwimUp;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct Tac;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct Crane;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct Mantle;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct Climbdown;
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct Crouch;
#[derive(Debug, InputAction)]
#[action_output(Vec2)]
pub struct RotateCamera;
#[derive(Debug, InputAction)]
#[action_output(f32)]
pub struct YankCamera;
#[cfg(feature = "pickup")]
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct PullObject;
#[cfg(feature = "pickup")]
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct DropObject;
#[cfg(feature = "pickup")]
#[derive(Debug, InputAction)]
#[action_output(bool)]
pub struct ThrowObject;
#[derive(Component, Clone, Reflect, PartialEq, Default, Debug, MapEntities)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Component)]
pub struct AccumulatedInput {
pub last_movement: Option<Vec2>,
pub jumped: Option<Stopwatch>,
pub swim_up: bool,
pub tac: Option<Stopwatch>,
pub crouched: bool,
pub craned: Option<Stopwatch>,
pub mantled: Option<Stopwatch>,
pub climbdown: Option<Stopwatch>,
}
fn apply_movement(
movement: On<Fire<Movement>>,
mut accumulated_inputs: Query<&mut AccumulatedInput>,
) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(movement.context) {
accumulated_inputs.last_movement = Some(movement.value);
}
}
fn apply_global_movement(
movement: On<Fire<GlobalMovement>>,
mut query: Query<(&mut AccumulatedInput, &CharacterControllerState)>,
) {
if let Ok((mut accumulated_inputs, state)) = query.get_mut(movement.context) {
let global_move = movement.value;
let right = right(state.orientation);
let forward = forward(state.orientation);
let local_x = global_move.dot(right);
let local_y = global_move.dot(forward);
accumulated_inputs.last_movement = Some(Vec2::new(local_x, local_y));
}
}
fn apply_jump(jump: On<Fire<Jump>>, mut accumulated_inputs: Query<&mut AccumulatedInput>) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(jump.context) {
accumulated_inputs.jumped = Some(Stopwatch::new());
}
}
fn apply_swim_up(swim_up: On<Fire<SwimUp>>, mut accumulated_inputs: Query<&mut AccumulatedInput>) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(swim_up.context) {
accumulated_inputs.swim_up = true;
}
}
fn apply_tac(tac: On<Fire<Tac>>, mut accumulated_inputs: Query<&mut AccumulatedInput>) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(tac.context) {
accumulated_inputs.tac = Some(Stopwatch::new());
}
}
fn apply_crouch(crouch: On<Fire<Crouch>>, mut accumulated_inputs: Query<&mut AccumulatedInput>) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(crouch.context) {
accumulated_inputs.crouched = true;
}
}
fn apply_crane(crouch: On<Fire<Crane>>, mut accumulated_inputs: Query<&mut AccumulatedInput>) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(crouch.context) {
accumulated_inputs.craned = Some(Stopwatch::new());
}
}
fn apply_mantle(crouch: On<Fire<Mantle>>, mut accumulated_inputs: Query<&mut AccumulatedInput>) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(crouch.context) {
accumulated_inputs.mantled = Some(Stopwatch::new());
}
}
fn apply_climbdown(
climbdown: On<Fire<Climbdown>>,
mut accumulated_inputs: Query<&mut AccumulatedInput>,
) {
if let Ok(mut accumulated_inputs) = accumulated_inputs.get_mut(climbdown.context) {
accumulated_inputs.climbdown = Some(Stopwatch::new());
}
}
#[cfg(feature = "pickup")]
fn apply_pull(
crouch: On<Fire<PullObject>>,
mut avian_pickup_input_writer: MessageWriter<AvianPickupInput>,
cams: Query<&CharacterControllerCamera>,
) {
let actor = if let Ok(camera) = cams.get(crouch.context) {
camera.get()
} else {
crouch.context
};
avian_pickup_input_writer.write(AvianPickupInput {
action: AvianPickupAction::Pull,
actor,
});
}
#[cfg(feature = "pickup")]
fn apply_drop(
crouch: On<Fire<DropObject>>,
mut avian_pickup_input_writer: MessageWriter<AvianPickupInput>,
cams: Query<&CharacterControllerCamera>,
) {
let actor = if let Ok(camera) = cams.get(crouch.context) {
camera.get()
} else {
crouch.context
};
avian_pickup_input_writer.write(AvianPickupInput {
action: AvianPickupAction::Drop,
actor,
});
}
#[cfg(feature = "pickup")]
fn apply_throw(
crouch: On<Fire<ThrowObject>>,
mut avian_pickup_input_writer: MessageWriter<AvianPickupInput>,
cams: Query<&CharacterControllerCamera>,
) {
let actor = if let Ok(camera) = cams.get(crouch.context) {
camera.get()
} else {
crouch.context
};
avian_pickup_input_writer.write(AvianPickupInput {
action: AvianPickupAction::Throw,
actor,
});
}
fn clear_accumulated_input(mut accumulated_inputs: Query<&mut AccumulatedInput>) {
for mut accumulated_input in &mut accumulated_inputs {
*accumulated_input = AccumulatedInput {
last_movement: default(),
jumped: accumulated_input.jumped.clone(),
swim_up: default(),
tac: accumulated_input.tac.clone(),
craned: accumulated_input.craned.clone(),
mantled: accumulated_input.mantled.clone(),
crouched: default(),
climbdown: accumulated_input.climbdown.clone(),
}
}
}
fn tick_timers(mut inputs: Query<&mut AccumulatedInput>, time: Res<Time>) {
for mut input in inputs.iter_mut() {
if let Some(jumped) = input.jumped.as_mut() {
jumped.tick(time.delta());
}
if let Some(tac) = input.tac.as_mut() {
tac.tick(time.delta());
}
if let Some(craned) = input.craned.as_mut() {
craned.tick(time.delta());
}
if let Some(mantled) = input.mantled.as_mut() {
mantled.tick(time.delta());
}
if let Some(climbdown) = input.climbdown.as_mut() {
climbdown.tick(time.delta());
}
}
}