use ::map as mapfile; use anyhow::Error; use common::{num::Cast, pretty, vec}; use log::{info, log, warn}; use ndarray::Array2; use std::{ collections::{hash_map, HashMap}, fs::File, mem, }; pub use mapfile::format; pub use mapfile::{ format::Tile, reader::{self, Color as BadColor, LayerTilemapType}, }; use super::helper::Color; pub const TILE_NUM: u32 = 16; pub struct Layer { pub color: Color, pub image: Option, pub tiles: Array2, pub kind: LayerTilemapType, } pub struct Map { pub layers: Vec, pub tilesets: HashMap, Array2>, } impl Map { pub fn empty() -> Self { Self { layers: Vec::new(), tilesets: HashMap::new(), } } pub fn load(file: File) -> Result { info!("loading map"); let datafile = datafile::Reader::new(file).unwrap(); let mut map = mapfile::Reader::from_datafile(datafile); let mut layers = vec![]; for group_idx in map.group_indices() { let group = map.group(group_idx).unwrap(); if group.parallax_x != 100 || group.parallax_y != 100 || group.offset_x != 0 || group.offset_y != 0 || group.clipping.is_some() { warn!( "skipping layer: {}: off_x:{} off_y:{} parallax_x:{} parallax_y:{} clipping:{:?}", pretty::AlmostString::new(&group.name), group.offset_x, group.offset_y, group.parallax_x, group.parallax_y, group.clipping, ); continue; } for layer_idx in group.layer_indices { let layer = map.layer(layer_idx).unwrap(); let tilemap = if let reader::LayerType::Tilemap(t) = layer.t { t } else { continue; }; let normal = if let Some(n) = tilemap.type_.to_normal() { n } else { continue; }; let tiles = map.layer_tiles(tilemap.tiles(normal.data)).unwrap(); layers.push(Layer { color: Color { r: normal.color.red, g: normal.color.green, b: normal.color.blue, a: normal.color.alpha, }, image: normal.image, kind: tilemap.type_, tiles, }); } } let mut tilesets = HashMap::new(); for layer in &layers { match tilesets.entry(layer.image) { hash_map::Entry::Occupied(_) => {} hash_map::Entry::Vacant(v) => { let data = match layer.image { None => Array2::from_elem( (1, 1), Color { a: 255, r: 255, g: 0, b: 255, }, ), Some(image_idx) => { let image = map.image(image_idx).unwrap(); let height = image.height.usize(); let width = image.width.usize(); match image.data { Some(d) => { let data = map.image_data(d).unwrap(); if data.len() % mem::size_of::() != 0 { panic!("image shape invalid"); } let data: Vec = unsafe { vec::transmute(data) }; Array2::from_shape_vec((height, width), data).unwrap() } None => { warn!("layer with external tileset skipped"); continue; // let image_name = map.image_name(image.name)?; // // WARN? Unknown external image // // WARN! Wrong dimensions // str::from_utf8(&image_name).ok() // .and_then(sanitize) // .map(&mut external_tileset_loader) // .transpose()? // .unwrap_or(None) // .unwrap_or_else(|| Array2::from_elem((1, 1), Color::white())) } } } }; v.insert(data); } } } info!( "{} layers + {} tilesets loaded", layers.len(), tilesets.len() ); Ok(Self { tilesets, layers }) } }