diff options
-rw-r--r-- | resources/background.png | bin | 0 -> 3910 bytes | |||
-rw-r--r-- | src/cosmonaut.rs | 110 | ||||
-rw-r--r-- | src/game.rs | 275 |
3 files changed, 385 insertions, 0 deletions
diff --git a/resources/background.png b/resources/background.png Binary files differnew file mode 100644 index 0000000..060989b --- /dev/null +++ b/resources/background.png diff --git a/src/cosmonaut.rs b/src/cosmonaut.rs new file mode 100644 index 0000000..90a7c07 --- /dev/null +++ b/src/cosmonaut.rs @@ -0,0 +1,110 @@ +use crate::constants; +use ggez::graphics::{self, DrawParam, FilterMode, Image, Rect}; +use ggez::mint::{Point2, Vector2}; +use ggez::{Context, GameResult}; +use std::time::Instant; + +#[derive(Clone, Copy)] +enum CosmonautFrames { + None, + One, + Two, + Three, + Four, +} + +impl CosmonautFrames { + pub fn next(&mut self) { + *self = match self { + CosmonautFrames::None => CosmonautFrames::One, + CosmonautFrames::One => CosmonautFrames::Two, + CosmonautFrames::Two => CosmonautFrames::Three, + CosmonautFrames::Three => CosmonautFrames::Four, + CosmonautFrames::Four => CosmonautFrames::None, + }; + } +} + +pub struct Cosmonaut { + destination: Point2<f32>, + image: Image, + frame: CosmonautFrames, + timer: Instant, + scale: Vector2<f32>, +} + +impl Cosmonaut { + pub fn new(context: &mut Context) -> GameResult<Cosmonaut> { + let mut image = Image::new(context, "/cosmonaut.png")?; + image.set_filter(FilterMode::Nearest); + + Ok(Cosmonaut { + image, + destination: Point2 { x: 600.0, y: 350.0 }, + frame: CosmonautFrames::None, + timer: Instant::now(), + scale: Vector2 { + x: constants::TILE_SCALE * 2.0, + y: constants::TILE_SCALE * 2.0, + }, + }) + } + + pub fn draw(&self, context: &mut Context) -> GameResult { + graphics::draw( + context, + &self.image, + DrawParam::default() + .dest(self.destination) + .scale(self.scale) + .src(Rect::new(0.0, 0.0, 1.0 / 3.0, 1.0)), + )?; + let source = match self.frame { + CosmonautFrames::None => None, + CosmonautFrames::One => Some(Rect::new(1.0 / 3.0, 0.0, 1.0 / 3.0, 1.0 / 2.0)), + CosmonautFrames::Two => Some(Rect::new(2.0 / 3.0, 0.0, 1.0, 1.0 / 2.0)), + CosmonautFrames::Three => Some(Rect::new(1.0 / 3.0, 1.0 / 2.0, 1.0 / 3.0, 1.0 / 2.0)), + CosmonautFrames::Four => Some(Rect::new(2.0 / 3.0, 1.0 / 2.0, 1.0, 1.0)), + }; + if let Some(source) = source { + graphics::draw( + context, + &self.image, + DrawParam::default() + .dest(self.destination) + .src(source) + .scale(self.scale), + )?; + } + + Ok(()) + } + + pub fn update(&mut self) { + match self.frame { + CosmonautFrames::None => (), + _ => { + if self.timer.elapsed().as_millis() > 50 { + self.frame.next(); + self.timer = Instant::now(); + } + } + } + } + + pub fn start(&mut self) { + if let CosmonautFrames::None = self.frame { + if self.timer.elapsed().as_secs() > 5 { + self.timer = Instant::now(); + self.frame.next() + } + } + } + + pub fn contains(&self, position: Point2<f32>) -> bool { + position.x > self.destination.x + && position.y > self.destination.y + && position.x < self.destination.x + constants::TILE_WIDTH * constants::TILE_SCALE * 2.0 + && position.y < self.destination.y + constants::TILE_WIDTH * constants::TILE_SCALE * 2.0 + } +} diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..d2b88e8 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,275 @@ +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<Vec<Cell>>, + background: Image, + cosmonaut: Cosmonaut, +} + +impl Game { + pub fn new(context: &mut Context) -> GameResult<Game> { + 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(()) + } +} |