|
|
|
|
@ -11,8 +11,7 @@ impl Plugin for GamePlugin {
|
|
|
|
|
Update,
|
|
|
|
|
(
|
|
|
|
|
menu::exit_to_menu.run_if(in_state(GameState::Play)),
|
|
|
|
|
update_board::<display2d::Display2d>.run_if(on_event::<Move>()),
|
|
|
|
|
update_board::<display3d::Display3d>.run_if(on_event::<Move>()),
|
|
|
|
|
update_board.run_if(on_event::<Move>()),
|
|
|
|
|
set_side.run_if(on_event::<Move>()), // TODO: correct run_if?
|
|
|
|
|
cancel_place.run_if(|buttons: Res<Input<MouseButton>>| -> bool {
|
|
|
|
|
buttons.just_pressed(MouseButton::Right)
|
|
|
|
|
@ -20,10 +19,7 @@ impl Plugin for GamePlugin {
|
|
|
|
|
select_sync.run_if(any_component_added::<Selected>),
|
|
|
|
|
deselect_sync.run_if(any_component_removed::<Selected>()),
|
|
|
|
|
move_piece.run_if(any_component_added::<Selected>),
|
|
|
|
|
exclusive_select::<display2d::Display2d>
|
|
|
|
|
.run_if(any_component_added::<Selected>),
|
|
|
|
|
exclusive_select::<display2d::Display2d>
|
|
|
|
|
.run_if(any_component_added::<Selected>),
|
|
|
|
|
capture_piece.run_if(any_component_added::<Captured>),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
@ -62,6 +58,9 @@ pub(crate) fn tiles() -> impl Iterator<Item = (BoardIndex, Tile)> {
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct BoardComponent;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct Captured;
|
|
|
|
|
|
|
|
|
|
// manually for the type.
|
|
|
|
|
impl std::fmt::Display for Piece {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
@ -76,7 +75,6 @@ impl std::fmt::Display for Piece {
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(crate) enum GameError {
|
|
|
|
|
NullMove,
|
|
|
|
|
InvalidMove,
|
|
|
|
|
InvalidIndex,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -150,12 +148,14 @@ impl Board {
|
|
|
|
|
) -> Result<Vec<Move>, GameError> {
|
|
|
|
|
if from == to {
|
|
|
|
|
Err(GameError::NullMove)
|
|
|
|
|
} else if self.at(to).is_none() {
|
|
|
|
|
} else {
|
|
|
|
|
self.at(from).map_or(Err(GameError::NullMove), |from_val| {
|
|
|
|
|
// The current epoch is the last epoch + 1
|
|
|
|
|
let epoch = self.moves.last().unwrap_or(&Move { ..default() }).epoch + 1;
|
|
|
|
|
|
|
|
|
|
// Local moves vec we can return
|
|
|
|
|
let mut moves = vec![];
|
|
|
|
|
|
|
|
|
|
// If the position we are moving to is occupied, capture the removal in the ledger
|
|
|
|
|
if self.inner[to.y][to.x].is_some() {
|
|
|
|
|
moves.push(Move {
|
|
|
|
|
@ -164,21 +164,21 @@ impl Board {
|
|
|
|
|
to: None,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Capture the intened move in the moves ledger
|
|
|
|
|
moves.push(Move {
|
|
|
|
|
epoch,
|
|
|
|
|
from: from.clone(),
|
|
|
|
|
to: Some(to.clone()),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.moves.extend(moves.clone());
|
|
|
|
|
// TODO: We can self.inner.swap(to, from) if board is single vec
|
|
|
|
|
// Update board to reflect move
|
|
|
|
|
|
|
|
|
|
self.inner[to.y][to.x] = Some(from_val);
|
|
|
|
|
self.inner[from.y][from.x] = None;
|
|
|
|
|
|
|
|
|
|
Ok(moves)
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
Err(GameError::InvalidMove)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -272,9 +272,10 @@ fn debug_board(board: Res<Board>, mut debug_info: ResMut<debug::DebugInfo>) {
|
|
|
|
|
|
|
|
|
|
/// Update this method to use a diff between the board and the state of the 2d/3d worlds
|
|
|
|
|
/// Only update the tiles without a corresponding board piece
|
|
|
|
|
pub(crate) fn update_board<T: Component>(
|
|
|
|
|
pub(crate) fn update_board(
|
|
|
|
|
mut events: EventReader<Move>,
|
|
|
|
|
mut pieces: Query<(Entity, &mut BoardIndex), (With<Piece>, With<T>)>,
|
|
|
|
|
mut pieces: Query<(Entity, &mut BoardIndex), (With<Piece>, Without<Tile>)>,
|
|
|
|
|
tiles: Query<(Entity, &BoardIndex), (With<Tile>, Without<Piece>)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|Move { from, to, .. }| {
|
|
|
|
|
@ -282,12 +283,25 @@ pub(crate) fn update_board<T: Component>(
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.filter(|(_, index)| **index == *from)
|
|
|
|
|
.for_each(|(entity, mut index)| {
|
|
|
|
|
if let Some(idx) = to {
|
|
|
|
|
*index = idx.clone();
|
|
|
|
|
} else {
|
|
|
|
|
commands.entity(entity).remove::<BoardIndex>();
|
|
|
|
|
match to {
|
|
|
|
|
Some(to_idx) => {
|
|
|
|
|
*index = to_idx.clone();
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.remove::<BoardIndex>()
|
|
|
|
|
.insert(Captured);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
});
|
|
|
|
|
tiles
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|(entity, idx)| (Some(idx) == to.as_ref() || idx == from).then_some(entity))
|
|
|
|
|
.for_each(|entity| {
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -349,44 +363,32 @@ fn cancel_place(current: Query<Entity, (With<Selected>, With<Piece>)>, mut comma
|
|
|
|
|
|
|
|
|
|
/// When a tile is selected, move all selected pieces to that index
|
|
|
|
|
fn move_piece(
|
|
|
|
|
events: Query<(Entity, &BoardIndex), (With<Tile>, Added<Selected>)>,
|
|
|
|
|
selected_pieces: Query<(Entity, &BoardIndex), (With<Selected>, With<Piece>)>,
|
|
|
|
|
events: Query<(Entity, &BoardIndex), (With<BoardIndex>, Added<Selected>)>,
|
|
|
|
|
selected_pieces: Query<&BoardIndex, (With<Selected>, With<Piece>)>,
|
|
|
|
|
mut board: ResMut<Board>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
mut move_events: EventWriter<Move>,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|(tile, to)| {
|
|
|
|
|
selected_pieces.iter().for_each(|(piece, from)| {
|
|
|
|
|
selected_pieces.iter().for_each(|from| {
|
|
|
|
|
// Move piece
|
|
|
|
|
match board.move_piece(from, to) {
|
|
|
|
|
Ok(moves) => {
|
|
|
|
|
// De-select the piece
|
|
|
|
|
commands.entity(piece).remove::<Selected>();
|
|
|
|
|
info!("Applying moves {:?}", moves);
|
|
|
|
|
moves.iter().for_each(|m| move_events.send(m.clone()));
|
|
|
|
|
}
|
|
|
|
|
Err(GameError::NullMove) => warn!("Null move!"),
|
|
|
|
|
Err(GameError::InvalidMove) => warn!("Invalid move!"),
|
|
|
|
|
Err(GameError::InvalidIndex) => warn!("Invalid index!"),
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// De-select the tile
|
|
|
|
|
commands.entity(tile).remove::<Selected>();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// TEMPORARY: Exclusive selection; only select one piece at a time
|
|
|
|
|
fn exclusive_select<T: Component>(
|
|
|
|
|
events: Query<Entity, (Added<Selected>, With<Piece>, With<T>)>,
|
|
|
|
|
selected: Query<Entity, (With<Selected>, With<Piece>, With<T>)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
// Iterate over selected component markers on pieces
|
|
|
|
|
events.iter().for_each(|entity| {
|
|
|
|
|
// Selected > 2 (one for each display2d/3d)
|
|
|
|
|
selected.iter().filter(|&e| e != entity).for_each(|e| {
|
|
|
|
|
// Remove the previously added selected marker
|
|
|
|
|
commands.entity(e).remove::<Selected>();
|
|
|
|
|
});
|
|
|
|
|
/// When a piece's _BoardIndex_ is removed, we hide that entity from the viewer
|
|
|
|
|
fn capture_piece(mut events: Query<&mut Visibility, Added<Captured>>) {
|
|
|
|
|
events.iter_mut().for_each(|mut vis| {
|
|
|
|
|
info!("Hiding captured piece");
|
|
|
|
|
*vis = Visibility::Hidden
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|