2022-03-22 02:58:34 +00:00
|
|
|
use std::{
|
|
|
|
collections::HashMap, error::Error, f32::consts::PI, fmt::Debug, hash::Hash,
|
|
|
|
marker::PhantomData,
|
|
|
|
};
|
2021-05-13 17:25:45 +00:00
|
|
|
|
|
|
|
use bevy::prelude::*;
|
|
|
|
use bevy_input_actionmap::InputMap;
|
2021-06-03 14:04:48 +00:00
|
|
|
use bevy_rapier2d::{na::UnitComplex, prelude::*};
|
2021-05-13 17:25:45 +00:00
|
|
|
use bevy_tts::Tts;
|
|
|
|
use derive_more::{Deref, DerefMut};
|
|
|
|
|
|
|
|
use crate::{
|
2021-06-07 17:43:20 +00:00
|
|
|
core::{Angle, CardinalDirection, Player},
|
2021-05-13 17:25:45 +00:00
|
|
|
error::error_handler,
|
|
|
|
exploration::{ExplorationFocused, Exploring},
|
|
|
|
pathfinding::Destination,
|
|
|
|
};
|
|
|
|
|
2022-01-10 19:50:52 +00:00
|
|
|
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
|
2021-05-13 17:25:45 +00:00
|
|
|
#[reflect(Component)]
|
|
|
|
pub struct MaxSpeed(pub f32);
|
|
|
|
|
|
|
|
impl Default for MaxSpeed {
|
|
|
|
fn default() -> Self {
|
|
|
|
MaxSpeed(2.)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-10 19:50:52 +00:00
|
|
|
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
|
2021-05-13 17:25:45 +00:00
|
|
|
#[reflect(Component)]
|
|
|
|
pub struct RotationSpeed(pub Angle);
|
|
|
|
|
|
|
|
impl Default for RotationSpeed {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(Angle::Radians(0.))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-10 19:50:52 +00:00
|
|
|
#[derive(Component, Clone, Copy, Debug, Default, Deref, DerefMut, Reflect)]
|
2021-05-13 17:25:45 +00:00
|
|
|
#[reflect(Component)]
|
|
|
|
pub struct Speed(pub f32);
|
|
|
|
|
2022-01-10 19:50:52 +00:00
|
|
|
#[derive(Component, Clone, Copy, Debug, Default, Reflect)]
|
2021-05-13 17:25:45 +00:00
|
|
|
#[reflect(Component)]
|
|
|
|
pub struct Sprinting;
|
|
|
|
|
2022-03-22 02:58:34 +00:00
|
|
|
#[derive(Default, Deref, DerefMut)]
|
|
|
|
struct Snapping(bool);
|
|
|
|
|
2021-05-26 21:46:20 +00:00
|
|
|
fn movement_controls<S, A: 'static>(
|
2021-05-13 17:25:45 +00:00
|
|
|
mut commands: Commands,
|
2021-05-26 21:46:20 +00:00
|
|
|
config: Res<NavigationConfig<S, A>>,
|
|
|
|
input: Res<InputMap<A>>,
|
2021-05-13 17:25:45 +00:00
|
|
|
time: Res<Time>,
|
2022-03-22 02:58:34 +00:00
|
|
|
mut snapping: ResMut<Snapping>,
|
2021-06-02 21:53:35 +00:00
|
|
|
mut query: Query<
|
|
|
|
(
|
|
|
|
Entity,
|
2022-01-10 19:50:52 +00:00
|
|
|
&mut RigidBodyVelocityComponent,
|
2021-06-02 21:53:35 +00:00
|
|
|
&mut Speed,
|
|
|
|
&MaxSpeed,
|
|
|
|
Option<&RotationSpeed>,
|
2022-01-10 19:50:52 +00:00
|
|
|
&mut RigidBodyPositionComponent,
|
2021-06-02 21:53:35 +00:00
|
|
|
Option<&Destination>,
|
|
|
|
),
|
|
|
|
With<Player>,
|
|
|
|
>,
|
2022-02-14 12:56:45 +00:00
|
|
|
exploration_focused: Query<Entity, With<ExplorationFocused>>,
|
2021-05-26 21:46:20 +00:00
|
|
|
) where
|
|
|
|
S: bevy::ecs::component::Component + Clone + Debug + Eq + Hash,
|
|
|
|
A: Hash + Eq + Clone + Send + Sync,
|
|
|
|
{
|
2021-06-03 14:04:48 +00:00
|
|
|
for (entity, mut velocity, mut speed, max_speed, rotation_speed, mut position, destination) in
|
2021-06-02 21:53:35 +00:00
|
|
|
query.iter_mut()
|
2021-05-13 17:25:45 +00:00
|
|
|
{
|
2022-03-22 02:58:34 +00:00
|
|
|
if **snapping {
|
|
|
|
if let Some(action) = config.action_forward.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-26 21:46:20 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let Some(action) = config.action_backward.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-26 21:46:20 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let Some(action) = config.action_left.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-26 21:46:20 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let Some(action) = config.action_right.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-26 21:46:20 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let (Some(_), Some(rotate_left), Some(rotate_right)) = (
|
|
|
|
rotation_speed,
|
|
|
|
config.action_rotate_left.clone(),
|
|
|
|
config.action_rotate_right.clone(),
|
|
|
|
) {
|
|
|
|
if input.active(rotate_left) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if input.active(rotate_right) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
**snapping = false;
|
|
|
|
} else {
|
|
|
|
let sprinting = if let Some(action) = config.action_sprint.clone() {
|
|
|
|
input.active(action)
|
2021-06-10 16:16:18 +00:00
|
|
|
} else {
|
2022-03-22 02:58:34 +00:00
|
|
|
false
|
|
|
|
};
|
|
|
|
if sprinting {
|
|
|
|
commands.entity(entity).insert(Sprinting::default());
|
|
|
|
} else {
|
|
|
|
commands.entity(entity).remove::<Sprinting>();
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
let mut direction = Vec2::default();
|
|
|
|
if let Some(action) = config.action_forward.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
direction.x += 1.;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(action) = config.action_backward.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
direction.x -= 1.;
|
|
|
|
}
|
2021-06-02 21:53:35 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let Some(action) = config.action_left.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
direction.y += 1.;
|
|
|
|
}
|
2021-05-30 19:35:10 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let Some(action) = config.action_right.clone() {
|
|
|
|
if input.active(action) {
|
|
|
|
direction.y -= 1.;
|
|
|
|
}
|
2021-05-30 19:35:10 +00:00
|
|
|
}
|
2022-03-22 02:58:34 +00:00
|
|
|
if let (Some(rotation_speed), Some(rotate_left), Some(rotate_right)) = (
|
|
|
|
rotation_speed,
|
|
|
|
config.action_rotate_left.clone(),
|
|
|
|
config.action_rotate_right.clone(),
|
2021-05-26 21:46:20 +00:00
|
|
|
) {
|
2022-03-22 02:58:34 +00:00
|
|
|
let delta = rotation_speed.radians() * time.delta_seconds();
|
|
|
|
if input.active(rotate_left) {
|
|
|
|
position.position.rotation *= UnitComplex::new(delta);
|
|
|
|
}
|
|
|
|
if input.active(rotate_right) {
|
|
|
|
position.position.rotation *= UnitComplex::new(-delta);
|
2021-05-26 21:46:20 +00:00
|
|
|
} else {
|
2022-03-22 02:58:34 +00:00
|
|
|
velocity.angvel = 0.;
|
|
|
|
}
|
2021-05-13 17:25:45 +00:00
|
|
|
} else {
|
2022-03-22 02:58:34 +00:00
|
|
|
velocity.angvel = 0.;
|
|
|
|
}
|
|
|
|
if direction.length_squared() != 0. {
|
|
|
|
commands.entity(entity).remove::<Destination>();
|
|
|
|
commands.entity(entity).remove::<Exploring>();
|
|
|
|
for entity in exploration_focused.iter() {
|
|
|
|
commands.entity(entity).remove::<ExplorationFocused>();
|
|
|
|
}
|
|
|
|
direction = direction.normalize();
|
|
|
|
if direction.x > 0. {
|
|
|
|
direction.x *= config.forward_movement_factor;
|
|
|
|
} else if direction.x < 0. {
|
|
|
|
direction.x *= config.backward_movement_factor;
|
|
|
|
}
|
|
|
|
if direction.y != 0. {
|
|
|
|
direction.y *= config.strafe_movement_factor;
|
|
|
|
}
|
|
|
|
let strength = if let (Some(forward), Some(backward), Some(left), Some(right)) = (
|
|
|
|
config.action_forward.clone(),
|
|
|
|
config.action_backward.clone(),
|
|
|
|
config.action_left.clone(),
|
|
|
|
config.action_right.clone(),
|
|
|
|
) {
|
|
|
|
let forward_x = input.strength(forward).abs();
|
|
|
|
let backward_x = input.strength(backward).abs();
|
|
|
|
let x = if forward_x > backward_x {
|
|
|
|
forward_x
|
|
|
|
} else {
|
|
|
|
backward_x
|
|
|
|
};
|
|
|
|
let right_y = input.strength(right).abs();
|
|
|
|
let left_y = input.strength(left).abs();
|
|
|
|
let y = if right_y > left_y { right_y } else { left_y };
|
|
|
|
Some(Vec2::new(x, y))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let s = if sprinting {
|
|
|
|
**max_speed
|
|
|
|
} else {
|
|
|
|
**max_speed / config.sprint_movement_factor
|
|
|
|
};
|
|
|
|
**speed = s;
|
|
|
|
if let Some(strength) = strength {
|
|
|
|
direction *= strength;
|
|
|
|
}
|
|
|
|
let mut v: Vector<Real> = (direction * **speed).into();
|
|
|
|
v = position.position.rotation.transform_vector(&v);
|
|
|
|
velocity.linvel = v;
|
|
|
|
//velocity.linvel = position.position.rotation.transform_vector(&velocity.linvel);
|
|
|
|
} else if destination.is_none() {
|
|
|
|
velocity.linvel = Vec2::ZERO.into();
|
|
|
|
**speed = 0.;
|
|
|
|
} else if sprinting {
|
|
|
|
**speed = **max_speed;
|
2021-05-13 17:25:45 +00:00
|
|
|
} else {
|
2022-03-22 02:58:34 +00:00
|
|
|
velocity.linvel = Vec2::ZERO.into();
|
|
|
|
**speed = **max_speed / 3.;
|
2021-05-26 21:46:20 +00:00
|
|
|
}
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-22 02:58:34 +00:00
|
|
|
fn snap<S, A: 'static>(
|
|
|
|
mut tts: ResMut<Tts>,
|
|
|
|
input: Res<InputMap<A>>,
|
|
|
|
config: Res<NavigationConfig<S, A>>,
|
|
|
|
mut snapping: ResMut<Snapping>,
|
|
|
|
mut position: Query<(&mut RigidBodyPositionComponent, &CardinalDirection), With<Player>>,
|
|
|
|
) -> Result<(), Box<dyn Error>>
|
|
|
|
where
|
|
|
|
S: bevy::ecs::component::Component + Clone + Debug + Eq + Hash,
|
|
|
|
A: Hash + Eq + Clone + Send + Sync,
|
|
|
|
{
|
|
|
|
if let Some(action) = config.action_snap_left.clone() {
|
|
|
|
if input.just_active(action) {
|
|
|
|
for (mut position, direction) in position.iter_mut() {
|
|
|
|
**snapping = true;
|
|
|
|
position.position.rotation = UnitComplex::new(match direction {
|
|
|
|
CardinalDirection::North => PI,
|
|
|
|
CardinalDirection::East => PI / 2.,
|
|
|
|
CardinalDirection::South => 0.,
|
|
|
|
CardinalDirection::West => PI * 1.5,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(action) = config.action_snap_right.clone() {
|
|
|
|
if input.just_active(action) {
|
|
|
|
for (mut position, direction) in position.iter_mut() {
|
|
|
|
**snapping = true;
|
|
|
|
position.position.rotation = UnitComplex::new(match direction {
|
|
|
|
CardinalDirection::North => 0.,
|
|
|
|
CardinalDirection::East => PI * 1.5,
|
|
|
|
CardinalDirection::South => PI,
|
|
|
|
CardinalDirection::West => PI / 2.,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(action) = config.action_snap_cardinal.clone() {
|
|
|
|
if input.just_active(action) {
|
|
|
|
for (mut position, direction) in position.iter_mut() {
|
|
|
|
**snapping = true;
|
|
|
|
let yaw: Angle = direction.into();
|
|
|
|
let yaw = yaw.radians();
|
|
|
|
position.position.rotation = UnitComplex::new(yaw);
|
|
|
|
tts.speak(direction.to_string(), true)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(action) = config.action_snap_reverse.clone() {
|
|
|
|
if input.just_active(action) {
|
|
|
|
for (mut position, _) in position.iter_mut() {
|
|
|
|
**snapping = true;
|
|
|
|
position.position.rotation *= UnitComplex::new(PI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-07-26 20:50:25 +00:00
|
|
|
fn update_direction(
|
|
|
|
mut commands: Commands,
|
2022-02-14 12:56:45 +00:00
|
|
|
mut query: Query<
|
|
|
|
(Entity, &Transform, Option<&mut CardinalDirection>),
|
2021-07-26 20:50:25 +00:00
|
|
|
(With<Player>, Changed<Transform>),
|
|
|
|
>,
|
|
|
|
) {
|
2022-02-14 12:56:45 +00:00
|
|
|
for (entity, transform, direction) in query.iter_mut() {
|
2021-05-13 17:25:45 +00:00
|
|
|
let forward = transform.local_x();
|
|
|
|
let yaw = Angle::Radians(forward.y.atan2(forward.x));
|
2021-07-26 20:50:25 +00:00
|
|
|
let new_direction: CardinalDirection = yaw.into();
|
2022-02-14 12:56:45 +00:00
|
|
|
if let Some(mut direction) = direction {
|
|
|
|
if *direction != new_direction {
|
|
|
|
*direction = new_direction;
|
|
|
|
}
|
|
|
|
} else {
|
2021-07-26 20:50:25 +00:00
|
|
|
commands.entity(entity).insert(new_direction);
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-26 20:50:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn speak_direction(
|
|
|
|
mut tts: ResMut<Tts>,
|
2022-03-15 15:48:17 +00:00
|
|
|
player: Query<
|
|
|
|
(&CardinalDirection, ChangeTrackers<CardinalDirection>),
|
|
|
|
(With<Player>, Changed<CardinalDirection>),
|
|
|
|
>,
|
2021-07-26 20:50:25 +00:00
|
|
|
) -> Result<(), Box<dyn Error>> {
|
2022-03-15 15:48:17 +00:00
|
|
|
if let Ok((direction, change)) = player.get_single() {
|
|
|
|
if !change.is_added() {
|
|
|
|
let direction: String = (*direction).into();
|
|
|
|
tts.speak(direction, true)?;
|
|
|
|
}
|
2022-01-13 20:43:02 +00:00
|
|
|
}
|
2021-05-13 17:25:45 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-01-11 05:05:51 +00:00
|
|
|
fn remove_speed(
|
|
|
|
removed: RemovedComponents<Speed>,
|
|
|
|
mut query: Query<&mut RigidBodyVelocityComponent>,
|
|
|
|
) {
|
2021-08-18 19:35:58 +00:00
|
|
|
for entity in removed.iter() {
|
|
|
|
if let Ok(mut velocity) = query.get_mut(entity) {
|
|
|
|
velocity.linvel = Vec2::ZERO.into();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-13 17:25:45 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2021-05-26 21:46:20 +00:00
|
|
|
pub struct NavigationConfig<S, A> {
|
|
|
|
pub action_backward: Option<A>,
|
|
|
|
pub action_forward: Option<A>,
|
|
|
|
pub action_left: Option<A>,
|
|
|
|
pub action_right: Option<A>,
|
|
|
|
pub action_rotate_left: Option<A>,
|
|
|
|
pub action_rotate_right: Option<A>,
|
2022-03-22 02:58:34 +00:00
|
|
|
pub action_snap_left: Option<A>,
|
|
|
|
pub action_snap_right: Option<A>,
|
|
|
|
pub action_snap_reverse: Option<A>,
|
|
|
|
pub action_snap_cardinal: Option<A>,
|
2021-05-26 21:46:20 +00:00
|
|
|
pub action_sprint: Option<A>,
|
2021-05-30 19:35:10 +00:00
|
|
|
pub forward_movement_factor: f32,
|
|
|
|
pub backward_movement_factor: f32,
|
|
|
|
pub strafe_movement_factor: f32,
|
|
|
|
pub sprint_movement_factor: f32,
|
2021-05-13 17:25:45 +00:00
|
|
|
pub movement_control_states: Vec<S>,
|
|
|
|
}
|
|
|
|
|
2021-05-26 21:46:20 +00:00
|
|
|
impl<S, A> Default for NavigationConfig<S, A> {
|
2021-05-13 17:25:45 +00:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2021-05-26 21:46:20 +00:00
|
|
|
action_backward: None,
|
|
|
|
action_forward: None,
|
|
|
|
action_left: None,
|
|
|
|
action_right: None,
|
|
|
|
action_rotate_left: None,
|
|
|
|
action_rotate_right: None,
|
2022-03-22 02:58:34 +00:00
|
|
|
action_snap_left: None,
|
|
|
|
action_snap_right: None,
|
|
|
|
action_snap_reverse: None,
|
|
|
|
action_snap_cardinal: None,
|
2021-05-26 21:46:20 +00:00
|
|
|
action_sprint: None,
|
2021-05-30 19:35:10 +00:00
|
|
|
forward_movement_factor: 1.,
|
|
|
|
backward_movement_factor: 1.,
|
|
|
|
strafe_movement_factor: 1.,
|
|
|
|
sprint_movement_factor: 3.,
|
2021-05-13 17:25:45 +00:00
|
|
|
movement_control_states: vec![],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 22:30:03 +00:00
|
|
|
pub struct NavigationPlugin<'a, S, A>(PhantomData<&'a S>, PhantomData<&'a A>);
|
2021-05-13 17:25:45 +00:00
|
|
|
|
2021-05-26 21:46:20 +00:00
|
|
|
impl<'a, S, A> Default for NavigationPlugin<'a, S, A> {
|
2021-05-13 17:25:45 +00:00
|
|
|
fn default() -> Self {
|
2021-05-26 21:46:20 +00:00
|
|
|
Self(PhantomData, PhantomData)
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 21:46:20 +00:00
|
|
|
impl<'a, S, A> Plugin for NavigationPlugin<'a, S, A>
|
2021-05-13 17:25:45 +00:00
|
|
|
where
|
|
|
|
S: bevy::ecs::component::Component + Clone + Debug + Eq + Hash,
|
2021-05-26 21:46:20 +00:00
|
|
|
A: Hash + Eq + Clone + Send + Sync,
|
2021-05-13 17:25:45 +00:00
|
|
|
'a: 'static,
|
|
|
|
{
|
2022-01-10 19:50:52 +00:00
|
|
|
fn build(&self, app: &mut App) {
|
2022-01-11 05:05:51 +00:00
|
|
|
if !app.world.contains_resource::<NavigationConfig<S, A>>() {
|
2021-05-26 21:46:20 +00:00
|
|
|
app.insert_resource(NavigationConfig::<S, A>::default());
|
2021-05-13 17:25:45 +00:00
|
|
|
}
|
|
|
|
let config = app
|
2022-01-11 05:05:51 +00:00
|
|
|
.world
|
2021-05-26 21:46:20 +00:00
|
|
|
.get_resource::<NavigationConfig<S, A>>()
|
2021-05-13 17:25:45 +00:00
|
|
|
.unwrap()
|
|
|
|
.clone();
|
2022-03-22 02:58:34 +00:00
|
|
|
app.init_resource::<Snapping>()
|
|
|
|
.register_type::<MaxSpeed>()
|
2021-05-13 17:25:45 +00:00
|
|
|
.register_type::<RotationSpeed>()
|
|
|
|
.register_type::<Sprinting>()
|
2022-01-10 19:50:52 +00:00
|
|
|
.add_system(update_direction)
|
|
|
|
.add_system(speak_direction.chain(error_handler))
|
|
|
|
.add_system_to_stage(CoreStage::PostUpdate, remove_speed);
|
2021-05-13 17:25:45 +00:00
|
|
|
if config.movement_control_states.is_empty() {
|
2022-03-22 02:58:34 +00:00
|
|
|
app.add_system(movement_controls::<S, A>)
|
|
|
|
.add_system(snap::<S, A>.chain(error_handler));
|
2021-05-13 17:25:45 +00:00
|
|
|
} else {
|
|
|
|
let states = config.movement_control_states;
|
|
|
|
for state in states {
|
|
|
|
app.add_system_set(
|
2022-03-22 02:58:34 +00:00
|
|
|
SystemSet::on_update(state)
|
|
|
|
.with_system(movement_controls::<S, A>)
|
|
|
|
.with_system(snap::<S, A>.chain(error_handler)),
|
2021-05-13 17:25:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|