Adding endgame tracking

main
Elijah C. Voigt 2 years ago
parent fe2e023036
commit 18f3797340

@ -795,6 +795,7 @@ fn select(
})
.iter()
.for_each(|&board_index| {
info!("Board index selected: {:?}", board_index);
selections
.send(game::Selection(board_index.clone()));
});

@ -28,8 +28,11 @@ impl Plugin for GamePlugin {
show_valid_moves.run_if(any_component_added::<Selected>),
hide_valid_moves.run_if(any_component_removed::<Selected>()),
manage_score.run_if(any_component_added::<Captured>),
check_endgame.run_if(resource_changed::<Board>()),
),
)
.add_systems(OnEnter(GameState::Endgame), set_endgame)
.add_systems(OnExit(GameState::Endgame), clear_endgame)
.add_systems(
PreUpdate,
asserts::<display3d::Display3d>
@ -151,15 +154,15 @@ impl Score {
/// The board is setup like this:
/// ```text
/// 0 1 2 3 4 5 6 7
/// +--+--+--+--+--+--+--+--+
/// a | | | | I | d| Q| Q|
/// +--+--+--+--+--+--+--+--+
/// b |d |p |p | I | p| d| Q|
/// +--+--+--+--+--+--+--+--+
/// c |Q |d |p | I | p| p| d|
/// +--+--+--+--+--+--+--+--+
/// d |Q |Q |d | I | | | |
/// +--+--+--+--+--+--+--+--+
/// +--+--+--+-----+--+--+--+
/// a | | | | l | d| Q| Q|
/// +--+--+--+--l--+--+--+--+
/// b |d |p |p | l | p| d| Q|
/// +--+--+--+--l--+--+--+--+
/// c |Q |d |p | l | p| p| d|
/// +--+--+--+--l--+--+--+--+
/// d |Q |Q |d | l | | | |
/// +--+--+--+-----+--+--+--+
/// ````
#[derive(Debug, Resource)]
pub(crate) struct Board {
@ -203,6 +206,11 @@ pub(crate) struct BoardIndex {
pub y: usize,
}
#[derive(Debug, Component, PartialEq, Clone, Default, Copy, Eq, Hash)]
pub(crate) struct Previous {
board_index: BoardIndex
}
#[derive(Debug, Component, PartialEq, Clone, Copy)]
pub(crate) enum Side {
A,
@ -226,6 +234,31 @@ impl Board {
self.inner[y][x].as_ref()
}
/// Show all pieces on one side of the board
/// OPTIMIZE: This is only used to tell if a side is empty, so it is more work than we need to do.
pub(crate) fn on(&self, side: Side) -> Vec<(&Piece, BoardIndex)> {
match side {
Side::A => {
// X: 0..3, Y: 0..3
(0..=3).flat_map(|x| {
(0..=3).map(move |y| {
self.at(BoardIndex { x, y }).map(|p| (p, BoardIndex { x, y }))
})
}).filter_map(|r| r)
.collect()
}
Side::B => {
// X: 4..7, Y: 0..3
(4..=7).flat_map(|x| {
(0..=3).map(move |y| {
self.at(BoardIndex { x, y }).map(|p| (p, BoardIndex { x, y }))
})
}).filter_map(|r| r)
.collect()
}
}
}
/// Returns a list of all pieces on the board with their location
pub(crate) fn pieces(&self) -> Vec<(BoardIndex, Piece)> {
self.inner
@ -299,7 +332,7 @@ impl Board {
}
/// Returns the possible moves the piece at this tile can make.
/// !!TODO: exclude pieces on your own side!!
/// TODO: Implement "no jumping" over pieces
pub(crate) fn valid_moves(&self, current_board_index: BoardIndex) -> HashSet<BoardIndex> {
let BoardIndex { x, y } = current_board_index;
@ -346,12 +379,8 @@ impl Board {
false
}
};
if !rejection {
// If all tests pass, this is a valid move
Some(this_board_index)
} else {
None
}
// If all tests pass, this is a valid move
(!rejection).then_some(this_board_index)
} else {
None
}
@ -449,8 +478,8 @@ fn setup_board(mut commands: Commands) {
inner: vec![
vec![
Some(Queen),
Some(Queen),
Some(Drone),
None, // Some(Queen),
None, // Some(Drone),
None,
None,
None,
@ -458,24 +487,24 @@ fn setup_board(mut commands: Commands) {
None,
],
vec![
Some(Queen),
Some(Drone),
Some(Pawn),
None,
None,
Some(Pawn),
Some(Pawn),
Some(Drone),
None, // Some(Queen),
None, // Some(Drone),
None, // Some(Pawn),
None, // None,
None, // None,
None, // Some(Pawn),
None, // Some(Pawn),
None, // Some(Drone),
],
vec![
Some(Drone),
Some(Pawn),
Some(Pawn),
None,
None,
Some(Pawn),
Some(Drone),
Some(Queen),
None, // Some(Drone),
None, // Some(Pawn),
None, // Some(Pawn),
None, // None,
None, // None,
None, // Some(Pawn),
None, // Some(Drone),
None, // Some(Queen),
],
vec![
None,
@ -483,8 +512,8 @@ fn setup_board(mut commands: Commands) {
None,
None,
None,
Some(Drone),
Some(Queen),
None, // Some(Drone),
None, // Some(Queen),
Some(Queen),
],
],
@ -542,7 +571,91 @@ pub(crate) fn update_board(
*played = false;
}
// We only track 3D (hack, for now) to prevent duplicates
// Track the last spot that a piece was at
fn track_previous_move(
events: Query<&BoardIndex, (With<Piece>, Changed<BoardIndex>)>,
mut commands: Commands,
) {
todo!()
}
#[derive(Debug, Component)]
struct Endgame;
fn check_endgame(
board: Res<Board>,
mut next_state: ResMut<NextState<GameState>>,
) {
if board.on(Side::A).is_empty() || board.on(Side::B).is_empty() {
warn!("The game is over!");
next_state.set(GameState::Endgame);
}
}
fn set_endgame(
score: Res<Score>,
mut commands: Commands,
) {
commands
.spawn((
Endgame,
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
flex_direction: FlexDirection::Column,
position_type: PositionType::Absolute,
..default()
},
background_color: Color::NONE.into(),
visibility: Visibility::Inherited,
..default()
},
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
"S C O R E",
TextStyle {
font_size: 48.0,
color: Color::ORANGE_RED,
..default()
},
));
parent.spawn(TextBundle::from_section(
format!("BLUE {}", score.b),
TextStyle {
font_size: 32.0,
color: Color::BLUE,
..default()
},
));
parent.spawn(TextBundle::from_section(
format!("RED {}", score.a),
TextStyle {
font_size: 32.0,
color: Color::RED,
..default()
},
));
});
}
fn clear_endgame(
query: Query<Entity, With<Endgame>>,
mut commands: Commands,
) {
query.iter().for_each(|e| {
commands.entity(e).despawn_recursive();
})
}
/// We only track 3D (hack, for now) to prevent duplicates
/// TODO: We can calculate this declaratively:
/// * All pieces without a BoardIndex are "captured"
/// * All captured pieces have their captured side preserved
/// We can iterate over these pieces and calculate the score on the fly
fn manage_score(
events: Query<&Side, (Added<Captured>, With<display3d::Display3d>)>,
mut debug_info: ResMut<debug::DebugInfo>,
@ -579,13 +692,14 @@ fn handle_selection(
mut commands: Commands,
mut audio_event: EventWriter<AudioEvent>,
mut done: Local<bool>, // Tracks if moves/audio submitted already even if multiple pieces (2d/3d) are moved.
mut latest: Local<BoardIndex>, // Tracks the last one worked on
mut latest: Local<Option<BoardIndex>>, // Tracks the last one worked on
) {
selections.read().for_each(|Selection(index)| {
// Skip indexes already processed
if *index != *latest {
if Some(*index) != *latest {
// Set the latest index to the current index
*latest = *index;
*latest = Some(*index);
// Reset the "done" marker
*done = false;
@ -635,7 +749,7 @@ fn handle_selection(
}
});
*done = false;
*latest = BoardIndex::default();
*latest = None;
}
/// Triggered when right-mouse-button clicked

@ -74,6 +74,7 @@ pub enum GameState {
Menu,
Credits,
Play,
Endgame,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)]

Loading…
Cancel
Save