use crate::constants; use bevy::prelude::*; use bevy::utils::Duration; use rand::{ distributions::{Distribution, Standard}, Rng, }; #[derive(Component)] pub struct AnimationTimer { timer: Timer, } impl AnimationTimer { pub fn from_seconds(duration: f32, mode: TimerMode) -> AnimationTimer { AnimationTimer { timer: Timer::from_seconds(duration, mode), } } pub fn finished(&self) -> bool { self.timer.finished() } pub fn tick(&mut self, delta: Duration) -> &Timer { self.timer.tick(delta) } pub fn just_finished(&self) -> bool { self.timer.just_finished() } pub fn reset(&mut self) { self.timer.reset(); } pub fn set_duration(&mut self, duration: Duration) { self.timer.set_duration(duration); } pub fn repeating(&self) -> bool { self.timer.mode() == TimerMode::Repeating } } #[derive(Debug, PartialEq, Clone, Copy)] pub enum Occupant { None, Green, Yellow, Red, Blue, Purple, Explosion, } impl Default for Occupant { fn default() -> Occupant { Occupant::None } } impl Occupant { pub fn to_index(&self) -> usize { match self { Occupant::Green => constants::TILESHEET_GREEN, Occupant::Yellow => constants::TILESHEET_YELLOW, Occupant::Red => constants::TILESHEET_RED, Occupant::Blue => constants::TILESHEET_BLUE, Occupant::Purple => constants::TILESHEET_PURPLE, Occupant::Explosion => constants::TILESHEET_EXPLOSION1, Occupant::None => 0, } } } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Occupant { match rng.gen_range(0..=4) { 0 => Occupant::Green, 1 => Occupant::Yellow, 2 => Occupant::Blue, 3 => Occupant::Red, 4 => Occupant::Purple, _ => Occupant::None, } } } #[derive(Debug, Clone, Default, Copy, Component)] pub struct Cell { pub x: usize, pub y: usize, pub occupant: Occupant, pub selected: bool, pub hovered: bool, } impl Cell { pub fn new(x: usize, y: usize) -> Cell { Cell { x, y, occupant: Occupant::None, selected: false, hovered: false, } } pub fn set_occupant(&mut self, occupant: Occupant, sprite: &mut TextureAtlasSprite) { self.occupant = occupant; sprite.index = self.occupant.to_index(); } } pub fn insert(mut q: Query<(&mut Cell, &mut TextureAtlasSprite)>) { for (mut cell, mut sprite) in q.iter_mut() { if cell.occupant == Occupant::None && cell.y == constants::GRID_SIZE - 1 { cell.set_occupant(rand::random(), &mut sprite); } } } pub fn start_explosion( mut commands: Commands, mut q: Query<(Entity, &Cell), Without>, ) { for (entity, cell) in q.iter_mut() { if cell.occupant == Occupant::Explosion { commands .entity(entity) .insert(AnimationTimer::from_seconds(0.1, TimerMode::Repeating)); } } } pub fn check(mut q: Query<(&mut Cell, &mut TextureAtlasSprite)>) { let mut cells = [[Cell::default(); constants::GRID_SIZE]; constants::GRID_SIZE]; for (cell, _) in q.iter_mut() { cells[cell.x][cell.y] = *cell; } let mut last = Occupant::None; let mut connected = Vec::new(); for (i, row) in cells.iter().enumerate() { let mut c = Vec::new(); for (j, _) in row.iter().enumerate() { if cells[i][j].occupant == last && last != Occupant::None && last != Occupant::Explosion { 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 (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 && last != Occupant::Explosion { 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; } connected.retain(|c| c.len() > 4); for c in connected.iter() { for (i, j) in c.iter() { for (mut cell, mut sprite) in q.iter_mut() { if &cell.x == i && &cell.y == j && cell.occupant != Occupant::Explosion { cell.set_occupant(Occupant::Explosion, &mut sprite); } } } } } pub fn falling(mut q: Query<(&mut Cell, &mut TextureAtlasSprite)>) { let mut have_gems = Vec::new(); for (cell, _sprite) in q.iter_mut() { if cell.occupant != Occupant::None { have_gems.push(*cell); } } let mut moved_gems = Vec::new(); for (mut cell, mut sprite) in q.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.set_occupant(c.occupant, &mut sprite); moved_gems.push(c); } } } for (mut cell, mut sprite) in q.iter_mut() { if moved_gems.iter().any(|c| (c.x, c.y) == (cell.x, cell.y)) { cell.set_occupant(Occupant::None, &mut sprite); } } }