summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--resources/background.pngbin0 -> 3910 bytes
-rw-r--r--src/cosmonaut.rs110
-rw-r--r--src/game.rs275
3 files changed, 385 insertions, 0 deletions
diff --git a/resources/background.png b/resources/background.png
new file mode 100644
index 0000000..060989b
--- /dev/null
+++ b/resources/background.png
Binary files differ
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(())
+ }
+}