Compare commits
No commits in common. "615af720cbd90747144de2b3e250c66737db6e2a" and "37d0f0fcde1ffdd968016e2991346cd8a8599d43" have entirely different histories.
615af720cb
...
37d0f0fcde
14
Cargo.toml
14
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.7"
|
version = "0.6"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"bevy_gilrs",
|
"bevy_gilrs",
|
||||||
|
@ -22,13 +22,18 @@ features = [
|
||||||
"serialize",
|
"serialize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[dependencies.bevy_rapier2d]
|
||||||
|
git = "https://github.com/dimforge/bevy_rapier"
|
||||||
|
branch = "rapier-master"
|
||||||
|
features = ["simd-stable"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
bevy_input_actionmap = { git = "https://github.com/lightsoutgames/bevy_input_actionmap" }
|
bevy_input_actionmap = { git = "https://github.com/lightsoutgames/bevy_input_actionmap" }
|
||||||
bevy_openal = { git = "https://github.com/lightsoutgames/bevy_openal" }
|
bevy_openal = { git = "https://github.com/lightsoutgames/bevy_openal" }
|
||||||
bevy_rapier2d = "0.13"
|
|
||||||
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"] }
|
bevy_tts = { git = "https://github.com/lightsoutgames/bevy_tts", default-features = false, features = ["tolk"] }
|
||||||
coord_2d = "0.3"
|
coord_2d = "0.3"
|
||||||
|
derive_more = "0.99"
|
||||||
futures-lite = "1"
|
futures-lite = "1"
|
||||||
gilrs = "0.8"
|
gilrs = "0.8"
|
||||||
here_be_dragons = "0.1"
|
here_be_dragons = "0.1"
|
||||||
|
@ -38,4 +43,7 @@ pathfinding = "3"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
sentry = "0.25"
|
sentry = "0.25"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
shadowcast = "0.8"
|
shadowcast = "0.8"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
rapier2d = { git = "https://github.com/dimforge/rapier", rev = "7efcff615e821" }
|
234
src/core.rs
234
src/core.rs
|
@ -3,16 +3,116 @@ use std::{
|
||||||
f32::consts::PI,
|
f32::consts::PI,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::Sub,
|
ops::{Add, AddAssign, Sub, SubAssign},
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy::{core::FloatOrd, ecs::query::WorldQuery, prelude::*};
|
use bevy::{core::FloatOrd, ecs::query::WorldQuery, prelude::*, transform::TransformSystem};
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Component, Clone, Copy, Debug, Default, Deref, DerefMut, PartialEq, PartialOrd, Reflect,
|
||||||
|
)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct Coordinates(pub (f32, f32));
|
||||||
|
|
||||||
|
impl Coordinates {
|
||||||
|
pub fn from_transform(transform: &Transform, config: &CoreConfig) -> Self {
|
||||||
|
Self((
|
||||||
|
transform.x() / config.pixels_per_unit as f32,
|
||||||
|
transform.y() / config.pixels_per_unit as f32,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_transform(&self, config: &CoreConfig) -> Transform {
|
||||||
|
Transform::from_translation(Vec3::new(
|
||||||
|
self.0 .0 * config.pixels_per_unit as f32,
|
||||||
|
self.0 .1 * config.pixels_per_unit as f32,
|
||||||
|
0.,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Coordinates {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self((self.0 .0 + rhs.0 .0, self.0 .1 + rhs.0 .1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for Coordinates {
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
*self = *self + rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Coordinates {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self((self.0 .0 - rhs.0 .0, self.0 .1 - rhs.0 .1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign for Coordinates {
|
||||||
|
fn sub_assign(&mut self, rhs: Self) {
|
||||||
|
*self = *self - rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec2> for Coordinates {
|
||||||
|
fn from(v: Vec2) -> Self {
|
||||||
|
Self((v.x, v.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec3> for Coordinates {
|
||||||
|
fn from(v: Vec3) -> Self {
|
||||||
|
Self((v.x, v.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Coordinates> for Vec2 {
|
||||||
|
fn from(c: Coordinates) -> Self {
|
||||||
|
Vec2::new(c.0 .0, c.0 .1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Coordinates> for Vec3 {
|
||||||
|
fn from(c: Coordinates) -> Self {
|
||||||
|
Vec3::new(c.0 .0, c.0 .1, 0.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(f32, f32)> for Coordinates {
|
||||||
|
fn from(v: (f32, f32)) -> Self {
|
||||||
|
Coordinates((v.0, v.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(i32, i32)> for Coordinates {
|
||||||
|
fn from(v: (i32, i32)) -> Self {
|
||||||
|
Coordinates((v.0 as f32, v.1 as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(u32, u32)> for Coordinates {
|
||||||
|
fn from(v: (u32, u32)) -> Self {
|
||||||
|
Coordinates((v.0 as f32, v.1 as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(usize, usize)> for Coordinates {
|
||||||
|
fn from(v: (usize, usize)) -> Self {
|
||||||
|
Coordinates((v.0 as f32, v.1 as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Reflect)]
|
#[derive(Clone, Copy, Debug, Reflect)]
|
||||||
pub enum Angle {
|
pub enum Angle {
|
||||||
Degrees(f32),
|
Degrees(f32),
|
||||||
|
@ -368,26 +468,6 @@ impl PointLike for Transform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointLike for &Transform {
|
|
||||||
fn x(&self) -> f32 {
|
|
||||||
self.translation.x
|
|
||||||
}
|
|
||||||
|
|
||||||
fn y(&self) -> f32 {
|
|
||||||
self.translation.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointLike for GlobalTransform {
|
|
||||||
fn x(&self) -> f32 {
|
|
||||||
self.translation.x
|
|
||||||
}
|
|
||||||
|
|
||||||
fn y(&self) -> f32 {
|
|
||||||
self.translation.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointLike for Vec2 {
|
impl PointLike for Vec2 {
|
||||||
fn x(&self) -> f32 {
|
fn x(&self) -> f32 {
|
||||||
self.x
|
self.x
|
||||||
|
@ -428,6 +508,16 @@ impl PointLike for (usize, usize) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PointLike for &Coordinates {
|
||||||
|
fn x(&self) -> f32 {
|
||||||
|
self.0 .0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y(&self) -> f32 {
|
||||||
|
self.0 .1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PointLike for here_be_dragons::geometry::Point {
|
impl PointLike for here_be_dragons::geometry::Point {
|
||||||
fn x(&self) -> f32 {
|
fn x(&self) -> f32 {
|
||||||
self.x as f32
|
self.x as f32
|
||||||
|
@ -453,6 +543,8 @@ macro_rules! impl_pointlike_for_tuple_component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_pointlike_for_tuple_component!(Coordinates);
|
||||||
|
|
||||||
impl From<&dyn PointLike> for (i32, i32) {
|
impl From<&dyn PointLike> for (i32, i32) {
|
||||||
fn from(val: &dyn PointLike) -> Self {
|
fn from(val: &dyn PointLike) -> Self {
|
||||||
val.i32()
|
val.i32()
|
||||||
|
@ -522,11 +614,76 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(core_config: Res<CoreConfig>) {
|
fn setup(core_config: Res<CoreConfig>, mut rapier_config: ResMut<RapierConfiguration>) {
|
||||||
|
rapier_config.scale = core_config.pixels_per_unit as f32;
|
||||||
let mut mode = RELATIVE_DIRECTION_MODE.write().unwrap();
|
let mut mode = RELATIVE_DIRECTION_MODE.write().unwrap();
|
||||||
*mode = core_config.relative_direction_mode;
|
*mode = core_config.relative_direction_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn copy_coordinates_to_transform(
|
||||||
|
config: Res<CoreConfig>,
|
||||||
|
mut query: Query<
|
||||||
|
(&Coordinates, &mut Transform),
|
||||||
|
(
|
||||||
|
Changed<Transform>,
|
||||||
|
Without<RigidBodyPositionComponent>,
|
||||||
|
Without<RigidBodyPositionSync>,
|
||||||
|
Without<ColliderPositionComponent>,
|
||||||
|
Without<ColliderPositionSync>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
for (coordinates, mut transform) in query.iter_mut() {
|
||||||
|
let x = coordinates.0 .0 * config.pixels_per_unit as f32;
|
||||||
|
if transform.translation.x != x {
|
||||||
|
transform.translation.x = x;
|
||||||
|
}
|
||||||
|
let y = coordinates.0 .1 * config.pixels_per_unit as f32;
|
||||||
|
if transform.translation.y != y {
|
||||||
|
transform.translation.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_rigid_body_position_to_coordinates(
|
||||||
|
mut query: Query<
|
||||||
|
(&mut Coordinates, &RigidBodyPositionComponent),
|
||||||
|
(
|
||||||
|
Changed<RigidBodyPositionComponent>,
|
||||||
|
With<RigidBodyPositionSync>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
for (mut coordinates, position) in query.iter_mut() {
|
||||||
|
if coordinates.0 .0 != position.position.translation.x {
|
||||||
|
coordinates.0 .0 = position.position.translation.x;
|
||||||
|
}
|
||||||
|
if coordinates.0 .1 != position.position.translation.y {
|
||||||
|
coordinates.0 .1 = position.position.translation.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_collider_position_to_coordinates(
|
||||||
|
mut query: Query<
|
||||||
|
(&mut Coordinates, &ColliderPositionComponent),
|
||||||
|
(
|
||||||
|
Without<RigidBodyPositionComponent>,
|
||||||
|
Changed<ColliderPositionComponent>,
|
||||||
|
With<ColliderPositionSync>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
for (mut coordinates, position) in query.iter_mut() {
|
||||||
|
if coordinates.0 .0 != position.translation.x {
|
||||||
|
coordinates.0 .0 = position.translation.x;
|
||||||
|
}
|
||||||
|
if coordinates.0 .1 != position.translation.y {
|
||||||
|
coordinates.0 .1 = position.translation.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct CoreConfig {
|
pub struct CoreConfig {
|
||||||
pub relative_direction_mode: RelativeDirectionMode,
|
pub relative_direction_mode: RelativeDirectionMode,
|
||||||
|
@ -549,25 +706,25 @@ fn sync_config(config: Res<CoreConfig>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CorePlugin<RapierUserData>(PhantomData<RapierUserData>);
|
pub struct CorePlugin;
|
||||||
|
|
||||||
impl<RapierUserData> Default for CorePlugin<RapierUserData> {
|
impl Plugin for CorePlugin {
|
||||||
fn default() -> Self {
|
|
||||||
Self(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<RapierUserData: 'static + WorldQuery + Send + Sync> Plugin for CorePlugin<RapierUserData> {
|
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
if !app.world.contains_resource::<CoreConfig>() {
|
if !app.world.contains_resource::<CoreConfig>() {
|
||||||
app.insert_resource(CoreConfig::default());
|
app.insert_resource(CoreConfig::default());
|
||||||
}
|
}
|
||||||
let config = *app.world.get_resource::<CoreConfig>().unwrap();
|
app.register_type::<Coordinates>()
|
||||||
app.add_plugin(RapierPhysicsPlugin::<RapierUserData>::pixels_per_meter(
|
.add_startup_system(setup)
|
||||||
config.pixels_per_unit as f32,
|
.add_system_to_stage(
|
||||||
))
|
CoreStage::PostUpdate,
|
||||||
.add_startup_system(setup)
|
copy_coordinates_to_transform.before(TransformSystem::TransformPropagate),
|
||||||
.add_system(sync_config);
|
)
|
||||||
|
.add_system_to_stage(
|
||||||
|
CoreStage::PostUpdate,
|
||||||
|
copy_rigid_body_position_to_coordinates,
|
||||||
|
)
|
||||||
|
.add_system_to_stage(CoreStage::PostUpdate, copy_collider_position_to_coordinates)
|
||||||
|
.add_system(sync_config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,6 +743,7 @@ impl<RapierUserData: 'static + WorldQuery + Send + Sync> PluginGroup
|
||||||
group
|
group
|
||||||
.add(crate::bevy_tts::TtsPlugin)
|
.add(crate::bevy_tts::TtsPlugin)
|
||||||
.add(crate::bevy_openal::OpenAlPlugin)
|
.add(crate::bevy_openal::OpenAlPlugin)
|
||||||
.add(CorePlugin::<RapierUserData>::default());
|
.add(RapierPhysicsPlugin::<RapierUserData>::default())
|
||||||
|
.add(CorePlugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl Plugin for ErrorPlugin {
|
||||||
dsn.as_str(),
|
dsn.as_str(),
|
||||||
sentry::ClientOptions {
|
sentry::ClientOptions {
|
||||||
release: Some(release.into()),
|
release: Some(release.into()),
|
||||||
..default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
app.insert_resource(guard);
|
app.insert_resource(guard);
|
||||||
|
|
|
@ -4,9 +4,10 @@ use bevy::prelude::*;
|
||||||
use bevy_input_actionmap::InputMap;
|
use bevy_input_actionmap::InputMap;
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
use bevy_tts::Tts;
|
use bevy_tts::Tts;
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Player, PointLike},
|
core::{Coordinates, Player, PointLike},
|
||||||
error::error_handler,
|
error::error_handler,
|
||||||
map::Map,
|
map::Map,
|
||||||
pathfinding::Destination,
|
pathfinding::Destination,
|
||||||
|
@ -154,7 +155,7 @@ fn exploration_type_focus<S, A: 'static>(
|
||||||
&FocusedExplorationType,
|
&FocusedExplorationType,
|
||||||
Option<&Exploring>,
|
Option<&Exploring>,
|
||||||
)>,
|
)>,
|
||||||
features: Query<(Entity, &Transform, &ExplorationType)>,
|
features: Query<(Entity, &Coordinates, &ExplorationType)>,
|
||||||
) -> Result<(), Box<dyn Error>>
|
) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
|
@ -246,7 +247,7 @@ fn exploration_focus<S, A: 'static, D: 'static + Clone + Default + Send + Sync>(
|
||||||
config: Res<ExplorationConfig<S, A>>,
|
config: Res<ExplorationConfig<S, A>>,
|
||||||
input: Res<InputMap<A>>,
|
input: Res<InputMap<A>>,
|
||||||
map: Query<&Map<D>>,
|
map: Query<&Map<D>>,
|
||||||
explorers: Query<(Entity, &Transform, Option<&Exploring>), With<Player>>,
|
explorers: Query<(Entity, &Coordinates, 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,
|
A: Hash + Eq + Clone + Send + Sync,
|
||||||
|
@ -263,13 +264,12 @@ fn exploration_focus<S, A: 'static, D: 'static + Clone + Default + Send + Sync>(
|
||||||
config.action_explore_right.clone(),
|
config.action_explore_right.clone(),
|
||||||
) {
|
) {
|
||||||
for map in map.iter() {
|
for map in map.iter() {
|
||||||
if let Ok((entity, transform, exploring)) = explorers.get_single() {
|
if let Ok((entity, coordinates, exploring)) = explorers.get_single() {
|
||||||
let coordinates = transform.translation;
|
let coordinates = **coordinates;
|
||||||
let mut exploring = if let Some(exploring) = exploring {
|
let mut exploring = if let Some(exploring) = exploring {
|
||||||
**exploring
|
**exploring
|
||||||
} else {
|
} else {
|
||||||
let floor = coordinates.floor();
|
coordinates.floor()
|
||||||
(floor.x, floor.y)
|
|
||||||
};
|
};
|
||||||
let orig = exploring;
|
let orig = exploring;
|
||||||
if input.just_active(explore_forward.clone()) {
|
if input.just_active(explore_forward.clone()) {
|
||||||
|
@ -324,20 +324,22 @@ fn exploration_changed_announcement<D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut tts: ResMut<Tts>,
|
mut tts: ResMut<Tts>,
|
||||||
map: Query<(&Map<D>, &RevealedTiles)>,
|
map: Query<(&Map<D>, &RevealedTiles)>,
|
||||||
explorer: Query<(&Transform, &Exploring, &Viewshed), Changed<Exploring>>,
|
explorer: Query<(&Coordinates, &Exploring, &Viewshed), Changed<Exploring>>,
|
||||||
focused: Query<Entity, With<ExplorationFocused>>,
|
focused: Query<Entity, With<ExplorationFocused>>,
|
||||||
explorable: Query<Entity, Or<(With<Visible>, With<Explorable>)>>,
|
explorable: Query<Entity, Or<(With<Visible>, With<Explorable>)>>,
|
||||||
names: Query<&Name>,
|
names: Query<&Name>,
|
||||||
types: Query<&ExplorationType>,
|
types: Query<&ExplorationType>,
|
||||||
mappables: Query<&Mappable>,
|
mappables: Query<&Mappable>,
|
||||||
rapier_context: Res<RapierContext>,
|
query_pipeline: Res<QueryPipeline>,
|
||||||
|
collider_query: QueryPipelineColliderComponentsQuery,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
if let Ok((coordinates, exploring, viewshed)) = explorer.get_single() {
|
if let Ok((coordinates, exploring, viewshed)) = explorer.get_single() {
|
||||||
|
let collider_set = QueryPipelineColliderComponentsSet(&collider_query);
|
||||||
let coordinates = coordinates.floor();
|
let coordinates = coordinates.floor();
|
||||||
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 shape = Collider::cuboid(0.49, 0.49);
|
let shape = Cuboid::new(Vec2::new(0.49, 0.49).into());
|
||||||
let known = revealed_tiles[idx];
|
let known = revealed_tiles[idx];
|
||||||
let visible = viewshed.is_point_visible(exploring);
|
let visible = viewshed.is_point_visible(exploring);
|
||||||
let fog_of_war = known && !visible;
|
let fog_of_war = known && !visible;
|
||||||
|
@ -346,14 +348,15 @@ fn exploration_changed_announcement<D: 'static + Clone + Default + Send + Sync>(
|
||||||
for entity in focused.iter() {
|
for entity in focused.iter() {
|
||||||
commands.entity(entity).remove::<ExplorationFocused>();
|
commands.entity(entity).remove::<ExplorationFocused>();
|
||||||
}
|
}
|
||||||
let exploring = Vec2::new(exploring.x(), exploring.y());
|
let shape_pos = (Vec2::new(exploring.x(), exploring.y()), 0.);
|
||||||
rapier_context.intersections_with_shape(
|
query_pipeline.intersections_with_shape(
|
||||||
exploring,
|
&collider_set,
|
||||||
0.,
|
&shape_pos.into(),
|
||||||
&shape,
|
&shape,
|
||||||
InteractionGroups::all(),
|
InteractionGroups::all(),
|
||||||
Some(&|v| explorable.get(v).is_ok()),
|
Some(&|v| explorable.get(v.entity()).is_ok()),
|
||||||
|entity| {
|
|handle| {
|
||||||
|
let entity = handle.entity();
|
||||||
commands.entity(entity).insert(ExplorationFocused);
|
commands.entity(entity).insert(ExplorationFocused);
|
||||||
if visible || mappables.get(entity).is_ok() {
|
if visible || mappables.get(entity).is_ok() {
|
||||||
if let Ok(name) = names.get(entity) {
|
if let Ok(name) = names.get(entity) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub mod commands;
|
||||||
pub use coord_2d;
|
pub use coord_2d;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod core;
|
pub mod core;
|
||||||
|
pub use derive_more;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod exploration;
|
pub mod exploration;
|
||||||
pub use futures_lite;
|
pub use futures_lite;
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::{error::Error, time::Instant};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_tts::Tts;
|
use bevy_tts::Tts;
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::error::error_handler;
|
use crate::error::error_handler;
|
||||||
|
|
||||||
|
|
107
src/map.rs
107
src/map.rs
|
@ -1,24 +1,27 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_rapier2d::{
|
use bevy_rapier2d::prelude::*;
|
||||||
na::{Isometry2, Vector2},
|
use derive_more::{Deref, DerefMut};
|
||||||
prelude::*,
|
|
||||||
rapier::prelude::AABB,
|
|
||||||
};
|
|
||||||
pub use here_be_dragons::Map as MapgenMap;
|
pub use here_be_dragons::Map as MapgenMap;
|
||||||
use here_be_dragons::{geometry::Rect as MRect, MapFilter, Tile};
|
use here_be_dragons::{geometry::Rect as MRect, MapFilter, Tile};
|
||||||
use maze_generator::{prelude::*, recursive_backtracking::RbGenerator};
|
use maze_generator::{prelude::*, recursive_backtracking::RbGenerator};
|
||||||
use rand::prelude::StdRng;
|
use rand::prelude::StdRng;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Player, PointLike},
|
core::{Coordinates, Player, PointLike},
|
||||||
exploration::{ExplorationType, Mappable},
|
exploration::{ExplorationType, Mappable},
|
||||||
log::Log,
|
log::Log,
|
||||||
utils::target_and_other,
|
utils::target_and_other,
|
||||||
visibility::Visible,
|
visibility::Visible,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
impl From<here_be_dragons::geometry::Point> for Coordinates {
|
||||||
|
fn from(point: here_be_dragons::geometry::Point) -> Self {
|
||||||
|
Self((point.x as f32, point.y as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Copy, Clone, Debug, Deref, DerefMut, PartialEq)]
|
#[derive(Component, Copy, Clone, Debug, Deref, DerefMut, PartialEq)]
|
||||||
pub struct Area(pub AABB);
|
pub struct Area(pub AABB);
|
||||||
|
|
||||||
|
@ -109,6 +112,7 @@ impl Default for MapConfig {
|
||||||
|
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
pub struct PortalBundle {
|
pub struct PortalBundle {
|
||||||
|
pub coordinates: Coordinates,
|
||||||
pub portal: Portal,
|
pub portal: Portal,
|
||||||
pub exploration_type: ExplorationType,
|
pub exploration_type: ExplorationType,
|
||||||
pub mappable: Mappable,
|
pub mappable: Mappable,
|
||||||
|
@ -119,6 +123,7 @@ pub struct PortalBundle {
|
||||||
impl Default for PortalBundle {
|
impl Default for PortalBundle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
coordinates: Default::default(),
|
||||||
portal: Default::default(),
|
portal: Default::default(),
|
||||||
exploration_type: ExplorationType::Portal,
|
exploration_type: ExplorationType::Portal,
|
||||||
mappable: Default::default(),
|
mappable: Default::default(),
|
||||||
|
@ -134,6 +139,7 @@ pub struct MapBundle<D: 'static + Clone + Default + Send + Sync> {
|
||||||
pub spawn_colliders: SpawnColliders,
|
pub spawn_colliders: SpawnColliders,
|
||||||
pub spawn_collider_per_tile: SpawnColliderPerTile,
|
pub spawn_collider_per_tile: SpawnColliderPerTile,
|
||||||
pub spawn_portals: SpawnPortals,
|
pub spawn_portals: SpawnPortals,
|
||||||
|
pub children: Children,
|
||||||
pub transform: Transform,
|
pub transform: Transform,
|
||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
}
|
}
|
||||||
|
@ -232,16 +238,21 @@ fn spawn_colliders<D: 'static + Clone + Default + Send + Sync>(
|
||||||
.entity(map_entity)
|
.entity(map_entity)
|
||||||
.remove::<SpawnColliders>()
|
.remove::<SpawnColliders>()
|
||||||
.remove::<SpawnColliderPerTile>()
|
.remove::<SpawnColliderPerTile>()
|
||||||
.insert(RigidBody::Fixed);
|
.insert_bundle(RigidBodyBundle {
|
||||||
|
body_type: RigidBodyTypeComponent(RigidBodyType::Fixed),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
if **spawn_collider_per_tile {
|
if **spawn_collider_per_tile {
|
||||||
for y in 0..map.height {
|
for y in 0..map.height {
|
||||||
for x in 0..map.width {
|
for x in 0..map.width {
|
||||||
let tile = map.at(x, y);
|
let tile = map.at(x, y);
|
||||||
if tile.blocks_motion() {
|
if tile.blocks_motion() {
|
||||||
let id = commands
|
let id = commands
|
||||||
.spawn()
|
.spawn_bundle(ColliderBundle {
|
||||||
.insert(Collider::cuboid(0.5, 0.5))
|
shape: ColliderShape::cuboid(0.5, 0.5).into(),
|
||||||
.insert(Transform::from_xyz(x as f32 + 0.5, y as f32 + 0.5, 0.))
|
position: Vec2::new(x as f32 + 0.5, y as f32 + 0.5).into(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
.insert(MapObstruction)
|
.insert(MapObstruction)
|
||||||
.id();
|
.id();
|
||||||
if tile.blocks_visibility() {
|
if tile.blocks_visibility() {
|
||||||
|
@ -323,9 +334,11 @@ fn spawn_colliders<D: 'static + Clone + Default + Send + Sync>(
|
||||||
height
|
height
|
||||||
);
|
);
|
||||||
let id = commands
|
let id = commands
|
||||||
.spawn()
|
.spawn_bundle(ColliderBundle {
|
||||||
.insert(Collider::cuboid(half_width, half_height))
|
shape: ColliderShape::cuboid(half_width, half_height).into(),
|
||||||
.insert(Transform::from_xyz(center.x(), center.y(), 0.))
|
position: Vec2::new(center.x(), center.y()).into(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
.insert(MapObstruction)
|
.insert(MapObstruction)
|
||||||
.id();
|
.id();
|
||||||
if map.at(x as usize, y as usize).blocks_visibility() {
|
if map.at(x as usize, y as usize).blocks_visibility() {
|
||||||
|
@ -339,20 +352,19 @@ fn spawn_colliders<D: 'static + Clone + Default + Send + Sync>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for room in &map.rooms {
|
for room in &map.rooms {
|
||||||
let shape = Collider::cuboid((room.width() / 2) as f32, (room.height() / 2) as f32);
|
let shape =
|
||||||
let position =
|
ColliderShape::cuboid((room.width() / 2) as f32, (room.height() / 2) as f32);
|
||||||
Isometry2::new(Vector2::new(room.center().x(), room.center().y()), 0.);
|
let position: ColliderPositionComponent =
|
||||||
let aabb = shape.raw.compute_aabb(&position);
|
point!(room.center().x(), room.center().y()).into();
|
||||||
|
let aabb = shape.compute_aabb(&position);
|
||||||
let id = commands
|
let id = commands
|
||||||
.spawn()
|
.spawn_bundle(ColliderBundle {
|
||||||
.insert(shape)
|
collider_type: ColliderType::Sensor.into(),
|
||||||
.insert(Sensor(true))
|
shape: shape.clone().into(),
|
||||||
.insert(ActiveEvents::COLLISION_EVENTS)
|
flags: ActiveEvents::COLLISION_EVENTS.into(),
|
||||||
.insert(Transform::from_xyz(
|
position,
|
||||||
position.translation.x,
|
..Default::default()
|
||||||
position.translation.y,
|
})
|
||||||
0.,
|
|
||||||
))
|
|
||||||
.insert(Area(aabb))
|
.insert(Area(aabb))
|
||||||
.id();
|
.id();
|
||||||
commands.entity(map_entity).push_children(&[id]);
|
commands.entity(map_entity).push_children(&[id]);
|
||||||
|
@ -365,9 +377,9 @@ fn spawn_portals<D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
map: Query<(Entity, &Map<D>, &SpawnPortals), Changed<SpawnPortals>>,
|
map: Query<(Entity, &Map<D>, &SpawnPortals), Changed<SpawnPortals>>,
|
||||||
) {
|
) {
|
||||||
for (map_entity, map, spawn_portals) in map.iter() {
|
for (entity, map, spawn_portals) in map.iter() {
|
||||||
if **spawn_portals {
|
if **spawn_portals {
|
||||||
commands.entity(map_entity).remove::<SpawnPortals>();
|
commands.entity(entity).remove::<SpawnPortals>();
|
||||||
let mut portals: Vec<(f32, f32)> = vec![];
|
let mut portals: Vec<(f32, f32)> = vec![];
|
||||||
for x in 1..map.width {
|
for x in 1..map.width {
|
||||||
for y in 1..map.height {
|
for y in 1..map.height {
|
||||||
|
@ -403,13 +415,14 @@ fn spawn_portals<D: 'static + Clone + Default + Send + Sync>(
|
||||||
for portal in portals {
|
for portal in portals {
|
||||||
let x = portal.0 as f32;
|
let x = portal.0 as f32;
|
||||||
let y = portal.1 as f32;
|
let y = portal.1 as f32;
|
||||||
|
let coordinates = Coordinates((x, y));
|
||||||
let portal = commands
|
let portal = commands
|
||||||
.spawn_bundle(PortalBundle {
|
.spawn_bundle(PortalBundle {
|
||||||
transform: Transform::from_translation(Vec3::new(x, y, 0.)),
|
coordinates,
|
||||||
..default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.id();
|
.id();
|
||||||
commands.entity(map_entity).push_children(&[portal]);
|
commands.entity(entity).push_children(&[portal]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -417,19 +430,24 @@ fn spawn_portals<D: 'static + Clone + Default + Send + Sync>(
|
||||||
|
|
||||||
fn spawn_portal_colliders<D: 'static + Clone + Default + Send + Sync>(
|
fn spawn_portal_colliders<D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
map: Query<&SpawnColliders, With<Map<D>>>,
|
map: Query<(Entity, &SpawnColliders), With<Map<D>>>,
|
||||||
portals: Query<(Entity, &Transform), (With<Portal>, Without<Collider>)>,
|
portals: Query<(Entity, &Coordinates), Without<ColliderShapeComponent>>,
|
||||||
) {
|
) {
|
||||||
for spawn_colliders in map.iter() {
|
for (map_entity, spawn_colliders) in map.iter() {
|
||||||
if **spawn_colliders {
|
if **spawn_colliders {
|
||||||
for (portal_entity, coordinates) in portals.iter() {
|
for (portal_entity, coordinates) in portals.iter() {
|
||||||
let position = Vec2::new(coordinates.x() + 0.5, coordinates.y() + 0.5);
|
let position = Vec2::new(coordinates.x() + 0.5, coordinates.y() + 0.5).into();
|
||||||
commands
|
commands
|
||||||
.entity(portal_entity)
|
.entity(portal_entity)
|
||||||
.insert(Transform::from_xyz(position.x(), position.y(), 0.))
|
.insert_bundle(ColliderBundle {
|
||||||
.insert(Collider::cuboid(0.5, 0.5))
|
collider_type: ColliderTypeComponent(ColliderType::Sensor),
|
||||||
.insert(Sensor(true))
|
shape: ColliderShape::cuboid(0.5, 0.5).into(),
|
||||||
.insert(ActiveEvents::COLLISION_EVENTS);
|
position,
|
||||||
|
flags: ActiveEvents::COLLISION_EVENTS.into(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(ColliderPositionSync::Discrete);
|
||||||
|
commands.entity(map_entity).push_children(&[portal_entity]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,11 +462,14 @@ fn area_description(
|
||||||
) {
|
) {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
let (entity1, entity2, started) = match event {
|
let (entity1, entity2, started) = match event {
|
||||||
CollisionEvent::Started(collider1, collider2, _) => (collider1, collider2, true),
|
CollisionEvent::Started(collider1, collider2) => {
|
||||||
CollisionEvent::Stopped(collider1, collider2, _) => (collider1, collider2, false),
|
(collider1.entity(), collider2.entity(), true)
|
||||||
|
}
|
||||||
|
CollisionEvent::Stopped(collider1, collider2, _) => {
|
||||||
|
(collider1.entity(), collider2.entity(), false)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Some((area, other)) = target_and_other(*entity1, *entity2, &|v| areas.get(v).is_ok())
|
if let Some((area, other)) = target_and_other(entity1, entity2, &|v| areas.get(v).is_ok()) {
|
||||||
{
|
|
||||||
if players.get(other).is_ok() {
|
if players.get(other).is_ok() {
|
||||||
if let Ok((aabb, area_name)) = areas.get(area) {
|
if let Ok((aabb, area_name)) = areas.get(area) {
|
||||||
let name = if let Some(name) = area_name {
|
let name = if let Some(name) = area_name {
|
||||||
|
|
|
@ -2,8 +2,9 @@ use std::{error::Error, f32::consts::PI, fmt::Debug, hash::Hash, marker::Phantom
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_input_actionmap::InputMap;
|
use bevy_input_actionmap::InputMap;
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::{na::UnitComplex, prelude::*};
|
||||||
use bevy_tts::Tts;
|
use bevy_tts::Tts;
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Angle, CardinalDirection, Player},
|
core::{Angle, CardinalDirection, Player},
|
||||||
|
@ -52,11 +53,11 @@ fn movement_controls<S, A: 'static>(
|
||||||
mut query: Query<
|
mut query: Query<
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
&mut Velocity,
|
&mut RigidBodyVelocityComponent,
|
||||||
&mut Speed,
|
&mut Speed,
|
||||||
&MaxSpeed,
|
&MaxSpeed,
|
||||||
Option<&RotationSpeed>,
|
Option<&RotationSpeed>,
|
||||||
&mut Transform,
|
&mut RigidBodyPositionComponent,
|
||||||
Option<&Destination>,
|
Option<&Destination>,
|
||||||
),
|
),
|
||||||
With<Player>,
|
With<Player>,
|
||||||
|
@ -64,16 +65,16 @@ 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,
|
A: Hash + Eq + Clone + Send + Sync,
|
||||||
{
|
{
|
||||||
for (entity, mut velocity, mut speed, max_speed, rotation_speed, mut transform, destination) in
|
for (entity, mut velocity, mut speed, max_speed, rotation_speed, mut position, destination) in
|
||||||
query.iter_mut()
|
query.iter_mut()
|
||||||
{
|
{
|
||||||
if **snapping {
|
if **snapping {
|
||||||
if let (Some(_), Some(rotate_left), Some(rotate_right)) = (
|
if let (Some(_), Some(rotate_left), Some(rotate_right)) = (
|
||||||
rotation_speed,
|
rotation_speed,
|
||||||
config.action_rotate_left,
|
config.action_rotate_left.clone(),
|
||||||
config.action_rotate_right,
|
config.action_rotate_right.clone(),
|
||||||
) {
|
) {
|
||||||
if input.active(rotate_left) {
|
if input.active(rotate_left) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -85,7 +86,7 @@ fn movement_controls<S, A: 'static>(
|
||||||
**snapping = false;
|
**snapping = false;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
let sprinting = if let Some(action) = config.action_sprint {
|
let sprinting = if let Some(action) = config.action_sprint.clone() {
|
||||||
input.active(action)
|
input.active(action)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -96,39 +97,38 @@ fn movement_controls<S, A: 'static>(
|
||||||
commands.entity(entity).remove::<Sprinting>();
|
commands.entity(entity).remove::<Sprinting>();
|
||||||
}
|
}
|
||||||
let mut direction = Vec2::default();
|
let mut direction = Vec2::default();
|
||||||
if let Some(action) = config.action_forward {
|
if let Some(action) = config.action_forward.clone() {
|
||||||
if input.active(action) {
|
if input.active(action) {
|
||||||
direction.x += 1.;
|
direction.x += 1.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(action) = config.action_backward {
|
if let Some(action) = config.action_backward.clone() {
|
||||||
if input.active(action) {
|
if input.active(action) {
|
||||||
direction.x -= 1.;
|
direction.x -= 1.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(action) = config.action_left {
|
if let Some(action) = config.action_left.clone() {
|
||||||
if input.active(action) {
|
if input.active(action) {
|
||||||
direction.y += 1.;
|
direction.y += 1.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(action) = config.action_right {
|
if let Some(action) = config.action_right.clone() {
|
||||||
if input.active(action) {
|
if input.active(action) {
|
||||||
direction.y -= 1.;
|
direction.y -= 1.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (Some(rotation_speed), Some(rotate_left), Some(rotate_right)) = (
|
if let (Some(rotation_speed), Some(rotate_left), Some(rotate_right)) = (
|
||||||
rotation_speed,
|
rotation_speed,
|
||||||
config.action_rotate_left,
|
config.action_rotate_left.clone(),
|
||||||
config.action_rotate_right,
|
config.action_rotate_right.clone(),
|
||||||
) {
|
) {
|
||||||
let delta = rotation_speed.radians() * time.delta_seconds();
|
let delta = rotation_speed.radians() * time.delta_seconds();
|
||||||
if input.active(rotate_left) {
|
if input.active(rotate_left) {
|
||||||
transform.rotate(Quat::from_rotation_z(delta));
|
position.position.rotation *= UnitComplex::new(delta);
|
||||||
}
|
}
|
||||||
if input.active(rotate_right) {
|
if input.active(rotate_right) {
|
||||||
transform.rotate(Quat::from_rotation_z(-delta));
|
position.position.rotation *= UnitComplex::new(-delta);
|
||||||
}
|
} else {
|
||||||
if !input.active(rotate_left) && !input.active(rotate_right) {
|
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,10 +150,10 @@ fn movement_controls<S, A: 'static>(
|
||||||
direction.y *= config.strafe_movement_factor;
|
direction.y *= config.strafe_movement_factor;
|
||||||
}
|
}
|
||||||
let strength = if let (Some(forward), Some(backward), Some(left), Some(right)) = (
|
let strength = if let (Some(forward), Some(backward), Some(left), Some(right)) = (
|
||||||
config.action_forward,
|
config.action_forward.clone(),
|
||||||
config.action_backward,
|
config.action_backward.clone(),
|
||||||
config.action_left,
|
config.action_left.clone(),
|
||||||
config.action_right,
|
config.action_right.clone(),
|
||||||
) {
|
) {
|
||||||
let forward_x = input.strength(forward).abs();
|
let forward_x = input.strength(forward).abs();
|
||||||
let backward_x = input.strength(backward).abs();
|
let backward_x = input.strength(backward).abs();
|
||||||
|
@ -178,19 +178,17 @@ fn movement_controls<S, A: 'static>(
|
||||||
if let Some(strength) = strength {
|
if let Some(strength) = strength {
|
||||||
direction *= strength;
|
direction *= strength;
|
||||||
}
|
}
|
||||||
let mut v = direction * **speed;
|
let mut v: Vector<Real> = (direction * **speed).into();
|
||||||
v = transform
|
v = position.position.rotation.transform_vector(&v);
|
||||||
.compute_matrix()
|
|
||||||
.transform_vector3(v.extend(0.))
|
|
||||||
.truncate();
|
|
||||||
velocity.linvel = v;
|
velocity.linvel = v;
|
||||||
|
//velocity.linvel = position.position.rotation.transform_vector(&velocity.linvel);
|
||||||
} else if destination.is_none() {
|
} else if destination.is_none() {
|
||||||
velocity.linvel = Vec2::ZERO;
|
velocity.linvel = Vec2::ZERO.into();
|
||||||
**speed = 0.;
|
**speed = 0.;
|
||||||
} else if sprinting {
|
} else if sprinting {
|
||||||
**speed = **max_speed;
|
**speed = **max_speed;
|
||||||
} else {
|
} else {
|
||||||
velocity.linvel = Vec2::ZERO;
|
velocity.linvel = Vec2::ZERO.into();
|
||||||
**speed = **max_speed / 3.;
|
**speed = **max_speed / 3.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,17 +200,24 @@ fn snap<S, A: 'static>(
|
||||||
input: Res<InputMap<A>>,
|
input: Res<InputMap<A>>,
|
||||||
config: Res<NavigationConfig<S, 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<
|
||||||
|
(
|
||||||
|
&mut RigidBodyPositionComponent,
|
||||||
|
&mut RigidBodyVelocityComponent,
|
||||||
|
&CardinalDirection,
|
||||||
|
),
|
||||||
|
With<Player>,
|
||||||
|
>,
|
||||||
) -> Result<(), Box<dyn Error>>
|
) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
S: 'static + Copy + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Clone + Send + Sync,
|
A: Hash + Eq + Clone + Send + Sync,
|
||||||
{
|
{
|
||||||
if let Some(action) = config.action_snap_left.clone() {
|
if let Some(action) = config.action_snap_left.clone() {
|
||||||
if input.just_active(action) {
|
if input.just_active(action) {
|
||||||
for (mut position, mut velocity, direction) in query.iter_mut() {
|
for (mut position, mut velocity, direction) in query.iter_mut() {
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
position.rotation = Quat::from_rotation_z(match direction {
|
position.position.rotation = UnitComplex::new(match direction {
|
||||||
CardinalDirection::North => PI,
|
CardinalDirection::North => PI,
|
||||||
CardinalDirection::East => PI / 2.,
|
CardinalDirection::East => PI / 2.,
|
||||||
CardinalDirection::South => 0.,
|
CardinalDirection::South => 0.,
|
||||||
|
@ -224,9 +229,9 @@ where
|
||||||
}
|
}
|
||||||
if let Some(action) = config.action_snap_right.clone() {
|
if let Some(action) = config.action_snap_right.clone() {
|
||||||
if input.just_active(action) {
|
if input.just_active(action) {
|
||||||
for (mut transform, mut velocity, direction) in query.iter_mut() {
|
for (mut position, mut velocity, direction) in query.iter_mut() {
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
transform.rotation = Quat::from_rotation_z(match direction {
|
position.position.rotation = UnitComplex::new(match direction {
|
||||||
CardinalDirection::North => 0.,
|
CardinalDirection::North => 0.,
|
||||||
CardinalDirection::East => PI * 1.5,
|
CardinalDirection::East => PI * 1.5,
|
||||||
CardinalDirection::South => PI,
|
CardinalDirection::South => PI,
|
||||||
|
@ -238,11 +243,11 @@ where
|
||||||
}
|
}
|
||||||
if let Some(action) = config.action_snap_cardinal.clone() {
|
if let Some(action) = config.action_snap_cardinal.clone() {
|
||||||
if input.just_active(action) {
|
if input.just_active(action) {
|
||||||
for (mut transform, mut velocity, direction) in query.iter_mut() {
|
for (mut position, 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();
|
||||||
transform.rotation = Quat::from_rotation_z(yaw);
|
position.position.rotation = UnitComplex::new(yaw);
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
tts.speak(direction.to_string(), true)?;
|
tts.speak(direction.to_string(), true)?;
|
||||||
}
|
}
|
||||||
|
@ -250,9 +255,9 @@ where
|
||||||
}
|
}
|
||||||
if let Some(action) = config.action_snap_reverse.clone() {
|
if let Some(action) = config.action_snap_reverse.clone() {
|
||||||
if input.just_active(action) {
|
if input.just_active(action) {
|
||||||
for (mut transform, mut velocity, _) in query.iter_mut() {
|
for (mut position, mut velocity, _) in query.iter_mut() {
|
||||||
**snapping = true;
|
**snapping = true;
|
||||||
transform.rotate(Quat::from_rotation_z(PI));
|
position.position.rotation *= UnitComplex::new(PI);
|
||||||
velocity.angvel = 0.;
|
velocity.angvel = 0.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,19 +302,13 @@ fn speak_direction(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_speed(mut commands: Commands, query: Query<Entity, (Added<Speed>, Without<Velocity>)>) {
|
fn remove_speed(
|
||||||
for entity in query.iter() {
|
removed: RemovedComponents<Speed>,
|
||||||
commands.entity(entity).insert(Velocity {
|
mut query: Query<&mut RigidBodyVelocityComponent>,
|
||||||
linvel: Vec2::ZERO,
|
) {
|
||||||
..default()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_speed(removed: RemovedComponents<Speed>, mut query: Query<&mut Velocity>) {
|
|
||||||
for entity in removed.iter() {
|
for entity in removed.iter() {
|
||||||
if let Ok(mut velocity) = query.get_mut(entity) {
|
if let Ok(mut velocity) = query.get_mut(entity) {
|
||||||
velocity.linvel = Vec2::ZERO;
|
velocity.linvel = Vec2::ZERO.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,8 +366,8 @@ impl<'a, S, A> Default for NavigationPlugin<'a, S, A> {
|
||||||
|
|
||||||
impl<'a, S, A> Plugin for NavigationPlugin<'a, S, A>
|
impl<'a, S, A> Plugin for NavigationPlugin<'a, S, A>
|
||||||
where
|
where
|
||||||
S: 'static + Clone + Copy + Debug + Eq + Hash + Send + Sync,
|
S: 'static + Clone + Debug + Eq + Hash + Send + Sync,
|
||||||
A: Hash + Eq + Copy + Send + Sync,
|
A: Hash + Eq + Clone + Send + Sync,
|
||||||
'a: 'static,
|
'a: 'static,
|
||||||
{
|
{
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
@ -386,7 +385,6 @@ where
|
||||||
.register_type::<Sprinting>()
|
.register_type::<Sprinting>()
|
||||||
.add_system(update_direction)
|
.add_system(update_direction)
|
||||||
.add_system(speak_direction.chain(error_handler))
|
.add_system(speak_direction.chain(error_handler))
|
||||||
.add_system(add_speed)
|
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, remove_speed);
|
.add_system_to_stage(CoreStage::PostUpdate, remove_speed);
|
||||||
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() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::marker::PhantomData;
|
use std::{collections::HashMap, marker::PhantomData};
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
ecs::entity::Entities,
|
ecs::entity::Entities,
|
||||||
|
@ -6,17 +6,18 @@ use bevy::{
|
||||||
tasks::{prelude::*, Task},
|
tasks::{prelude::*, Task},
|
||||||
};
|
};
|
||||||
use bevy_rapier2d::{
|
use bevy_rapier2d::{
|
||||||
na::{Isometry2, Vector2},
|
na::UnitComplex,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
rapier::prelude::{ColliderHandle, ColliderSet, QueryPipeline},
|
rapier::data::{ComponentSet, ComponentSetOption, Index},
|
||||||
};
|
};
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
use futures_lite::future;
|
use futures_lite::future;
|
||||||
|
|
||||||
use pathfinding::prelude::*;
|
use pathfinding::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::RunIfExistsExt,
|
commands::RunIfExistsExt,
|
||||||
core::PointLike,
|
core::{Coordinates, PointLike},
|
||||||
map::{Map, MapObstruction},
|
map::{Map, MapObstruction},
|
||||||
navigation::{RotationSpeed, Speed},
|
navigation::{RotationSpeed, Speed},
|
||||||
};
|
};
|
||||||
|
@ -59,14 +60,107 @@ pub fn find_path<D: 'static + Clone + Default + Send + Sync>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StaticColliderComponentsSet(
|
||||||
|
HashMap<Entity, (ColliderPosition, ColliderShape, ColliderFlags)>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'world, 'state>
|
||||||
|
From<(
|
||||||
|
&Query<
|
||||||
|
'world,
|
||||||
|
'state,
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&ColliderPositionComponent,
|
||||||
|
&ColliderShapeComponent,
|
||||||
|
&ColliderFlagsComponent,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
&Query<'world, 'state, &MapObstruction>,
|
||||||
|
)> for StaticColliderComponentsSet
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
query: (
|
||||||
|
&Query<(
|
||||||
|
Entity,
|
||||||
|
&ColliderPositionComponent,
|
||||||
|
&ColliderShapeComponent,
|
||||||
|
&ColliderFlagsComponent,
|
||||||
|
)>,
|
||||||
|
&Query<&MapObstruction>,
|
||||||
|
),
|
||||||
|
) -> Self {
|
||||||
|
let entries = query
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|(a, _, _, _)| query.1.get(*a).is_ok())
|
||||||
|
.map(|(a, b, c, d)| (a, **b, (*c).clone(), **d))
|
||||||
|
.collect::<Vec<(Entity, ColliderPosition, ColliderShape, ColliderFlags)>>();
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
for (e, a, b, c) in entries {
|
||||||
|
m.insert(e, (a, b, c));
|
||||||
|
}
|
||||||
|
Self(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentSet<ColliderShape> for StaticColliderComponentsSet {
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_each(&self, _f: impl FnMut(Index, &ColliderShape)) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentSetOption<ColliderShape> for StaticColliderComponentsSet {
|
||||||
|
fn get(&self, index: Index) -> Option<&ColliderShape> {
|
||||||
|
self.0.get(&index.entity()).map(|v| &v.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentSet<ColliderFlags> for StaticColliderComponentsSet {
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_each(&self, _f: impl FnMut(Index, &ColliderFlags)) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentSetOption<ColliderFlags> for StaticColliderComponentsSet {
|
||||||
|
fn get(&self, index: Index) -> Option<&ColliderFlags> {
|
||||||
|
self.0.get(&index.entity()).map(|v| &v.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentSet<ColliderPosition> for StaticColliderComponentsSet {
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_each(&self, _f: impl FnMut(Index, &ColliderPosition)) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentSetOption<ColliderPosition> for StaticColliderComponentsSet {
|
||||||
|
fn get(&self, index: Index) -> Option<&ColliderPosition> {
|
||||||
|
let v = self.0.get(&index.entity()).map(|v| &v.0);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn find_path_for_shape<D: 'static + Clone + Default + Send + Sync>(
|
fn find_path_for_shape<D: 'static + Clone + Default + Send + Sync>(
|
||||||
initiator: ColliderHandle,
|
initiator: Entity,
|
||||||
start: Transform,
|
start: Coordinates,
|
||||||
destination: Destination,
|
destination: Destination,
|
||||||
map: Map<D>,
|
|
||||||
query_pipeline: QueryPipeline,
|
query_pipeline: QueryPipeline,
|
||||||
collider_set: ColliderSet,
|
map: Map<D>,
|
||||||
shape: Collider,
|
collider_set: StaticColliderComponentsSet,
|
||||||
|
shape: ColliderShape,
|
||||||
) -> Option<Path> {
|
) -> Option<Path> {
|
||||||
let path = astar(
|
let path = astar(
|
||||||
&start.i32(),
|
&start.i32(),
|
||||||
|
@ -75,13 +169,13 @@ fn find_path_for_shape<D: 'static + Clone + Default + Send + Sync>(
|
||||||
if map.at(p.0 as usize, p.1 as usize).is_walkable() {
|
if map.at(p.0 as usize, p.1 as usize).is_walkable() {
|
||||||
for tile in map.get_available_exits(p.0 as usize, p.1 as usize) {
|
for tile in map.get_available_exits(p.0 as usize, p.1 as usize) {
|
||||||
let mut should_push = true;
|
let mut should_push = true;
|
||||||
let shape_pos = Isometry2::new(Vector2::new(tile.0 as f32, tile.1 as f32), 0.);
|
let shape_pos = Isometry::new(vector![tile.0 as f32, tile.1 as f32], 0.);
|
||||||
query_pipeline.intersections_with_shape(
|
query_pipeline.intersections_with_shape(
|
||||||
&collider_set,
|
&collider_set,
|
||||||
&shape_pos,
|
&shape_pos,
|
||||||
&*shape.raw,
|
&*shape,
|
||||||
InteractionGroups::all(),
|
InteractionGroups::all(),
|
||||||
Some(&|v| v != initiator),
|
Some(&|v| v.entity() != initiator),
|
||||||
|_handle| {
|
|_handle| {
|
||||||
should_push = false;
|
should_push = false;
|
||||||
false
|
false
|
||||||
|
@ -107,21 +201,16 @@ 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>,
|
pool: Res<AsyncComputeTaskPool>,
|
||||||
rapier_context: Res<RapierContext>,
|
query_pipeline: Res<QueryPipeline>,
|
||||||
obstructions: Query<&RapierColliderHandle, With<MapObstruction>>,
|
obstructions: Query<&MapObstruction>,
|
||||||
|
collider_query: QueryPipelineColliderComponentsQuery,
|
||||||
query: Query<
|
query: Query<
|
||||||
(
|
(Entity, &Destination, &Coordinates, &ColliderShapeComponent),
|
||||||
Entity,
|
|
||||||
&RapierColliderHandle,
|
|
||||||
&Destination,
|
|
||||||
&Transform,
|
|
||||||
&Collider,
|
|
||||||
),
|
|
||||||
Changed<Destination>,
|
Changed<Destination>,
|
||||||
>,
|
>,
|
||||||
map: Query<&Map<D>>,
|
map: Query<&Map<D>>,
|
||||||
) {
|
) {
|
||||||
for (entity, handle, destination, coordinates, shape) in query.iter() {
|
for (entity, destination, coordinates, shape) in query.iter() {
|
||||||
if coordinates.i32() == **destination {
|
if coordinates.i32() == **destination {
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
|
@ -135,31 +224,17 @@ fn calculate_path<D: 'static + Clone + Default + Send + Sync>(
|
||||||
if let Ok(map) = map.get_single() {
|
if let Ok(map) = map.get_single() {
|
||||||
let coordinates_clone = *coordinates;
|
let coordinates_clone = *coordinates;
|
||||||
let destination_clone = *destination;
|
let destination_clone = *destination;
|
||||||
let query_pipeline = rapier_context.query_pipeline.clone();
|
let query_pipeline_clone = query_pipeline.clone();
|
||||||
let map_clone = map.clone();
|
let map_clone = map.clone();
|
||||||
let handle_clone = *handle;
|
|
||||||
let mut collider_set = rapier_context.colliders.clone();
|
|
||||||
let mut to_remove = vec![];
|
|
||||||
for handle in collider_set.iter() {
|
|
||||||
if !obstructions.iter().map(|v| v.0).any(|x| x == handle.0) {
|
|
||||||
to_remove.push(handle.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !to_remove.is_empty() {
|
|
||||||
let mut islands = rapier_context.islands.clone();
|
|
||||||
let mut bodies = rapier_context.bodies.clone();
|
|
||||||
for handle in to_remove {
|
|
||||||
collider_set.remove(handle, &mut islands, &mut bodies, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let shape_clone = (*shape).clone();
|
let shape_clone = (*shape).clone();
|
||||||
|
let collider_set: StaticColliderComponentsSet = (&collider_query, &obstructions).into();
|
||||||
let task = pool.spawn(async move {
|
let task = pool.spawn(async move {
|
||||||
find_path_for_shape(
|
find_path_for_shape(
|
||||||
handle_clone.0,
|
entity,
|
||||||
coordinates_clone,
|
coordinates_clone,
|
||||||
destination_clone,
|
destination_clone,
|
||||||
|
query_pipeline_clone,
|
||||||
map_clone,
|
map_clone,
|
||||||
query_pipeline,
|
|
||||||
collider_set,
|
collider_set,
|
||||||
shape_clone,
|
shape_clone,
|
||||||
)
|
)
|
||||||
|
@ -203,14 +278,18 @@ fn negotiate_path(
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
&mut Path,
|
&mut Path,
|
||||||
&mut Transform,
|
&mut RigidBodyPositionComponent,
|
||||||
&mut Velocity,
|
&mut RigidBodyVelocityComponent,
|
||||||
&Speed,
|
&Speed,
|
||||||
Option<&RotationSpeed>,
|
Option<&RotationSpeed>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut path, mut transform, mut velocity, speed, rotation_speed) in query.iter_mut() {
|
for (entity, mut path, mut position, mut velocity, speed, rotation_speed) in query.iter_mut() {
|
||||||
let start_i32 = (transform.translation.x, transform.translation.y).i32();
|
let start_i32 = (
|
||||||
|
position.position.translation.x,
|
||||||
|
position.position.translation.y,
|
||||||
|
)
|
||||||
|
.i32();
|
||||||
let mut new_path = path
|
let mut new_path = path
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -219,16 +298,19 @@ fn negotiate_path(
|
||||||
new_path.retain(|v| *v != start_i32);
|
new_path.retain(|v| *v != start_i32);
|
||||||
**path = new_path;
|
**path = new_path;
|
||||||
if let Some(next) = path.first() {
|
if let Some(next) = path.first() {
|
||||||
let start = Vec2::new(transform.translation.x, transform.translation.y);
|
let start = Vec2::new(
|
||||||
|
position.position.translation.x,
|
||||||
|
position.position.translation.y,
|
||||||
|
);
|
||||||
let next = Vec2::new(next.0 as f32 + 0.5, next.1 as f32 + 0.5);
|
let next = Vec2::new(next.0 as f32 + 0.5, next.1 as f32 + 0.5);
|
||||||
let mut direction = next - start;
|
let mut direction = next - start;
|
||||||
direction = direction.normalize();
|
direction = direction.normalize();
|
||||||
direction *= **speed;
|
direction *= **speed;
|
||||||
velocity.linvel = direction;
|
velocity.linvel = direction.into();
|
||||||
if rotation_speed.is_some() {
|
if rotation_speed.is_some() {
|
||||||
let v = next - start;
|
let v = next - start;
|
||||||
let angle = v.y.atan2(v.x);
|
let angle = v.y.atan2(v.x);
|
||||||
transform.rotation = Quat::from_rotation_z(angle);
|
position.position.rotation = UnitComplex::new(angle);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -238,7 +320,7 @@ fn negotiate_path(
|
||||||
.remove::<NoPath>()
|
.remove::<NoPath>()
|
||||||
.remove::<Destination>()
|
.remove::<Destination>()
|
||||||
.remove::<Speed>();
|
.remove::<Speed>();
|
||||||
velocity.linvel = Vec2::ZERO;
|
velocity.linvel = Vec2::ZERO.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use bevy::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
bevy_openal::{Listener, Sound},
|
bevy_openal::{Listener, Sound},
|
||||||
commands::RunIfExistsExt,
|
commands::RunIfExistsExt,
|
||||||
|
derive_more::{Deref, DerefMut},
|
||||||
sound::SoundIcon,
|
sound::SoundIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
22
src/sound.rs
22
src/sound.rs
|
@ -7,9 +7,9 @@ use rand::random;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::RunIfExistsExt,
|
commands::RunIfExistsExt,
|
||||||
core::{CoreConfig, Player, PointLike},
|
core::{Coordinates, CoreConfig, Player, PointLike},
|
||||||
exploration::ExplorationFocused,
|
exploration::ExplorationFocused,
|
||||||
visibility::{Visible, VisibleEntities},
|
visibility::VisibleEntities,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Component, Clone, Debug, Reflect)]
|
#[derive(Component, Clone, Debug, Reflect)]
|
||||||
|
@ -92,23 +92,25 @@ fn add_footstep_sounds(
|
||||||
) {
|
) {
|
||||||
for (entity, footstep) in footsteps.iter() {
|
for (entity, footstep) in footsteps.iter() {
|
||||||
let buffer = assets.get_handle(footstep.sound);
|
let buffer = assets.get_handle(footstep.sound);
|
||||||
|
let gain = footstep.gain;
|
||||||
commands.run_if_exists(entity, move |mut entity| {
|
commands.run_if_exists(entity, move |mut entity| {
|
||||||
entity.insert(Sound {
|
entity.insert(Sound {
|
||||||
buffer,
|
buffer,
|
||||||
state: SoundState::Stopped,
|
state: SoundState::Stopped,
|
||||||
..default()
|
gain,
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn footstep(
|
fn footstep(
|
||||||
mut last_step_distance: Local<HashMap<Entity, (f32, Transform)>>,
|
mut last_step_distance: Local<HashMap<Entity, (f32, Coordinates)>>,
|
||||||
mut footsteps: Query<(Entity, &Footstep, &Parent, &mut Sound), Changed<GlobalTransform>>,
|
mut footsteps: Query<(Entity, &Footstep, &Parent, &mut Sound), Changed<GlobalTransform>>,
|
||||||
transforms_storage: Query<&Transform>,
|
coordinates_storage: Query<&Coordinates>,
|
||||||
) {
|
) {
|
||||||
for (entity, footstep, parent, mut sound) in footsteps.iter_mut() {
|
for (entity, footstep, parent, mut sound) in footsteps.iter_mut() {
|
||||||
let coordinates = transforms_storage.get(**parent).unwrap();
|
let coordinates = coordinates_storage.get(**parent).unwrap();
|
||||||
if let Some(last) = last_step_distance.get(&entity) {
|
if let Some(last) = last_step_distance.get(&entity) {
|
||||||
let distance = last.0 + (last.1.distance(coordinates));
|
let distance = last.0 + (last.1.distance(coordinates));
|
||||||
if distance >= footstep.step_length {
|
if distance >= footstep.step_length {
|
||||||
|
@ -156,7 +158,7 @@ fn add_sound_icon_sounds(
|
||||||
reference_distance,
|
reference_distance,
|
||||||
max_distance,
|
max_distance,
|
||||||
rolloff_factor,
|
rolloff_factor,
|
||||||
..default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,7 +172,7 @@ fn sound_icon<S>(
|
||||||
mut icons: Query<(
|
mut icons: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
&mut SoundIcon,
|
&mut SoundIcon,
|
||||||
Option<&Visible>,
|
Option<&Coordinates>,
|
||||||
Option<&Parent>,
|
Option<&Parent>,
|
||||||
&mut Sound,
|
&mut Sound,
|
||||||
)>,
|
)>,
|
||||||
|
@ -184,8 +186,8 @@ fn sound_icon<S>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for visible in viewers.iter() {
|
for visible in viewers.iter() {
|
||||||
for (icon_entity, mut icon, visibility, parent, mut sound) in icons.iter_mut() {
|
for (icon_entity, mut icon, coordinates, parent, mut sound) in icons.iter_mut() {
|
||||||
let entity = if visibility.is_some() {
|
let entity = if coordinates.is_some() {
|
||||||
icon_entity
|
icon_entity
|
||||||
} else {
|
} else {
|
||||||
**parent.unwrap()
|
**parent.unwrap()
|
||||||
|
|
|
@ -8,14 +8,15 @@ use bevy::{
|
||||||
core::{FixedTimestep, FloatOrd},
|
core::{FixedTimestep, FloatOrd},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use bevy_rapier2d::na::{self, Point2};
|
use bevy_rapier2d::na;
|
||||||
use coord_2d::{Coord, Size};
|
use coord_2d::{Coord, Size};
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
use shadowcast::{vision_distance, Context, InputGrid};
|
use shadowcast::{vision_distance, Context, InputGrid};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bevy_rapier2d::prelude::*,
|
bevy_rapier2d::prelude::*,
|
||||||
commands::RunIfExistsExt,
|
commands::RunIfExistsExt,
|
||||||
core::{Angle, Player, PointLike},
|
core::{Angle, Coordinates, Player, PointLike},
|
||||||
log::Log,
|
log::Log,
|
||||||
map::{ITileType, Map, MapConfig},
|
map::{ITileType, Map, MapConfig},
|
||||||
};
|
};
|
||||||
|
@ -51,13 +52,13 @@ impl Viewshed {
|
||||||
pub fn is_point_visible(&self, point: &dyn PointLike) -> bool {
|
pub fn is_point_visible(&self, point: &dyn PointLike) -> bool {
|
||||||
self.visible_points.contains(&point.into())
|
self.visible_points.contains(&point.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update<D: 'static + Clone + Default + Send + Sync>(
|
fn update<D: 'static + Clone + Default + Send + Sync>(
|
||||||
&mut self,
|
&mut self,
|
||||||
viewer_entity: &Entity,
|
viewer_entity: &Entity,
|
||||||
visible_entities: &mut VisibleEntities,
|
visible_entities: &mut VisibleEntities,
|
||||||
start: &dyn PointLike,
|
start: &dyn PointLike,
|
||||||
rapier_context: &RapierContext,
|
query_pipeline: &QueryPipeline,
|
||||||
|
collider_query: &QueryPipelineColliderComponentsQuery,
|
||||||
map: &Map<D>,
|
map: &Map<D>,
|
||||||
visible_query: &Query<&Visible>,
|
visible_query: &Query<&Visible>,
|
||||||
events: &mut EventWriter<VisibilityChanged>,
|
events: &mut EventWriter<VisibilityChanged>,
|
||||||
|
@ -65,27 +66,21 @@ impl Viewshed {
|
||||||
) {
|
) {
|
||||||
let mut context: Context<u8> = Context::default();
|
let mut context: Context<u8> = Context::default();
|
||||||
let vision_distance = vision_distance::Circle::new(self.range);
|
let vision_distance = vision_distance::Circle::new(self.range);
|
||||||
let shape = Collider::cuboid(0.5, 0.5);
|
let shape = ColliderShape::cuboid(0.5, 0.5);
|
||||||
let origin = Point2::new(start.x(), start.y());
|
let origin = point!(start.x(), start.y());
|
||||||
let coord = Coord::new(start.x_i32(), start.y_i32());
|
let coord = Coord::new(start.x_i32(), start.y_i32());
|
||||||
|
let collider_set = QueryPipelineColliderComponentsSet(collider_query);
|
||||||
let mut new_visible_entities = HashSet::new();
|
let mut new_visible_entities = HashSet::new();
|
||||||
let visibility_grid = VisibilityGrid(
|
let visibility_grid = VisibilityGrid(
|
||||||
map,
|
map,
|
||||||
RefCell::new(Box::new(|coord: Coord| {
|
RefCell::new(Box::new(|coord: Coord| {
|
||||||
let dest = Point2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5);
|
let dest = point!(coord.x as f32 + 0.5, coord.y as f32 + 0.5);
|
||||||
if na::distance(&origin, &dest) > self.range as f32 {
|
|
||||||
return u8::MAX;
|
|
||||||
}
|
|
||||||
if let Some((opacity, entities)) = cache.get(&(FloatOrd(dest.x), FloatOrd(dest.y)))
|
if let Some((opacity, entities)) = cache.get(&(FloatOrd(dest.x), FloatOrd(dest.y)))
|
||||||
{
|
{
|
||||||
for e in entities {
|
for e in entities {
|
||||||
new_visible_entities.insert(*e);
|
new_visible_entities.insert(*e);
|
||||||
}
|
}
|
||||||
if entities.contains(&viewer_entity) {
|
return *opacity;
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return *opacity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let tile = map.at(coord.x as usize, coord.y as usize);
|
let tile = map.at(coord.x as usize, coord.y as usize);
|
||||||
if tile.blocks_visibility() {
|
if tile.blocks_visibility() {
|
||||||
|
@ -95,32 +90,37 @@ impl Viewshed {
|
||||||
);
|
);
|
||||||
return u8::MAX;
|
return u8::MAX;
|
||||||
}
|
}
|
||||||
|
if na::distance(&origin, &dest) > self.range as f32 {
|
||||||
|
return u8::MAX;
|
||||||
|
}
|
||||||
let mut opacity = 0;
|
let mut opacity = 0;
|
||||||
let mut entities = HashSet::new();
|
let mut entities = HashSet::new();
|
||||||
let shape_pos = Vec2::new(dest.x, dest.y);
|
let shape_pos = (Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5), 0.);
|
||||||
rapier_context.intersections_with_shape(
|
query_pipeline.intersections_with_shape(
|
||||||
shape_pos,
|
&collider_set,
|
||||||
0.,
|
&shape_pos.into(),
|
||||||
&shape,
|
&*shape,
|
||||||
InteractionGroups::all(),
|
InteractionGroups::all(),
|
||||||
Some(&|v| visible_query.get(v).is_ok()),
|
Some(&|v| {
|
||||||
|entity| {
|
v.entity() != *viewer_entity && visible_query.get(v.entity()).is_ok()
|
||||||
|
}),
|
||||||
|
|handle| {
|
||||||
|
let entity = handle.entity();
|
||||||
entities.insert(entity);
|
entities.insert(entity);
|
||||||
if let Ok(visible) = visible_query.get(entity) {
|
if let Ok(visible) = visible_query.get(entity) {
|
||||||
opacity = opacity.max(**visible);
|
opacity = **visible;
|
||||||
}
|
}
|
||||||
|
cache.insert(
|
||||||
|
(FloatOrd(dest.x), FloatOrd(dest.y)),
|
||||||
|
(opacity, entities.clone()),
|
||||||
|
);
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
for e in &entities {
|
for e in &entities {
|
||||||
new_visible_entities.insert(*e);
|
new_visible_entities.insert(*e);
|
||||||
}
|
}
|
||||||
if entities.contains(&viewer_entity) {
|
opacity
|
||||||
0
|
|
||||||
} else {
|
|
||||||
cache.insert((FloatOrd(dest.x), FloatOrd(dest.y)), (opacity, entities));
|
|
||||||
opacity
|
|
||||||
}
|
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
let mut new_visible = HashSet::new();
|
let mut new_visible = HashSet::new();
|
||||||
|
@ -187,8 +187,11 @@ pub struct ViewshedBundle {
|
||||||
impl ViewshedBundle {
|
impl ViewshedBundle {
|
||||||
pub fn new(range: u32) -> Self {
|
pub fn new(range: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
viewshed: Viewshed { range, ..default() },
|
viewshed: Viewshed {
|
||||||
..default()
|
range,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,24 +268,50 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
config: Res<RapierConfiguration>,
|
config: Res<RapierConfiguration>,
|
||||||
positions: Query<
|
positions: Query<
|
||||||
(Entity, &Transform, &Collider, Option<&LastCoordinates>),
|
(
|
||||||
Or<(Changed<Transform>, Changed<Visible>)>,
|
Entity,
|
||||||
|
Option<&RigidBodyPositionComponent>,
|
||||||
|
Option<&ColliderPositionComponent>,
|
||||||
|
Option<&LastCoordinates>,
|
||||||
|
),
|
||||||
|
Or<(
|
||||||
|
Changed<RigidBodyPositionComponent>,
|
||||||
|
Changed<ColliderPositionComponent>,
|
||||||
|
)>,
|
||||||
>,
|
>,
|
||||||
visible: Query<&Visible>,
|
visible: Query<&Visible>,
|
||||||
mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>,
|
mut viewers: Query<(
|
||||||
|
Entity,
|
||||||
|
&mut Viewshed,
|
||||||
|
&mut VisibleEntities,
|
||||||
|
&Coordinates,
|
||||||
|
Option<&LastCoordinates>,
|
||||||
|
)>,
|
||||||
map: Query<&Map<D>>,
|
map: Query<&Map<D>>,
|
||||||
rapier_context: Res<RapierContext>,
|
query_pipeline: Res<QueryPipeline>,
|
||||||
|
collider_query: QueryPipelineColliderComponentsQuery,
|
||||||
mut changed: EventWriter<VisibilityChanged>,
|
mut changed: EventWriter<VisibilityChanged>,
|
||||||
) {
|
) {
|
||||||
if !config.query_pipeline_active {
|
if !config.query_pipeline_active {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut to_update = HashSet::new();
|
let mut to_update = HashSet::new();
|
||||||
for (entity, transform, collider, last_coordinates) in positions.iter() {
|
for (entity, body_position, collider_position, last_coordinates) in positions.iter() {
|
||||||
if to_update.contains(&entity) {
|
if to_update.contains(&entity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let coordinates = (transform.translation.x, transform.translation.y);
|
let coordinates = if let Some(body_position) = body_position {
|
||||||
|
(
|
||||||
|
body_position.position.translation.x,
|
||||||
|
body_position.position.translation.y,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let collider_position = collider_position.unwrap();
|
||||||
|
(
|
||||||
|
collider_position.translation.x,
|
||||||
|
collider_position.translation.y,
|
||||||
|
)
|
||||||
|
};
|
||||||
let coordinates_i32 = coordinates.i32();
|
let coordinates_i32 = coordinates.i32();
|
||||||
if viewers.get_mut(entity).is_ok() {
|
if viewers.get_mut(entity).is_ok() {
|
||||||
commands.run_if_exists(entity, move |mut entity| {
|
commands.run_if_exists(entity, move |mut entity| {
|
||||||
|
@ -299,26 +328,34 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
|
||||||
if visible.get(entity).is_err() {
|
if visible.get(entity).is_err() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (viewer_entity, viewshed, visible_entities, viewer_coordinates) in viewers.iter_mut() {
|
for (
|
||||||
if viewer_entity != entity {
|
viewer_entity,
|
||||||
let forward = transform.local_x();
|
viewshed,
|
||||||
let rotation = forward.y.atan2(forward.x);
|
visible_entities,
|
||||||
let distance = collider.distance_to_point(
|
viewer_coordinates,
|
||||||
transform.translation.truncate(),
|
viewer_last_coordinates,
|
||||||
rotation,
|
) in viewers.iter_mut()
|
||||||
viewer_coordinates.translation.truncate(),
|
{
|
||||||
true,
|
if let (Some(last_coordinates), Some(viewer_last_coordinates)) =
|
||||||
);
|
(last_coordinates, viewer_last_coordinates)
|
||||||
if distance <= viewshed.range as f32 || visible_entities.contains(&entity) {
|
{
|
||||||
to_update.insert(viewer_entity);
|
if viewer_coordinates.i32() == **viewer_last_coordinates
|
||||||
to_update.insert(entity);
|
&& coordinates.i32() == **last_coordinates
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if viewer_entity != entity
|
||||||
|
&& (viewer_coordinates.distance(&coordinates) <= viewshed.range as f32
|
||||||
|
|| visible_entities.contains(&entity))
|
||||||
|
{
|
||||||
|
to_update.insert(viewer_entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut cache = HashMap::new();
|
let mut cache = HashMap::new();
|
||||||
for entity in to_update.iter() {
|
for entity in to_update.iter() {
|
||||||
if let Ok((viewer_entity, mut viewshed, mut visible_entities, viewer_coordinates)) =
|
if let Ok((viewer_entity, mut viewshed, mut visible_entities, viewer_coordinates, _)) =
|
||||||
viewers.get_mut(*entity)
|
viewers.get_mut(*entity)
|
||||||
{
|
{
|
||||||
if let Ok(map) = map.get_single() {
|
if let Ok(map) = map.get_single() {
|
||||||
|
@ -326,7 +363,8 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
|
||||||
&viewer_entity,
|
&viewer_entity,
|
||||||
&mut visible_entities,
|
&mut visible_entities,
|
||||||
viewer_coordinates,
|
viewer_coordinates,
|
||||||
&*rapier_context,
|
&*query_pipeline,
|
||||||
|
&collider_query,
|
||||||
map,
|
map,
|
||||||
&visible,
|
&visible,
|
||||||
&mut changed,
|
&mut changed,
|
||||||
|
@ -339,9 +377,10 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
|
||||||
|
|
||||||
fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
|
fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
|
||||||
removed: RemovedComponents<Visible>,
|
removed: RemovedComponents<Visible>,
|
||||||
mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>,
|
mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Coordinates)>,
|
||||||
map: Query<&Map<D>>,
|
map: Query<&Map<D>>,
|
||||||
rapier_context: Res<RapierContext>,
|
query_pipeline: Res<QueryPipeline>,
|
||||||
|
collider_query: QueryPipelineColliderComponentsQuery,
|
||||||
visible: Query<&Visible>,
|
visible: Query<&Visible>,
|
||||||
mut changed: EventWriter<VisibilityChanged>,
|
mut changed: EventWriter<VisibilityChanged>,
|
||||||
) {
|
) {
|
||||||
|
@ -357,7 +396,8 @@ fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
|
||||||
&viewer_entity,
|
&viewer_entity,
|
||||||
&mut visible_entities,
|
&mut visible_entities,
|
||||||
start,
|
start,
|
||||||
&*rapier_context,
|
&*query_pipeline,
|
||||||
|
&collider_query,
|
||||||
map,
|
map,
|
||||||
&visible,
|
&visible,
|
||||||
&mut changed,
|
&mut changed,
|
||||||
|
@ -390,8 +430,15 @@ fn log_visible(
|
||||||
mut recently_lost: Local<HashMap<Entity, Timer>>,
|
mut recently_lost: Local<HashMap<Entity, Timer>>,
|
||||||
mut events: EventReader<VisibilityChanged>,
|
mut events: EventReader<VisibilityChanged>,
|
||||||
mut log: Query<&mut Log>,
|
mut log: Query<&mut Log>,
|
||||||
viewers: Query<(Entity, &Transform), (With<Player>, With<Viewshed>)>,
|
viewers: Query<(Entity, &Coordinates, &Transform), (With<Player>, With<Viewshed>)>,
|
||||||
visible: Query<(&Name, &Transform), Without<DontLogWhenVisible>>,
|
visible: Query<
|
||||||
|
(
|
||||||
|
&Name,
|
||||||
|
Option<&RigidBodyPositionComponent>,
|
||||||
|
Option<&ColliderPositionComponent>,
|
||||||
|
),
|
||||||
|
Without<DontLogWhenVisible>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
for timer in recently_lost.values_mut() {
|
for timer in recently_lost.values_mut() {
|
||||||
timer.tick(time.delta());
|
timer.tick(time.delta());
|
||||||
|
@ -402,23 +449,25 @@ fn log_visible(
|
||||||
VisibilityChanged::Gained { viewer, .. } => viewer,
|
VisibilityChanged::Gained { viewer, .. } => viewer,
|
||||||
VisibilityChanged::Lost { viewer, .. } => viewer,
|
VisibilityChanged::Lost { viewer, .. } => viewer,
|
||||||
};
|
};
|
||||||
if let Ok((viewer_entity, viewer_transform)) = viewers.get(*viewer) {
|
if let Ok((viewer_entity, viewer_coordinates, viewer_transform)) = viewers.get(*viewer) {
|
||||||
if let VisibilityChanged::Gained { viewed, .. } = event {
|
if let VisibilityChanged::Gained { viewed, .. } = event {
|
||||||
if *viewed == viewer_entity || recently_lost.contains_key(viewed) {
|
if *viewed == viewer_entity || recently_lost.contains_key(viewed) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Ok((name, transform)) = visible.get(*viewed) {
|
if let Ok((name, body_position, collider_position)) = visible.get(*viewed) {
|
||||||
let viewed_coordinates = (transform.translation.x, transform.translation.y);
|
let viewed_coordinates = if let Some(p) = body_position {
|
||||||
|
(p.position.translation.x, p.position.translation.y)
|
||||||
|
} else if let Some(p) = collider_position {
|
||||||
|
(p.translation.x, p.translation.y)
|
||||||
|
} else {
|
||||||
|
(0., 0.)
|
||||||
|
};
|
||||||
let forward = viewer_transform.local_x();
|
let forward = viewer_transform.local_x();
|
||||||
let yaw = Angle::Radians(forward.y.atan2(forward.x));
|
let yaw = Angle::Radians(forward.y.atan2(forward.x));
|
||||||
let viewer_coordinates = (
|
|
||||||
viewer_transform.translation.x,
|
|
||||||
viewer_transform.translation.y,
|
|
||||||
);
|
|
||||||
let location =
|
let location =
|
||||||
viewer_coordinates.direction_and_distance(&viewed_coordinates, Some(yaw));
|
viewer_coordinates.direction_and_distance(&viewed_coordinates, Some(yaw));
|
||||||
if let Ok(mut log) = log.get_single_mut() {
|
if let Ok(mut log) = log.get_single_mut() {
|
||||||
log.push(format!("{}: {location}", name));
|
log.push(format!("{}: {location}", **name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let VisibilityChanged::Lost { viewed, .. } = event {
|
} else if let VisibilityChanged::Lost { viewed, .. } = event {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user