|
|
|
@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
use bevy::utils::HashSet;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) struct GamePlugin;
|
|
|
|
pub(crate) struct GamePlugin;
|
|
|
|
@ -20,6 +22,14 @@ impl Plugin for GamePlugin {
|
|
|
|
deselect_sync.run_if(any_component_removed::<Selected>()),
|
|
|
|
deselect_sync.run_if(any_component_removed::<Selected>()),
|
|
|
|
move_piece.run_if(any_component_added::<Selected>),
|
|
|
|
move_piece.run_if(any_component_added::<Selected>),
|
|
|
|
capture_piece.run_if(any_component_added::<Captured>),
|
|
|
|
capture_piece.run_if(any_component_added::<Captured>),
|
|
|
|
|
|
|
|
null_selections.run_if(any_component_added::<Selected>),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.add_systems(
|
|
|
|
|
|
|
|
PostUpdate,
|
|
|
|
|
|
|
|
(
|
|
|
|
|
|
|
|
asserts::<display2d::Display2d>,
|
|
|
|
|
|
|
|
asserts::<display3d::Display3d>,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
@ -110,7 +120,7 @@ pub(crate) struct Move {
|
|
|
|
pub to: Option<BoardIndex>,
|
|
|
|
pub to: Option<BoardIndex>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component, PartialEq, Clone, Default, Copy)]
|
|
|
|
#[derive(Debug, Component, PartialEq, Clone, Default, Copy, Eq, Hash)]
|
|
|
|
pub(crate) struct BoardIndex {
|
|
|
|
pub(crate) struct BoardIndex {
|
|
|
|
pub x: usize,
|
|
|
|
pub x: usize,
|
|
|
|
pub y: usize,
|
|
|
|
pub y: usize,
|
|
|
|
@ -196,24 +206,46 @@ impl Board {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Returns the possible moves the piece at this tile can make.
|
|
|
|
/// Returns the possible moves the piece at this tile can make.
|
|
|
|
pub(crate) fn possible_moves(&self, BoardIndex { x, y }: BoardIndex) -> Vec<BoardIndex> {
|
|
|
|
pub(crate) fn possible_moves(&self, BoardIndex { x, y }: BoardIndex) -> HashSet<BoardIndex> {
|
|
|
|
match self.at(BoardIndex { x, y }) {
|
|
|
|
match self.at(BoardIndex { x, y }) {
|
|
|
|
// One space in any diagonal
|
|
|
|
// One space in any diagonal
|
|
|
|
Some(Piece::Pawn) => {
|
|
|
|
Some(Piece::Pawn) => (-1..=1)
|
|
|
|
self.at(BoardIndex { x: x + 1, y });
|
|
|
|
.flat_map(move |xi| {
|
|
|
|
vec![]
|
|
|
|
(-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
|
|
|
|
// One or two spaces in either horizontal
|
|
|
|
Some(Piece::Drone) => {
|
|
|
|
Some(Piece::Drone) => std::iter::empty()
|
|
|
|
todo!("Where can a drone move?");
|
|
|
|
.chain((-2..=2).map(|i| BoardIndex {
|
|
|
|
vec![]
|
|
|
|
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
|
|
|
|
// Any distance in any straight line
|
|
|
|
Some(Piece::Queen) => {
|
|
|
|
Some(Piece::Queen) => std::iter::empty()
|
|
|
|
todo!("Where can a queen move?");
|
|
|
|
.chain((-7..=7).map(|i| BoardIndex {
|
|
|
|
vec![]
|
|
|
|
x: x.checked_add_signed(i).map_or(0, |val| val).clamp(0, 7),
|
|
|
|
}
|
|
|
|
y,
|
|
|
|
None => Vec::new(),
|
|
|
|
}))
|
|
|
|
|
|
|
|
.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<Board>, mut debug_info: ResMut<debug::DebugInfo>) {
|
|
|
|
/// Only update the tiles without a corresponding board piece
|
|
|
|
/// Only update the tiles without a corresponding board piece
|
|
|
|
pub(crate) fn update_board(
|
|
|
|
pub(crate) fn update_board(
|
|
|
|
mut events: EventReader<Move>,
|
|
|
|
mut events: EventReader<Move>,
|
|
|
|
mut pieces: Query<(Entity, &mut BoardIndex), (With<Piece>, Without<Tile>)>,
|
|
|
|
mut pieces: Query<(Entity, &mut BoardIndex), With<Piece>>,
|
|
|
|
tiles: Query<(Entity, &BoardIndex), (With<Tile>, Without<Piece>)>,
|
|
|
|
selected: Query<Entity, With<Selected>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.iter().for_each(|Move { from, to, .. }| {
|
|
|
|
events.iter().for_each(|Move { from, to, .. }| {
|
|
|
|
pieces
|
|
|
|
pieces.iter_mut().for_each(|(entity, mut index)| {
|
|
|
|
.iter_mut()
|
|
|
|
if *index == *from {
|
|
|
|
.filter(|(_, index)| **index == *from)
|
|
|
|
|
|
|
|
.for_each(|(entity, mut index)| {
|
|
|
|
|
|
|
|
match to {
|
|
|
|
match to {
|
|
|
|
Some(to_idx) => {
|
|
|
|
Some(to_idx) => {
|
|
|
|
*index = to_idx.clone();
|
|
|
|
*index = to_idx.clone();
|
|
|
|
@ -321,12 +351,9 @@ pub(crate) fn update_board(
|
|
|
|
.insert(Captured);
|
|
|
|
.insert(Captured);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
tiles
|
|
|
|
selected.iter().for_each(|entity| {
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.filter_map(|(entity, idx)| (Some(idx) == to.as_ref() || idx == from).then_some(entity))
|
|
|
|
|
|
|
|
.for_each(|entity| {
|
|
|
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})
|
|
|
|
})
|
|
|
|
@ -381,7 +408,7 @@ fn deselect_sync(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Triggered when right-mouse-button clicked
|
|
|
|
/// Triggered when right-mouse-button clicked
|
|
|
|
fn cancel_place(current: Query<Entity, (With<Selected>, With<Piece>)>, mut commands: Commands) {
|
|
|
|
fn cancel_place(current: Query<Entity, With<Selected>>, mut commands: Commands) {
|
|
|
|
current.iter().for_each(|entity| {
|
|
|
|
current.iter().for_each(|entity| {
|
|
|
|
info!("De-selecting {:?}", entity);
|
|
|
|
info!("De-selecting {:?}", entity);
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
@ -390,13 +417,12 @@ fn cancel_place(current: Query<Entity, (With<Selected>, With<Piece>)>, mut comma
|
|
|
|
|
|
|
|
|
|
|
|
/// When a tile is selected, move all selected pieces to that index
|
|
|
|
/// When a tile is selected, move all selected pieces to that index
|
|
|
|
fn move_piece(
|
|
|
|
fn move_piece(
|
|
|
|
events: Query<(Entity, &BoardIndex), (With<BoardIndex>, Added<Selected>)>,
|
|
|
|
events: Query<&BoardIndex, (With<BoardIndex>, Added<Selected>)>,
|
|
|
|
selected_pieces: Query<&BoardIndex, (With<Selected>, With<Piece>)>,
|
|
|
|
selected_pieces: Query<&BoardIndex, (With<Selected>, With<Piece>)>,
|
|
|
|
mut board: ResMut<Board>,
|
|
|
|
mut board: ResMut<Board>,
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
mut move_events: EventWriter<Move>,
|
|
|
|
mut move_events: EventWriter<Move>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.iter().for_each(|(tile, to)| {
|
|
|
|
events.iter().for_each(|to| {
|
|
|
|
selected_pieces.iter().for_each(|from| {
|
|
|
|
selected_pieces.iter().for_each(|from| {
|
|
|
|
// Move piece
|
|
|
|
// Move piece
|
|
|
|
match board.move_piece(*from, *to) {
|
|
|
|
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<Entity, Added<Selected>>,
|
|
|
|
|
|
|
|
selected_pieces: Query<Entity, (With<Selected>, With<Piece>)>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|entity| {
|
|
|
|
|
|
|
|
if selected_pieces.is_empty() {
|
|
|
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// When a piece's _BoardIndex_ is removed, we hide that entity from the viewer
|
|
|
|
/// When a piece's _BoardIndex_ is removed, we hide that entity from the viewer
|
|
|
|
fn capture_piece(mut events: Query<&mut Visibility, Added<Captured>>) {
|
|
|
|
fn capture_piece(mut events: Query<&mut Visibility, Added<Captured>>) {
|
|
|
|
events.iter_mut().for_each(|mut vis| {
|
|
|
|
events.iter_mut().for_each(|mut vis| {
|
|
|
|
@ -419,3 +459,19 @@ fn capture_piece(mut events: Query<&mut Visibility, Added<Captured>>) {
|
|
|
|
*vis = Visibility::Hidden
|
|
|
|
*vis = Visibility::Hidden
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Panics if more than two pieces are selected at a time
|
|
|
|
|
|
|
|
fn asserts<T: Component>(
|
|
|
|
|
|
|
|
selected_pieces: Query<Entity, (With<Piece>, With<T>, With<Selected>)>,
|
|
|
|
|
|
|
|
selected_tiles: Query<Entity, (With<Tile>, With<T>, With<Selected>)>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|