use std::{collections::HashMap, error::Error, f32::consts::PI, fmt::Debug, hash::Hash}; use bevy::prelude::*; use bevy_rapier2d::prelude::*; use bevy_tts::Tts; use leafwing_input_manager::{axislike::DualAxisData, prelude::*}; use crate::{ commands::RunIfExistsExt, core::{Angle, Area, CardinalDirection, GlobalTransformExt, Player}, error::error_handler, exploration::{ExplorationFocused, Exploring}, log::Log, pathfinding::Destination, utils::target_and_other, }; #[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum NavigationAction { Move, Rotate, SetLinearVelocity, SetAngularVelocity, SnapLeft, SnapRight, SnapCardinal, SnapReverse, } #[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct MaxSpeed(pub f32); impl Default for MaxSpeed { fn default() -> Self { MaxSpeed(2.) } } #[derive(Component, Clone, Copy, Default, Debug, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct RotationSpeed(pub Angle); #[derive(Component, Clone, Copy, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct Speed(pub f32); #[derive(Deref, DerefMut)] struct SnapTimer(Timer); impl Default for SnapTimer { fn default() -> Self { Self(Timer::from_seconds(0.2, TimerMode::Once)) } } #[derive(Resource, Default, Deref, DerefMut)] struct SnapTimers(HashMap); fn movement_controls( mut commands: Commands, config: Res>, snap_timers: Res, mut query: Query<( Entity, &mut ActionState, &mut Velocity, &mut Speed, &MaxSpeed, Option<&RotationSpeed>, &Transform, )>, exploration_focused: Query>, ) where State: 'static + Clone + Debug + Eq + Hash + Send + Sync, { for (entity, mut actions, mut velocity, mut speed, max_speed, rotation_speed, transform) in &mut query { let mut cleanup = false; if actions.pressed(NavigationAction::Move) { if let Some(pair) = actions.clamped_axis_pair(NavigationAction::Move) { cleanup = true; let mut direction = pair.xy(); if direction.x.abs() < 0.5 { direction.x = 0.; } if direction.y.abs() < 0.5 { direction.y = 0.; } let forward_backward_movement_factor = if direction.y > 0. { config.forward_movement_factor } else if direction.y < 0. { config.backward_movement_factor } else { 0. }; let movement_factor = if direction.x != 0. && direction.y != 0. { config .strafe_movement_factor .min(forward_backward_movement_factor) } else if direction.x != 0. { config.strafe_movement_factor } else { forward_backward_movement_factor }; let mut s = **max_speed; s *= movement_factor; **speed = s; let mut v = direction * **speed; v = Vec2::new(v.y, -v.x); v = transform .compute_matrix() .transform_vector3(v.extend(0.)) .truncate(); actions.press(NavigationAction::SetLinearVelocity); actions .action_data_mut(NavigationAction::SetLinearVelocity) .axis_pair = Some(DualAxisData::from_xy(v)); } } if actions.released(NavigationAction::Move) && actions.axis_pair(NavigationAction::Move).is_some() { actions.press(NavigationAction::SetLinearVelocity); actions .action_data_mut(NavigationAction::SetLinearVelocity) .axis_pair = None; } if actions.pressed(NavigationAction::SetLinearVelocity) { if let Some(pair) = actions.axis_pair(NavigationAction::SetLinearVelocity) { velocity.linvel = pair.into(); } else { velocity.linvel = Vec2::ZERO; } } if !snap_timers.contains_key(&entity) { if let Some(rotation_speed) = rotation_speed { if actions.pressed(NavigationAction::Rotate) { cleanup = true; let delta = -rotation_speed.radians() * actions.clamped_value(NavigationAction::Rotate); actions.press(NavigationAction::SetAngularVelocity); actions .action_data_mut(NavigationAction::SetAngularVelocity) .value = delta; } } } if actions.released(NavigationAction::Rotate) { actions.press(NavigationAction::SetAngularVelocity); actions .action_data_mut(NavigationAction::SetAngularVelocity) .value = 0.; } if actions.pressed(NavigationAction::SetAngularVelocity) { velocity.angvel = actions.value(NavigationAction::SetAngularVelocity); } if cleanup { commands.entity(entity).remove::(); commands.entity(entity).remove::(); for entity in exploration_focused.iter() { commands.entity(entity).remove::(); } } } } fn snap( mut tts: ResMut, mut snap_timers: ResMut, mut query: Query< ( Entity, &ActionState, &mut Transform, &CardinalDirection, ), With, >, ) -> Result<(), Box> { for (entity, actions, mut transform, direction) in &mut query { if snap_timers.contains_key(&entity) { continue; } else if actions.pressed(NavigationAction::SnapLeft) { snap_timers.insert(entity, SnapTimer::default()); transform.rotation = Quat::from_rotation_z(match direction { CardinalDirection::North => PI, CardinalDirection::East => PI / 2., CardinalDirection::South => 0., CardinalDirection::West => -PI / 2., }); } else if actions.pressed(NavigationAction::SnapRight) { snap_timers.insert(entity, SnapTimer::default()); transform.rotation = Quat::from_rotation_z(match direction { CardinalDirection::North => 0., CardinalDirection::East => -PI / 2., CardinalDirection::South => PI, CardinalDirection::West => PI / 2., }); } else if actions.pressed(NavigationAction::SnapReverse) { snap_timers.insert(entity, SnapTimer::default()); transform.rotate(Quat::from_rotation_z(PI)); } else if actions.pressed(NavigationAction::SnapCardinal) { let yaw: Angle = direction.into(); let yaw = yaw.radians(); transform.rotation = Quat::from_rotation_z(yaw); tts.speak(direction.to_string(), true)?; } } Ok(()) } fn tick_snap_timers(time: Res