gsa 0.2.1

Game development library modelled after an imaginary console
Documentation
use crate::background::Background;
use crate::rgb::Rgb;
use crate::sprite::Sprite;
use crate::{
    Buttons, BACKGROUND_MAX_SIZE, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, EMPTY_TILE,
    MAX_BACKGROUNDS, MAX_SPRITES,
};
use ascii::{AsciiChar, AsciiStr};
use glam::IVec2;

/// Complete state of GSA
pub struct Gsa {
    /// Sprites available
    pub sprites: [Sprite; MAX_SPRITES],

    /// Palette used to draw graphics, initially loaded from gfx.gif
    pub palette: [Rgb; 256],

    /// Tilemap layers available, layer 0 in the back, layer 3 in front; layer 3 defaults to half-tile mode
    pub bgs: [Background; MAX_BACKGROUNDS],

    /// Currently selected font
    ///
    /// Chosen as half-size tile index, extends 16x16 half tiles in x and y
    pub font: u16,

    pub(crate) pressed: Buttons,
    pub(crate) released: Buttons,
    pub(crate) down: Buttons,
}

impl Gsa {
    /// Clears all tiles of given bg
    pub fn clear_bg(&mut self, bg: usize) {
        self.fill_bg(bg, EMPTY_TILE);
    }

    /// Resets all bg
    pub fn reset_bgs(&mut self) {
        for bg in 0..MAX_BACKGROUNDS {
            self.clear_bg(bg);
            self.bgs[bg].scroll = IVec2::ZERO;
            self.bgs[bg].half_tile = false;
        }
        //todo: document ui layer defaulting to half tile
        self.bgs[3].half_tile = true;
    }

    /// Clears all sprites
    pub fn reset_sprites(&mut self) {
        for sprite in 0..MAX_SPRITES {
            self.sprites[sprite].pos = IVec2::ZERO;
            self.sprites[sprite].tile = EMPTY_TILE;
            self.sprites[sprite].priority = 0;
        }
    }

    /// Sets all tiles of bg to val
    pub fn fill_bg(&mut self, bg: usize, val: u16) {
        for x in 0..BACKGROUND_MAX_SIZE {
            for y in 0..BACKGROUND_MAX_SIZE {
                self.bgs[bg].tiles[x][y] = val;
            }
        }
    }

    /// Checks if any of given buttons currently held down
    pub fn button_down(&self, button: Buttons) -> bool {
        self.down & button != 0
    }

    /// Checks if any of given buttons been pressed in the current frame
    pub fn button_pressed(&self, button: Buttons) -> bool {
        self.pressed & button != 0
    }

    /// Checks if any of given buttons been released in the current frame
    pub fn button_released(&self, button: Buttons) -> bool {
        self.released & button != 0
    }

    /// Directional vector (-1 to 1) from current dpad state
    pub fn input_dir(&self) -> IVec2 {
        IVec2 {
            x: if self.button_down(DPAD_LEFT) { -1 } else { 0 }
                + if self.button_down(DPAD_RIGHT) { 1 } else { 0 },
            y: if self.button_down(DPAD_UP) { -1 } else { 0 }
                + if self.button_down(DPAD_DOWN) { 1 } else { 0 },
        }
    }

    /// Write given string on given map, at given position, with font [Gsa::font]
    pub fn write_string(&mut self, map: usize, pos: IVec2, str: &str) {
        let str = AsciiStr::from_ascii(str).unwrap();
        for (i, ch) in str.into_iter().enumerate() {
            self.bgs[map].tiles[pos.x as usize + i][pos.y as usize] = self.get_char_tile(*ch);
        }
    }

    fn get_char_tile(&self, ch: AsciiChar) -> u16 {
        let ch = ch as u16;
        self.font + (ch % 0x10) + (ch / 0x10) * 0x100
    }
}