/// Handles mouse actions.
///
/// Despite of the title, this is the main system for our game.
/// It picks the tile, user clicked on last and either opens or
/// flags it depending on the mouse button clicked.
/// Those actions may result in the state transition from
/// [GameState::Playing] to [GameState::Over] if the game was
/// won or lost.
Handles mouse actions.
Despite of the title, this is the main system for our game.
It picks the tile, user clicked on last and either opens or
flags it depending on the mouse button clicked.
Those actions may result in the state transition from
[GameState::Playing] to [GameState::Over] if the game was
won or lost.
Handles mouse actions . Despite of the title this is the main system for our game . It picks the tile user clicked on last and either opens or flags it depending on the mouse button clicked . Those actions may result in the state transition from [ GameState :: Playing ] to [ GameState :: Over ] if the game was won or lost .
fn handle_mouse_action(
    mut commands: Commands,
    mut state: ResMut<State<GameState>>,
    button: Res<Input<MouseButton>>,
    materials: Res<GameMaterials>,
    mut game: ResMut<game::Game>,
    picking_camera_query: Query<&PickingCamera>,
    mut tile_query: Query<(&Tile, Entity, &Children), With<Tile>>,
) {
    // First get a tile a user hovered over.
    // See https://github.com/aevyrie/bevy_mod_picking
    let (tile, entity, children) = if let Some(query) = picking_camera_query
        .iter()
        .last()
        .and_then(|picking_camera| picking_camera.intersect_top())
        .and_then(|(entity, _intersection)| tile_query.get_mut(entity).ok())
    {
        query
    } else {
        return;
    };
    trace!("{}", game!(game, dump, tile.x, tile.y));
    // If a user clicked on the cell - either open or flag it
    if button.just_pressed(MouseButton::Left) {
        game.open(tile.x, tile.y);
    } else if button.just_pressed(MouseButton::Right) {
        match game.flag(tile.x, tile.y) {
            Some(true) => {
                for entity in children.iter() {
                    commands.entity(*entity).despawn_recursive();
                }
                commands.entity(entity).with_children(|parent| {
                    parent.spawn_scene(materials.empty.clone());
                    parent.spawn_scene(materials.flag.clone());
                });
            }
            Some(false) => {
                for entity in children.iter() {
                    commands.entity(*entity).despawn_recursive();
                }
                commands.entity(entity).with_children(|parent| {
                    parent.spawn_scene(materials.trees.clone());
                });
            }
            _ => unreachable!(),
        };
    }
    // Reflect on the game state:
    // 1. If the game continues it's possible that a user clicked open and more cells were uncovered.
    // 2. If the game is won - flagged cells should be marked as mined
    // 3. If the game is lost - all mined cells should be uncovered.
    let (entities, scene) = match game.state() {
        game::GameState::Continue => {
            let entities = tile_query
                .iter()
                .filter(|(tile, _entity, _children)| {
                    game!(game, cell_state, tile.x, tile.y) == game::CellState::Uncovered
                })
                .map(|(_tile, entity, children)| (entity, children))
                .collect::<Vec<(Entity, &Children)>>();
            (entities, materials.empty.clone())
        }
        game::GameState::Won => {
            let entities = tile_query
                .iter()
                .filter(|(tile, _entity, _children)| {
                    game!(game, cell_state, tile.x, tile.y) == game::CellState::Flagged
                })
                .map(|(_tile, entity, children)| (entity, children))
                .collect::<Vec<(Entity, &Children)>>();
            state
                .set(GameState::Over)
                .expect("Failed to change the game state");
            (entities, materials.flag.clone())
        }
        game::GameState::Lost => {
            let entities = tile_query
                .iter()
                .filter(|(tile, _entity, _children)| game!(game, has_mine, tile.x, tile.y))
                .map(|(_tile, entity, children)| (entity, children))
                .collect::<Vec<(Entity, &Children)>>();
            state
                .set(GameState::Over)
                .expect("Failed to change the game state");
            (entities, materials.mine.clone())
        }
    };
    for (entity, children) in entities {
        for entity in children.iter() {
            commands.entity(*entity).despawn_recursive();
        }
        commands.entity(entity).with_children(|parent| {
            parent.spawn_scene(scene.clone());
        });
    }
}
fn handle_mouse_action ( mut commands : Commands , mut state : ResMut < State < GameState > > , button : Res < Input < MouseButton > > , materials : Res < GameMaterials > , mut game : ResMut < game :: Game > , picking_camera_query : Query < & PickingCamera > , mut tile_query : Query < ( & Tile , Entity , & Children ) , With < Tile > > , ) { // First get a tile a user hovered over. // See https://github.com/aevyrie/bevy_mod_picking let ( tile , entity , children ) = if let Some ( query ) = picking_camera_query . iter ( ) . last ( ) . and_then ( | picking_camera | picking_camera . intersect_top ( ) ) . and_then ( | ( entity , _intersection ) | tile_query . get_mut ( entity ) . ok ( ) ) { query } else { return ; } ; trace ! ( " " game ( game dump tile x tile y ) ) ; // If a user clicked on the cell - either open or flag it if button . just_pressed ( MouseButton :: Left ) { game . open ( tile . x , tile . y ) ; } else if button . just_pressed ( MouseButton :: Right ) { match game . flag ( tile . x , tile . y ) { Some ( true ) => { for entity in children . iter ( ) { commands . entity ( * entity ) . despawn_recursive ( ) ; } commands . entity ( entity ) . with_children ( | parent | { parent . spawn_scene ( materials . empty . clone ( ) ) ; parent . spawn_scene ( materials . flag . clone ( ) ) ; } ) ; } Some ( false ) => { for entity in children . iter ( ) { commands . entity ( * entity ) . despawn_recursive ( ) ; } commands . entity ( entity ) . with_children ( | parent | { parent . spawn_scene ( materials . trees . clone ( ) ) ; } ) ; } _ => unreachable ! ( ) , } ; } // Reflect on the game state: // 1. If the game continues it's possible that a user clicked open and more cells were uncovered. // 2. If the game is won - flagged cells should be marked as mined // 3. If the game is lost - all mined cells should be uncovered. let ( entities , scene ) = match game . state ( ) { game :: GameState :: Continue => { let entities = tile_query . iter ( ) . filter ( | ( tile , _entity , _children ) | { game ! ( game cell_state tile x tile y ) == game :: CellState :: Uncovered } ) . map ( | ( _tile , entity , children ) | ( entity , children ) ) . collect :: < Vec < ( Entity , & Children ) > > ( ) ; ( entities , materials . empty . clone ( ) ) } game :: GameState :: Won => { let entities = tile_query . iter ( ) . filter ( | ( tile , _entity , _children ) | { game ! ( game cell_state tile x tile y ) == game :: CellState :: Flagged } ) . map ( | ( _tile , entity , children ) | ( entity , children ) ) . collect :: < Vec < ( Entity , & Children ) > > ( ) ; state . set ( GameState :: Over ) . expect ( " " ) ; ( entities , materials . flag . clone ( ) ) } game :: GameState :: Lost => { let entities = tile_query . iter ( ) . filter ( | ( tile , _entity , _children ) | game ! ( game has_mine tile x tile y ) ) . map ( | ( _tile , entity , children ) | ( entity , children ) ) . collect :: < Vec < ( Entity , & Children ) > > ( ) ; state . set ( GameState :: Over ) . expect ( " " ) ; ( entities , materials . mine . clone ( ) ) } } ; for ( entity , children ) in entities { for entity in children . iter ( ) { commands . entity ( * entity ) . despawn_recursive ( ) ; } commands . entity ( entity ) . with_children ( | parent | { parent . spawn_scene ( scene . clone ( ) ) ; } ) ; } }
Handles mouse actions.
Handles mouse actions .
commands
Commands
state
ResMut<State<GameState>>
button
Res<Input<MouseButton>>
materials
Res<GameMaterials>
game
ResMut<game::Game>
picking_camera_query
Query<&PickingCamera>
tile_query
Query<(&Tile, Entity, &Children), With<Tile>>