diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cosmonaut.rs | 110 | ||||
| -rw-r--r-- | src/game.rs | 275 | 
2 files changed, 385 insertions, 0 deletions
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(()) +    } +}  | 
