use agb::display::GraphicsFrame;
use agb::display::object::{Object, Sprite, Tag};
use agb::display::tile_data::TileData;
use agb::display::tiled::{RegularBackground, TileSetting};
use agb::fixnum::{Vector2D, vec2};
pub trait ShowSprite {
fn show(&self, at: Vector2D<i32>, frame: &mut GraphicsFrame);
}
pub trait ShowTag {
fn show(&self, idx: usize, at: Vector2D<i32>, frame: &mut GraphicsFrame);
}
impl ShowSprite for &'static Sprite {
#[inline]
fn show(&self, at: Vector2D<i32>, frame: &mut GraphicsFrame) {
Object::new(*self).set_pos(at).show(frame);
}
}
impl ShowTag for Tag {
#[inline]
fn show(&self, idx: usize, at: Vector2D<i32>, frame: &mut GraphicsFrame) {
Object::new(self.sprite(idx)).set_pos(at).show(frame);
}
}
pub trait ShowAllTag {
fn show_all(&self, at: Vector2D<i32>, frame: &mut GraphicsFrame);
}
impl ShowAllTag for Tag {
#[inline]
fn show_all(&self, at: Vector2D<i32>, frame: &mut GraphicsFrame) {
let (width, _) = self.sprite(0).size().to_width_height();
let mut pos = at;
for sprite in self.sprites() {
sprite.show(pos, frame);
pos.x += width as i32;
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Corners {
Single(u16),
All([u16; 4]),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct FrameTiles<'a> {
pub top: &'a [u16],
pub left: &'a [u16],
pub corners: Corners,
pub center: &'a [u16],
}
pub fn draw_bg_frame(
bg: &mut RegularBackground,
tile_data: &TileData,
at: impl Into<Vector2D<i32>>,
size: impl Into<Vector2D<i32>>,
tiles: &FrameTiles,
) {
let at = at.into();
let size = size.into();
assert!(
!tiles.top.is_empty() && !tiles.left.is_empty(),
"frame top and left tiles must not be empty"
);
assert!(size.x >= 2 && size.y >= 2, "frame must be at least 2x2");
let setting = |idx: u16| tile_data.tile_settings[idx as usize];
let (tl, tr, bl, br) = match tiles.corners {
Corners::Single(idx) => {
let s = setting(idx);
(s, s.hflip(true), s.vflip(true), s.hflip(true).vflip(true))
}
Corners::All([tl, tr, bl, br]) => (setting(tl), setting(tr), setting(bl), setting(br)),
};
let right = at.x + size.x - 1;
let bottom = at.y + size.y - 1;
bg.set_tile(at, &tile_data.tiles, tl);
bg.set_tile(vec2(right, at.y), &tile_data.tiles, tr);
bg.set_tile(vec2(at.x, bottom), &tile_data.tiles, bl);
bg.set_tile(vec2(right, bottom), &tile_data.tiles, br);
for (x, &idx) in (at.x + 1..right).zip(tiles.top.iter().cycle()) {
let s = setting(idx);
bg.set_tile(vec2(x, at.y), &tile_data.tiles, s);
bg.set_tile(vec2(x, bottom), &tile_data.tiles, s.vflip(true));
}
for (y, &idx) in (at.y + 1..bottom).zip(tiles.left.iter().cycle()) {
let s = setting(idx);
bg.set_tile(vec2(at.x, y), &tile_data.tiles, s);
bg.set_tile(vec2(right, y), &tile_data.tiles, s.hflip(true));
}
if !tiles.center.is_empty() {
for y in at.y + 1..bottom {
for (x, &idx) in (at.x + 1..right).zip(tiles.center.iter().cycle()) {
bg.set_tile(vec2(x, y), &tile_data.tiles, setting(idx));
}
}
}
}
pub fn draw_bg_tiles(
bg: &mut RegularBackground,
tile_data: &TileData,
pos: impl Into<Vector2D<i32>>,
start_tile_idx: u16,
meta_tile_size: impl Into<Vector2D<i32>>,
count: impl Into<Vector2D<i32>>,
) {
let pos = pos.into();
let meta = meta_tile_size.into();
let count = count.into();
let stride = tile_data.width as i32;
let mut row_start = start_tile_idx as i32;
for meta_row in 0..meta.y {
for meta_col in 0..meta.x {
let setting: TileSetting = tile_data.tile_settings[(row_start + meta_col) as usize];
let mut y = pos.y + meta_row;
for _ in 0..count.y {
let mut x = pos.x + meta_col;
for _ in 0..count.x {
bg.set_tile(vec2(x, y), &tile_data.tiles, setting);
x += meta.x;
}
y += meta.y;
}
}
row_start += stride;
}
}