use ggez; use ggez::event; use ggez::graphics::{ self, spritebatch::SpriteBatch, Color, DrawMode, DrawParam, FilterMode, Image, Mesh, Rect, StrokeOptions, WrapMode, }; use ggez::input::mouse; use ggez::mint::{Point2, Vector2}; use rand::{ distributions::{Distribution, Standard}, Rng, }; pub const TILE_SCALE: f32 = 2.0; pub const TILE_HEIGHT: f32 = 16.0; pub const TILE_WIDTH: f32 = 16.0; pub const BORDER_SIZE: f32 = 3.0; #[derive(Clone, Copy)] enum Occupant { None, Green, Yellow, Diamond, Red, } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Occupant { match rng.gen_range(0..=3) { 0 => Occupant::Green, 1 => Occupant::Yellow, 2 => Occupant::Diamond, 3 => Occupant::Red, _ => Occupant::None, } } } #[derive(Clone, Copy)] struct Cell { occupant: Occupant, position: Point2, hover: bool, clicked: bool, } impl Cell { pub fn new(position: Point2) -> Cell { Cell { occupant: rand::random(), position: position, hover: false, clicked: false, } } pub fn contains(&self, position: Point2) -> bool { if position.x > self.position.x { if position.y > self.position.y { if position.x < self.position.x + TILE_WIDTH * TILE_SCALE { if position.y < self.position.y + TILE_WIDTH * TILE_SCALE { return true; } } } } false } pub fn clicked_on(&mut self) { self.clicked = true; } pub fn clicked_off(&mut self) { self.clicked = false; } pub fn hover_on(&mut self) { self.hover = true; } pub fn hover_off(&mut self) { self.hover = false; } pub fn draw(&self, context: &mut ggez::Context, spritebatch: &mut SpriteBatch) { let source = match self.occupant { Occupant::None => None, Occupant::Green => Some(Rect::new(0.0, 0.0, 0.25, 1.0)), Occupant::Yellow => Some(Rect::new(0.25, 0.0, 0.25, 1.0)), Occupant::Diamond => Some(Rect::new(0.50, 0.0, 0.25, 1.0)), Occupant::Red => Some(Rect::new(0.75, 0.0, 0.25, 1.0)), }; if let Some(source) = source { spritebatch.add( DrawParam::default() .src(source) .dest(self.position) .scale(Vector2 { x: TILE_SCALE, y: TILE_SCALE, }), ); } if self.hover { let mesh = Mesh::new_rectangle( context, DrawMode::Stroke(StrokeOptions::default()), Rect::new( self.position.x, self.position.y, TILE_WIDTH * TILE_SCALE, TILE_HEIGHT * TILE_SCALE, ), Color::from_rgb(255, 100, 100), ); graphics::draw(context, &mesh.unwrap(), DrawParam::default()).unwrap(); } if self.clicked { let mesh = Mesh::new_rectangle( context, DrawMode::Stroke(StrokeOptions::default()), Rect::new( self.position.x, self.position.y, TILE_WIDTH * TILE_SCALE, TILE_HEIGHT * TILE_SCALE, ), Color::from_rgb(100, 255, 100), ); graphics::draw(context, &mesh.unwrap(), DrawParam::default()).unwrap(); } } } struct Game { selected: Option<(usize, usize)>, spritebatch: SpriteBatch, grid: Vec>, } impl Game { fn new(context: &mut ggez::Context) -> ggez::GameResult { let mut image = Image::new(context, "/gem.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..8 { let mut column = Vec::new(); column.push(Cell::new(Point2 { x: 0.0, y: y })); column.push(Cell::new(Point2 { x: 1.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); column.push(Cell::new(Point2 { x: 2.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); column.push(Cell::new(Point2 { x: 3.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); column.push(Cell::new(Point2 { x: 4.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); column.push(Cell::new(Point2 { x: 5.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); column.push(Cell::new(Point2 { x: 6.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); column.push(Cell::new(Point2 { x: 7.0 * (TILE_WIDTH * TILE_SCALE + BORDER_SIZE), y: y, })); y += TILE_HEIGHT * TILE_SCALE + BORDER_SIZE; grid.push(column); } Ok(Game { grid: grid, selected: None, spritebatch: SpriteBatch::new(image), }) } } impl event::EventHandler for Game { fn update(&mut self, context: &mut ggez::Context) -> ggez::GameResult { for row in self.grid.iter_mut() { for cell in row.iter_mut() { cell.hover_off(); } } let position = mouse::position(context); for row in self.grid.iter_mut() { for cell in row.iter_mut() { if cell.contains(position) { cell.hover_on(); } } } Ok(()) } fn mouse_button_down_event( &mut self, _context: &mut ggez::Context, button: mouse::MouseButton, x: f32, y: f32, ) { if button == mouse::MouseButton::Left { let position = Point2 { x: x, y: y }; for (i, row) in self.grid.iter_mut().enumerate() { for (j, cell) in row.iter_mut().enumerate() { if cell.contains(position) { self.selected = Some((i, j)); cell.clicked_on(); } } } } } fn mouse_button_up_event( &mut self, _context: &mut ggez::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: x, y: 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) { if (i + 1 == selected.0) && (j == selected.1) { swap = Some((i, j)); } } } } if let Some((i, j)) = swap { let clone = self.grid[i][j].occupant.clone(); 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 ggez::Context) -> ggez::GameResult { graphics::clear(context, [0.1, 0.2, 0.3, 1.0].into()); 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(()) } } pub fn main() -> ggez::GameResult { let cb = ggez::ContextBuilder::new("super_simple", "ggez"); let (context, event_loop) = &mut cb.add_resource_path("./resources").build()?; let game = &mut Game::new(context)?; event::run(context, event_loop, game) }