use super::{Tile, Error};
use std::iter;
#[derive(Serialize, Deserialize, Debug)]
pub struct Fill {
pub value: u8,
pub name: String,
pub count: usize,
}
impl Fill {
pub fn pull_tiles(&self) -> Result<Vec<Tile>, Error> {
if self.value > 3 {
return Err(Error::FormatError(
format!("Value must be between 0 and 3, but was {}", self.value)
));
}
let data: Vec<u8> = iter::repeat(self.value).take(64).collect();
let mut output = Vec::new();
for num in 0..self.count {
let tile = Tile::from_bytes(
&data,
Some(&format!(
"{name}_{num}",
name=self.name,
num=num,
)))?;
output.push(tile);
}
Ok(output)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Simple {
pub file: String,
pub name: String,
pub width: usize,
pub height: usize,
}
impl Simple {
pub fn pull_tiles(&self) -> Result<Vec<Tile>, Error> {
let tiles = self.load_tiles()?;
let mut output = Vec::new();
let width = self.width;
let height = self.height;
for y in 0..height {
for x in 0..width {
let mut tile_number = (y * width) + x;
let mut tile = tiles[tile_number].clone();
tile.name = Some(format!("{name}_{num}",
name = self.name,
num = tile_number,
));
output.push(tile);
}
}
Ok(output)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Animation {
pub file: String,
pub name: String,
pub frame_width: usize,
pub frame_height: usize,
pub frames: usize,
}
impl Animation {
pub fn pull_tiles(&self) -> Result<Vec<Tile>, Error> {
let tiles = self.load_tiles()?;
let mut output = Vec::new();
let sheet_width = self.frame_width * self.frames;
for frame in 0..self.frames {
let frame_offset = frame * self.frame_width;
for y in 0..self.frame_height {
let y_offset = y * sheet_width;
for x in 0..self.frame_width {
let mut tile_number = frame_offset + y_offset + x;
let mut tile = tiles[tile_number].clone();
let frame_tile_number = y * self.frame_width + x;
tile.name = Some(format!("{name}_{frame}_{tile}",
name = self.name,
frame = frame,
tile = frame_tile_number,
));
output.push(tile);
}
}
}
Ok(output)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Slice {
pub file: String,
pub name: String,
pub width: usize,
pub height: usize,
pub slices: Vec<Vec<usize>>,
}
impl Slice {
pub fn pull_tiles(&self) -> Result<Vec<Tile>, Error> {
let tiles = self.load_tiles()?;
let mut output = Vec::new();
for (slice_number, slice) in self.slices.iter().enumerate() {
for (tile_number, slice_tile) in slice.iter().enumerate() {
let mut tile = tiles[*slice_tile].clone();
tile.name = Some(format!("{name}_{slicenumber}_{tilenumber}",
name = self.name,
slicenumber = slice_number,
tilenumber = tile_number,
));
output.push(tile);
}
}
Ok(output)
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum Sheet {
Animation(Animation),
Slice(Slice),
Simple(Simple),
Fill(Fill),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SheetPatternTable {
pub left: Vec<Sheet>,
pub right: Vec<Sheet>,
}
pub trait LoadTiles {
fn sheet_width(&self) -> usize;
fn sheet_height(&self) -> usize;
fn image_path<'a>(&'a self) -> &'a str;
fn name<'a>(&'a self) -> &'a str;
fn load_tiles(&self) -> Result<Vec<Tile>, Error> {
let width = self.sheet_width();
let height = self.sheet_height();
let image_result = ::lodepng::decode_file(self.image_path(), ::lodepng::ffi::ColorType::PALETTE, 8);
match image_result {
Ok(image) => {
if let ::lodepng::Image::RawData(bitmap) = image {
if bitmap.width < width * 8 {
return Err(Error::DimensionsError(
format!("Image too thin, need {}, got {}.", width * 8, bitmap.width)
));
} else if bitmap.height < height * 8 {
return Err(Error::DimensionsError(
format!("Image too short, need {}, got {}.", height * 8, bitmap.height)
));
}
if let Some(item) = bitmap.buffer.iter().find(|&&item| item > 3) {
return Err(Error::PaletteError(
format!("Image has a byte out of bounds; needs to be under 4, got {}.", item)
));
}
let mut tiles = Vec::new();
for row in 0..height {
let y_offset = bitmap.width * row * 8;
for column in 0..width {
let x_offset = column * 8;
let bytes: Vec<u8> = (0..8).flat_map(|line| {
let offset = y_offset + x_offset + (line * bitmap.width);
&bitmap.buffer[offset..(offset + 8)]
}).cloned().collect();
tiles.push(Tile::from_bytes(&bytes, Some(self.name()))?);
}
}
Ok(tiles)
} else {
return Err(Error::FormatError(String::from("Image format was incorrect")));
}
},
Err(err) => {
Err(Error::PNGError(err))
},
}
}
}
impl LoadTiles for Simple {
fn sheet_width(&self) -> usize {
self.width
}
fn sheet_height(&self) -> usize {
self.height
}
fn image_path<'a>(&'a self) -> &'a str {
&self.file
}
fn name<'a>(&'a self) -> &'a str {
&self.name
}
}
impl LoadTiles for Animation {
fn sheet_width(&self) -> usize {
self.frame_width * self.frames
}
fn sheet_height(&self) -> usize {
self.frame_height
}
fn image_path<'a>(&'a self) -> &'a str {
&self.file
}
fn name<'a>(&'a self) -> &'a str {
&self.name
}
}
impl LoadTiles for Slice {
fn sheet_width(&self) -> usize {
self.width
}
fn sheet_height(&self) -> usize {
self.height
}
fn image_path<'a>(&'a self) -> &'a str {
&self.file
}
fn name<'a>(&'a self) -> &'a str {
&self.name
}
}