use std::fmt::Formatter;
use bevy::{
math::{Vec2, Vec4},
reflect::Reflect,
render::color::Color,
};
use serde::{de::Visitor, Deserialize, Serialize};
use crate::tilemap::{coordinates::StaggerMode, map::TilemapType};
use self::{default::*, layer::TiledLayer};
pub mod default;
pub mod layer;
pub mod property;
pub mod tileset;
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct TiledTilemap {
#[serde(rename = "@version")]
pub version: String,
#[serde(rename = "@tiledversion")]
pub tiled_version: String,
#[serde(rename = "@orientation")]
pub orientation: MapOrientation,
#[serde(rename = "@renderorder")]
pub render_order: TileRenderOrder,
#[serde(rename = "@width")]
pub width: u32,
#[serde(rename = "@height")]
pub height: u32,
#[serde(rename = "@tilewidth")]
pub tile_width: u32,
#[serde(rename = "@tileheight")]
pub tile_height: u32,
#[serde(rename = "@hexsidelength")]
#[serde(default)]
pub hex_side_length: u32,
#[serde(rename = "@staggeraxis")]
#[serde(default)]
pub stagger_axis: StaggeredAxis,
#[serde(rename = "@staggerindex")]
#[serde(default)]
pub stagger_index: StaggerIndex,
#[serde(rename = "@parallaxoriginx")]
#[serde(default)]
pub parallax_origin_x: f32,
#[serde(rename = "@parallaxoriginy")]
#[serde(default)]
pub parallax_origin_y: f32,
#[serde(rename = "@backgroundcolor")]
#[serde(default)]
pub background_color: TiledColor,
#[serde(rename = "tileset")]
pub tilesets: Vec<TilesetDef>,
#[serde(rename = "$value")]
#[serde(default)]
pub layers: Vec<TiledLayer>,
#[serde(rename = "group")]
#[serde(default)]
pub groups: Vec<TiledGroup>,
}
#[derive(Debug, Clone, Reflect, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum MapOrientation {
Orthogonal,
Isometric,
Staggered,
Hexagonal,
}
impl MapOrientation {
pub fn as_tilemap_type(self, leg: u32) -> TilemapType {
match self {
MapOrientation::Orthogonal => TilemapType::Square,
MapOrientation::Isometric => TilemapType::Isometric,
MapOrientation::Staggered => TilemapType::Hexagonal(0),
MapOrientation::Hexagonal => TilemapType::Hexagonal(leg),
}
}
}
#[derive(Debug, Clone, Reflect, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum TileRenderOrder {
RightDown,
RightUp,
LeftDown,
LeftUp,
}
#[derive(Debug, Default, Clone, Copy, Reflect, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum StaggeredAxis {
X,
#[default]
Y,
}
#[derive(Debug, Default, Clone, Copy, Reflect, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum StaggerIndex {
#[default]
Odd,
Even,
}
impl Into<StaggerMode> for StaggerIndex {
fn into(self) -> StaggerMode {
match self {
StaggerIndex::Odd => StaggerMode::Odd,
StaggerIndex::Even => StaggerMode::Even,
}
}
}
impl StaggerIndex {
pub fn get_offset(self) -> Vec2 {
match self {
StaggerIndex::Odd => Vec2::ZERO,
StaggerIndex::Even => Vec2::new(0.5, 0.),
}
}
}
#[derive(Debug, Default, Clone, Reflect, Copy, Serialize)]
pub struct TiledColor {
pub a: f32,
pub r: f32,
pub g: f32,
pub b: f32,
}
impl From<String> for TiledColor {
fn from(value: String) -> Self {
if value.len() == 7 {
let r = u8::from_str_radix(&value[1..3], 16).unwrap() as f32 / 255.;
let g = u8::from_str_radix(&value[3..5], 16).unwrap() as f32 / 255.;
let b = u8::from_str_radix(&value[5..7], 16).unwrap() as f32 / 255.;
Self { a: 1., r, g, b }
} else {
let a = u8::from_str_radix(&value[1..3], 16).unwrap() as f32 / 255.;
let r = u8::from_str_radix(&value[3..5], 16).unwrap() as f32 / 255.;
let g = u8::from_str_radix(&value[5..7], 16).unwrap() as f32 / 255.;
let b = u8::from_str_radix(&value[7..9], 16).unwrap() as f32 / 255.;
Self { a, r, g, b }
}
}
}
impl Into<Color> for TiledColor {
fn into(self) -> Color {
Color::rgba(self.r, self.g, self.b, self.a)
}
}
impl Into<Vec4> for TiledColor {
fn into(self) -> Vec4 {
Vec4::new(self.r, self.g, self.b, self.a)
}
}
impl<'de> Deserialize<'de> for TiledColor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
pub struct TiledColorVisitor;
impl<'de> Visitor<'de> for TiledColorVisitor {
type Value = TiledColor;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a color in the format #AARRGGBB")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(TiledColor::from(v.to_string()))
}
}
deserializer.deserialize_str(TiledColorVisitor)
}
}
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct TilesetDef {
#[serde(rename = "@firstgid")]
pub first_gid: u32,
#[serde(rename = "@source")]
pub source: String,
}
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
pub struct TiledGroup {
#[serde(rename = "@id")]
pub id: u32,
#[serde(rename = "@name")]
pub name: String,
#[serde(rename = "@x")]
#[serde(default)]
pub x: i32,
#[serde(rename = "@y")]
#[serde(default)]
pub y: i32,
#[serde(rename = "@opacity")]
#[serde(default = "default_onef")]
pub opacity: f32,
#[serde(rename = "@visible")]
#[serde(default = "default_true")]
pub visible: bool,
#[serde(rename = "@tintcolor")]
#[serde(default = "default_white")]
pub tint: TiledColor,
#[serde(rename = "@offsetx")]
#[serde(default)]
pub offset_x: f32,
#[serde(rename = "@offsety")]
#[serde(default)]
pub offset_y: f32,
#[serde(rename = "@parallaxx")]
#[serde(default = "default_onef")]
pub parallax_x: f32,
#[serde(rename = "@parallaxy")]
#[serde(default = "default_onef")]
pub parallax_y: f32,
#[serde(rename = "@width")]
#[serde(default)]
pub width: u32,
#[serde(rename = "@height")]
#[serde(default)]
pub height: u32,
#[serde(rename = "$value")]
#[serde(default)]
pub layers: Vec<TiledLayer>,
#[serde(rename = "group")]
#[serde(default)]
#[reflect(ignore)]
pub groups: Vec<TiledGroup>,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse() {
let map = quick_xml::de::from_str::<TiledTilemap>(
std::fs::read_to_string("assets/tiled/tilemaps/isometric.tmx")
.unwrap()
.as_str(),
)
.unwrap();
dbg!(map);
}
}