gridit 0.1.0

2D grid library utilizing the fun of iterators
Documentation
use ggez::event::{self, EventHandler, MouseButton};
use ggez::graphics::DrawParam;
use ggez::graphics::Rect;
use ggez::input::mouse;
use ggez::mint::Point2;
use ggez::{graphics, Context, ContextBuilder, GameResult};

mod board;
mod picker;
mod piece;
use crate::board::*;
use crate::picker::*;
use crate::piece::*;

const BACKGROUND: graphics::Color = graphics::Color::new(100. / 255., 61. / 255., 1. / 255., 1.0);

fn main() {
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let mut path = std::path::PathBuf::from(manifest_dir);
    path.push("resources");

    let (mut ctx, event_loop) = ContextBuilder::new("pawn_example", "")
        .add_resource_path(path)
        .build()
        .expect("Could not create context");

    let game = BoardGame::new(&mut ctx);
    event::run(ctx, event_loop, game);
}

struct BoardGame {
    board: Board,
    picker: Picker,
    has_resized: bool,
    hdpi_factor: f32,
    draggin: Option<(graphics::Image, Name, PColor, [f32; 2])>,
}

const START_BOARD: &str = r#"
BR BN BB BQ BK BB BN BR
BL BG BP BP BP BP BG BL
N N N N N N N N
N N N N N N N N
N N N N N N N N
N N N N N N N N
WL WG WP WP WP WP WG WL
WR WN WB WQ WK WB WN WR
"#;

fn parse_to_piece(ctx: &mut Context, s: &str) -> BoardPiece {
    let mut chars = s.chars();
    let first = chars.next().unwrap();
    let pcolor = match first {
        'B' => PColor::BLACK,
        'W' => PColor::WHITE,
        _ => return None,
    };
    let second = chars.next().unwrap();
    let name = match second {
        'R' => Name::ROOK,
        'N' => Name::KNIGHT,
        'B' => Name::BISHOP,
        'Q' => Name::QUEEN,
        'K' => Name::KING,
        'P' => Name::PAWN,
        'L' => Name::BLOCKER,
        'G' => Name::GIRAFFE,
        _ => return None,
    };

    Some(new_piece(ctx, name, pcolor))
}

impl BoardGame {
    fn new(ctx: &mut Context) -> Self {
        let start_board = START_BOARD
            .split_whitespace()
            .map(|s| parse_to_piece(ctx, s))
            .collect();
        let mut grid = gridit::Grid::from(start_board, 8, 8);

        let hdpi_factor = graphics::window(&ctx).scale_factor() as f32;

        Self {
            board: Board::new(grid, (50.0, 50.0), 400.0),
            picker: Picker::new(ctx, Rect::new(550., 50., 200., 700.)),
            has_resized: true,
            hdpi_factor,
            draggin: None,
        }
    }

    fn resize_board(&mut self, ctx: &Context) {
        let hdpi_factor = graphics::window(ctx).scale_factor() as f32;
        let (x, y) = graphics::size(ctx);
        let size = if x >= y { y } else { x * 0.8 };
        let padding = x / 16.;
        let draw_rect = Rect::new(
            padding,
            padding,
            size * hdpi_factor - (padding * 2.),
            size * hdpi_factor - (padding * 2.),
        );
        self.board.set_rect(draw_rect);
    }

    fn resize_picker(&mut self, ctx: &Context) {
        let hdpi_factor = graphics::window(ctx).scale_factor() as f32;
        let (x, y) = graphics::size(ctx);
        let height = y;
        let width = x * 0.20;
        let padding = x / 16.;

        let draw_rect = Rect::new(
            (x - width) * hdpi_factor,
            padding,
            width * hdpi_factor - padding,
            height * hdpi_factor - (padding * 2.),
        );
        self.picker.set_rect(draw_rect);
    }
}

impl EventHandler for BoardGame {
    fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
        let mpoint = mouse::position(ctx);
        if self.draggin.is_some() {
            mouse::set_cursor_type(ctx, mouse::CursorIcon::Grabbing);
        } else if self.picker.on_dragable(mpoint) {
            mouse::set_cursor_type(ctx, mouse::CursorIcon::Grab);
        } else {
            mouse::set_cursor_type(ctx, mouse::CursorIcon::Default);
        }

        if self.board.contains_point(mpoint) {
            self.board.hover_field(mpoint);
        } else {
            self.board.unhover();
        }
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx, BACKGROUND);
        let hdpi_factor = graphics::window(&ctx).scale_factor() as f32;
        if self.has_resized || self.hdpi_factor != hdpi_factor {
            self.hdpi_factor = hdpi_factor;
            let (x, y) = graphics::size(&ctx);
            let draw_rect = Rect::new(0.0, 0.0, x * hdpi_factor, y * hdpi_factor);
            graphics::set_screen_coordinates(ctx, draw_rect)?;
            self.resize_board(ctx);
            self.resize_picker(ctx);
            self.has_resized = false;
        }

        graphics::draw(ctx, &self.board, DrawParam::default())?;
        graphics::draw(ctx, &self.picker, DrawParam::default())?;

        if let Some(item) = &self.draggin {
            let mpos = mouse::position(ctx);
            let [sx, sy] = item.3;
            let img = &item.0;
            let mut ir = img.dimensions();
            ir.scale(sx, sy);
            let img_center_w = ir.w / 2.;
            let dest: Point2<f32> = [(mpos.x - img_center_w), (mpos.y - ir.h)].into();
            graphics::draw(ctx, img, DrawParam::default().dest(dest).scale(item.3))?;
        }
        graphics::present(ctx)
    }

    fn resize_event(&mut self, _ctx: &mut Context, _width: f32, _height: f32) {
        self.has_resized = true;
    }

    fn mouse_button_up_event(&mut self, ctx: &mut Context, _button: MouseButton, x: f32, y: f32) {
        let mpoint = [x, y].into();
        if self.board.contains_point(mpoint) && self.draggin.is_some() {
            let info = self.draggin.take();
            let (_, name, pcolor, _) = info.unwrap();
            self.board.set_field(mpoint, new_piece(ctx, name, pcolor));
        } else if self.draggin.is_some() {
            self.draggin = None;
        }
    }

    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _button: MouseButton,
        x: f32,
        y: f32,
    ) {
        let mpoint = [x, y].into();
        if self.board.contains_point(mpoint) {
            self.board.select_field(mpoint);
            return;
        }
        if self.picker.contains_point(mpoint) {
            if self.picker.on_dragable(mpoint) {
                let piece_info = self.picker.get_item_at(mpoint);
                self.draggin = Some(piece_info);
            }
        }
        self.board.unselect_field();
    }
}