use bevy::math::Vec3; use bevy::prelude::*; use gems::constants; use rand::{ distributions::{Distribution, Standard}, Rng, }; #[derive(Debug, PartialEq, Clone, Copy)] enum Occupant { None, Green, Yellow, Red, Diamond, Explosion, } impl Default for Occupant { fn default() -> Occupant { Occupant::None } } impl Occupant { pub fn to_index(&self) -> u32 { match self { Occupant::Green => 0, Occupant::Yellow => 1, Occupant::Red => 2, Occupant::Diamond => 3, Occupant::Explosion => 4, Occupant::None => 13, } } } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Occupant { match rng.gen_range(0..=3) { 0 => Occupant::Green, 1 => Occupant::Yellow, 2 => Occupant::Diamond, 3 => Occupant::Red, _ => Occupant::None, } } } #[derive(Debug, Clone, Default, Copy)] struct Cell { x: usize, y: usize, occupant: Occupant, } impl Cell { pub fn new(x: usize, y: usize) -> Cell { Cell { x, y, occupant: Occupant::None, } } } fn cell_insert_system(mut cell_query: Query<(&mut Cell, &mut TextureAtlasSprite)>) { for (mut cell, mut sprite) in cell_query.iter_mut() { if cell.occupant == Occupant::None && cell.y == constants::ROWS - 1 { cell.occupant = rand::random(); sprite.index = cell.occupant.to_index(); } } } fn cell_check_system(mut cell_query: Query<(&mut Cell, &mut TextureAtlasSprite)>) { let mut cells = [[Cell::default(); constants::COLUMNS]; constants::ROWS]; for (cell, _) in cell_query.iter_mut() { cells[cell.x][cell.y] = *cell; } let mut last = Occupant::None; let mut connected = Vec::new(); for (i, _) in cells.iter().enumerate() { let mut c = Vec::new(); for j in 0..constants::ROWS { if cells[i][j].occupant == last && last != Occupant::None { c.push((i, j)); c.push((i, j - 1)); } else { connected.push(c.clone()); c.clear(); } last = cells[i][j].occupant; } connected.push(c); last = Occupant::None; } for c in connected.iter() { if c.len() > 3 { for (i, j) in c.iter() { for (mut cell, mut sprite) in cell_query.iter_mut() { if &cell.x == i && &cell.y == j { cell.occupant = Occupant::Explosion; sprite.index = cell.occupant.to_index(); } } } } } connected.clear(); for (i, row) in cells.iter().enumerate() { let mut c = Vec::new(); for (j, _) in row.iter().enumerate() { if cells[j][i].occupant == last && last != Occupant::None { c.push((j, i)); c.push((j - 1, i)); } else { connected.push(c.clone()); c.clear(); } last = cells[j][i].occupant; } connected.push(c); last = Occupant::None; } for c in connected.iter() { if c.len() > 3 { for (i, j) in c.iter() { for (mut cell, mut sprite) in cell_query.iter_mut() { if &cell.x == i && &cell.y == j { cell.occupant = Occupant::Explosion; sprite.index = cell.occupant.to_index(); } } } } } } fn cell_falling_system(mut cell_query: Query<(&mut Cell, &mut TextureAtlasSprite)>) { let mut have_gems = Vec::new(); for (cell, _sprite) in cell_query.iter_mut() { if cell.occupant != Occupant::None { have_gems.push(*cell); } } let mut moved_gems = Vec::new(); for (mut cell, mut sprite) in cell_query.iter_mut() { if cell.occupant == Occupant::None { if let Some(c) = have_gems .iter() .find(|&c| (c.x, c.y) == (cell.x, cell.y + 1)) { cell.occupant = c.occupant; sprite.index = cell.occupant.to_index(); moved_gems.push(c); } } } for (mut cell, mut sprite) in cell_query.iter_mut() { if moved_gems.iter().any(|c| (c.x, c.y) == (cell.x, cell.y)) { cell.occupant = Occupant::None; sprite.index = cell.occupant.to_index(); } } } pub fn setup( commands: &mut Commands, asset_server: Res, mut materials: ResMut>, mut texture_atlases: ResMut>, ) { let background = asset_server.load("background.png"); let tileset = asset_server.load("tileset.png"); let atlas = TextureAtlas::from_grid(tileset, Vec2::new(16.0, 16.0), 4, 4); let atlas_handle = texture_atlases.add(atlas); commands .spawn(Camera2dBundle::default()) .spawn(SpriteBundle { material: materials.add(background.into()), transform: Transform { translation: Vec3 { x: 50.0, y: 0.0, z: 0.0, }, scale: Vec3::splat(3.5), ..Default::default() }, ..Default::default() }) .spawn(SpriteSheetBundle { sprite: TextureAtlasSprite::new(8), texture_atlas: atlas_handle.clone(), transform: Transform { translation: Vec3 { x: 225.0, y: -200.0, z: 0.0, }, scale: Vec3::splat(3.5), ..Default::default() }, ..Default::default() }) .spawn(SpriteSheetBundle { sprite: TextureAtlasSprite::new(12), texture_atlas: atlas_handle.clone(), transform: Transform { translation: Vec3 { x: 225.0, y: -200.0 + (-16.0) * 3.5, z: 0.0, }, scale: Vec3::splat(3.5), ..Default::default() }, ..Default::default() }); for i in 0..constants::COLUMNS { for j in 0..constants::ROWS { commands .spawn(SpriteSheetBundle { texture_atlas: atlas_handle.clone(), sprite: TextureAtlasSprite::new(11), transform: Transform { translation: Vec3 { x: ((i as f32) * 16.0 * 3.5) - 320.0, y: ((j as f32) * 16.0 * 3.5) - 160.0, z: 0.0, }, scale: Vec3::splat(3.5), ..Default::default() }, ..Default::default() }) .with(Cell::new(i, j)); } } } pub struct GemsPlugin; impl Plugin for GemsPlugin { fn build(&self, app: &mut AppBuilder) { app.add_startup_system(setup.system()); app.add_system(cell_insert_system.system()); app.add_system(cell_falling_system.system()); app.add_system(cell_check_system.system()); } } pub fn main() { App::build() .add_resource(WindowDescriptor { title: "gems".to_string(), width: 800.0, height: 600.0, resizable: false, ..Default::default() }) .add_plugins(DefaultPlugins) .add_plugin(GemsPlugin) .run(); }