blackout/src/navigation.rs

394 lines
13 KiB
Rust
Raw Normal View History

use std::{collections::HashMap, error::Error, f32::consts::PI, fmt::Debug, hash::Hash};
2021-05-13 17:25:45 +00:00
use bevy::{math::CompassQuadrant, prelude::*};
2022-05-06 16:07:59 +00:00
use bevy_rapier2d::prelude::*;
2021-05-13 17:25:45 +00:00
use bevy_tts::Tts;
use leafwing_input_manager::prelude::*;
2021-05-13 17:25:45 +00:00
use crate::{
core::{Area, CardinalDirection, GlobalTransformExt, Player, TransformExt},
2021-05-13 17:25:45 +00:00
error::error_handler,
2022-07-19 16:56:41 +00:00
log::Log,
utils::target_and_other,
2021-05-13 17:25:45 +00:00
};
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)]
2022-08-02 22:15:22 +00:00
pub enum NavigationAction {
Move,
Translate,
2022-08-02 22:15:22 +00:00
Rotate,
SetLinearVelocity,
SetAngularVelocity,
2022-08-02 22:15:22 +00:00
SnapLeft,
SnapRight,
SnapCardinal,
SnapReverse,
}
impl Actionlike for NavigationAction {
fn input_control_kind(&self) -> InputControlKind {
match &self {
NavigationAction::Move
| NavigationAction::Translate
| NavigationAction::SetLinearVelocity => InputControlKind::DualAxis,
NavigationAction::Rotate | NavigationAction::SetAngularVelocity => {
InputControlKind::Axis
}
NavigationAction::SnapLeft
| NavigationAction::SnapRight
| NavigationAction::SnapCardinal
| NavigationAction::SnapReverse => InputControlKind::Button,
}
}
}
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 BackwardMovementFactor(pub f32);
2021-05-13 17:25:45 +00:00
impl Default for BackwardMovementFactor {
2021-05-13 17:25:45 +00:00
fn default() -> Self {
Self(1.)
2021-05-13 17:25:45 +00:00
}
}
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
2021-05-13 17:25:45 +00:00
#[reflect(Component)]
pub struct ForwardMovementFactor(pub f32);
2021-05-13 17:25:45 +00:00
impl Default for ForwardMovementFactor {
fn default() -> Self {
Self(1.)
}
}
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
2021-05-13 17:25:45 +00:00
#[reflect(Component)]
pub struct StrafeMovementFactor(pub f32);
impl Default for StrafeMovementFactor {
fn default() -> Self {
Self(1.)
}
}
#[derive(Component, Clone, Copy, Default, Debug, Deref, DerefMut, Reflect)]
#[reflect(Component)]
pub struct RotationSpeed(pub Rot2);
2021-05-13 17:25:45 +00:00
2022-12-19 20:08:31 +00:00
#[derive(Deref, DerefMut)]
2022-08-04 16:25:09 +00:00
struct SnapTimer(Timer);
2021-05-13 17:25:45 +00:00
2022-08-04 16:25:09 +00:00
impl Default for SnapTimer {
fn default() -> Self {
2022-12-19 20:08:31 +00:00
Self(Timer::from_seconds(0.2, TimerMode::Once))
2022-08-04 16:25:09 +00:00
}
}
2022-03-22 02:58:34 +00:00
2022-12-19 20:08:31 +00:00
#[derive(Resource, Default, Deref, DerefMut)]
struct SnapTimers(HashMap<Entity, SnapTimer>);
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
#[reflect(Component)]
pub struct Speed(pub f32);
impl Default for Speed {
fn default() -> Self {
Self(1.)
}
}
2023-04-03 14:08:23 +00:00
fn snap(
mut tts: ResMut<Tts>,
mut snap_timers: ResMut<SnapTimers>,
mut query: Query<
(
Entity,
&ActionState<NavigationAction>,
&mut Transform,
&CardinalDirection,
),
With<Player>,
>,
) -> Result<(), Box<dyn Error>> {
for (entity, actions, mut transform, direction) in &mut query {
if snap_timers.contains_key(&entity) {
continue;
2024-03-14 18:37:46 +00:00
} else if actions.just_pressed(&NavigationAction::SnapLeft) {
2023-04-03 14:08:23 +00:00
snap_timers.insert(entity, SnapTimer::default());
transform.rotation = Quat::from_rotation_z(match direction.0 {
CompassQuadrant::North => PI,
CompassQuadrant::East => PI / 2.,
CompassQuadrant::South => 0.,
CompassQuadrant::West => -PI / 2.,
2023-04-03 14:08:23 +00:00
});
2024-03-14 18:37:46 +00:00
} else if actions.just_pressed(&NavigationAction::SnapRight) {
2023-04-03 14:08:23 +00:00
snap_timers.insert(entity, SnapTimer::default());
transform.rotation = Quat::from_rotation_z(match direction.0 {
CompassQuadrant::North => 0.,
CompassQuadrant::East => -PI / 2.,
CompassQuadrant::South => PI,
CompassQuadrant::West => PI / 2.,
2023-04-03 14:08:23 +00:00
});
2024-03-14 18:37:46 +00:00
} else if actions.just_pressed(&NavigationAction::SnapReverse) {
2023-04-03 14:08:23 +00:00
snap_timers.insert(entity, SnapTimer::default());
transform.rotate(Quat::from_rotation_z(PI));
2024-03-14 18:37:46 +00:00
} else if actions.just_pressed(&NavigationAction::SnapCardinal) {
println!("Direction: {direction:?}");
let yaw: Rot2 = direction.into();
let yaw = yaw.as_radians();
println!("Yaw: {yaw}");
2023-04-03 14:08:23 +00:00
transform.rotation = Quat::from_rotation_z(yaw);
tts.speak(direction.to_string(), true)?;
}
}
Ok(())
}
fn tick_snap_timers(time: Res<Time>, mut snap_timers: ResMut<SnapTimers>) {
for timer in snap_timers.values_mut() {
timer.tick(time.delta());
}
snap_timers.retain(|_, v| !v.finished());
}
fn controls(
rapier_context: Res<RapierContext>,
time: Res<Time>,
2022-12-19 20:08:31 +00:00
snap_timers: Res<SnapTimers>,
2022-08-04 16:25:09 +00:00
mut query: Query<(
Entity,
&mut ActionState<NavigationAction>,
2022-08-04 16:25:09 +00:00
&mut Velocity,
&Speed,
2022-08-04 16:25:09 +00:00
Option<&RotationSpeed>,
Option<&BackwardMovementFactor>,
Option<&ForwardMovementFactor>,
Option<&StrafeMovementFactor>,
2024-03-24 21:12:11 +00:00
&mut Transform,
&Collider,
2022-08-04 16:25:09 +00:00
)>,
) {
for (
entity,
mut actions,
mut velocity,
speed,
rotation_speed,
backward_movement_factor,
forward_movement_factor,
strafe_movement_factor,
2024-03-24 21:12:11 +00:00
mut transform,
collider,
) in &mut query
2021-05-13 17:25:45 +00:00
{
if !actions.action_disabled(&NavigationAction::Move) {
let mut direction = actions.clamped_axis_pair(&NavigationAction::Move);
let forward_movement_factor =
forward_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.);
let backward_movement_factor =
backward_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.);
let strafe_movement_factor = strafe_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.);
let forward_backward_movement_factor = if direction.x > 0. {
forward_movement_factor
} else if direction.x < 0. {
backward_movement_factor
} else {
1.
};
let movement_factor = if direction.x != 0. && direction.y != 0. {
strafe_movement_factor.min(forward_backward_movement_factor)
} else if direction.y != 0. {
strafe_movement_factor
} else {
forward_backward_movement_factor
};
trace!("{entity:?}: move: {direction:?}");
direction = transform
.compute_matrix()
.transform_vector3(direction.extend(0.))
.truncate();
let mut speed = **speed;
speed *= movement_factor;
let move_velocity = direction * speed;
// println!("{entity:?}: SetLinearVelocity: {velocity:?}");
actions.set_axis_pair(&NavigationAction::SetLinearVelocity, move_velocity);
}
if velocity.linvel != actions.axis_pair(&NavigationAction::SetLinearVelocity) {
velocity.linvel = actions.axis_pair(&NavigationAction::SetLinearVelocity);
}
if actions.axis_pair(&NavigationAction::Translate) != Vec2::ZERO {
let pair = actions.axis_pair(&NavigationAction::Translate);
if rapier_context
.cast_shape(
transform.translation.truncate(),
transform.yaw().as_radians(),
pair,
collider,
ShapeCastOptions {
max_time_of_impact: 1.,
..default()
},
QueryFilter::new()
.exclude_sensors()
.exclude_collider(entity),
)
.is_none()
{
transform.translation += pair.extend(0.);
}
actions.set_axis_pair(&NavigationAction::Translate, Vec2::ZERO);
2022-08-04 16:25:09 +00:00
}
if !snap_timers.contains_key(&entity) {
2022-08-02 22:15:22 +00:00
if let Some(rotation_speed) = rotation_speed {
let delta =
rotation_speed.as_radians() * actions.clamped_value(&NavigationAction::Rotate);
actions.set_value(&NavigationAction::SetAngularVelocity, delta);
2022-03-22 02:58:34 +00:00
}
}
if actions.value(&NavigationAction::SetAngularVelocity) != 0. {
2024-03-24 21:12:11 +00:00
// velocity.angvel =
// actions.value(&NavigationAction::SetAngularVelocity);
transform.rotation *= Quat::from_rotation_z(
actions.value(&NavigationAction::SetAngularVelocity) * time.delta_seconds(),
);
2021-05-13 17:25:45 +00:00
}
}
}
2022-08-04 16:25:09 +00:00
2021-07-26 20:50:25 +00:00
fn update_direction(
mut commands: Commands,
mut query: Query<
(Entity, &GlobalTransform, Option<&mut CardinalDirection>),
2021-07-26 20:50:25 +00:00
(With<Player>, Changed<Transform>),
>,
) {
2023-03-28 17:13:23 +00:00
for (entity, transform, direction) in &mut query {
let yaw = transform.yaw();
2021-07-26 20:50:25 +00:00
let new_direction: CardinalDirection = yaw.into();
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 remove_direction(
mut commands: Commands,
2023-03-28 16:57:37 +00:00
mut removed: RemovedComponents<Transform>,
directions: Query<&CardinalDirection>,
) {
2024-02-09 20:59:16 +00:00
for entity in removed.read() {
if directions.contains(entity) {
2023-04-01 12:20:59 +00:00
commands.entity(entity).remove::<CardinalDirection>();
}
}
}
2021-07-26 20:50:25 +00:00
fn speak_direction(
mut tts: ResMut<Tts>,
player: Query<&CardinalDirection, (With<Player>, Changed<CardinalDirection>)>,
2021-07-26 20:50:25 +00:00
) -> Result<(), Box<dyn Error>> {
if let Ok(direction) = player.get_single() {
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(())
}
fn add_speed(mut commands: Commands, query: Query<Entity, (Added<Speed>, Without<Velocity>)>) {
2023-03-28 17:13:23 +00:00
for entity in &query {
commands.entity(entity).insert(Velocity {
linvel: Vec2::ZERO,
..default()
});
}
}
fn log_area_descriptions(
2022-07-19 16:56:41 +00:00
mut events: EventReader<CollisionEvent>,
areas: Query<(&Area, Option<&Name>)>,
players: Query<&Player>,
config: Res<NavigationPlugin>,
2022-07-19 16:56:41 +00:00
mut log: Query<&mut Log>,
) {
2022-07-19 16:56:41 +00:00
if !config.log_area_descriptions {
return;
}
2024-02-09 20:59:16 +00:00
for event in events.read() {
2022-07-19 16:56:41 +00:00
let (entity1, entity2, started) = match event {
CollisionEvent::Started(collider1, collider2, _) => (collider1, collider2, true),
CollisionEvent::Stopped(collider1, collider2, _) => (collider1, collider2, false),
};
if let Some((area, other)) = target_and_other(*entity1, *entity2, &|v| areas.get(v).is_ok())
{
if players.get(other).is_ok() {
if let Ok((aabb, area_name)) = areas.get(area) {
let name = if let Some(name) = area_name {
Some(name.to_string())
} else if config.describe_undescribed_areas {
Some(format!("{}-by-{} area", aabb.extents().x, aabb.extents().y))
} else {
None
};
if let Some(name) = name {
if let Ok(mut log) = log.get_single_mut() {
if started {
log.push(format!("Entering {name}."));
} else {
log.push(format!("Leaving {name}."));
}
}
}
}
}
}
}
}
2023-03-28 16:57:37 +00:00
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
2023-04-03 14:08:23 +00:00
pub struct Movement;
2022-12-19 20:08:31 +00:00
#[derive(Resource, Clone, Debug)]
pub struct NavigationPlugin {
2022-07-19 16:56:41 +00:00
pub describe_undescribed_areas: bool,
pub log_area_descriptions: bool,
2021-05-13 17:25:45 +00:00
}
impl Default for NavigationPlugin {
2021-05-13 17:25:45 +00:00
fn default() -> Self {
Self {
2022-07-19 16:56:41 +00:00
describe_undescribed_areas: false,
log_area_descriptions: true,
2021-05-13 17:25:45 +00:00
}
}
}
impl Plugin for NavigationPlugin {
2022-01-10 19:50:52 +00:00
fn build(&self, app: &mut App) {
app.insert_resource(self.clone())
.init_resource::<SnapTimers>()
.register_type::<BackwardMovementFactor>()
.register_type::<ForwardMovementFactor>()
.register_type::<StrafeMovementFactor>()
2021-05-13 17:25:45 +00:00
.register_type::<RotationSpeed>()
2022-12-19 20:08:31 +00:00
.register_type::<Speed>()
2023-09-08 21:12:35 +00:00
.add_plugins(InputManagerPlugin::<NavigationAction>::default())
.add_systems(PreUpdate, (update_direction, add_speed))
2023-03-28 16:57:37 +00:00
.add_systems(
2023-09-08 21:12:35 +00:00
Update,
2023-04-03 14:08:23 +00:00
(snap.pipe(error_handler), controls)
.chain()
.in_set(Movement),
2023-03-28 16:57:37 +00:00
)
2023-04-03 14:08:23 +00:00
.add_systems(
FixedUpdate,
2023-09-08 21:12:35 +00:00
(tick_snap_timers, speak_direction.pipe(error_handler)),
)
.add_systems(PostUpdate, (remove_direction, log_area_descriptions));
2021-05-13 17:25:45 +00:00
}
}