Compare commits

..

No commits in common. "615af720cbd90747144de2b3e250c66737db6e2a" and "37d0f0fcde1ffdd968016e2991346cd8a8599d43" have entirely different histories.

12 changed files with 600 additions and 276 deletions

View File

@ -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" }

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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() {

View File

@ -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();
} }
} }
} }

View File

@ -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,
}; };

View File

@ -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()

View File

@ -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 {