Compare commits
No commits in common. "627f63211d737291f08dcd16f4a11f940e63bf48" and "da413ef4e4c01e4570fad26384d60a71d5c7f039" have entirely different histories.
627f63211d
...
da413ef4e4
12
Cargo.toml
12
Cargo.toml
|
@ -11,7 +11,7 @@ edition = "2021"
|
||||||
speech_dispatcher_0_10 = ["bevy_tts/speech_dispatcher_0_10"]
|
speech_dispatcher_0_10 = ["bevy_tts/speech_dispatcher_0_10"]
|
||||||
|
|
||||||
[dependencies.bevy]
|
[dependencies.bevy]
|
||||||
version = "0.8"
|
version = "0.7"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"bevy_gilrs",
|
"bevy_gilrs",
|
||||||
|
@ -24,14 +24,14 @@ features = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bevy_rapier2d = { version = "0.16", features = ["serde-serialize"] }
|
bevy_input_actionmap = { git = "https://github.com/lightsoutgames/bevy_input_actionmap" }
|
||||||
bevy_synthizer = { git = "https://labs.lightsout.games/projects/bevy_synthizer", branch = "bevy-0.8" }
|
bevy_rapier2d = { version = "0.15", features = ["serde-serialize"] }
|
||||||
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"], branch = "bevy-0.8" }
|
bevy_synthizer = { git = "https://labs.lightsout.games/projects/bevy_synthizer" }
|
||||||
|
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"] }
|
||||||
coord_2d = "0.3"
|
coord_2d = "0.3"
|
||||||
futures-lite = "1"
|
futures-lite = "1"
|
||||||
gilrs = "0.9"
|
gilrs = "0.8"
|
||||||
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"
|
||||||
|
|
33
src/core.rs
33
src/core.rs
|
@ -7,9 +7,10 @@ use std::{
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy::{ecs::query::WorldQuery, prelude::*, utils::FloatOrd};
|
use bevy::{core::FloatOrd, ecs::query::WorldQuery, prelude::*};
|
||||||
use bevy_rapier2d::{
|
use bevy_rapier2d::{
|
||||||
parry::query::{closest_points, distance, ClosestPoints},
|
na,
|
||||||
|
parry::query::ClosestPoints,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
rapier::{math::Isometry, prelude::AABB},
|
rapier::{math::Isometry, prelude::AABB},
|
||||||
};
|
};
|
||||||
|
@ -390,11 +391,11 @@ impl PointLike for &Transform {
|
||||||
|
|
||||||
impl PointLike for GlobalTransform {
|
impl PointLike for GlobalTransform {
|
||||||
fn x(&self) -> f32 {
|
fn x(&self) -> f32 {
|
||||||
self.translation().x
|
self.translation.x
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y(&self) -> f32 {
|
fn y(&self) -> f32 {
|
||||||
self.translation().y
|
self.translation.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +482,7 @@ pub trait GlobalTransformExt {
|
||||||
|
|
||||||
impl GlobalTransformExt for GlobalTransform {
|
impl GlobalTransformExt for GlobalTransform {
|
||||||
fn yaw(&self) -> Angle {
|
fn yaw(&self) -> Angle {
|
||||||
let forward = self.right();
|
let forward = self.local_x();
|
||||||
Angle::Radians(forward.y.atan2(forward.x))
|
Angle::Radians(forward.y.atan2(forward.x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,25 +494,27 @@ impl GlobalTransformExt for GlobalTransform {
|
||||||
) -> String {
|
) -> String {
|
||||||
use bevy::math::Vec3Swizzles;
|
use bevy::math::Vec3Swizzles;
|
||||||
let scale = PHYSICS_SCALE.read().unwrap();
|
let scale = PHYSICS_SCALE.read().unwrap();
|
||||||
let (_, rotation, _) = self.to_scale_rotation_translation();
|
|
||||||
let pos1 = Isometry::new(
|
let pos1 = Isometry::new(
|
||||||
(self.translation() / *scale).xy().into(),
|
(self.translation / *scale).xy().into(),
|
||||||
rotation.to_scaled_axis().z,
|
self.rotation.to_scaled_axis().z,
|
||||||
);
|
);
|
||||||
let (_, other_rotation, _) = self.to_scale_rotation_translation();
|
|
||||||
let pos2 = Isometry::new(
|
let pos2 = Isometry::new(
|
||||||
(other.translation() / *scale).xy().into(),
|
(other.translation / *scale).xy().into(),
|
||||||
other_rotation.to_scaled_axis().z,
|
other.rotation.to_scaled_axis().z,
|
||||||
);
|
);
|
||||||
let closest =
|
let closest = bevy_rapier2d::parry::query::closest_points(
|
||||||
closest_points(&pos1, &*collider.raw, &pos2, &*other_collider.raw, f32::MAX).unwrap();
|
|
||||||
let distance = distance(
|
|
||||||
&pos1,
|
&pos1,
|
||||||
&*collider.raw,
|
&*collider.raw,
|
||||||
&pos2,
|
&pos2,
|
||||||
&*other_collider.raw,
|
&*other_collider.raw,
|
||||||
|
f32::MAX,
|
||||||
)
|
)
|
||||||
.unwrap() as u32;
|
.unwrap();
|
||||||
|
let distance: u32 = match closest {
|
||||||
|
ClosestPoints::Intersecting => 0,
|
||||||
|
ClosestPoints::WithinMargin(p1, p2) => na::distance(&p1, &p2) as u32,
|
||||||
|
ClosestPoints::Disjoint => 0,
|
||||||
|
};
|
||||||
let tile_or_tiles = if distance == 1 { "tile" } else { "tiles" };
|
let tile_or_tiles = if distance == 1 { "tile" } else { "tiles" };
|
||||||
if distance > 0 {
|
if distance > 0 {
|
||||||
if let ClosestPoints::WithinMargin(p1, p2) = closest {
|
if let ClosestPoints::WithinMargin(p1, p2) = closest {
|
||||||
|
|
|
@ -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,19 +13,6 @@ 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;
|
||||||
|
@ -47,75 +34,85 @@ pub struct FocusedExplorationType<T>(pub Option<T>);
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Mappable;
|
pub struct Mappable;
|
||||||
|
|
||||||
fn exploration_type_change<T, S>(
|
fn exploration_type_change<T, S, A: 'static>(
|
||||||
|
config: Res<ExplorationConfig<S, A>>,
|
||||||
mut tts: ResMut<Tts>,
|
mut tts: ResMut<Tts>,
|
||||||
mut explorers: Query<(
|
input: Res<InputMap<A>>,
|
||||||
&ActionState<ExplorationAction>,
|
mut explorers: Query<(&VisibleEntities, &mut FocusedExplorationType<T>)>,
|
||||||
&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,
|
||||||
{
|
{
|
||||||
for (actions, visible, mut focused) in explorers.iter_mut() {
|
if let (Some(select_next_type), Some(select_prev_type)) = (
|
||||||
let mut types: Vec<T> = vec![];
|
config.action_explore_select_next_type.clone(),
|
||||||
for e in visible.iter() {
|
config.action_explore_select_prev_type.clone(),
|
||||||
if let Ok(t) = features.get(*e) {
|
) {
|
||||||
types.push(*t);
|
let changed = input.just_active(select_next_type.clone())
|
||||||
}
|
|| input.just_active(select_prev_type.clone());
|
||||||
|
if !changed {
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
types.sort();
|
for (visible, mut focused) in explorers.iter_mut() {
|
||||||
types.dedup();
|
let mut types: Vec<T> = vec![];
|
||||||
if types.is_empty() {
|
for e in visible.iter() {
|
||||||
tts.speak("Nothing visible.", true)?;
|
if let Ok(t) = features.get(*e) {
|
||||||
} else if actions.just_pressed(ExplorationAction::SelectPrevType) {
|
types.push(*t);
|
||||||
if let Some(t) = &focused.0 {
|
}
|
||||||
if let Some(i) = types.iter().position(|v| *v == *t) {
|
}
|
||||||
if i == 0 {
|
types.sort();
|
||||||
focused.0 = None;
|
types.dedup();
|
||||||
|
if types.is_empty() {
|
||||||
|
tts.speak("Nothing visible.", true)?;
|
||||||
|
} else if input.just_active(select_prev_type.clone()) {
|
||||||
|
if let Some(t) = &focused.0 {
|
||||||
|
if let Some(i) = types.iter().position(|v| *v == *t) {
|
||||||
|
if i == 0 {
|
||||||
|
focused.0 = None;
|
||||||
|
} else {
|
||||||
|
let t = &types[i - 1];
|
||||||
|
focused.0 = Some(*t);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = &types[i - 1];
|
let t = types.last().unwrap();
|
||||||
focused.0 = Some(*t);
|
focused.0 = Some(*t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = types.last().unwrap();
|
let t = types.last().unwrap();
|
||||||
focused.0 = Some(*t);
|
focused.0 = Some(*t);
|
||||||
}
|
}
|
||||||
} else {
|
} else if input.just_active(select_next_type.clone()) {
|
||||||
let t = types.last().unwrap();
|
if let Some(t) = &focused.0 {
|
||||||
focused.0 = Some(*t);
|
if let Some(i) = types.iter().position(|v| *v == *t) {
|
||||||
}
|
if i == types.len() - 1 {
|
||||||
} else if actions.just_pressed(ExplorationAction::SelectNextType) {
|
focused.0 = None;
|
||||||
if let Some(t) = &focused.0 {
|
} else {
|
||||||
if let Some(i) = types.iter().position(|v| *v == *t) {
|
let t = &types[i + 1];
|
||||||
if i == types.len() - 1 {
|
focused.0 = Some(*t);
|
||||||
focused.0 = None;
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = &types[i + 1];
|
let t = types.first().unwrap();
|
||||||
focused.0 = Some(*t);
|
focused.0 = Some(*t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = types.first().unwrap();
|
let t = types.first().unwrap();
|
||||||
focused.0 = Some(*t);
|
focused.0 = Some(*t)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let t = types.first().unwrap();
|
|
||||||
focused.0 = Some(*t)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exploration_type_focus<T, S>(
|
fn exploration_type_focus<T, S, A>(
|
||||||
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>,
|
||||||
|
@ -125,46 +122,57 @@ fn exploration_type_focus<T, S>(
|
||||||
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,
|
||||||
{
|
{
|
||||||
for (entity, actions, visible_entities, focused_type, exploring) in explorers.iter() {
|
if let (Some(explore_focus_next), Some(explore_focus_prev)) = (
|
||||||
let mut features = features
|
config.action_explore_focus_next.clone(),
|
||||||
.iter()
|
config.action_explore_focus_prev.clone(),
|
||||||
.filter(|v| visible_entities.contains(&v.0))
|
) {
|
||||||
.map(|v| (v.1.floor(), v.2))
|
let changed = input.just_active(explore_focus_next.clone())
|
||||||
.collect::<Vec<((f32, f32), &T)>>();
|
|| input.just_active(explore_focus_prev.clone());
|
||||||
if features.is_empty() {
|
if !changed {
|
||||||
tts.speak("Nothing visible.", true)?;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
features.sort_by(|(c1, _), (c2, _)| c1.partial_cmp(c2).unwrap());
|
for (entity, visible_entities, focused_type, exploring) in explorers.iter() {
|
||||||
if let Some(focused) = &focused_type.0 {
|
let mut features = features
|
||||||
features.retain(|(_, t)| **t == *focused);
|
.iter()
|
||||||
}
|
.filter(|v| visible_entities.contains(&v.0))
|
||||||
let mut target: Option<&((f32, f32), &T)> = None;
|
.map(|v| (v.1.floor(), v.2))
|
||||||
if actions.just_pressed(ExplorationAction::FocusNext) {
|
.collect::<Vec<((f32, f32), &T)>>();
|
||||||
if let Some(exploring) = exploring {
|
if features.is_empty() {
|
||||||
target = features.iter().find(|(c, _)| *c > **exploring);
|
tts.speak("Nothing visible.", true)?;
|
||||||
if target.is_none() {
|
return Ok(());
|
||||||
|
}
|
||||||
|
features.sort_by(|(c1, _), (c2, _)| c1.partial_cmp(c2).unwrap());
|
||||||
|
if let Some(focused) = &focused_type.0 {
|
||||||
|
features.retain(|(_, t)| **t == *focused);
|
||||||
|
}
|
||||||
|
let mut target: Option<&((f32, f32), &T)> = None;
|
||||||
|
if input.just_active(explore_focus_next.clone()) {
|
||||||
|
if let Some(exploring) = exploring {
|
||||||
|
target = features.iter().find(|(c, _)| *c > **exploring);
|
||||||
|
if target.is_none() {
|
||||||
|
target = features.first();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
target = features.first();
|
target = features.first();
|
||||||
}
|
}
|
||||||
} else {
|
} else if input.just_active(explore_focus_prev.clone()) {
|
||||||
target = features.first();
|
if let Some(exploring) = exploring {
|
||||||
}
|
features.reverse();
|
||||||
} else if actions.just_pressed(ExplorationAction::FocusPrev) {
|
target = features.iter().find(|(c, _)| *c < **exploring);
|
||||||
if let Some(exploring) = exploring {
|
if target.is_none() {
|
||||||
features.reverse();
|
target = features.first();
|
||||||
target = features.iter().find(|(c, _)| *c < **exploring);
|
}
|
||||||
if target.is_none() {
|
} else {
|
||||||
target = features.first();
|
target = features.last();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
target = features.last();
|
|
||||||
}
|
}
|
||||||
}
|
if let Some((coordinates, _)) = target {
|
||||||
if let Some((coordinates, _)) = target {
|
commands
|
||||||
commands
|
.entity(entity)
|
||||||
.entity(entity)
|
.insert(Exploring(coordinates.floor()));
|
||||||
.insert(Exploring(coordinates.floor()));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -200,71 +208,83 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exploration_focus<S, D: 'static + Clone + Default + Send + Sync>(
|
fn exploration_focus<S, A: 'static, 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<
|
explorers: Query<(Entity, &Transform, Option<&Exploring>), With<Player>>,
|
||||||
(
|
|
||||||
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,
|
||||||
{
|
{
|
||||||
for (entity, actions, transform, exploring) in explorers.iter() {
|
if let (
|
||||||
let coordinates = transform.translation;
|
Some(explore_forward),
|
||||||
let mut exploring = if let Some(exploring) = exploring {
|
Some(explore_backward),
|
||||||
**exploring
|
Some(explore_left),
|
||||||
} else {
|
Some(explore_right),
|
||||||
let floor = coordinates.floor();
|
) = (
|
||||||
(floor.x, floor.y)
|
config.action_explore_forward.clone(),
|
||||||
};
|
config.action_explore_backward.clone(),
|
||||||
let orig = exploring;
|
config.action_explore_left.clone(),
|
||||||
if actions.just_pressed(ExplorationAction::Forward) {
|
config.action_explore_right.clone(),
|
||||||
exploring.1 += 1.;
|
) {
|
||||||
} else if actions.just_pressed(ExplorationAction::Backward) {
|
if let Ok((entity, transform, exploring)) = explorers.get_single() {
|
||||||
exploring.1 -= 1.;
|
let coordinates = transform.translation;
|
||||||
} else if actions.just_pressed(ExplorationAction::Left) {
|
let mut exploring = if let Some(exploring) = exploring {
|
||||||
exploring.0 -= 1.;
|
**exploring
|
||||||
} else if actions.just_pressed(ExplorationAction::Right) {
|
} else {
|
||||||
exploring.0 += 1.;
|
let floor = coordinates.floor();
|
||||||
}
|
(floor.x, floor.y)
|
||||||
let dimensions = if let Ok(map) = map.get_single() {
|
};
|
||||||
Some((map.width as f32, map.height as f32))
|
let orig = exploring;
|
||||||
} else {
|
if input.just_active(explore_forward) {
|
||||||
None
|
exploring.1 += 1.;
|
||||||
};
|
} else if input.just_active(explore_backward) {
|
||||||
if let Some((width, height)) = dimensions {
|
exploring.1 -= 1.;
|
||||||
if exploring.0 >= width || exploring.1 >= height {
|
} else if input.just_active(explore_left) {
|
||||||
return;
|
exploring.0 -= 1.;
|
||||||
|
} else if input.just_active(explore_right) {
|
||||||
|
exploring.0 += 1.;
|
||||||
|
}
|
||||||
|
let dimensions = if let Ok(map) = map.get_single() {
|
||||||
|
Some((map.width as f32, map.height as f32))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some((width, height)) = dimensions {
|
||||||
|
if exploring.0 >= width || exploring.1 >= height {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if orig != exploring && exploring.0 >= 0. && exploring.1 >= 0. {
|
||||||
|
commands.entity(entity).insert(Exploring(exploring));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if orig != exploring && exploring.0 >= 0. && exploring.1 >= 0. {
|
|
||||||
commands.entity(entity).insert(Exploring(exploring));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn navigate_to_explored<S, D: 'static + Clone + Default + Send + Sync>(
|
fn navigate_to_explored<S, A: 'static, 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, &ActionState<ExplorationAction>, &Exploring)>,
|
explorers: Query<(Entity, &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,
|
||||||
{
|
{
|
||||||
for (entity, actions, exploring) in explorers.iter() {
|
if let Some(navigate_to_explored) = config.action_navigate_to_explored.clone() {
|
||||||
for (map, revealed_tiles) in map.iter() {
|
for (entity, exploring) in explorers.iter() {
|
||||||
let point = **exploring;
|
for (map, revealed_tiles) in map.iter() {
|
||||||
let idx = point.to_index(map.width);
|
let point = **exploring;
|
||||||
let known = revealed_tiles[idx];
|
let idx = point.to_index(map.width);
|
||||||
if actions.just_pressed(ExplorationAction::NavigateTo) && known {
|
let known = revealed_tiles[idx];
|
||||||
commands
|
if input.just_active(navigate_to_explored.clone()) && known {
|
||||||
.entity(entity)
|
commands
|
||||||
.insert(Destination((point.x_i32(), point.y_i32())));
|
.entity(entity)
|
||||||
|
.insert(Destination((point.x_i32(), point.y_i32())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,14 +388,32 @@ fn cleanup(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ExplorationConfig<S> {
|
pub struct ExplorationConfig<S, A> {
|
||||||
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> Default for ExplorationConfig<S> {
|
impl<S, A> Default for ExplorationConfig<S, A> {
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -401,23 +439,22 @@ 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>>() {
|
if !app.world.contains_resource::<ExplorationConfig<S, A>>() {
|
||||||
app.insert_resource(ExplorationConfig::<S>::default());
|
app.insert_resource(ExplorationConfig::<S, A>::default());
|
||||||
}
|
}
|
||||||
let config = app
|
let config = app
|
||||||
.world
|
.world
|
||||||
.get_resource::<ExplorationConfig<S>>()
|
.get_resource::<ExplorationConfig<S, A>>()
|
||||||
.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, D>)
|
app.add_system(exploration_focus::<S, A, D>)
|
||||||
.add_system(exploration_type_focus::<T, S>.chain(error_handler))
|
.add_system(exploration_type_focus::<T, S, A>.chain(error_handler))
|
||||||
.add_system(exploration_type_change::<T, S>.chain(error_handler))
|
.add_system(exploration_type_change::<T, S, A>.chain(error_handler))
|
||||||
.add_system(navigate_to_explored::<S, D>)
|
.add_system(navigate_to_explored::<S, A, 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),
|
||||||
|
@ -427,10 +464,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, D>)
|
.with_system(exploration_focus::<S, A, D>)
|
||||||
.with_system(exploration_type_focus::<T, S>.chain(error_handler))
|
.with_system(exploration_type_focus::<T, S, A>.chain(error_handler))
|
||||||
.with_system(exploration_type_change::<T, S>.chain(error_handler))
|
.with_system(exploration_type_change::<T, S, A>.chain(error_handler))
|
||||||
.with_system(navigate_to_explored::<S, D>)
|
.with_system(navigate_to_explored::<S, A, D>)
|
||||||
.with_system(
|
.with_system(
|
||||||
exploration_type_changed_announcement::<T>.chain(error_handler),
|
exploration_type_changed_announcement::<T>.chain(error_handler),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#![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;
|
||||||
|
@ -14,7 +15,6 @@ 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,17 +15,6 @@ 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);
|
||||||
|
@ -57,15 +46,15 @@ pub struct Sprinting;
|
||||||
#[derive(Default, Deref, DerefMut)]
|
#[derive(Default, Deref, DerefMut)]
|
||||||
struct Snapping(bool);
|
struct Snapping(bool);
|
||||||
|
|
||||||
fn movement_controls<S>(
|
fn movement_controls<S, A: 'static>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
config: Res<NavigationConfig<S>>,
|
config: Res<NavigationConfig<S, A>>,
|
||||||
|
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,
|
||||||
|
@ -78,133 +67,197 @@ fn movement_controls<S>(
|
||||||
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 (
|
for (entity, mut velocity, mut speed, max_speed, rotation_speed, mut transform, destination) in
|
||||||
entity,
|
query.iter_mut()
|
||||||
actions,
|
|
||||||
mut velocity,
|
|
||||||
mut speed,
|
|
||||||
max_speed,
|
|
||||||
rotation_speed,
|
|
||||||
mut transform,
|
|
||||||
destination,
|
|
||||||
) in &mut query
|
|
||||||
{
|
{
|
||||||
if **snapping {
|
if **snapping {
|
||||||
if actions.pressed(NavigationAction::Rotate) {
|
if let (Some(_), Some(rotate_left), Some(rotate_right)) = (
|
||||||
continue;
|
rotation_speed,
|
||||||
|
config.action_rotate_left,
|
||||||
|
config.action_rotate_right,
|
||||||
|
) {
|
||||||
|
if input.active(rotate_left) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if input.active(rotate_right) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
**snapping = false;
|
**snapping = false;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let sprinting = actions.pressed(NavigationAction::Sprint);
|
let sprinting = if let Some(action) = config.action_sprint {
|
||||||
|
input.active(action)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
if sprinting {
|
if sprinting {
|
||||||
commands.entity(entity).insert(Sprinting);
|
commands.entity(entity).insert(Sprinting::default());
|
||||||
} else {
|
} else {
|
||||||
commands.entity(entity).remove::<Sprinting>();
|
commands.entity(entity).remove::<Sprinting>();
|
||||||
}
|
}
|
||||||
let mut cleanup = false;
|
let mut direction = Vec2::default();
|
||||||
if actions.pressed(NavigationAction::Move) {
|
if let Some(action) = config.action_forward {
|
||||||
if let Some(pair) = actions.clamped_axis_pair(NavigationAction::Move) {
|
if input.active(action) {
|
||||||
cleanup = true;
|
direction.x += 1.;
|
||||||
let mut direction = pair.xy();
|
|
||||||
let strength = direction.length();
|
|
||||||
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 s = if sprinting {
|
|
||||||
**max_speed
|
|
||||||
} else {
|
|
||||||
**max_speed / config.sprint_movement_factor
|
|
||||||
};
|
|
||||||
**speed = s;
|
|
||||||
direction *= strength;
|
|
||||||
let mut v = direction * **speed;
|
|
||||||
v = transform
|
|
||||||
.compute_matrix()
|
|
||||||
.transform_vector3(v.extend(0.))
|
|
||||||
.truncate();
|
|
||||||
velocity.linvel = v;
|
|
||||||
}
|
}
|
||||||
} else if destination.is_none() {
|
|
||||||
velocity.linvel = Vec2::ZERO;
|
|
||||||
}
|
}
|
||||||
if let Some(rotation_speed) = rotation_speed {
|
if let Some(action) = config.action_backward {
|
||||||
if actions.pressed(NavigationAction::Rotate) {
|
if input.active(action) {
|
||||||
cleanup = true;
|
direction.x -= 1.;
|
||||||
let delta = rotation_speed.radians()
|
}
|
||||||
* time.delta_seconds()
|
}
|
||||||
* actions.clamped_value(NavigationAction::Rotate);
|
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));
|
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 {
|
} else {
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
}
|
}
|
||||||
if cleanup {
|
if direction.length_squared() != 0. {
|
||||||
commands.entity(entity).remove::<Destination>();
|
commands.entity(entity).remove::<Destination>();
|
||||||
commands.entity(entity).remove::<Exploring>();
|
commands.entity(entity).remove::<Exploring>();
|
||||||
for entity in exploration_focused.iter() {
|
for entity in exploration_focused.iter() {
|
||||||
commands.entity(entity).remove::<ExplorationFocused>();
|
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,
|
||||||
|
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 {
|
||||||
|
**max_speed
|
||||||
|
} else {
|
||||||
|
**max_speed / config.sprint_movement_factor
|
||||||
|
};
|
||||||
|
**speed = s;
|
||||||
|
if let Some(strength) = strength {
|
||||||
|
direction *= strength;
|
||||||
|
}
|
||||||
|
let mut v = direction * **speed;
|
||||||
|
v = transform
|
||||||
|
.compute_matrix()
|
||||||
|
.transform_vector3(v.extend(0.))
|
||||||
|
.truncate();
|
||||||
|
velocity.linvel = v;
|
||||||
|
} else if destination.is_none() {
|
||||||
|
velocity.linvel = Vec2::ZERO;
|
||||||
|
**speed = 0.;
|
||||||
|
} else if sprinting {
|
||||||
|
**speed = **max_speed;
|
||||||
|
} else {
|
||||||
|
velocity.linvel = Vec2::ZERO;
|
||||||
|
**speed = **max_speed / 3.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snap<S>(
|
fn snap<S, A: 'static>(
|
||||||
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 query: Query<(&mut Transform, &mut Velocity, &CardinalDirection), With<Player>>,
|
||||||
(
|
|
||||||
&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,
|
||||||
{
|
{
|
||||||
for (actions, mut transform, mut velocity, direction) in query.iter_mut() {
|
if let Some(action) = config.action_snap_left.clone() {
|
||||||
if actions.just_pressed(NavigationAction::SnapLeft) {
|
if input.just_active(action) {
|
||||||
**snapping = true;
|
for (mut position, mut velocity, direction) in query.iter_mut() {
|
||||||
transform.rotation = Quat::from_rotation_z(match direction {
|
**snapping = true;
|
||||||
CardinalDirection::North => PI,
|
position.rotation = Quat::from_rotation_z(match direction {
|
||||||
CardinalDirection::East => PI / 2.,
|
CardinalDirection::North => PI,
|
||||||
CardinalDirection::South => 0.,
|
CardinalDirection::East => PI / 2.,
|
||||||
CardinalDirection::West => PI * 1.5,
|
CardinalDirection::South => 0.,
|
||||||
});
|
CardinalDirection::West => PI * 1.5,
|
||||||
velocity.angvel = 0.;
|
});
|
||||||
|
velocity.angvel = 0.;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if actions.just_pressed(NavigationAction::SnapRight) {
|
}
|
||||||
**snapping = true;
|
if let Some(action) = config.action_snap_right.clone() {
|
||||||
transform.rotation = Quat::from_rotation_z(match direction {
|
if input.just_active(action) {
|
||||||
CardinalDirection::North => 0.,
|
for (mut transform, mut velocity, direction) in query.iter_mut() {
|
||||||
CardinalDirection::East => PI * 1.5,
|
**snapping = true;
|
||||||
CardinalDirection::South => PI,
|
transform.rotation = Quat::from_rotation_z(match direction {
|
||||||
CardinalDirection::West => PI / 2.,
|
CardinalDirection::North => 0.,
|
||||||
});
|
CardinalDirection::East => PI * 1.5,
|
||||||
velocity.angvel = 0.;
|
CardinalDirection::South => PI,
|
||||||
|
CardinalDirection::West => PI / 2.,
|
||||||
|
});
|
||||||
|
velocity.angvel = 0.;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if actions.just_pressed(NavigationAction::SnapCardinal) {
|
}
|
||||||
**snapping = true;
|
if let Some(action) = config.action_snap_cardinal.clone() {
|
||||||
let yaw: Angle = direction.into();
|
if input.just_active(action) {
|
||||||
let yaw = yaw.radians();
|
for (mut transform, mut velocity, direction) in query.iter_mut() {
|
||||||
transform.rotation = Quat::from_rotation_z(yaw);
|
**snapping = true;
|
||||||
velocity.angvel = 0.;
|
let yaw: Angle = direction.into();
|
||||||
tts.speak(direction.to_string(), true)?;
|
let yaw = yaw.radians();
|
||||||
|
transform.rotation = Quat::from_rotation_z(yaw);
|
||||||
|
velocity.angvel = 0.;
|
||||||
|
tts.speak(direction.to_string(), true)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if actions.just_pressed(NavigationAction::SnapReverse) {
|
}
|
||||||
**snapping = true;
|
if let Some(action) = config.action_snap_reverse.clone() {
|
||||||
transform.rotate(Quat::from_rotation_z(PI));
|
if input.just_active(action) {
|
||||||
velocity.angvel = 0.;
|
for (mut transform, mut velocity, _) in query.iter_mut() {
|
||||||
|
**snapping = true;
|
||||||
|
transform.rotate(Quat::from_rotation_z(PI));
|
||||||
|
velocity.angvel = 0.;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -275,7 +328,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>>,
|
config: Res<NavigationConfig<S, A>>,
|
||||||
mut log: Query<&mut Log>,
|
mut log: Query<&mut Log>,
|
||||||
) where
|
) where
|
||||||
S: 'static + Send + Sync,
|
S: 'static + Send + Sync,
|
||||||
|
@ -316,7 +369,18 @@ fn log_area_descriptions<S, A>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NavigationConfig<S> {
|
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>,
|
||||||
|
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,
|
||||||
|
@ -326,9 +390,20 @@ pub struct NavigationConfig<S> {
|
||||||
pub log_area_descriptions: bool,
|
pub log_area_descriptions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Default for NavigationConfig<S> {
|
impl<S, A> Default for NavigationConfig<S, A> {
|
||||||
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.,
|
||||||
|
@ -354,12 +429,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>>() {
|
if !app.world.contains_resource::<NavigationConfig<S, A>>() {
|
||||||
app.insert_resource(NavigationConfig::<S>::default());
|
app.insert_resource(NavigationConfig::<S, A>::default());
|
||||||
}
|
}
|
||||||
let config = app
|
let config = app
|
||||||
.world
|
.world
|
||||||
.get_resource::<NavigationConfig<S>>()
|
.get_resource::<NavigationConfig<S, A>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
const SNAP: &str = "SNAP";
|
const SNAP: &str = "SNAP";
|
||||||
|
@ -367,7 +442,6 @@ 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))
|
||||||
|
@ -376,9 +450,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>.label(MOVEMENT_CONTROLS))
|
app.add_system(movement_controls::<S, A>.label(MOVEMENT_CONTROLS))
|
||||||
.add_system(
|
.add_system(
|
||||||
snap::<S>
|
snap::<S, A>
|
||||||
.chain(error_handler)
|
.chain(error_handler)
|
||||||
.label(SNAP)
|
.label(SNAP)
|
||||||
.before(MOVEMENT_CONTROLS),
|
.before(MOVEMENT_CONTROLS),
|
||||||
|
@ -388,9 +462,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>.label(MOVEMENT_CONTROLS))
|
.with_system(movement_controls::<S, A>.label(MOVEMENT_CONTROLS))
|
||||||
.with_system(
|
.with_system(
|
||||||
snap::<S>
|
snap::<S, A>
|
||||||
.chain(error_handler)
|
.chain(error_handler)
|
||||||
.label(SNAP)
|
.label(SNAP)
|
||||||
.before(MOVEMENT_CONTROLS),
|
.before(MOVEMENT_CONTROLS),
|
||||||
|
|
|
@ -110,6 +110,7 @@ fn find_path_for_shape<D: 'static + Clone + Default + Send + Sync>(
|
||||||
|
|
||||||
fn calculate_path<D: 'static + Clone + Default + Send + Sync>(
|
fn calculate_path<D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
pool: Res<AsyncComputeTaskPool>,
|
||||||
rapier_context: Res<RapierContext>,
|
rapier_context: Res<RapierContext>,
|
||||||
obstructions: Query<&RapierColliderHandle, With<MapObstruction>>,
|
obstructions: Query<&RapierColliderHandle, With<MapObstruction>>,
|
||||||
query: Query<
|
query: Query<
|
||||||
|
@ -156,7 +157,6 @@ fn calculate_path<D: 'static + Clone + Default + Send + Sync>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let shape_clone = (*shape).clone();
|
let shape_clone = (*shape).clone();
|
||||||
let pool = AsyncComputeTaskPool::get();
|
|
||||||
let task = pool.spawn(async move {
|
let task = pool.spawn(async move {
|
||||||
find_path_for_shape(
|
find_path_for_shape(
|
||||||
handle_clone.0,
|
handle_clone.0,
|
||||||
|
|
|
@ -35,12 +35,12 @@ fn tag_behind(
|
||||||
) {
|
) {
|
||||||
if config.downshift_behind {
|
if config.downshift_behind {
|
||||||
if let Ok(listener_transform) = listener.get_single() {
|
if let Ok(listener_transform) = listener.get_single() {
|
||||||
let listener_forward = listener_transform.forward();
|
let listener_forward = listener_transform.local_x();
|
||||||
for (entity, transform, sound, icon) in sounds.iter() {
|
for (entity, transform, sound, icon) in sounds.iter() {
|
||||||
if icon.is_none() && sound.is_none() {
|
if icon.is_none() && sound.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let v = transform.translation() - listener_transform.translation();
|
let v = transform.translation - listener_transform.translation;
|
||||||
let dot = v.dot(listener_forward);
|
let dot = v.dot(listener_forward);
|
||||||
let is_behind = dot <= 0.;
|
let is_behind = dot <= 0.;
|
||||||
if is_behind {
|
if is_behind {
|
||||||
|
|
|
@ -63,14 +63,14 @@ impl Viewshed {
|
||||||
let mut boxes = vec![];
|
let mut boxes = vec![];
|
||||||
let shape = Collider::cuboid(self.range as f32, self.range as f32);
|
let shape = Collider::cuboid(self.range as f32, self.range as f32);
|
||||||
rapier_context.intersections_with_shape(
|
rapier_context.intersections_with_shape(
|
||||||
start.translation().truncate(),
|
start.translation.truncate(),
|
||||||
0.,
|
0.,
|
||||||
&shape,
|
&shape,
|
||||||
QueryFilter::new().predicate(&|e| visible_query.get(e).is_ok()),
|
QueryFilter::new().predicate(&|e| visible_query.get(e).is_ok()),
|
||||||
|entity| {
|
|entity| {
|
||||||
if let Ok((_, collider, transform)) = visible_query.get(entity) {
|
if let Ok((_, collider, transform)) = visible_query.get(entity) {
|
||||||
let position = Isometry2::new(
|
let position = Isometry2::new(
|
||||||
Vector2::new(transform.translation().x, transform.translation().y),
|
Vector2::new(transform.translation.x, transform.translation.y),
|
||||||
0.,
|
0.,
|
||||||
);
|
);
|
||||||
// println!(
|
// println!(
|
||||||
|
@ -88,8 +88,8 @@ impl Viewshed {
|
||||||
let shape = Collider::cuboid(0.5, 0.5);
|
let shape = Collider::cuboid(0.5, 0.5);
|
||||||
let mut new_visible_entities = HashSet::new();
|
let mut new_visible_entities = HashSet::new();
|
||||||
let size = (
|
let size = (
|
||||||
(start.translation().x.abs() + self.range as f32) as u32,
|
(start.translation.x.abs() + self.range as f32) as u32,
|
||||||
(start.translation().y.abs() + self.range as f32) as u32,
|
(start.translation.y.abs() + self.range as f32) as u32,
|
||||||
);
|
);
|
||||||
let visibility_grid = VisibilityGrid(
|
let visibility_grid = VisibilityGrid(
|
||||||
size,
|
size,
|
||||||
|
@ -410,7 +410,7 @@ fn log_visible(
|
||||||
{
|
{
|
||||||
viewer_transform.collider_direction_and_distance(
|
viewer_transform.collider_direction_and_distance(
|
||||||
viewer_collider,
|
viewer_collider,
|
||||||
viewed_transform,
|
&viewed_transform,
|
||||||
viewed_collider,
|
viewed_collider,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user