diff --git a/src/core.rs b/src/core.rs index d70e236..3880a00 100644 --- a/src/core.rs +++ b/src/core.rs @@ -383,27 +383,6 @@ impl PointLike for here_be_dragons::geometry::Point { } } -#[macro_export] -macro_rules! impl_pointlike_for_tuple_component { - ($source:ty) => { - impl PointLike for $source { - fn x(&self) -> f32 { - self.0 .0 as f32 - } - - fn y(&self) -> f32 { - self.0 .1 as f32 - } - } - }; -} - -impl From<&dyn PointLike> for (i32, i32) { - fn from(val: &dyn PointLike) -> Self { - val.i32() - } -} - pub trait TransformExt { fn yaw(&self) -> Rot2; } diff --git a/src/exploration.rs b/src/exploration.rs index 60bff1a..3270381 100644 --- a/src/exploration.rs +++ b/src/exploration.rs @@ -42,9 +42,17 @@ pub struct ExplorationFocused; #[derive(Component, Clone, Copy, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] -pub struct Exploring(pub (f32, f32)); +pub struct Exploring(pub Vec2); -impl_pointlike_for_tuple_component!(Exploring); +impl PointLike for Exploring { + fn x(&self) -> f32 { + self.0.x + } + + fn y(&self) -> f32 { + self.0.y + } +} #[derive(Component, Clone, Debug, Default, Deref, DerefMut)] pub struct FocusedExplorationType(pub Option) @@ -122,34 +130,45 @@ fn exploration_type_focus( mut tts: ResMut, explorers: Query<( Entity, + &GlobalTransform, &ActionState, &VisibleEntities, &FocusedExplorationType, Option<&Exploring>, )>, - features: Query<(Entity, &Transform, &ExplorationType)>, + features: Query<(Entity, &GlobalTransform, &ExplorationType)>, ) -> Result<(), Box> where ExplorationType: Component + Default + PartialEq, { - for (entity, actions, visible_entities, focused_type, exploring) in &explorers { + for (entity, transform, actions, visible_entities, focused_type, exploring) in &explorers { let mut features = features .iter() .filter(|v| visible_entities.contains(&v.0)) - .map(|v| (v.1.trunc(), v.2)) - .collect::>(); + .map(|v| (v.1.translation().truncate(), v.2)) + .collect::>(); if features.is_empty() { tts.speak("Nothing visible.", true)?; return Ok(()); } - features.sort_by(|(c1, _), (c2, _)| c1.partial_cmp(c2).unwrap()); + features.sort_by(|(c1, _), (c2, _)| { + transform + .translation() + .truncate() + .distance(*c1) + .partial_cmp(&transform.translation().truncate().distance(*c2)) + .unwrap() + }); if let Some(focused) = &focused_type.0 { features.retain(|(_, t)| **t == *focused); } - let mut target: Option<&((f32, f32), &ExplorationType)> = None; + let mut target = None; if actions.just_pressed(&ExplorationAction::FocusNext) { if let Some(exploring) = exploring { - target = features.iter().find(|(c, _)| *c > **exploring); + target = features.iter().find(|(c, _)| { + transform.translation().truncate().distance(*c) + > transform.translation().truncate().distance(**exploring) + }); if target.is_none() { target = features.first(); } @@ -159,7 +178,10 @@ where } else if actions.just_pressed(&ExplorationAction::FocusPrev) { if let Some(exploring) = exploring { features.reverse(); - target = features.iter().find(|(c, _)| *c < **exploring); + target = features.iter().find(|(c, _)| { + transform.translation().truncate().distance(*c) + < transform.translation().truncate().distance(**exploring) + }); if target.is_none() { target = features.first(); } @@ -170,7 +192,7 @@ where if let Some((coordinates, _)) = target { commands .entity(entity) - .insert(Exploring(coordinates.trunc())); + .insert(Exploring(*coordinates)); } } Ok(()) @@ -212,7 +234,7 @@ fn exploration_focus( ( Entity, &ActionState, - &Transform, + &GlobalTransform, Option<&Exploring>, ), With, @@ -221,22 +243,20 @@ fn exploration_focus( MapData: 'static + Clone + Default + Send + Sync, { for (entity, actions, transform, exploring) in &explorers { - let coordinates = transform.translation; let mut exploring = if let Some(exploring) = exploring { **exploring } else { - let floor = coordinates.floor(); - (floor.x, floor.y) + transform.translation().truncate() }; let orig = exploring; if actions.just_pressed(&ExplorationAction::Forward) { - exploring.1 += 1.; + exploring.y += 1.; } else if actions.just_pressed(&ExplorationAction::Backward) { - exploring.1 -= 1.; + exploring.y -= 1.; } else if actions.just_pressed(&ExplorationAction::Left) { - exploring.0 -= 1.; + exploring.x -= 1.; } else if actions.just_pressed(&ExplorationAction::Right) { - exploring.0 += 1.; + exploring.x += 1.; } let dimensions = if let Ok(map) = map.get_single() { Some((map.width as f32, map.height as f32)) @@ -244,11 +264,11 @@ fn exploration_focus( None }; if let Some((width, height)) = dimensions { - if exploring.0 >= width || exploring.1 >= height { + if exploring.x >= width || exploring.y >= height { return; } } - if orig != exploring && exploring.0 >= 0. && exploring.1 >= 0. { + if orig != exploring && exploring.x >= 0. && exploring.y >= 0. { commands.entity(entity).insert(Exploring(exploring)); } } @@ -269,7 +289,7 @@ fn navigate_to_explored( if actions.just_pressed(&ExplorationAction::NavigateTo) && known { commands .entity(entity) - .insert(Destination((point.x_i32(), point.y_i32()))); + .insert(Destination(point.as_ivec2())); } } } @@ -294,14 +314,14 @@ where if let Ok((coordinates, exploring, viewshed)) = explorer.get_single() { let coordinates = coordinates.trunc(); let point = **exploring; - let shape = Collider::rectangle(0.5 - f32::EPSILON, 0.5 - f32::EPSILON); + let shape = Collider::rectangle(1. - f32::EPSILON, 1. - f32::EPSILON); let (known, idx) = if let Ok((map, revealed_tiles)) = map.get_single() { let idx = point.to_index(map.width); (revealed_tiles[idx], Some(idx)) } else { (false, None) }; - let visible = viewshed.is_point_visible(exploring); + let visible = viewshed.is_point_visible(exploring.as_ivec2()); let fog_of_war = !visible && known; let description: String = if known || visible { let mut tokens: Vec = vec![]; diff --git a/src/map.rs b/src/map.rs index b8ffc4c..83496d9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -98,7 +98,7 @@ impl Default for TileBundle { fn default() -> Self { Self { transform: default(), - collider: Collider::rectangle(0.5, 0.5), + collider: Collider::rectangle(1., 1.), rigid_body: RigidBody::Static, map_obstruction: default(), } @@ -136,8 +136,8 @@ impl ZoneBundle { impl From<&MRect> for ZoneBundle { fn from(rect: &MRect) -> Self { let collider = Collider::rectangle( - rect.width() as f32 / 2. + 0.5, - rect.height() as f32 / 2. + 0.5, + rect.width() as f32 + 1., + rect.height() as f32 + 0.1, ); let position = Vec2::new(rect.center().x(), rect.center().y()); Self::new(collider, position) @@ -315,7 +315,7 @@ fn spawn_portal_colliders( for portal_entity in &portals { commands .entity(portal_entity) - .insert((Collider::rectangle(0.5, 0.5), Sensor)); + .insert((Collider::rectangle(1., 1.), Sensor)); } commands.entity(entity).remove::(); } diff --git a/src/pathfinding.rs b/src/pathfinding.rs index 6f5bdd8..3de085d 100644 --- a/src/pathfinding.rs +++ b/src/pathfinding.rs @@ -4,7 +4,7 @@ use leafwing_input_manager::{plugin::InputManagerSystem, prelude::*}; use pathfinding::prelude::*; use crate::{ - core::{PointLike, TransformExt}, + core::{GlobalTransformExt, TransformExt}, map::Map, navigation::{NavigationAction, RotationSpeed, Speed}, }; @@ -20,10 +20,7 @@ impl Actionlike for NegotiatePathAction { #[derive(Component, Clone, Copy, Debug, Default, Deref, DerefMut, Eq, Hash, PartialEq, Reflect)] #[reflect(Component)] -pub struct Destination(pub (i32, i32)); - -impl_pointlike_for_tuple_component!(Destination); -impl_pointlike_for_tuple_component!(&Destination); +pub struct Destination(pub IVec2); #[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component)] @@ -31,11 +28,11 @@ pub struct NoPath; #[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] -pub struct Path(pub Vec<(i32, i32)>); +pub struct Path(pub Vec); #[derive(Component, Clone, Debug, Default, Reflect, Deref, DerefMut)] #[reflect(Component)] -pub struct CostMap(pub HashMap<(i32, i32), f32>); +pub struct CostMap(pub HashMap); #[derive(Bundle, Deref, DerefMut)] pub struct PathfindingControlsBundle(pub ActionState); @@ -49,36 +46,34 @@ impl Default for PathfindingControlsBundle { } pub fn find_path( - start: &dyn PointLike, - destination: &dyn PointLike, + start: IVec2, + destination: IVec2, map: &Map, cost_map: &Option, -) -> Option<(Vec<(i32, i32)>, u32)> { +) -> Option<(Vec, u32)> { astar( - &start.into(), + &start, |p| { - let mut successors: Vec<((i32, i32), u32)> = vec![]; - if p.0 >= 0 && p.1 >= 0 { - if let Some(tile) = map.at(p.0 as usize, p.1 as usize) { + let mut successors: Vec<(IVec2, u32)> = vec![]; + if p.x >= 0 && p.y >= 0 { + if let Some(tile) = map.at(p.x as usize, p.y as usize) { if tile.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.x as usize, p.y as usize) { let mut cost = tile.2 * 100.; if let Some(cost_map) = cost_map { - if let Some(modifier) = - cost_map.get(&(tile.0 as i32, tile.1 as i32)) - { + if let Some(modifier) = cost_map.get(p) { cost *= modifier; } } - successors.push(((tile.0 as i32, tile.1 as i32), cost as u32)); + successors.push((IVec2::new(tile.0 as i32, tile.1 as i32), cost as u32)); } } } } successors }, - |p| (p.distance_squared(destination) * 100.) as u32, - |p| *p == destination.into(), + |p| (p.distance_squared(destination) * 100) as u32, + |p| *p == destination, ) } @@ -89,7 +84,7 @@ fn calculate_path( ( Entity, &Destination, - &Transform, + &GlobalTransform, &Collider, Option<&CostMap>, Option<&mut ActionState>, @@ -97,9 +92,9 @@ fn calculate_path( Changed, >, ) { - for (entity, destination, start, collider, cost_map, actions) in &mut query { + for (entity, destination, transform, collider, cost_map, actions) in &mut query { trace!("{entity}: destination: {destination:?}"); - if start.i32() == **destination { + if transform.translation().truncate().as_ivec2() == **destination { trace!("{entity}: remove1"); commands .entity(entity) @@ -110,36 +105,32 @@ fn calculate_path( } trace!( "{entity:?}: Calculating path from {:?} to {destination:?}", - start.translation.truncate().i32() - ); - trace!( - "{entity:?}: path: calculating from {:?} to {destination:?}", - start.i32(), + transform.translation().truncate().as_ivec2() ); commands.entity(entity).remove::().remove::(); let path = astar( - &start.i32(), + &transform.translation().truncate().as_ivec2(), |p| { - let mut successors: Vec<((i32, i32), u32)> = vec![]; - let x = p.0; - let y = p.1; + let mut successors: Vec<(IVec2, u32)> = vec![]; + let x = p.x; + let y = p.y; let exits = vec![ - ((x - 1, y), 1.), - ((x + 1, y), 1.), - ((x, y - 1), 1.), - ((x, y + 1), 1.), - ((x - 1, y - 1), 1.5), - ((x + 1, y - 1), 1.5), - ((x - 1, y + 1), 1.5), - ((x + 1, y + 1), 1.5), + (IVec2::new(x - 1, y), 1.), + (IVec2::new(x + 1, y), 1.), + (IVec2::new(x, y - 1), 1.), + (IVec2::new(x, y + 1), 1.), + (IVec2::new(x - 1, y - 1), 1.5), + (IVec2::new(x + 1, y - 1), 1.5), + (IVec2::new(x - 1, y + 1), 1.5), + (IVec2::new(x + 1, y + 1), 1.5), ]; for exit in &exits { let mut should_push = true; if !spatial_query .shape_intersections( collider, - start.translation.truncate(), - start.yaw().as_radians(), + transform.translation().truncate(), + transform.yaw().as_radians(), SpatialQueryFilter::default(), ) .is_empty() @@ -149,7 +140,7 @@ fn calculate_path( if should_push { let mut cost = exit.1 * 100.; if let Some(cost_map) = cost_map { - if let Some(modifier) = cost_map.get(&(exit.0 .0, exit.0 .1)) { + if let Some(modifier) = cost_map.get(&exit.0) { cost *= modifier; } } @@ -158,8 +149,8 @@ fn calculate_path( } successors }, - |p| (p.distance_squared(&destination) * 100.) as u32, - |p| *p == destination.i32(), + |p| (p.distance_squared(**destination) * 100) as u32, + |p| *p == **destination, ); if let Some(path) = path { commands.entity(entity).insert(Path(path.0)); @@ -209,19 +200,19 @@ fn negotiate_path( ) in &mut query { trace!("{entity:?}: negotiating path"); - let start_i32 = transform.translation.truncate().i32(); + let start = transform.translation.truncate().as_ivec2(); trace!( - "{entity:?}: start pathfinding from {start_i32:?} to {:?}: at {:?}", + "{entity:?}: start pathfinding from {start:?} to {:?}: at {:?}", path.last(), - transform.translation.truncate().i32() + transform.translation.truncate() ); - if path.len() > 0 && path[0] == start_i32 { + if path.len() > 0 && path[0] == start { trace!("At start, removing"); path.remove(0); } if let Some(next) = path.first() { let start = transform.translation.truncate(); - let mut next = Vec2::new(next.0 as f32, next.1 as f32); + let mut next = next.as_vec2(); if next.x >= 0. { next.x += 0.5; } else { @@ -298,7 +289,7 @@ fn actions( } else { let pair = actions.axis_pair(&NegotiatePathAction); trace!("{entity:?}: Negotiating path to {pair:?}"); - let dest = Destination(pair.xy().i32()); + let dest = Destination(pair.as_ivec2()); if let Some(mut current_dest) = destination { trace!("Got a destination"); if *current_dest != dest { diff --git a/src/visibility.rs b/src/visibility.rs index 1342178..f71a280 100644 --- a/src/visibility.rs +++ b/src/visibility.rs @@ -25,7 +25,7 @@ pub struct RevealedTiles(pub Vec); #[derive(Component, Clone, Debug, Eq, PartialEq)] pub struct Viewshed { pub range: u32, - pub visible_points: HashSet<(i32, i32)>, + pub visible_points: HashSet, } impl Default for Viewshed { @@ -38,8 +38,8 @@ impl Default for Viewshed { } impl Viewshed { - pub fn is_point_visible(&self, point: &dyn PointLike) -> bool { - self.visible_points.contains(&point.into()) + pub fn is_point_visible(&self, point: IVec2) -> bool { + self.visible_points.contains(&point) } fn update( @@ -61,7 +61,7 @@ impl Viewshed { let visibility_grid = VisibilityGrid(size, *viewer_entity, opacity_map.clone()); let mut new_visible = HashSet::new(); let mut new_visible_entities = HashSet::new(); - // println!("Start: {viewer_entity}"); + println!("Start: {viewer_entity}"); context.for_each_visible( Coord::new(start.x_i32(), start.y_i32()), &visibility_grid, @@ -69,18 +69,19 @@ impl Viewshed { vision_distance, u8::MAX, |coord, _directions, _visibility| { - new_visible.insert((coord.x, coord.y)); let coord = IVec2::new(coord.x, coord.y); - // println!("Checking {coord:?}"); + new_visible.insert(coord); + println!("Checking {coord:?}"); if let Some((_, entities)) = opacity_map.get(&coord) { for e in entities { if entities.len() == 1 || sensors.contains(*e) { + println!("Spotted {e:?}"); new_visible_entities.insert(*e); } else { let should_push = std::cell::RefCell::new(true); let coord = Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5); let dir = Dir2::new_unchecked((coord - *start).normalize()); - // println!("Casting from {coord}"); + println!("Casting from {coord} to {dir:?}"); spatial_query.cast_ray_predicate( *start, dir, @@ -89,8 +90,7 @@ impl Viewshed { default(), &|entity| { if *e != entity && entities.contains(e) { - // println!("{entities:?} contains {e} and hits at {:?}", hit.point); - // commands.entity(*e).log_components(); + println!("{entities:?} contains {e}"); *should_push.borrow_mut() = false; } true @@ -109,9 +109,11 @@ impl Viewshed { } if new_visible_entities != **visible_entities { for lost in visible_entities.difference(&new_visible_entities) { + println!("Lost {lost}"); commands.trigger_targets(VisibilityChanged::Lost(*lost), *viewer_entity); } for gained in new_visible_entities.difference(visible_entities) { + println!("Gained {gained}"); commands.trigger_targets(VisibilityChanged::Gained(*gained), *viewer_entity); } **visible_entities = new_visible_entities; @@ -150,7 +152,7 @@ impl OpacityMap { visible: &Query<(Entity, &GlobalTransform, &Collider, &Visible)>, ) { self.retain(|k, _| !coordinates.contains(k)); - let shape = Collider::rectangle(0.49, 0.49); + let shape = Collider::rectangle(0.98, 0.98); for coords in &coordinates { let shape_pos = Vec2::new(coords.x as f32 + 0.5, coords.y as f32 + 0.5); let mut opacity = 0;