use crate::{Buffer, BufferMut, Color, TileId, Tileset};
use alloc::vec;
use alloc::vec::Vec;
use core::ops::{Index, IndexMut};
use fast_srgb8::{f32x4_to_srgb8, srgb8_to_f32};
use simple_blit::{blit_with, BlitOptions};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Tile<U = ()> {
pub id: TileId,
pub color: Color,
pub opts: BlitOptions,
pub data: U,
}
impl<U> Tile<U>
where
U: Default,
{
#[inline]
pub fn new(id: TileId) -> Self {
Self {
id,
color: Color::new(255, 255, 255, 255),
opts: BlitOptions::None,
data: U::default(),
}
}
}
impl<U> Tile<U> {
#[inline]
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
#[inline]
pub fn set_color(&mut self, color: Color) -> &mut Self {
self.color = color;
self
}
#[inline]
pub fn with_user_data(mut self, data: U) -> Self {
self.data = data;
self
}
#[inline]
pub fn set_user_data(&mut self, data: U) -> &mut Self {
self.data = data;
self
}
#[inline]
pub fn with_blit_options(mut self, opts: BlitOptions) -> Self {
self.opts = opts;
self
}
#[inline]
pub fn set_blit_options(&mut self, opts: BlitOptions) -> &mut Self {
self.opts = opts;
self
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Tilemap<C, U = ()> {
tileset: Tileset<C>,
tiles: Vec<Tile<U>>,
width: u32,
height: u32,
}
impl<C, U> Tilemap<C, U>
where
U: Default + Clone,
{
#[inline]
pub fn new(width: u32, height: u32, tileset: Tileset<C>) -> Self {
Self {
width,
height,
tiles: vec![Tile::default(); (width * height) as usize],
tileset,
}
}
}
impl<C, U> Tilemap<C, U> {
#[inline]
pub fn width(&self) -> u32 {
self.width
}
#[inline]
pub fn height(&self) -> u32 {
self.height
}
#[inline]
pub fn tileset(&self) -> &Tileset<C> {
&self.tileset
}
#[inline]
pub fn tiles(&self) -> &[Tile<U>] {
&self.tiles
}
#[inline]
pub fn tiles_mut(&mut self) -> &mut [Tile<U>] {
&mut self.tiles
}
#[inline]
pub fn get_tile(&self, x: u32, y: u32) -> Option<&Tile<U>> {
self.tiles.get((y * self.width + x) as usize)
}
#[inline]
pub fn get_mut_tile(&mut self, x: u32, y: u32) -> Option<&mut Tile<U>> {
self.tiles.get_mut((y * self.width + x) as usize)
}
#[inline]
pub fn set_tile(&mut self, x: u32, y: u32, tile: Tile<U>) {
if let Some(t) = self.tiles.get_mut((y * self.width + x) as usize) {
*t = tile;
}
}
}
impl<C> Tilemap<C>
where
C: AsRef<[u8]>,
{
pub fn render(
&self,
surface: &mut (impl BufferMut<Color> + ?Sized),
offset_x: i32,
offset_y: i32,
) {
for ty in 0..self.height {
for tx in 0..self.width {
let &Tile {
id: tile,
color,
opts,
..
} = self.get(tx, ty);
if let Some((x, y)) = self.tileset.get_tile_pos(tile) {
blit_with(
surface,
(offset_x, offset_y),
&self.tileset,
(x as _, y as _),
self.tileset.opts.tile_size,
opts,
|dest, src, _| {
if Some(*src) != self.tileset.opts.key_color {
let [r, g, b, a] = f32x4_to_srgb8([
srgb8_to_f32(src.r) * srgb8_to_f32(color.r),
srgb8_to_f32(src.g) * srgb8_to_f32(color.g),
srgb8_to_f32(src.b) * srgb8_to_f32(color.b),
srgb8_to_f32(src.a) * srgb8_to_f32(color.a),
]);
*dest = Color::new(r, g, b, a);
}
},
);
}
}
}
}
}
impl<C> Buffer<Tile> for Tilemap<C> {
#[inline]
fn width(&self) -> u32 {
self.width
}
#[inline]
fn height(&self) -> u32 {
self.height
}
#[inline]
fn get(&self, x: u32, y: u32) -> &Tile {
self.tiles.index((y * self.width + x) as usize)
}
}
impl<C> BufferMut<Tile> for Tilemap<C> {
#[inline]
fn get_mut(&mut self, x: u32, y: u32) -> &mut Tile {
self.tiles.index_mut((y * self.width + x) as usize)
}
}