|
|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
use bevy::utils::HashSet;
|
|
|
|
|
|
|
|
|
|
use crate::audio::AudioEvent;
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
|
|
pub(crate) struct GamePlugin;
|
|
|
|
|
@ -7,23 +8,21 @@ pub(crate) struct GamePlugin;
|
|
|
|
|
impl Plugin for GamePlugin {
|
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
|
app.add_event::<Move>()
|
|
|
|
|
.add_event::<Selection>()
|
|
|
|
|
.add_systems(Startup, setup_board)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
(
|
|
|
|
|
menu::exit_to_menu.run_if(in_state(GameState::Play)),
|
|
|
|
|
update_board.run_if(on_event::<Move>()).after(select_sync),
|
|
|
|
|
update_board
|
|
|
|
|
.run_if(on_event::<Move>())
|
|
|
|
|
.after(handle_selection),
|
|
|
|
|
set_side.run_if(on_event::<Move>()).after(update_board),
|
|
|
|
|
cancel_place.run_if(|buttons: Res<Input<MouseButton>>| -> bool {
|
|
|
|
|
buttons.just_pressed(MouseButton::Right)
|
|
|
|
|
}),
|
|
|
|
|
select_sync
|
|
|
|
|
.run_if(any_component_added::<Selected>)
|
|
|
|
|
.after(deselect_sync),
|
|
|
|
|
deselect_sync.run_if(any_component_removed::<Selected>()),
|
|
|
|
|
move_piece.run_if(any_component_added::<Selected>),
|
|
|
|
|
handle_selection.run_if(on_event::<Selection>()),
|
|
|
|
|
capture_piece.run_if(any_component_added::<Captured>),
|
|
|
|
|
null_selections.run_if(any_component_added::<Selected>),
|
|
|
|
|
pick_up_audio::<display2d::Display2d>
|
|
|
|
|
.run_if(in_state(DisplayState::Display2d))
|
|
|
|
|
.run_if(any_component_added::<Selected>),
|
|
|
|
|
@ -292,6 +291,10 @@ pub(crate) struct Selected;
|
|
|
|
|
#[derive(Debug, Default, Component)]
|
|
|
|
|
pub(crate) struct Selectable;
|
|
|
|
|
|
|
|
|
|
/// Event for selecting board indexes for Moves
|
|
|
|
|
#[derive(Debug, Default, Event, Clone)]
|
|
|
|
|
pub(crate) struct Selection(pub BoardIndex);
|
|
|
|
|
|
|
|
|
|
fn setup_board(mut commands: Commands) {
|
|
|
|
|
use Piece::*;
|
|
|
|
|
commands.insert_resource(Board {
|
|
|
|
|
@ -347,6 +350,7 @@ 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
|
|
|
|
|
pub(crate) fn update_board(
|
|
|
|
|
mut audio_events: EventWriter<AudioEvent>,
|
|
|
|
|
mut events: EventReader<Move>,
|
|
|
|
|
mut pieces: Query<(Entity, &mut BoardIndex), With<Piece>>,
|
|
|
|
|
selected: Query<Entity, With<Selected>>,
|
|
|
|
|
@ -359,6 +363,7 @@ pub(crate) fn update_board(
|
|
|
|
|
Some(to_idx) => {
|
|
|
|
|
info!("Moving piece {:?} to {:?}", entity, to_idx);
|
|
|
|
|
*index = to_idx.clone();
|
|
|
|
|
audio_events.send(audio::AudioEvent::PutDown);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
info!("Capturing piece {:?}", entity);
|
|
|
|
|
@ -391,74 +396,59 @@ pub(crate) fn set_side(mut events: Query<(&mut Side, &BoardIndex), Changed<Board
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn select_sync(
|
|
|
|
|
events: Query<Entity, Added<Selected>>,
|
|
|
|
|
pieces: Query<(Entity, &BoardIndex), (With<Selectable>, With<Piece>)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|entity| {
|
|
|
|
|
if let Ok((this_e, this_idx)) = pieces.get(entity) {
|
|
|
|
|
if let Some((entangled, _)) = pieces
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|(e, idx)| *idx == this_idx && *e != this_e)
|
|
|
|
|
{
|
|
|
|
|
commands.entity(entangled).insert(Selected);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn deselect_sync(
|
|
|
|
|
mut events: RemovedComponents<Selected>,
|
|
|
|
|
pieces: Query<(Entity, &BoardIndex), (With<Selectable>, With<Piece>)>,
|
|
|
|
|
// TODO: Handle 3d Pickup (Not showing hover animation, but still selected?)
|
|
|
|
|
// TODO: Handle cancel move (currently 2d just drops it in place)
|
|
|
|
|
fn handle_selection(
|
|
|
|
|
mut selections: EventReader<Selection>,
|
|
|
|
|
mut move_events: EventWriter<Move>,
|
|
|
|
|
selected: Query<(Entity, &BoardIndex), (With<Selected>, With<Piece>)>,
|
|
|
|
|
pieces: Query<(Entity, &BoardIndex), (With<Selectable>, Without<Selected>, With<Piece>)>,
|
|
|
|
|
tiles: Query<(Entity, &BoardIndex), (With<Selectable>, With<Tile>)>,
|
|
|
|
|
mut board: ResMut<Board>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|entity| {
|
|
|
|
|
if let Ok((this_e, this_idx)) = pieces.get(entity) {
|
|
|
|
|
if let Some((entangled, _)) = pieces
|
|
|
|
|
selections.iter().for_each(|Selection(index)| {
|
|
|
|
|
// There are no currently selected entities
|
|
|
|
|
// Mark the piece at this index as selected
|
|
|
|
|
if selected.is_empty() {
|
|
|
|
|
pieces
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|(e, idx)| *idx == this_idx && *e != this_e)
|
|
|
|
|
{
|
|
|
|
|
info!("De-selecting entangled piece {:?}", entity);
|
|
|
|
|
commands.entity(entangled).remove::<Selected>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Triggered when right-mouse-button clicked
|
|
|
|
|
fn cancel_place(current: Query<Entity, With<Selected>>, mut commands: Commands) {
|
|
|
|
|
current.iter().for_each(|entity| {
|
|
|
|
|
info!("De-selecting {:?}", entity);
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
.filter(|(this, this_index)| *this_index == index)
|
|
|
|
|
.for_each(|(piece, piece_index)| {
|
|
|
|
|
info!("Selecting {:?} at {:?}", piece, piece_index);
|
|
|
|
|
commands.entity(piece).insert(Selected);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// When a tile is selected, move all selected pieces to that index
|
|
|
|
|
fn move_piece(
|
|
|
|
|
events: Query<&BoardIndex, (With<BoardIndex>, Added<Selected>)>,
|
|
|
|
|
selected_pieces: Query<&BoardIndex, (With<Selected>, With<Piece>)>,
|
|
|
|
|
mut board: ResMut<Board>,
|
|
|
|
|
mut move_events: EventWriter<Move>,
|
|
|
|
|
mut writer: EventWriter<audio::AudioEvent>,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|to| {
|
|
|
|
|
selected_pieces.iter().for_each(|from| {
|
|
|
|
|
if from != to {
|
|
|
|
|
info!("Applying move {:?} -> {:?}", from, to);
|
|
|
|
|
// Move piece
|
|
|
|
|
match board.move_piece(*from, *to) {
|
|
|
|
|
}
|
|
|
|
|
// There is a currently selected entity, so submit moves
|
|
|
|
|
else {
|
|
|
|
|
assert!(
|
|
|
|
|
selected.iter().len() <= 2,
|
|
|
|
|
"There are too many pieces selected!"
|
|
|
|
|
);
|
|
|
|
|
selected.iter().for_each(|(current, current_index)| {
|
|
|
|
|
match board.move_piece(*current_index, *index) {
|
|
|
|
|
Ok(moves) => {
|
|
|
|
|
// De-select the piece
|
|
|
|
|
info!("Applying moves {:?}", moves);
|
|
|
|
|
moves.iter().for_each(|m| move_events.send(m.clone()));
|
|
|
|
|
writer.send(audio::AudioEvent::PutDown)
|
|
|
|
|
}
|
|
|
|
|
Err(GameError::NullMove) => warn!("Null move!"),
|
|
|
|
|
Err(GameError::InvalidIndex) => warn!("Invalid index!"),
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Triggered when right-mouse-button clicked
|
|
|
|
|
fn cancel_place(current: Query<&BoardIndex, With<Selected>>, mut events: EventWriter<Move>) {
|
|
|
|
|
current.iter().for_each(|board_index| {
|
|
|
|
|
info!("De-selecting piece at {:?}", board_index);
|
|
|
|
|
events.send(Move {
|
|
|
|
|
from: board_index.clone(),
|
|
|
|
|
to: Some(board_index.clone()),
|
|
|
|
|
..default()
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -471,26 +461,6 @@ fn pick_up_audio<D: Component>(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
|
mut writer: EventWriter<audio::AudioEvent>,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|entity| {
|
|
|
|
|
if selected_pieces.is_empty() {
|
|
|
|
|
info!(
|
|
|
|
|
"De-selecting piece that should not be selected {:?}",
|
|
|
|
|
entity
|
|
|
|
|
);
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
writer.send(audio::AudioEvent::PutDown);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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| {
|
|
|
|
|
|