use ggez::event::EventHandler; use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image, WrapMode}; use ggez::input::mouse; use ggez::mint::{Point2, Vector2}; use ggez::{Context, GameResult}; use std::time::Instant; use crate::cell::{Cell, Occupant}; use crate::constants; use crate::cosmonaut::Cosmonaut; pub struct Game { selected: Option<(usize, usize)>, spritebatch: SpriteBatch, grid: Vec>, background: Image, cosmonaut: Cosmonaut, } impl Game { pub fn new(context: &mut Context) -> GameResult { let mut background = Image::new(context, "/background.png")?; background.set_filter(FilterMode::Nearest); let mut image = Image::new(context, "/tileset.png")?; image.set_filter(FilterMode::Nearest); image.set_wrap(WrapMode::Mirror, WrapMode::Mirror); let mut grid = Vec::new(); let mut y = 0.0; for _ in 0..constants::COLUMNS { let mut column = Vec::new(); for j in 0..constants::ROWS { column.push(Cell::new(Point2 { x: (j as f32) * (constants::TILE_WIDTH * constants::TILE_SCALE + constants::BORDER_SIZE) + constants::SHIFT_X, y: y + constants::SHIFT_Y, })); } y += constants::TILE_HEIGHT * constants::TILE_SCALE + constants::BORDER_SIZE; grid.push(column); } Ok(Game { grid, selected: None, cosmonaut: Cosmonaut::new(context)?, spritebatch: SpriteBatch::new(image), background, }) } fn update_explosions(&mut self) { let mut last = Occupant::None; let mut connected = Vec::new(); for i in 0..constants::COLUMNS { let mut c = Vec::new(); for j in 0..constants::ROWS { if self.grid[i][j].occupant == last && last != Occupant::None { c.push((i, j)); c.push((i, j - 1)); } else { connected.push(c.clone()); c.clear(); } last = self.grid[i][j].occupant; } connected.push(c); last = Occupant::None; } for c in connected.iter() { if c.len() > 3 { for (i, j) in c.iter() { self.grid[*i][*j].occupant = Occupant::Explosion { frame: 0, timer: Instant::now(), }; } } } connected.clear(); for i in 0..constants::COLUMNS { let mut c = Vec::new(); for j in 0..constants::ROWS { if self.grid[j][i].occupant == last && last != Occupant::None { c.push((j, i)); c.push((j - 1, i)); } else { connected.push(c.clone()); c.clear(); } last = self.grid[j][i].occupant; } connected.push(c); last = Occupant::None; } for c in connected.iter() { if c.len() > 3 { for (j, i) in c.iter() { self.grid[*j][*i].occupant = Occupant::Explosion { frame: 0, timer: Instant::now(), }; } } } } fn update_dropping(&mut self) { for i in 1..constants::COLUMNS { for j in 0..constants::ROWS { if self.grid[i][j].occupant == Occupant::None && self.grid[i - 1][j].occupant != Occupant::None { self.grid[i][j].occupant = self.grid[i - 1][j].occupant; self.grid[i - 1][j].occupant = Occupant::None; } } } } fn update_feeding(&mut self) { for i in 0..constants::COLUMNS { if self.grid[0][i].occupant == Occupant::None { self.grid[0][i].occupant = rand::random(); } } } fn update_frames(&mut self) { for row in self.grid.iter_mut() { for cell in row.iter_mut() { let mut done = false; if let Occupant::Explosion { ref mut frame, ref mut timer, } = cell.occupant { if timer.elapsed().as_millis() > 500 { if *frame < 4 { *frame += 1; *timer = Instant::now(); } else { done = true; } } } if done { cell.occupant = Occupant::None; } } } } } impl EventHandler for Game { fn update(&mut self, context: &mut Context) -> GameResult { for row in self.grid.iter_mut() { for cell in row.iter_mut() { cell.hover_off(); } } let position = mouse::position(context); if self.cosmonaut.contains(position) { self.cosmonaut.start(); } self.cosmonaut.update(); for row in self.grid.iter_mut() { for cell in row.iter_mut() { if cell.contains(position) { cell.hover_on(); } } } self.update_explosions(); self.update_frames(); self.update_dropping(); self.update_feeding(); Ok(()) } fn mouse_button_down_event( &mut self, _context: &mut Context, button: mouse::MouseButton, x: f32, y: f32, ) { if button == mouse::MouseButton::Left { let position = Point2 { x, y }; for (i, row) in self.grid.iter_mut().enumerate() { for (j, cell) in row.iter_mut().enumerate() { if cell.contains(position) && cell.moveable() { self.selected = Some((i, j)); cell.clicked_on(); } } } } } fn mouse_button_up_event( &mut self, _context: &mut Context, button: mouse::MouseButton, x: f32, y: f32, ) { if button == mouse::MouseButton::Left { for row in self.grid.iter_mut() { for cell in row.iter_mut() { cell.clicked_off(); } } if let Some(selected) = self.selected { let position = Point2 { x, y }; let mut swap = None; for (i, row) in self.grid.iter_mut().enumerate() { for (j, cell) in row.iter_mut().enumerate() { if cell.contains(position) && cell.moveable() && (((i + 1 == selected.0) && (j == selected.1)) || ((i.overflowing_sub(1).0 == selected.0) && (j == selected.1)) || ((i == selected.0) && (j + 1 == selected.1)) || ((i == selected.0) && (j.overflowing_sub(1)).0 == selected.1)) { swap = Some((i, j)); } } } if let Some((i, j)) = swap { let clone = self.grid[i][j].occupant; self.grid[i][j].occupant = self.grid[selected.0][selected.1].occupant; self.grid[selected.0][selected.1].occupant = clone; self.selected = None; } } } } fn draw(&mut self, context: &mut Context) -> GameResult { graphics::clear(context, [0.0, 0.0, 0.0, 1.0].into()); graphics::draw( context, &self.background, DrawParam::default().scale(Vector2 { x: 3.35, y: 3.35 }), )?; self.cosmonaut.draw(context)?; for row in self.grid.iter() { for cell in row.iter() { cell.draw(context, &mut self.spritebatch)?; } } graphics::draw(context, &self.spritebatch, DrawParam::default())?; self.spritebatch.clear(); graphics::present(context)?; Ok(()) } }