use std::{collections::HashMap, path::Path, sync::Arc};
use crate::{
Color, MapTilesetGid, Object, ObjectData, Properties, ResourceCache, ResourceReader, Result,
Tileset, parse_properties,
util::{get_attrs, map_wrapper, parse_tag},
};
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
pub enum DrawOrder {
#[default]
TopDown,
Index,
}
#[derive(Debug)]
pub struct DrawOrderParseError {
pub str_found: String,
}
impl std::fmt::Display for DrawOrderParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"failed to parse draw order, valid options are `topdown` and `index` \
but got `{}` instead",
self.str_found
))
}
}
impl std::str::FromStr for DrawOrder {
type Err = DrawOrderParseError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"topdown" => Ok(DrawOrder::TopDown),
"index" => Ok(DrawOrder::Index),
_ => Err(DrawOrderParseError {
str_found: s.to_owned(),
}),
}
}
}
impl std::fmt::Display for DrawOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DrawOrder::TopDown => write!(f, "topdown"),
DrawOrder::Index => write!(f, "index"),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ObjectLayerData {
objects: Vec<ObjectData>,
pub colour: Option<Color>,
pub draw_order: DrawOrder,
}
impl ObjectLayerData {
pub(crate) fn new<R: std::io::BufRead>(
elem: crate::util::XmlElement<'_, R>,
tilesets: Option<&[MapTilesetGid]>,
for_tileset: Option<Arc<Tileset>>,
path_relative_to: &Path,
reader: &mut impl ResourceReader,
cache: &mut impl ResourceCache,
) -> Result<(ObjectLayerData, Properties)> {
let (c, draw_order) = get_attrs!(
for v in (elem.attrs) {
Some("color") => color ?= v.parse(),
Some("draworder") => draw_order ?= v.parse::<DrawOrder>(),
}
(color, draw_order)
);
let draw_order = draw_order.unwrap_or_default();
let mut objects = Vec::new();
let mut properties = HashMap::new();
parse_tag!(elem, {
"object" => |elem| {
objects.push(ObjectData::new(elem, tilesets, for_tileset.as_ref().cloned(), path_relative_to, reader, cache)?);
Ok(())
},
"properties" => |elem| {
properties = parse_properties(elem)?;
Ok(())
},
});
Ok((
ObjectLayerData {
objects,
colour: c,
draw_order,
},
properties,
))
}
#[inline]
pub fn object_data(&self) -> &[ObjectData] {
self.objects.as_ref()
}
}
map_wrapper!(
#[doc = "Also called an \"object group\". Used for storing [`Object`]s in a map."]
ObjectLayer => ObjectLayerData);
impl<'map> ObjectLayer<'map> {
pub fn get_object(&self, idx: usize) -> Option<Object<'map>> {
self.data
.objects
.get(idx)
.map(|data| Object::new(self.map, data))
}
#[inline]
pub fn objects(&self) -> impl ExactSizeIterator<Item = Object<'map>> + 'map + use<'map> {
let map: &'map crate::Map = self.map;
self.data
.objects
.iter()
.map(move |object| Object::new(map, object))
}
}