Compare commits
5 Commits
da413ef4e4
...
627f63211d
Author | SHA1 | Date | |
---|---|---|---|
627f63211d | |||
c0a1befd98 | |||
4495174be0 | |||
5f14a90b92 | |||
2ff8a858ed |
12
Cargo.toml
12
Cargo.toml
|
@ -11,7 +11,7 @@ edition = "2021"
|
|||
speech_dispatcher_0_10 = ["bevy_tts/speech_dispatcher_0_10"]
|
||||
|
||||
[dependencies.bevy]
|
||||
version = "0.7"
|
||||
version = "0.8"
|
||||
default-features = false
|
||||
features = [
|
||||
"bevy_gilrs",
|
||||
|
@ -24,14 +24,14 @@ features = [
|
|||
|
||||
[dependencies]
|
||||
backtrace = "0.3"
|
||||
bevy_input_actionmap = { git = "https://github.com/lightsoutgames/bevy_input_actionmap" }
|
||||
bevy_rapier2d = { version = "0.15", features = ["serde-serialize"] }
|
||||
bevy_synthizer = { git = "https://labs.lightsout.games/projects/bevy_synthizer" }
|
||||
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"] }
|
||||
bevy_rapier2d = { version = "0.16", features = ["serde-serialize"] }
|
||||
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" }
|
||||
coord_2d = "0.3"
|
||||
futures-lite = "1"
|
||||
gilrs = "0.8"
|
||||
gilrs = "0.9"
|
||||
here_be_dragons = "0.1"
|
||||
leafwing_input_manager = { git = "https://github.com/ndarilek/leafwing-input-manager", branch = "dev" }
|
||||
maze_generator = "2"
|
||||
once_cell = "1"
|
||||
pathfinding = "3"
|
||||
|
|
33
src/core.rs
33
src/core.rs
|
@ -7,10 +7,9 @@ use std::{
|
|||
sync::RwLock,
|
||||
};
|
||||
|
||||
use bevy::{core::FloatOrd, ecs::query::WorldQuery, prelude::*};
|
||||
use bevy::{ecs::query::WorldQuery, prelude::*, utils::FloatOrd};
|
||||
use bevy_rapier2d::{
|
||||
na,
|
||||
parry::query::ClosestPoints,
|
||||
parry::query::{closest_points, distance, ClosestPoints},
|
||||
prelude::*,
|
||||
rapier::{math::Isometry, prelude::AABB},
|
||||
};
|
||||
|
@ -391,11 +390,11 @@ impl PointLike for &Transform {
|
|||
|
||||
impl PointLike for GlobalTransform {
|
||||
fn x(&self) -> f32 {
|
||||
self.translation.x
|
||||
self.translation().x
|
||||
}
|
||||
|
||||
fn y(&self) -> f32 {
|
||||
self.translation.y
|
||||
self.translation().y
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,7 +481,7 @@ pub trait GlobalTransformExt {
|
|||
|
||||
impl GlobalTransformExt for GlobalTransform {
|
||||
fn yaw(&self) -> Angle {
|
||||
let forward = self.local_x();
|
||||
let forward = self.right();
|
||||
Angle::Radians(forward.y.atan2(forward.x))
|
||||
}
|
||||
|
||||
|
@ -494,27 +493,25 @@ impl GlobalTransformExt for GlobalTransform {
|
|||
) -> String {
|
||||
use bevy::math::Vec3Swizzles;
|
||||
let scale = PHYSICS_SCALE.read().unwrap();
|
||||
let (_, rotation, _) = self.to_scale_rotation_translation();
|
||||
let pos1 = Isometry::new(
|
||||
(self.translation / *scale).xy().into(),
|
||||
self.rotation.to_scaled_axis().z,
|
||||
(self.translation() / *scale).xy().into(),
|
||||
rotation.to_scaled_axis().z,
|
||||
);
|
||||
let (_, other_rotation, _) = self.to_scale_rotation_translation();
|
||||
let pos2 = Isometry::new(
|
||||
(other.translation / *scale).xy().into(),
|
||||
other.rotation.to_scaled_axis().z,
|
||||
(other.translation() / *scale).xy().into(),
|
||||
other_rotation.to_scaled_axis().z,
|
||||
);
|
||||
let closest = bevy_rapier2d::parry::query::closest_points(
|
||||
let closest =
|
||||
closest_points(&pos1, &*collider.raw, &pos2, &*other_collider.raw, f32::MAX).unwrap();
|
||||
let distance = distance(
|
||||
&pos1,
|
||||
&*collider.raw,
|
||||
&pos2,
|
||||
&*other_collider.raw,
|
||||
f32::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
let distance: u32 = match closest {
|
||||
ClosestPoints::Intersecting => 0,
|
||||
ClosestPoints::WithinMargin(p1, p2) => na::distance(&p1, &p2) as u32,
|
||||
ClosestPoints::Disjoint => 0,
|
||||
};
|
||||
.unwrap() as u32;
|
||||
let tile_or_tiles = if distance == 1 { "tile" } else { "tiles" };
|
||||
if distance > 0 {
|
||||
if let ClosestPoints::WithinMargin(p1, p2) = closest {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::{error::Error, fmt::Debug, hash::Hash, marker::PhantomData};
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_input_actionmap::InputMap;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
use bevy_tts::Tts;
|
||||
use leafwing_input_manager::prelude::*;
|
||||
|
||||
use crate::{
|
||||
core::{Player, PointLike},
|
||||
|
@ -13,6 +13,19 @@ use crate::{
|
|||
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)]
|
||||
#[reflect(Component)]
|
||||
pub struct Explorable;
|
||||
|
@ -34,85 +47,75 @@ pub struct FocusedExplorationType<T>(pub Option<T>);
|
|||
#[reflect(Component)]
|
||||
pub struct Mappable;
|
||||
|
||||
fn exploration_type_change<T, S, A: 'static>(
|
||||
config: Res<ExplorationConfig<S, A>>,
|
||||
fn exploration_type_change<T, S>(
|
||||
mut tts: ResMut<Tts>,
|
||||
input: Res<InputMap<A>>,
|
||||
mut explorers: Query<(&VisibleEntities, &mut FocusedExplorationType<T>)>,
|
||||
mut explorers: Query<(
|
||||
&ActionState<ExplorationAction>,
|
||||
&VisibleEntities,
|
||||
&mut FocusedExplorationType<T>,
|
||||
)>,
|
||||
features: Query<&T>,
|
||||
) -> Result<(), Box<dyn Error>>
|
||||
where
|
||||
T: Component + Copy + Ord,
|
||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||
A: Hash + Eq + Clone + Send + Sync,
|
||||
{
|
||||
if let (Some(select_next_type), Some(select_prev_type)) = (
|
||||
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![];
|
||||
for e in visible.iter() {
|
||||
if let Ok(t) = features.get(*e) {
|
||||
types.push(*t);
|
||||
}
|
||||
for (actions, visible, mut focused) in explorers.iter_mut() {
|
||||
let mut types: Vec<T> = vec![];
|
||||
for e in visible.iter() {
|
||||
if let Ok(t) = features.get(*e) {
|
||||
types.push(*t);
|
||||
}
|
||||
types.sort();
|
||||
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);
|
||||
}
|
||||
}
|
||||
types.sort();
|
||||
types.dedup();
|
||||
if types.is_empty() {
|
||||
tts.speak("Nothing visible.", true)?;
|
||||
} else if actions.just_pressed(ExplorationAction::SelectPrevType) {
|
||||
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.last().unwrap();
|
||||
let t = &types[i - 1];
|
||||
focused.0 = Some(*t);
|
||||
}
|
||||
} else {
|
||||
let t = types.last().unwrap();
|
||||
focused.0 = Some(*t);
|
||||
}
|
||||
} else if input.just_active(select_next_type.clone()) {
|
||||
if let Some(t) = &focused.0 {
|
||||
if let Some(i) = types.iter().position(|v| *v == *t) {
|
||||
if i == types.len() - 1 {
|
||||
focused.0 = None;
|
||||
} else {
|
||||
let t = &types[i + 1];
|
||||
focused.0 = Some(*t);
|
||||
}
|
||||
} else {
|
||||
let t = types.last().unwrap();
|
||||
focused.0 = Some(*t);
|
||||
}
|
||||
} else if actions.just_pressed(ExplorationAction::SelectNextType) {
|
||||
if let Some(t) = &focused.0 {
|
||||
if let Some(i) = types.iter().position(|v| *v == *t) {
|
||||
if i == types.len() - 1 {
|
||||
focused.0 = None;
|
||||
} else {
|
||||
let t = types.first().unwrap();
|
||||
let t = &types[i + 1];
|
||||
focused.0 = Some(*t);
|
||||
}
|
||||
} else {
|
||||
let t = types.first().unwrap();
|
||||
focused.0 = Some(*t)
|
||||
focused.0 = Some(*t);
|
||||
}
|
||||
} else {
|
||||
let t = types.first().unwrap();
|
||||
focused.0 = Some(*t)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exploration_type_focus<T, S, A>(
|
||||
fn exploration_type_focus<T, S>(
|
||||
mut commands: Commands,
|
||||
config: Res<ExplorationConfig<S, A>>,
|
||||
input: Res<InputMap<A>>,
|
||||
mut tts: ResMut<Tts>,
|
||||
explorers: Query<(
|
||||
Entity,
|
||||
&ActionState<ExplorationAction>,
|
||||
&VisibleEntities,
|
||||
&FocusedExplorationType<T>,
|
||||
Option<&Exploring>,
|
||||
|
@ -122,57 +125,46 @@ fn exploration_type_focus<T, S, A>(
|
|||
where
|
||||
T: Component + PartialEq,
|
||||
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)) = (
|
||||
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 {
|
||||
for (entity, actions, visible_entities, focused_type, exploring) in explorers.iter() {
|
||||
let mut features = features
|
||||
.iter()
|
||||
.filter(|v| visible_entities.contains(&v.0))
|
||||
.map(|v| (v.1.floor(), v.2))
|
||||
.collect::<Vec<((f32, f32), &T)>>();
|
||||
if features.is_empty() {
|
||||
tts.speak("Nothing visible.", true)?;
|
||||
return Ok(());
|
||||
}
|
||||
for (entity, visible_entities, focused_type, exploring) in explorers.iter() {
|
||||
let mut features = features
|
||||
.iter()
|
||||
.filter(|v| visible_entities.contains(&v.0))
|
||||
.map(|v| (v.1.floor(), v.2))
|
||||
.collect::<Vec<((f32, f32), &T)>>();
|
||||
if features.is_empty() {
|
||||
tts.speak("Nothing visible.", true)?;
|
||||
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 {
|
||||
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 actions.just_pressed(ExplorationAction::FocusNext) {
|
||||
if let Some(exploring) = exploring {
|
||||
target = features.iter().find(|(c, _)| *c > **exploring);
|
||||
if target.is_none() {
|
||||
target = features.first();
|
||||
}
|
||||
} else if input.just_active(explore_focus_prev.clone()) {
|
||||
if let Some(exploring) = exploring {
|
||||
features.reverse();
|
||||
target = features.iter().find(|(c, _)| *c < **exploring);
|
||||
if target.is_none() {
|
||||
target = features.first();
|
||||
}
|
||||
} else {
|
||||
target = features.last();
|
||||
} else {
|
||||
target = features.first();
|
||||
}
|
||||
} else if actions.just_pressed(ExplorationAction::FocusPrev) {
|
||||
if let Some(exploring) = exploring {
|
||||
features.reverse();
|
||||
target = features.iter().find(|(c, _)| *c < **exploring);
|
||||
if target.is_none() {
|
||||
target = features.first();
|
||||
}
|
||||
} else {
|
||||
target = features.last();
|
||||
}
|
||||
if let Some((coordinates, _)) = target {
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(Exploring(coordinates.floor()));
|
||||
}
|
||||
}
|
||||
if let Some((coordinates, _)) = target {
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(Exploring(coordinates.floor()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -208,83 +200,71 @@ where
|
|||
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,
|
||||
config: Res<ExplorationConfig<S, A>>,
|
||||
input: Res<InputMap<A>>,
|
||||
map: Query<&Map<D>>,
|
||||
explorers: Query<(Entity, &Transform, Option<&Exploring>), With<Player>>,
|
||||
explorers: Query<
|
||||
(
|
||||
Entity,
|
||||
&ActionState<ExplorationAction>,
|
||||
&Transform,
|
||||
Option<&Exploring>,
|
||||
),
|
||||
With<Player>,
|
||||
>,
|
||||
) where
|
||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||
A: Hash + Eq + Clone + Send + Sync,
|
||||
{
|
||||
if let (
|
||||
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 mut exploring = if let Some(exploring) = exploring {
|
||||
**exploring
|
||||
} else {
|
||||
let floor = coordinates.floor();
|
||||
(floor.x, floor.y)
|
||||
};
|
||||
let orig = exploring;
|
||||
if input.just_active(explore_forward) {
|
||||
exploring.1 += 1.;
|
||||
} else if input.just_active(explore_backward) {
|
||||
exploring.1 -= 1.;
|
||||
} else if input.just_active(explore_left) {
|
||||
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));
|
||||
for (entity, actions, transform, exploring) in explorers.iter() {
|
||||
let coordinates = transform.translation;
|
||||
let mut exploring = if let Some(exploring) = exploring {
|
||||
**exploring
|
||||
} else {
|
||||
let floor = coordinates.floor();
|
||||
(floor.x, floor.y)
|
||||
};
|
||||
let orig = exploring;
|
||||
if actions.just_pressed(ExplorationAction::Forward) {
|
||||
exploring.1 += 1.;
|
||||
} else if actions.just_pressed(ExplorationAction::Backward) {
|
||||
exploring.1 -= 1.;
|
||||
} else if actions.just_pressed(ExplorationAction::Left) {
|
||||
exploring.0 -= 1.;
|
||||
} else if actions.just_pressed(ExplorationAction::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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
config: Res<ExplorationConfig<S, A>>,
|
||||
input: Res<InputMap<A>>,
|
||||
map: Query<(&Map<D>, &RevealedTiles)>,
|
||||
explorers: Query<(Entity, &Exploring)>,
|
||||
explorers: Query<(Entity, &ActionState<ExplorationAction>, &Exploring)>,
|
||||
) where
|
||||
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, exploring) in explorers.iter() {
|
||||
for (map, revealed_tiles) in map.iter() {
|
||||
let point = **exploring;
|
||||
let idx = point.to_index(map.width);
|
||||
let known = revealed_tiles[idx];
|
||||
if input.just_active(navigate_to_explored.clone()) && known {
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(Destination((point.x_i32(), point.y_i32())));
|
||||
}
|
||||
for (entity, actions, exploring) in explorers.iter() {
|
||||
for (map, revealed_tiles) in map.iter() {
|
||||
let point = **exploring;
|
||||
let idx = point.to_index(map.width);
|
||||
let known = revealed_tiles[idx];
|
||||
if actions.just_pressed(ExplorationAction::NavigateTo) && known {
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(Destination((point.x_i32(), point.y_i32())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -388,32 +368,14 @@ fn cleanup(
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExplorationConfig<S, A> {
|
||||
pub struct ExplorationConfig<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 {
|
||||
Self {
|
||||
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,
|
||||
{
|
||||
fn build(&self, app: &mut App) {
|
||||
if !app.world.contains_resource::<ExplorationConfig<S, A>>() {
|
||||
app.insert_resource(ExplorationConfig::<S, A>::default());
|
||||
if !app.world.contains_resource::<ExplorationConfig<S>>() {
|
||||
app.insert_resource(ExplorationConfig::<S>::default());
|
||||
}
|
||||
let config = app
|
||||
.world
|
||||
.get_resource::<ExplorationConfig<S, A>>()
|
||||
.get_resource::<ExplorationConfig<S>>()
|
||||
.unwrap()
|
||||
.clone();
|
||||
app.register_type::<ExplorationFocused>()
|
||||
.register_type::<Mappable>()
|
||||
.add_plugin(InputManagerPlugin::<ExplorationAction>::default())
|
||||
.add_system(exploration_changed_announcement::<T, D>.chain(error_handler));
|
||||
if config.exploration_control_states.is_empty() {
|
||||
app.add_system(exploration_focus::<S, A, D>)
|
||||
.add_system(exploration_type_focus::<T, S, A>.chain(error_handler))
|
||||
.add_system(exploration_type_change::<T, S, A>.chain(error_handler))
|
||||
.add_system(navigate_to_explored::<S, A, D>)
|
||||
app.add_system(exploration_focus::<S, D>)
|
||||
.add_system(exploration_type_focus::<T, S>.chain(error_handler))
|
||||
.add_system(exploration_type_change::<T, S>.chain(error_handler))
|
||||
.add_system(navigate_to_explored::<S, D>)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
exploration_type_changed_announcement::<T>.chain(error_handler),
|
||||
|
@ -464,10 +427,10 @@ where
|
|||
for state in states {
|
||||
app.add_system_set(
|
||||
SystemSet::on_update(state.clone())
|
||||
.with_system(exploration_focus::<S, A, D>)
|
||||
.with_system(exploration_type_focus::<T, S, A>.chain(error_handler))
|
||||
.with_system(exploration_type_change::<T, S, A>.chain(error_handler))
|
||||
.with_system(navigate_to_explored::<S, A, D>)
|
||||
.with_system(exploration_focus::<S, D>)
|
||||
.with_system(exploration_type_focus::<T, S>.chain(error_handler))
|
||||
.with_system(exploration_type_change::<T, S>.chain(error_handler))
|
||||
.with_system(navigate_to_explored::<S, D>)
|
||||
.with_system(
|
||||
exploration_type_changed_announcement::<T>.chain(error_handler),
|
||||
),
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
pub use bevy_input_actionmap;
|
||||
pub use bevy_rapier2d;
|
||||
pub use bevy_synthizer;
|
||||
pub use bevy_tts;
|
||||
|
@ -15,6 +14,7 @@ pub mod exploration;
|
|||
pub use futures_lite;
|
||||
pub use gilrs;
|
||||
pub use here_be_dragons as mapgen;
|
||||
pub use leafwing_input_manager;
|
||||
pub mod log;
|
||||
pub mod map;
|
||||
pub mod navigation;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::{error::Error, f32::consts::PI, fmt::Debug, hash::Hash, marker::PhantomData};
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_input_actionmap::InputMap;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
use bevy_tts::Tts;
|
||||
use leafwing_input_manager::prelude::*;
|
||||
|
||||
use crate::{
|
||||
commands::RunIfExistsExt,
|
||||
|
@ -15,6 +15,17 @@ use crate::{
|
|||
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)]
|
||||
#[reflect(Component)]
|
||||
pub struct MaxSpeed(pub f32);
|
||||
|
@ -46,15 +57,15 @@ pub struct Sprinting;
|
|||
#[derive(Default, Deref, DerefMut)]
|
||||
struct Snapping(bool);
|
||||
|
||||
fn movement_controls<S, A: 'static>(
|
||||
fn movement_controls<S>(
|
||||
mut commands: Commands,
|
||||
config: Res<NavigationConfig<S, A>>,
|
||||
input: Res<InputMap<A>>,
|
||||
config: Res<NavigationConfig<S>>,
|
||||
time: Res<Time>,
|
||||
mut snapping: ResMut<Snapping>,
|
||||
mut query: Query<
|
||||
(
|
||||
Entity,
|
||||
&ActionState<NavigationAction>,
|
||||
&mut Velocity,
|
||||
&mut Speed,
|
||||
&MaxSpeed,
|
||||
|
@ -67,197 +78,133 @@ fn movement_controls<S, A: 'static>(
|
|||
exploration_focused: Query<Entity, With<ExplorationFocused>>,
|
||||
) where
|
||||
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
|
||||
query.iter_mut()
|
||||
for (
|
||||
entity,
|
||||
actions,
|
||||
mut velocity,
|
||||
mut speed,
|
||||
max_speed,
|
||||
rotation_speed,
|
||||
mut transform,
|
||||
destination,
|
||||
) in &mut query
|
||||
{
|
||||
if **snapping {
|
||||
if let (Some(_), Some(rotate_left), Some(rotate_right)) = (
|
||||
rotation_speed,
|
||||
config.action_rotate_left,
|
||||
config.action_rotate_right,
|
||||
) {
|
||||
if input.active(rotate_left) {
|
||||
continue;
|
||||
}
|
||||
if input.active(rotate_right) {
|
||||
continue;
|
||||
}
|
||||
if actions.pressed(NavigationAction::Rotate) {
|
||||
continue;
|
||||
}
|
||||
**snapping = false;
|
||||
continue;
|
||||
} else {
|
||||
let sprinting = if let Some(action) = config.action_sprint {
|
||||
input.active(action)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let sprinting = actions.pressed(NavigationAction::Sprint);
|
||||
if sprinting {
|
||||
commands.entity(entity).insert(Sprinting::default());
|
||||
commands.entity(entity).insert(Sprinting);
|
||||
} else {
|
||||
commands.entity(entity).remove::<Sprinting>();
|
||||
}
|
||||
let mut direction = Vec2::default();
|
||||
if let Some(action) = config.action_forward {
|
||||
if input.active(action) {
|
||||
direction.x += 1.;
|
||||
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();
|
||||
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(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) {
|
||||
if let Some(rotation_speed) = rotation_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));
|
||||
}
|
||||
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. {
|
||||
if cleanup {
|
||||
commands.entity(entity).remove::<Destination>();
|
||||
commands.entity(entity).remove::<Exploring>();
|
||||
for entity in exploration_focused.iter() {
|
||||
commands.entity(entity).remove::<ExplorationFocused>();
|
||||
}
|
||||
direction = direction.normalize();
|
||||
if direction.x > 0. {
|
||||
direction.x *= config.forward_movement_factor;
|
||||
} else if direction.x < 0. {
|
||||
direction.x *= config.backward_movement_factor;
|
||||
}
|
||||
if direction.y != 0. {
|
||||
direction.y *= config.strafe_movement_factor;
|
||||
}
|
||||
let strength = if let (Some(forward), Some(backward), Some(left), Some(right)) = (
|
||||
config.action_forward,
|
||||
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, A: 'static>(
|
||||
fn snap<S>(
|
||||
mut tts: ResMut<Tts>,
|
||||
input: Res<InputMap<A>>,
|
||||
config: Res<NavigationConfig<S, A>>,
|
||||
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>>
|
||||
where
|
||||
S: 'static + Copy + Debug + Eq + Hash + Send + Sync,
|
||||
A: Hash + Eq + Clone + Send + Sync,
|
||||
{
|
||||
if let Some(action) = config.action_snap_left.clone() {
|
||||
if input.just_active(action) {
|
||||
for (mut position, mut velocity, direction) in query.iter_mut() {
|
||||
**snapping = true;
|
||||
position.rotation = Quat::from_rotation_z(match direction {
|
||||
CardinalDirection::North => PI,
|
||||
CardinalDirection::East => PI / 2.,
|
||||
CardinalDirection::South => 0.,
|
||||
CardinalDirection::West => PI * 1.5,
|
||||
});
|
||||
velocity.angvel = 0.;
|
||||
}
|
||||
for (actions, mut transform, mut velocity, direction) in query.iter_mut() {
|
||||
if actions.just_pressed(NavigationAction::SnapLeft) {
|
||||
**snapping = true;
|
||||
transform.rotation = Quat::from_rotation_z(match direction {
|
||||
CardinalDirection::North => PI,
|
||||
CardinalDirection::East => PI / 2.,
|
||||
CardinalDirection::South => 0.,
|
||||
CardinalDirection::West => PI * 1.5,
|
||||
});
|
||||
velocity.angvel = 0.;
|
||||
}
|
||||
}
|
||||
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;
|
||||
transform.rotation = Quat::from_rotation_z(match direction {
|
||||
CardinalDirection::North => 0.,
|
||||
CardinalDirection::East => PI * 1.5,
|
||||
CardinalDirection::South => PI,
|
||||
CardinalDirection::West => PI / 2.,
|
||||
});
|
||||
velocity.angvel = 0.;
|
||||
}
|
||||
if actions.just_pressed(NavigationAction::SnapRight) {
|
||||
**snapping = true;
|
||||
transform.rotation = Quat::from_rotation_z(match direction {
|
||||
CardinalDirection::North => 0.,
|
||||
CardinalDirection::East => PI * 1.5,
|
||||
CardinalDirection::South => PI,
|
||||
CardinalDirection::West => PI / 2.,
|
||||
});
|
||||
velocity.angvel = 0.;
|
||||
}
|
||||
}
|
||||
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;
|
||||
let yaw: Angle = direction.into();
|
||||
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::SnapCardinal) {
|
||||
**snapping = true;
|
||||
let yaw: Angle = direction.into();
|
||||
let yaw = yaw.radians();
|
||||
transform.rotation = Quat::from_rotation_z(yaw);
|
||||
velocity.angvel = 0.;
|
||||
tts.speak(direction.to_string(), true)?;
|
||||
}
|
||||
}
|
||||
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;
|
||||
transform.rotate(Quat::from_rotation_z(PI));
|
||||
velocity.angvel = 0.;
|
||||
}
|
||||
if actions.just_pressed(NavigationAction::SnapReverse) {
|
||||
**snapping = true;
|
||||
transform.rotate(Quat::from_rotation_z(PI));
|
||||
velocity.angvel = 0.;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -328,7 +275,7 @@ fn log_area_descriptions<S, A>(
|
|||
mut events: EventReader<CollisionEvent>,
|
||||
areas: Query<(&Area, Option<&Name>)>,
|
||||
players: Query<&Player>,
|
||||
config: Res<NavigationConfig<S, A>>,
|
||||
config: Res<NavigationConfig<S>>,
|
||||
mut log: Query<&mut Log>,
|
||||
) where
|
||||
S: 'static + Send + Sync,
|
||||
|
@ -369,18 +316,7 @@ fn log_area_descriptions<S, A>(
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
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 struct NavigationConfig<S> {
|
||||
pub forward_movement_factor: f32,
|
||||
pub backward_movement_factor: f32,
|
||||
pub strafe_movement_factor: f32,
|
||||
|
@ -390,20 +326,9 @@ pub struct NavigationConfig<S, A> {
|
|||
pub log_area_descriptions: bool,
|
||||
}
|
||||
|
||||
impl<S, A> Default for NavigationConfig<S, A> {
|
||||
impl<S> Default for NavigationConfig<S> {
|
||||
fn default() -> 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.,
|
||||
backward_movement_factor: 1.,
|
||||
strafe_movement_factor: 1.,
|
||||
|
@ -429,12 +354,12 @@ where
|
|||
A: Hash + Eq + Copy + Send + Sync,
|
||||
{
|
||||
fn build(&self, app: &mut App) {
|
||||
if !app.world.contains_resource::<NavigationConfig<S, A>>() {
|
||||
app.insert_resource(NavigationConfig::<S, A>::default());
|
||||
if !app.world.contains_resource::<NavigationConfig<S>>() {
|
||||
app.insert_resource(NavigationConfig::<S>::default());
|
||||
}
|
||||
let config = app
|
||||
.world
|
||||
.get_resource::<NavigationConfig<S, A>>()
|
||||
.get_resource::<NavigationConfig<S>>()
|
||||
.unwrap()
|
||||
.clone();
|
||||
const SNAP: &str = "SNAP";
|
||||
|
@ -442,6 +367,7 @@ where
|
|||
.register_type::<MaxSpeed>()
|
||||
.register_type::<RotationSpeed>()
|
||||
.register_type::<Sprinting>()
|
||||
.add_plugin(InputManagerPlugin::<NavigationAction>::default())
|
||||
.add_system_to_stage(CoreStage::PostUpdate, update_direction)
|
||||
.add_system_to_stage(CoreStage::PostUpdate, remove_direction)
|
||||
.add_system(speak_direction.chain(error_handler))
|
||||
|
@ -450,9 +376,9 @@ where
|
|||
.add_system_to_stage(CoreStage::PostUpdate, log_area_descriptions::<S, A>);
|
||||
const MOVEMENT_CONTROLS: &str = "MOVEMENT_CONTROLS";
|
||||
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(
|
||||
snap::<S, A>
|
||||
snap::<S>
|
||||
.chain(error_handler)
|
||||
.label(SNAP)
|
||||
.before(MOVEMENT_CONTROLS),
|
||||
|
@ -462,9 +388,9 @@ where
|
|||
for state in states {
|
||||
app.add_system_set(
|
||||
SystemSet::on_update(state)
|
||||
.with_system(movement_controls::<S, A>.label(MOVEMENT_CONTROLS))
|
||||
.with_system(movement_controls::<S>.label(MOVEMENT_CONTROLS))
|
||||
.with_system(
|
||||
snap::<S, A>
|
||||
snap::<S>
|
||||
.chain(error_handler)
|
||||
.label(SNAP)
|
||||
.before(MOVEMENT_CONTROLS),
|
||||
|
|
|
@ -110,7 +110,6 @@ fn find_path_for_shape<D: 'static + Clone + Default + Send + Sync>(
|
|||
|
||||
fn calculate_path<D: 'static + Clone + Default + Send + Sync>(
|
||||
mut commands: Commands,
|
||||
pool: Res<AsyncComputeTaskPool>,
|
||||
rapier_context: Res<RapierContext>,
|
||||
obstructions: Query<&RapierColliderHandle, With<MapObstruction>>,
|
||||
query: Query<
|
||||
|
@ -157,6 +156,7 @@ fn calculate_path<D: 'static + Clone + Default + Send + Sync>(
|
|||
}
|
||||
}
|
||||
let shape_clone = (*shape).clone();
|
||||
let pool = AsyncComputeTaskPool::get();
|
||||
let task = pool.spawn(async move {
|
||||
find_path_for_shape(
|
||||
handle_clone.0,
|
||||
|
|
|
@ -35,12 +35,12 @@ fn tag_behind(
|
|||
) {
|
||||
if config.downshift_behind {
|
||||
if let Ok(listener_transform) = listener.get_single() {
|
||||
let listener_forward = listener_transform.local_x();
|
||||
let listener_forward = listener_transform.forward();
|
||||
for (entity, transform, sound, icon) in sounds.iter() {
|
||||
if icon.is_none() && sound.is_none() {
|
||||
continue;
|
||||
}
|
||||
let v = transform.translation - listener_transform.translation;
|
||||
let v = transform.translation() - listener_transform.translation();
|
||||
let dot = v.dot(listener_forward);
|
||||
let is_behind = dot <= 0.;
|
||||
if is_behind {
|
||||
|
|
|
@ -63,14 +63,14 @@ impl Viewshed {
|
|||
let mut boxes = vec![];
|
||||
let shape = Collider::cuboid(self.range as f32, self.range as f32);
|
||||
rapier_context.intersections_with_shape(
|
||||
start.translation.truncate(),
|
||||
start.translation().truncate(),
|
||||
0.,
|
||||
&shape,
|
||||
QueryFilter::new().predicate(&|e| visible_query.get(e).is_ok()),
|
||||
|entity| {
|
||||
if let Ok((_, collider, transform)) = visible_query.get(entity) {
|
||||
let position = Isometry2::new(
|
||||
Vector2::new(transform.translation.x, transform.translation.y),
|
||||
Vector2::new(transform.translation().x, transform.translation().y),
|
||||
0.,
|
||||
);
|
||||
// println!(
|
||||
|
@ -88,8 +88,8 @@ impl Viewshed {
|
|||
let shape = Collider::cuboid(0.5, 0.5);
|
||||
let mut new_visible_entities = HashSet::new();
|
||||
let size = (
|
||||
(start.translation.x.abs() + self.range as f32) as u32,
|
||||
(start.translation.y.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,
|
||||
);
|
||||
let visibility_grid = VisibilityGrid(
|
||||
size,
|
||||
|
@ -410,7 +410,7 @@ fn log_visible(
|
|||
{
|
||||
viewer_transform.collider_direction_and_distance(
|
||||
viewer_collider,
|
||||
&viewed_transform,
|
||||
viewed_transform,
|
||||
viewed_collider,
|
||||
)
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue
Block a user