Refactor to leafwing-input-manager.
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
c0a1befd98
commit
627f63211d
|
@ -24,7 +24,6 @@ features = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bevy_input_actionmap = { git = "https://github.com/lightsoutgames/bevy_input_actionmap", branch = "bevy-0.8" }
|
|
||||||
bevy_rapier2d = { version = "0.16", features = ["serde-serialize"] }
|
bevy_rapier2d = { version = "0.16", features = ["serde-serialize"] }
|
||||||
bevy_synthizer = { git = "https://labs.lightsout.games/projects/bevy_synthizer", branch = "bevy-0.8" }
|
bevy_synthizer = { git = "https://labs.lightsout.games/projects/bevy_synthizer", branch = "bevy-0.8" }
|
||||||
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"], branch = "bevy-0.8" }
|
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"], branch = "bevy-0.8" }
|
||||||
|
@ -32,6 +31,7 @@ coord_2d = "0.3"
|
||||||
futures-lite = "1"
|
futures-lite = "1"
|
||||||
gilrs = "0.9"
|
gilrs = "0.9"
|
||||||
here_be_dragons = "0.1"
|
here_be_dragons = "0.1"
|
||||||
|
leafwing_input_manager = { git = "https://github.com/ndarilek/leafwing-input-manager", branch = "dev" }
|
||||||
maze_generator = "2"
|
maze_generator = "2"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
pathfinding = "3"
|
pathfinding = "3"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{error::Error, fmt::Debug, hash::Hash, marker::PhantomData};
|
use std::{error::Error, fmt::Debug, hash::Hash, marker::PhantomData};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_input_actionmap::InputMap;
|
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
use bevy_tts::Tts;
|
use bevy_tts::Tts;
|
||||||
|
use leafwing_input_manager::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Player, PointLike},
|
core::{Player, PointLike},
|
||||||
|
@ -13,6 +13,19 @@ use crate::{
|
||||||
visibility::{RevealedTiles, Viewshed, Visible, VisibleEntities},
|
visibility::{RevealedTiles, Viewshed, Visible, VisibleEntities},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||||
|
pub enum ExplorationAction {
|
||||||
|
Forward,
|
||||||
|
Backward,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
FocusNext,
|
||||||
|
FocusPrev,
|
||||||
|
SelectNextType,
|
||||||
|
SelectPrevType,
|
||||||
|
NavigateTo,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Reflect)]
|
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Explorable;
|
pub struct Explorable;
|
||||||
|
@ -34,28 +47,20 @@ pub struct FocusedExplorationType<T>(pub Option<T>);
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Mappable;
|
pub struct Mappable;
|
||||||
|
|
||||||
fn exploration_type_change<T, S, A: 'static>(
|
fn exploration_type_change<T, S>(
|
||||||
config: Res<ExplorationConfig<S, A>>,
|
|
||||||
mut tts: ResMut<Tts>,
|
mut tts: ResMut<Tts>,
|
||||||
input: Res<InputMap<A>>,
|
mut explorers: Query<(
|
||||||
mut explorers: Query<(&VisibleEntities, &mut FocusedExplorationType<T>)>,
|
&ActionState<ExplorationAction>,
|
||||||
|
&VisibleEntities,
|
||||||
|
&mut FocusedExplorationType<T>,
|
||||||
|
)>,
|
||||||
features: Query<&T>,
|
features: Query<&T>,
|
||||||
) -> Result<(), Box<dyn Error>>
|
) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
T: Component + Copy + Ord,
|
T: Component + Copy + Ord,
|
||||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Clone + Send + Sync,
|
|
||||||
{
|
{
|
||||||
if let (Some(select_next_type), Some(select_prev_type)) = (
|
for (actions, visible, mut focused) in explorers.iter_mut() {
|
||||||
config.action_explore_select_next_type.clone(),
|
|
||||||
config.action_explore_select_prev_type.clone(),
|
|
||||||
) {
|
|
||||||
let changed = input.just_active(select_next_type.clone())
|
|
||||||
|| input.just_active(select_prev_type.clone());
|
|
||||||
if !changed {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
for (visible, mut focused) in explorers.iter_mut() {
|
|
||||||
let mut types: Vec<T> = vec![];
|
let mut types: Vec<T> = vec![];
|
||||||
for e in visible.iter() {
|
for e in visible.iter() {
|
||||||
if let Ok(t) = features.get(*e) {
|
if let Ok(t) = features.get(*e) {
|
||||||
|
@ -66,7 +71,7 @@ where
|
||||||
types.dedup();
|
types.dedup();
|
||||||
if types.is_empty() {
|
if types.is_empty() {
|
||||||
tts.speak("Nothing visible.", true)?;
|
tts.speak("Nothing visible.", true)?;
|
||||||
} else if input.just_active(select_prev_type.clone()) {
|
} else if actions.just_pressed(ExplorationAction::SelectPrevType) {
|
||||||
if let Some(t) = &focused.0 {
|
if let Some(t) = &focused.0 {
|
||||||
if let Some(i) = types.iter().position(|v| *v == *t) {
|
if let Some(i) = types.iter().position(|v| *v == *t) {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
@ -83,7 +88,7 @@ where
|
||||||
let t = types.last().unwrap();
|
let t = types.last().unwrap();
|
||||||
focused.0 = Some(*t);
|
focused.0 = Some(*t);
|
||||||
}
|
}
|
||||||
} else if input.just_active(select_next_type.clone()) {
|
} else if actions.just_pressed(ExplorationAction::SelectNextType) {
|
||||||
if let Some(t) = &focused.0 {
|
if let Some(t) = &focused.0 {
|
||||||
if let Some(i) = types.iter().position(|v| *v == *t) {
|
if let Some(i) = types.iter().position(|v| *v == *t) {
|
||||||
if i == types.len() - 1 {
|
if i == types.len() - 1 {
|
||||||
|
@ -102,17 +107,15 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exploration_type_focus<T, S, A>(
|
fn exploration_type_focus<T, S>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
config: Res<ExplorationConfig<S, A>>,
|
|
||||||
input: Res<InputMap<A>>,
|
|
||||||
mut tts: ResMut<Tts>,
|
mut tts: ResMut<Tts>,
|
||||||
explorers: Query<(
|
explorers: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
|
&ActionState<ExplorationAction>,
|
||||||
&VisibleEntities,
|
&VisibleEntities,
|
||||||
&FocusedExplorationType<T>,
|
&FocusedExplorationType<T>,
|
||||||
Option<&Exploring>,
|
Option<&Exploring>,
|
||||||
|
@ -122,18 +125,8 @@ fn exploration_type_focus<T, S, A>(
|
||||||
where
|
where
|
||||||
T: Component + PartialEq,
|
T: Component + PartialEq,
|
||||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: 'static + Hash + Eq + Clone + Send + Sync,
|
|
||||||
{
|
{
|
||||||
if let (Some(explore_focus_next), Some(explore_focus_prev)) = (
|
for (entity, actions, visible_entities, focused_type, exploring) in explorers.iter() {
|
||||||
config.action_explore_focus_next.clone(),
|
|
||||||
config.action_explore_focus_prev.clone(),
|
|
||||||
) {
|
|
||||||
let changed = input.just_active(explore_focus_next.clone())
|
|
||||||
|| input.just_active(explore_focus_prev.clone());
|
|
||||||
if !changed {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
for (entity, visible_entities, focused_type, exploring) in explorers.iter() {
|
|
||||||
let mut features = features
|
let mut features = features
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|v| visible_entities.contains(&v.0))
|
.filter(|v| visible_entities.contains(&v.0))
|
||||||
|
@ -148,7 +141,7 @@ where
|
||||||
features.retain(|(_, t)| **t == *focused);
|
features.retain(|(_, t)| **t == *focused);
|
||||||
}
|
}
|
||||||
let mut target: Option<&((f32, f32), &T)> = None;
|
let mut target: Option<&((f32, f32), &T)> = None;
|
||||||
if input.just_active(explore_focus_next.clone()) {
|
if actions.just_pressed(ExplorationAction::FocusNext) {
|
||||||
if let Some(exploring) = exploring {
|
if let Some(exploring) = exploring {
|
||||||
target = features.iter().find(|(c, _)| *c > **exploring);
|
target = features.iter().find(|(c, _)| *c > **exploring);
|
||||||
if target.is_none() {
|
if target.is_none() {
|
||||||
|
@ -157,7 +150,7 @@ where
|
||||||
} else {
|
} else {
|
||||||
target = features.first();
|
target = features.first();
|
||||||
}
|
}
|
||||||
} else if input.just_active(explore_focus_prev.clone()) {
|
} else if actions.just_pressed(ExplorationAction::FocusPrev) {
|
||||||
if let Some(exploring) = exploring {
|
if let Some(exploring) = exploring {
|
||||||
features.reverse();
|
features.reverse();
|
||||||
target = features.iter().find(|(c, _)| *c < **exploring);
|
target = features.iter().find(|(c, _)| *c < **exploring);
|
||||||
|
@ -174,7 +167,6 @@ where
|
||||||
.insert(Exploring(coordinates.floor()));
|
.insert(Exploring(coordinates.floor()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,28 +200,22 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exploration_focus<S, A: 'static, D: 'static + Clone + Default + Send + Sync>(
|
fn exploration_focus<S, D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
config: Res<ExplorationConfig<S, A>>,
|
|
||||||
input: Res<InputMap<A>>,
|
|
||||||
map: Query<&Map<D>>,
|
map: Query<&Map<D>>,
|
||||||
explorers: Query<(Entity, &Transform, Option<&Exploring>), With<Player>>,
|
explorers: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&ActionState<ExplorationAction>,
|
||||||
|
&Transform,
|
||||||
|
Option<&Exploring>,
|
||||||
|
),
|
||||||
|
With<Player>,
|
||||||
|
>,
|
||||||
) where
|
) where
|
||||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Clone + Send + Sync,
|
|
||||||
{
|
{
|
||||||
if let (
|
for (entity, actions, transform, exploring) in explorers.iter() {
|
||||||
Some(explore_forward),
|
|
||||||
Some(explore_backward),
|
|
||||||
Some(explore_left),
|
|
||||||
Some(explore_right),
|
|
||||||
) = (
|
|
||||||
config.action_explore_forward.clone(),
|
|
||||||
config.action_explore_backward.clone(),
|
|
||||||
config.action_explore_left.clone(),
|
|
||||||
config.action_explore_right.clone(),
|
|
||||||
) {
|
|
||||||
if let Ok((entity, transform, exploring)) = explorers.get_single() {
|
|
||||||
let coordinates = transform.translation;
|
let coordinates = transform.translation;
|
||||||
let mut exploring = if let Some(exploring) = exploring {
|
let mut exploring = if let Some(exploring) = exploring {
|
||||||
**exploring
|
**exploring
|
||||||
|
@ -238,13 +224,13 @@ fn exploration_focus<S, A: 'static, D: 'static + Clone + Default + Send + Sync>(
|
||||||
(floor.x, floor.y)
|
(floor.x, floor.y)
|
||||||
};
|
};
|
||||||
let orig = exploring;
|
let orig = exploring;
|
||||||
if input.just_active(explore_forward) {
|
if actions.just_pressed(ExplorationAction::Forward) {
|
||||||
exploring.1 += 1.;
|
exploring.1 += 1.;
|
||||||
} else if input.just_active(explore_backward) {
|
} else if actions.just_pressed(ExplorationAction::Backward) {
|
||||||
exploring.1 -= 1.;
|
exploring.1 -= 1.;
|
||||||
} else if input.just_active(explore_left) {
|
} else if actions.just_pressed(ExplorationAction::Left) {
|
||||||
exploring.0 -= 1.;
|
exploring.0 -= 1.;
|
||||||
} else if input.just_active(explore_right) {
|
} else if actions.just_pressed(ExplorationAction::Right) {
|
||||||
exploring.0 += 1.;
|
exploring.0 += 1.;
|
||||||
}
|
}
|
||||||
let dimensions = if let Ok(map) = map.get_single() {
|
let dimensions = if let Ok(map) = map.get_single() {
|
||||||
|
@ -261,33 +247,27 @@ fn exploration_focus<S, A: 'static, D: 'static + Clone + Default + Send + Sync>(
|
||||||
commands.entity(entity).insert(Exploring(exploring));
|
commands.entity(entity).insert(Exploring(exploring));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn navigate_to_explored<S, A: 'static, D: 'static + Clone + Default + Send + Sync>(
|
fn navigate_to_explored<S, D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
config: Res<ExplorationConfig<S, A>>,
|
|
||||||
input: Res<InputMap<A>>,
|
|
||||||
map: Query<(&Map<D>, &RevealedTiles)>,
|
map: Query<(&Map<D>, &RevealedTiles)>,
|
||||||
explorers: Query<(Entity, &Exploring)>,
|
explorers: Query<(Entity, &ActionState<ExplorationAction>, &Exploring)>,
|
||||||
) where
|
) where
|
||||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Clone + Send + Sync,
|
|
||||||
{
|
{
|
||||||
if let Some(navigate_to_explored) = config.action_navigate_to_explored.clone() {
|
for (entity, actions, exploring) in explorers.iter() {
|
||||||
for (entity, exploring) in explorers.iter() {
|
|
||||||
for (map, revealed_tiles) in map.iter() {
|
for (map, revealed_tiles) in map.iter() {
|
||||||
let point = **exploring;
|
let point = **exploring;
|
||||||
let idx = point.to_index(map.width);
|
let idx = point.to_index(map.width);
|
||||||
let known = revealed_tiles[idx];
|
let known = revealed_tiles[idx];
|
||||||
if input.just_active(navigate_to_explored.clone()) && known {
|
if actions.just_pressed(ExplorationAction::NavigateTo) && known {
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
.insert(Destination((point.x_i32(), point.y_i32())));
|
.insert(Destination((point.x_i32(), point.y_i32())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exploration_changed_announcement<T, D>(
|
fn exploration_changed_announcement<T, D>(
|
||||||
|
@ -388,32 +368,14 @@ fn cleanup(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ExplorationConfig<S, A> {
|
pub struct ExplorationConfig<S> {
|
||||||
pub exploration_control_states: Vec<S>,
|
pub exploration_control_states: Vec<S>,
|
||||||
pub action_explore_forward: Option<A>,
|
|
||||||
pub action_explore_backward: Option<A>,
|
|
||||||
pub action_explore_left: Option<A>,
|
|
||||||
pub action_explore_right: Option<A>,
|
|
||||||
pub action_explore_focus_next: Option<A>,
|
|
||||||
pub action_explore_focus_prev: Option<A>,
|
|
||||||
pub action_explore_select_next_type: Option<A>,
|
|
||||||
pub action_explore_select_prev_type: Option<A>,
|
|
||||||
pub action_navigate_to_explored: Option<A>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, A> Default for ExplorationConfig<S, A> {
|
impl<S> Default for ExplorationConfig<S> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
exploration_control_states: vec![],
|
exploration_control_states: vec![],
|
||||||
action_explore_forward: None,
|
|
||||||
action_explore_backward: None,
|
|
||||||
action_explore_left: None,
|
|
||||||
action_explore_right: None,
|
|
||||||
action_explore_focus_next: None,
|
|
||||||
action_explore_focus_prev: None,
|
|
||||||
action_explore_select_next_type: None,
|
|
||||||
action_explore_select_prev_type: None,
|
|
||||||
action_navigate_to_explored: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,22 +401,23 @@ where
|
||||||
D: 'static + Clone + Default + Send + Sync,
|
D: 'static + Clone + Default + Send + Sync,
|
||||||
{
|
{
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
if !app.world.contains_resource::<ExplorationConfig<S, A>>() {
|
if !app.world.contains_resource::<ExplorationConfig<S>>() {
|
||||||
app.insert_resource(ExplorationConfig::<S, A>::default());
|
app.insert_resource(ExplorationConfig::<S>::default());
|
||||||
}
|
}
|
||||||
let config = app
|
let config = app
|
||||||
.world
|
.world
|
||||||
.get_resource::<ExplorationConfig<S, A>>()
|
.get_resource::<ExplorationConfig<S>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
app.register_type::<ExplorationFocused>()
|
app.register_type::<ExplorationFocused>()
|
||||||
.register_type::<Mappable>()
|
.register_type::<Mappable>()
|
||||||
|
.add_plugin(InputManagerPlugin::<ExplorationAction>::default())
|
||||||
.add_system(exploration_changed_announcement::<T, D>.chain(error_handler));
|
.add_system(exploration_changed_announcement::<T, D>.chain(error_handler));
|
||||||
if config.exploration_control_states.is_empty() {
|
if config.exploration_control_states.is_empty() {
|
||||||
app.add_system(exploration_focus::<S, A, D>)
|
app.add_system(exploration_focus::<S, D>)
|
||||||
.add_system(exploration_type_focus::<T, S, A>.chain(error_handler))
|
.add_system(exploration_type_focus::<T, S>.chain(error_handler))
|
||||||
.add_system(exploration_type_change::<T, S, A>.chain(error_handler))
|
.add_system(exploration_type_change::<T, S>.chain(error_handler))
|
||||||
.add_system(navigate_to_explored::<S, A, D>)
|
.add_system(navigate_to_explored::<S, D>)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
CoreStage::PostUpdate,
|
CoreStage::PostUpdate,
|
||||||
exploration_type_changed_announcement::<T>.chain(error_handler),
|
exploration_type_changed_announcement::<T>.chain(error_handler),
|
||||||
|
@ -464,10 +427,10 @@ where
|
||||||
for state in states {
|
for state in states {
|
||||||
app.add_system_set(
|
app.add_system_set(
|
||||||
SystemSet::on_update(state.clone())
|
SystemSet::on_update(state.clone())
|
||||||
.with_system(exploration_focus::<S, A, D>)
|
.with_system(exploration_focus::<S, D>)
|
||||||
.with_system(exploration_type_focus::<T, S, A>.chain(error_handler))
|
.with_system(exploration_type_focus::<T, S>.chain(error_handler))
|
||||||
.with_system(exploration_type_change::<T, S, A>.chain(error_handler))
|
.with_system(exploration_type_change::<T, S>.chain(error_handler))
|
||||||
.with_system(navigate_to_explored::<S, A, D>)
|
.with_system(navigate_to_explored::<S, D>)
|
||||||
.with_system(
|
.with_system(
|
||||||
exploration_type_changed_announcement::<T>.chain(error_handler),
|
exploration_type_changed_announcement::<T>.chain(error_handler),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
pub use bevy_input_actionmap;
|
|
||||||
pub use bevy_rapier2d;
|
pub use bevy_rapier2d;
|
||||||
pub use bevy_synthizer;
|
pub use bevy_synthizer;
|
||||||
pub use bevy_tts;
|
pub use bevy_tts;
|
||||||
|
@ -15,6 +14,7 @@ pub mod exploration;
|
||||||
pub use futures_lite;
|
pub use futures_lite;
|
||||||
pub use gilrs;
|
pub use gilrs;
|
||||||
pub use here_be_dragons as mapgen;
|
pub use here_be_dragons as mapgen;
|
||||||
|
pub use leafwing_input_manager;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
pub mod navigation;
|
pub mod navigation;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{error::Error, f32::consts::PI, fmt::Debug, hash::Hash, marker::PhantomData};
|
use std::{error::Error, f32::consts::PI, fmt::Debug, hash::Hash, marker::PhantomData};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_input_actionmap::InputMap;
|
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
use bevy_tts::Tts;
|
use bevy_tts::Tts;
|
||||||
|
use leafwing_input_manager::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::RunIfExistsExt,
|
commands::RunIfExistsExt,
|
||||||
|
@ -15,6 +15,17 @@ use crate::{
|
||||||
utils::target_and_other,
|
utils::target_and_other,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||||
|
pub enum NavigationAction {
|
||||||
|
Move,
|
||||||
|
Rotate,
|
||||||
|
SnapLeft,
|
||||||
|
SnapRight,
|
||||||
|
SnapCardinal,
|
||||||
|
SnapReverse,
|
||||||
|
Sprint,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
|
#[derive(Component, Clone, Copy, Debug, Deref, DerefMut, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct MaxSpeed(pub f32);
|
pub struct MaxSpeed(pub f32);
|
||||||
|
@ -46,15 +57,15 @@ pub struct Sprinting;
|
||||||
#[derive(Default, Deref, DerefMut)]
|
#[derive(Default, Deref, DerefMut)]
|
||||||
struct Snapping(bool);
|
struct Snapping(bool);
|
||||||
|
|
||||||
fn movement_controls<S, A: 'static>(
|
fn movement_controls<S>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
config: Res<NavigationConfig<S, A>>,
|
config: Res<NavigationConfig<S>>,
|
||||||
input: Res<InputMap<A>>,
|
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut snapping: ResMut<Snapping>,
|
mut snapping: ResMut<Snapping>,
|
||||||
mut query: Query<
|
mut query: Query<
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
|
&ActionState<NavigationAction>,
|
||||||
&mut Velocity,
|
&mut Velocity,
|
||||||
&mut Speed,
|
&mut Speed,
|
||||||
&MaxSpeed,
|
&MaxSpeed,
|
||||||
|
@ -67,83 +78,37 @@ fn movement_controls<S, A: 'static>(
|
||||||
exploration_focused: Query<Entity, With<ExplorationFocused>>,
|
exploration_focused: Query<Entity, With<ExplorationFocused>>,
|
||||||
) where
|
) where
|
||||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Copy + Send + Sync,
|
|
||||||
{
|
{
|
||||||
for (entity, mut velocity, mut speed, max_speed, rotation_speed, mut transform, destination) in
|
for (
|
||||||
query.iter_mut()
|
entity,
|
||||||
|
actions,
|
||||||
|
mut velocity,
|
||||||
|
mut speed,
|
||||||
|
max_speed,
|
||||||
|
rotation_speed,
|
||||||
|
mut transform,
|
||||||
|
destination,
|
||||||
|
) in &mut query
|
||||||
{
|
{
|
||||||
if **snapping {
|
if **snapping {
|
||||||
if let (Some(_), Some(rotate_left), Some(rotate_right)) = (
|
if actions.pressed(NavigationAction::Rotate) {
|
||||||
rotation_speed,
|
|
||||||
config.action_rotate_left,
|
|
||||||
config.action_rotate_right,
|
|
||||||
) {
|
|
||||||
if input.active(rotate_left) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if input.active(rotate_right) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
**snapping = false;
|
**snapping = false;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let sprinting = if let Some(action) = config.action_sprint {
|
let sprinting = actions.pressed(NavigationAction::Sprint);
|
||||||
input.active(action)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
if sprinting {
|
if sprinting {
|
||||||
commands.entity(entity).insert(Sprinting::default());
|
commands.entity(entity).insert(Sprinting);
|
||||||
} else {
|
} else {
|
||||||
commands.entity(entity).remove::<Sprinting>();
|
commands.entity(entity).remove::<Sprinting>();
|
||||||
}
|
}
|
||||||
let mut direction = Vec2::default();
|
let mut cleanup = false;
|
||||||
if let Some(action) = config.action_forward {
|
if actions.pressed(NavigationAction::Move) {
|
||||||
if input.active(action) {
|
if let Some(pair) = actions.clamped_axis_pair(NavigationAction::Move) {
|
||||||
direction.x += 1.;
|
cleanup = true;
|
||||||
}
|
let mut direction = pair.xy();
|
||||||
}
|
let strength = direction.length();
|
||||||
if let Some(action) = config.action_backward {
|
|
||||||
if input.active(action) {
|
|
||||||
direction.x -= 1.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(action) = config.action_left {
|
|
||||||
if input.active(action) {
|
|
||||||
direction.y += 1.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(action) = config.action_right {
|
|
||||||
if input.active(action) {
|
|
||||||
direction.y -= 1.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let (Some(rotation_speed), Some(rotate_left), Some(rotate_right)) = (
|
|
||||||
rotation_speed,
|
|
||||||
config.action_rotate_left,
|
|
||||||
config.action_rotate_right,
|
|
||||||
) {
|
|
||||||
let delta = rotation_speed.radians() * time.delta_seconds();
|
|
||||||
if input.active(rotate_left) {
|
|
||||||
transform.rotate(Quat::from_rotation_z(delta));
|
|
||||||
}
|
|
||||||
if input.active(rotate_right) {
|
|
||||||
transform.rotate(Quat::from_rotation_z(-delta));
|
|
||||||
}
|
|
||||||
if !input.active(rotate_left) && !input.active(rotate_right) {
|
|
||||||
velocity.angvel = 0.;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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. {
|
if direction.x > 0. {
|
||||||
direction.x *= config.forward_movement_factor;
|
direction.x *= config.forward_movement_factor;
|
||||||
} else if direction.x < 0. {
|
} else if direction.x < 0. {
|
||||||
|
@ -152,70 +117,65 @@ fn movement_controls<S, A: 'static>(
|
||||||
if direction.y != 0. {
|
if direction.y != 0. {
|
||||||
direction.y *= config.strafe_movement_factor;
|
direction.y *= config.strafe_movement_factor;
|
||||||
}
|
}
|
||||||
let strength = if let (Some(forward), Some(backward), Some(left), Some(right)) = (
|
|
||||||
config.action_forward,
|
|
||||||
config.action_backward,
|
|
||||||
config.action_left,
|
|
||||||
config.action_right,
|
|
||||||
) {
|
|
||||||
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 {
|
let s = if sprinting {
|
||||||
**max_speed
|
**max_speed
|
||||||
} else {
|
} else {
|
||||||
**max_speed / config.sprint_movement_factor
|
**max_speed / config.sprint_movement_factor
|
||||||
};
|
};
|
||||||
**speed = s;
|
**speed = s;
|
||||||
if let Some(strength) = strength {
|
|
||||||
direction *= strength;
|
direction *= strength;
|
||||||
}
|
|
||||||
let mut v = direction * **speed;
|
let mut v = direction * **speed;
|
||||||
v = transform
|
v = transform
|
||||||
.compute_matrix()
|
.compute_matrix()
|
||||||
.transform_vector3(v.extend(0.))
|
.transform_vector3(v.extend(0.))
|
||||||
.truncate();
|
.truncate();
|
||||||
velocity.linvel = v;
|
velocity.linvel = v;
|
||||||
|
}
|
||||||
} else if destination.is_none() {
|
} else if destination.is_none() {
|
||||||
velocity.linvel = Vec2::ZERO;
|
velocity.linvel = Vec2::ZERO;
|
||||||
**speed = 0.;
|
}
|
||||||
} else if sprinting {
|
if let Some(rotation_speed) = rotation_speed {
|
||||||
**speed = **max_speed;
|
if actions.pressed(NavigationAction::Rotate) {
|
||||||
|
cleanup = true;
|
||||||
|
let delta = rotation_speed.radians()
|
||||||
|
* time.delta_seconds()
|
||||||
|
* actions.clamped_value(NavigationAction::Rotate);
|
||||||
|
transform.rotate(Quat::from_rotation_z(delta));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
velocity.linvel = Vec2::ZERO;
|
velocity.angvel = 0.;
|
||||||
**speed = **max_speed / 3.;
|
}
|
||||||
|
if cleanup {
|
||||||
|
commands.entity(entity).remove::<Destination>();
|
||||||
|
commands.entity(entity).remove::<Exploring>();
|
||||||
|
for entity in exploration_focused.iter() {
|
||||||
|
commands.entity(entity).remove::<ExplorationFocused>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snap<S, A: 'static>(
|
fn snap<S>(
|
||||||
mut tts: ResMut<Tts>,
|
mut tts: ResMut<Tts>,
|
||||||
input: Res<InputMap<A>>,
|
|
||||||
config: Res<NavigationConfig<S, A>>,
|
|
||||||
mut snapping: ResMut<Snapping>,
|
mut snapping: ResMut<Snapping>,
|
||||||
mut query: Query<(&mut Transform, &mut Velocity, &CardinalDirection), With<Player>>,
|
mut query: Query<
|
||||||
|
(
|
||||||
|
&ActionState<NavigationAction>,
|
||||||
|
&mut Transform,
|
||||||
|
&mut Velocity,
|
||||||
|
&CardinalDirection,
|
||||||
|
),
|
||||||
|
With<Player>,
|
||||||
|
>,
|
||||||
) -> Result<(), Box<dyn Error>>
|
) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
S: 'static + Copy + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Copy + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Clone + Send + Sync,
|
|
||||||
{
|
{
|
||||||
if let Some(action) = config.action_snap_left.clone() {
|
for (actions, mut transform, mut velocity, direction) in query.iter_mut() {
|
||||||
if input.just_active(action) {
|
if actions.just_pressed(NavigationAction::SnapLeft) {
|
||||||
for (mut position, mut velocity, direction) in query.iter_mut() {
|
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
position.rotation = Quat::from_rotation_z(match direction {
|
transform.rotation = Quat::from_rotation_z(match direction {
|
||||||
CardinalDirection::North => PI,
|
CardinalDirection::North => PI,
|
||||||
CardinalDirection::East => PI / 2.,
|
CardinalDirection::East => PI / 2.,
|
||||||
CardinalDirection::South => 0.,
|
CardinalDirection::South => 0.,
|
||||||
|
@ -223,11 +183,7 @@ where
|
||||||
});
|
});
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
}
|
}
|
||||||
}
|
if actions.just_pressed(NavigationAction::SnapRight) {
|
||||||
}
|
|
||||||
if let Some(action) = config.action_snap_right.clone() {
|
|
||||||
if input.just_active(action) {
|
|
||||||
for (mut transform, mut velocity, direction) in query.iter_mut() {
|
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
transform.rotation = Quat::from_rotation_z(match direction {
|
transform.rotation = Quat::from_rotation_z(match direction {
|
||||||
CardinalDirection::North => 0.,
|
CardinalDirection::North => 0.,
|
||||||
|
@ -237,11 +193,7 @@ where
|
||||||
});
|
});
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
}
|
}
|
||||||
}
|
if actions.just_pressed(NavigationAction::SnapCardinal) {
|
||||||
}
|
|
||||||
if let Some(action) = config.action_snap_cardinal.clone() {
|
|
||||||
if input.just_active(action) {
|
|
||||||
for (mut transform, mut velocity, direction) in query.iter_mut() {
|
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
let yaw: Angle = direction.into();
|
let yaw: Angle = direction.into();
|
||||||
let yaw = yaw.radians();
|
let yaw = yaw.radians();
|
||||||
|
@ -249,17 +201,12 @@ where
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
tts.speak(direction.to_string(), true)?;
|
tts.speak(direction.to_string(), true)?;
|
||||||
}
|
}
|
||||||
}
|
if actions.just_pressed(NavigationAction::SnapReverse) {
|
||||||
}
|
|
||||||
if let Some(action) = config.action_snap_reverse.clone() {
|
|
||||||
if input.just_active(action) {
|
|
||||||
for (mut transform, mut velocity, _) in query.iter_mut() {
|
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
transform.rotate(Quat::from_rotation_z(PI));
|
transform.rotate(Quat::from_rotation_z(PI));
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +275,7 @@ fn log_area_descriptions<S, A>(
|
||||||
mut events: EventReader<CollisionEvent>,
|
mut events: EventReader<CollisionEvent>,
|
||||||
areas: Query<(&Area, Option<&Name>)>,
|
areas: Query<(&Area, Option<&Name>)>,
|
||||||
players: Query<&Player>,
|
players: Query<&Player>,
|
||||||
config: Res<NavigationConfig<S, A>>,
|
config: Res<NavigationConfig<S>>,
|
||||||
mut log: Query<&mut Log>,
|
mut log: Query<&mut Log>,
|
||||||
) where
|
) where
|
||||||
S: 'static + Send + Sync,
|
S: 'static + Send + Sync,
|
||||||
|
@ -369,18 +316,7 @@ fn log_area_descriptions<S, A>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NavigationConfig<S, A> {
|
pub struct NavigationConfig<S> {
|
||||||
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>,
|
|
||||||
pub action_snap_left: Option<A>,
|
|
||||||
pub action_snap_right: Option<A>,
|
|
||||||
pub action_snap_reverse: Option<A>,
|
|
||||||
pub action_snap_cardinal: Option<A>,
|
|
||||||
pub action_sprint: Option<A>,
|
|
||||||
pub forward_movement_factor: f32,
|
pub forward_movement_factor: f32,
|
||||||
pub backward_movement_factor: f32,
|
pub backward_movement_factor: f32,
|
||||||
pub strafe_movement_factor: f32,
|
pub strafe_movement_factor: f32,
|
||||||
|
@ -390,20 +326,9 @@ pub struct NavigationConfig<S, A> {
|
||||||
pub log_area_descriptions: bool,
|
pub log_area_descriptions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, A> Default for NavigationConfig<S, A> {
|
impl<S> Default for NavigationConfig<S> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
action_backward: None,
|
|
||||||
action_forward: None,
|
|
||||||
action_left: None,
|
|
||||||
action_right: None,
|
|
||||||
action_rotate_left: None,
|
|
||||||
action_rotate_right: None,
|
|
||||||
action_snap_left: None,
|
|
||||||
action_snap_right: None,
|
|
||||||
action_snap_reverse: None,
|
|
||||||
action_snap_cardinal: None,
|
|
||||||
action_sprint: None,
|
|
||||||
forward_movement_factor: 1.,
|
forward_movement_factor: 1.,
|
||||||
backward_movement_factor: 1.,
|
backward_movement_factor: 1.,
|
||||||
strafe_movement_factor: 1.,
|
strafe_movement_factor: 1.,
|
||||||
|
@ -429,12 +354,12 @@ where
|
||||||
A: Hash + Eq + Copy + Send + Sync,
|
A: Hash + Eq + Copy + Send + Sync,
|
||||||
{
|
{
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
if !app.world.contains_resource::<NavigationConfig<S, A>>() {
|
if !app.world.contains_resource::<NavigationConfig<S>>() {
|
||||||
app.insert_resource(NavigationConfig::<S, A>::default());
|
app.insert_resource(NavigationConfig::<S>::default());
|
||||||
}
|
}
|
||||||
let config = app
|
let config = app
|
||||||
.world
|
.world
|
||||||
.get_resource::<NavigationConfig<S, A>>()
|
.get_resource::<NavigationConfig<S>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
const SNAP: &str = "SNAP";
|
const SNAP: &str = "SNAP";
|
||||||
|
@ -442,6 +367,7 @@ where
|
||||||
.register_type::<MaxSpeed>()
|
.register_type::<MaxSpeed>()
|
||||||
.register_type::<RotationSpeed>()
|
.register_type::<RotationSpeed>()
|
||||||
.register_type::<Sprinting>()
|
.register_type::<Sprinting>()
|
||||||
|
.add_plugin(InputManagerPlugin::<NavigationAction>::default())
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, update_direction)
|
.add_system_to_stage(CoreStage::PostUpdate, update_direction)
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, remove_direction)
|
.add_system_to_stage(CoreStage::PostUpdate, remove_direction)
|
||||||
.add_system(speak_direction.chain(error_handler))
|
.add_system(speak_direction.chain(error_handler))
|
||||||
|
@ -450,9 +376,9 @@ where
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, log_area_descriptions::<S, A>);
|
.add_system_to_stage(CoreStage::PostUpdate, log_area_descriptions::<S, A>);
|
||||||
const MOVEMENT_CONTROLS: &str = "MOVEMENT_CONTROLS";
|
const MOVEMENT_CONTROLS: &str = "MOVEMENT_CONTROLS";
|
||||||
if config.movement_control_states.is_empty() {
|
if config.movement_control_states.is_empty() {
|
||||||
app.add_system(movement_controls::<S, A>.label(MOVEMENT_CONTROLS))
|
app.add_system(movement_controls::<S>.label(MOVEMENT_CONTROLS))
|
||||||
.add_system(
|
.add_system(
|
||||||
snap::<S, A>
|
snap::<S>
|
||||||
.chain(error_handler)
|
.chain(error_handler)
|
||||||
.label(SNAP)
|
.label(SNAP)
|
||||||
.before(MOVEMENT_CONTROLS),
|
.before(MOVEMENT_CONTROLS),
|
||||||
|
@ -462,9 +388,9 @@ where
|
||||||
for state in states {
|
for state in states {
|
||||||
app.add_system_set(
|
app.add_system_set(
|
||||||
SystemSet::on_update(state)
|
SystemSet::on_update(state)
|
||||||
.with_system(movement_controls::<S, A>.label(MOVEMENT_CONTROLS))
|
.with_system(movement_controls::<S>.label(MOVEMENT_CONTROLS))
|
||||||
.with_system(
|
.with_system(
|
||||||
snap::<S, A>
|
snap::<S>
|
||||||
.chain(error_handler)
|
.chain(error_handler)
|
||||||
.label(SNAP)
|
.label(SNAP)
|
||||||
.before(MOVEMENT_CONTROLS),
|
.before(MOVEMENT_CONTROLS),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user