diff --git a/src/display3d.rs b/src/display3d.rs index e524538..e92dd98 100644 --- a/src/display3d.rs +++ b/src/display3d.rs @@ -47,22 +47,15 @@ impl Plugin for Display3dPlugin { .add_systems( Update, ( - move_camera - .run_if(resource_exists::()) - .run_if(in_state(GameState::Play)) - .run_if(in_state(DisplayState::Display3d)) - .run_if(on_event::()), - gizmo_system - .run_if(resource_exists::()) - .run_if(in_state(GameState::Play)) - .run_if(in_state(DisplayState::Display3d)), - mouse_zoom - .run_if(resource_exists::()) - .run_if(in_state(GameState::Play)) - .run_if(in_state(DisplayState::Display3d)) - .run_if(on_event::()), - selected_gizmo.run_if(resource_exists::()), - ), + move_camera.run_if(on_event::()), + mouse_zoom.run_if(on_event::()), + gizmo_system, + selected_gizmo, + moves_gizmo, + ) + .run_if(resource_exists::()) + .run_if(in_state(GameState::Play)) + .run_if(in_state(DisplayState::Display3d)), ) .add_systems( OnEnter(DisplayState::Display3d), @@ -516,14 +509,7 @@ fn select( } fn selected_gizmo( - selected: Query< - &Transform, - ( - With, - With, - With, - ), - >, + selected: Query<&Transform, (With, With)>, mut gizmos: Gizmos, ) { selected.iter().for_each(|transform| { @@ -531,6 +517,20 @@ fn selected_gizmo( }) } +fn moves_gizmo( + selected: Query<&BoardIndex, (With, With, With)>, + board: Res, + mut gizmos: Gizmos, +) { + selected.iter().for_each(|idx| { + board + .possible_moves(*idx) + .iter() + .map(|i| Transform::from_translation(board_translation(i))) + .for_each(|t| gizmos.cuboid(t, Color::WHITE)) + }); +} + fn pick_up( mut events: Query< (Entity, &game::Piece), diff --git a/src/game.rs b/src/game.rs index f9d253a..733ed0f 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,3 +1,5 @@ +use bevy::utils::HashSet; + use crate::prelude::*; pub(crate) struct GamePlugin; @@ -20,6 +22,14 @@ impl Plugin for GamePlugin { deselect_sync.run_if(any_component_removed::()), move_piece.run_if(any_component_added::), capture_piece.run_if(any_component_added::), + null_selections.run_if(any_component_added::), + ), + ) + .add_systems( + PostUpdate, + ( + asserts::, + asserts::, ), ) .add_systems( @@ -110,7 +120,7 @@ pub(crate) struct Move { pub to: Option, } -#[derive(Debug, Component, PartialEq, Clone, Default, Copy)] +#[derive(Debug, Component, PartialEq, Clone, Default, Copy, Eq, Hash)] pub(crate) struct BoardIndex { pub x: usize, pub y: usize, @@ -196,24 +206,46 @@ impl Board { } /// Returns the possible moves the piece at this tile can make. - pub(crate) fn possible_moves(&self, BoardIndex { x, y }: BoardIndex) -> Vec { + pub(crate) fn possible_moves(&self, BoardIndex { x, y }: BoardIndex) -> HashSet { match self.at(BoardIndex { x, y }) { // One space in any diagonal - Some(Piece::Pawn) => { - self.at(BoardIndex { x: x + 1, y }); - vec![] - } + Some(Piece::Pawn) => (-1..=1) + .flat_map(move |xi| { + (-1..=1).map(move |yi| BoardIndex { + x: x.checked_add_signed(xi).map_or(0, |val| val).clamp(0, 7), + y: y.checked_add_signed(yi).map_or(0, |val| val).clamp(0, 3), + }) + }) + .collect(), // One or two spaces in either horizontal - Some(Piece::Drone) => { - todo!("Where can a drone move?"); - vec![] - } + Some(Piece::Drone) => std::iter::empty() + .chain((-2..=2).map(|i| BoardIndex { + x: x.checked_add_signed(i).map_or(0, |val| val).clamp(0, 7), + y, + })) + .chain((-2..=2).map(|i| BoardIndex { + x, + y: y.checked_add_signed(i).map_or(0, |val| val).clamp(0, 3), + })) + .collect(), // Any distance in any straight line - Some(Piece::Queen) => { - todo!("Where can a queen move?"); - vec![] - } - None => Vec::new(), + Some(Piece::Queen) => std::iter::empty() + .chain((-7..=7).map(|i| BoardIndex { + x: x.checked_add_signed(i).map_or(0, |val| val).clamp(0, 7), + y, + })) + .chain((-3..=3).map(|i| BoardIndex { + x, + y: y.checked_add_signed(i).map_or(0, |val| val).clamp(0, 3), + })) + .chain((-3..=3).flat_map(move |xi| { + (-3..=3).map(move |yi| BoardIndex { + x: x.checked_add_signed(xi).map_or(0, |val| val).clamp(0, 7), + y: y.checked_add_signed(yi).map_or(0, |val| val).clamp(0, 3), + }) + })) + .collect(), + None => std::iter::empty().collect(), } } } @@ -301,15 +333,13 @@ fn debug_board(board: Res, mut debug_info: ResMut) { /// Only update the tiles without a corresponding board piece pub(crate) fn update_board( mut events: EventReader, - mut pieces: Query<(Entity, &mut BoardIndex), (With, Without)>, - tiles: Query<(Entity, &BoardIndex), (With, Without)>, + mut pieces: Query<(Entity, &mut BoardIndex), With>, + selected: Query>, mut commands: Commands, ) { events.iter().for_each(|Move { from, to, .. }| { - pieces - .iter_mut() - .filter(|(_, index)| **index == *from) - .for_each(|(entity, mut index)| { + pieces.iter_mut().for_each(|(entity, mut index)| { + if *index == *from { match to { Some(to_idx) => { *index = to_idx.clone(); @@ -321,14 +351,11 @@ pub(crate) fn update_board( .insert(Captured); } } - commands.entity(entity).remove::(); - }); - tiles - .iter() - .filter_map(|(entity, idx)| (Some(idx) == to.as_ref() || idx == from).then_some(entity)) - .for_each(|entity| { - commands.entity(entity).remove::(); - }); + } + }); + selected.iter().for_each(|entity| { + commands.entity(entity).remove::(); + }); }) } @@ -381,7 +408,7 @@ fn deselect_sync( } /// Triggered when right-mouse-button clicked -fn cancel_place(current: Query, With)>, mut commands: Commands) { +fn cancel_place(current: Query>, mut commands: Commands) { current.iter().for_each(|entity| { info!("De-selecting {:?}", entity); commands.entity(entity).remove::(); @@ -390,13 +417,12 @@ fn cancel_place(current: Query, With)>, mut comma /// When a tile is selected, move all selected pieces to that index fn move_piece( - events: Query<(Entity, &BoardIndex), (With, Added)>, + events: Query<&BoardIndex, (With, Added)>, selected_pieces: Query<&BoardIndex, (With, With)>, mut board: ResMut, - mut commands: Commands, mut move_events: EventWriter, ) { - events.iter().for_each(|(tile, to)| { + events.iter().for_each(|to| { selected_pieces.iter().for_each(|from| { // Move piece match board.move_piece(*from, *to) { @@ -412,6 +438,20 @@ fn move_piece( }); } +/// De-select anything that shouldn't be selected +/// Namely tiles when there are not selected pieces +fn null_selections( + events: Query>, + selected_pieces: Query, With)>, + mut commands: Commands, +) { + events.iter().for_each(|entity| { + if selected_pieces.is_empty() { + commands.entity(entity).remove::(); + } + }); +} + /// When a piece's _BoardIndex_ is removed, we hide that entity from the viewer fn capture_piece(mut events: Query<&mut Visibility, Added>) { events.iter_mut().for_each(|mut vis| { @@ -419,3 +459,19 @@ fn capture_piece(mut events: Query<&mut Visibility, Added>) { *vis = Visibility::Hidden }); } + +/// Panics if more than two pieces are selected at a time +fn asserts( + selected_pieces: Query, With, With)>, + selected_tiles: Query, With, With)>, +) { + if selected_pieces.iter().len() > 2 { + panic!("More than two piece is selected"); + } + if selected_tiles.iter().len() > 1 { + panic!( + "More than one tile is selected {:?}", + selected_tiles.iter().len() + ); + } +}