use std::fs;
use std::path::{Path, PathBuf};
use either::Either;
use hashbrown::HashMap;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize, Serializer};
use crate::{Error, Vec2};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Value {
Boolean(bool),
String(String),
Number(f32),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Level {
pub ogmo_version: String,
pub width: f32,
pub height: f32,
pub offset_x: f32,
pub offset_y: f32,
#[serde(default)]
pub values: HashMap<String, Value>,
pub layers: Vec<Layer>,
}
impl Level {
pub fn from_json(s: &str) -> Result<Level, Error> {
serde_json::from_str(s).map_err(Error::Json)
}
pub fn from_file(path: impl AsRef<Path>) -> Result<Level, Error> {
let json = fs::read_to_string(path).map_err(Error::Io)?;
Level::from_json(&json)
}
pub fn to_json(&self) -> Result<String, Error> {
serde_json::to_string(self).map_err(Error::Json)
}
pub fn to_json_pretty(&self) -> Result<String, Error> {
serde_json::to_string_pretty(self).map_err(Error::Json)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Entity {
pub name: String,
pub id: i32,
#[serde(rename = "_eid")]
pub export_id: String,
pub x: f32,
pub y: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub height: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub origin_x: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub origin_y: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rotation: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flipped_x: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flipped_y: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nodes: Option<Vec<Vec2<f32>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub values: Option<HashMap<String, Value>>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Decal {
pub x: f32,
pub y: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub scale_x: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scale_y: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rotation: Option<f32>,
pub texture: String,
pub values: HashMap<String, Value>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Layer {
Tile(TileLayer),
TileCoords(TileCoordsLayer),
Grid(GridLayer),
Entity(EntityLayer),
Decal(DecalLayer),
}
impl Layer {
pub fn name(&self) -> &str {
match self {
Layer::Tile(data) => &data.name,
Layer::TileCoords(data) => &data.name,
Layer::Grid(data) => &data.name,
Layer::Entity(data) => &data.name,
Layer::Decal(data) => &data.name,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TileLayer {
pub name: String,
#[serde(rename = "_eid")]
pub export_id: String,
pub offset_x: f32,
pub offset_y: f32,
pub grid_cell_width: i32,
pub grid_cell_height: i32,
pub grid_cells_x: i32,
pub grid_cells_y: i32,
pub tileset: String,
#[serde(flatten)]
pub data: TileLayerStorage,
}
impl TileLayer {
pub fn unpack(&self) -> impl Iterator<Item = Tile> + '_ {
match &self.data {
TileLayerStorage::Data(data) => {
Either::Left(data.iter().enumerate().map(move |(i, &v)| {
let grid_x = i as i32 % self.grid_cells_x;
let grid_y = i as i32 / self.grid_cells_x;
let pixel_x = grid_x * self.grid_cell_width;
let pixel_y = grid_y * self.grid_cell_height;
let id = if v == -1 { None } else { Some(v) };
Tile {
id,
grid_position: Vec2 {
x: grid_x,
y: grid_y,
},
pixel_position: Vec2 {
x: pixel_x,
y: pixel_y,
},
}
}))
}
TileLayerStorage::Data2D(data) => {
Either::Right(data.iter().enumerate().flat_map(move |(y, row)| {
row.iter().enumerate().map(move |(x, &v)| {
let grid_x = x as i32;
let grid_y = y as i32;
let pixel_x = grid_x * self.grid_cell_width;
let pixel_y = grid_y * self.grid_cell_height;
let id = if v == -1 { None } else { Some(v) };
Tile {
id,
grid_position: Vec2 {
x: grid_x,
y: grid_y,
},
pixel_position: Vec2 {
x: pixel_x,
y: pixel_y,
},
}
})
}))
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Tile {
pub id: Option<i32>,
pub grid_position: Vec2<i32>,
pub pixel_position: Vec2<i32>,
}
#[derive(Clone, Debug, Deserialize)]
pub enum TileLayerStorage {
#[serde(rename = "data")]
Data(Vec<i32>),
#[serde(rename = "data2D")]
Data2D(Vec<Vec<i32>>),
}
impl Serialize for TileLayerStorage {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("TileLayerStorage", 3)?;
match self {
TileLayerStorage::Data(data) => {
state.serialize_field("data", &data)?;
state.serialize_field("exportMode", &0)?;
state.serialize_field("arrayMode", &0)?;
}
TileLayerStorage::Data2D(data) => {
state.serialize_field("data2D", &data)?;
state.serialize_field("exportMode", &0)?;
state.serialize_field("arrayMode", &1)?;
}
}
state.end()
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TileCoordsLayer {
pub name: String,
#[serde(rename = "_eid")]
pub export_id: String,
pub offset_x: f32,
pub offset_y: f32,
pub grid_cell_width: i32,
pub grid_cell_height: i32,
pub grid_cells_x: i32,
pub grid_cells_y: i32,
pub tileset: String,
#[serde(flatten)]
pub data: TileCoordsLayerStorage,
}
impl TileCoordsLayer {
pub fn unpack(&self) -> impl Iterator<Item = TileCoords> + '_ {
match &self.data {
TileCoordsLayerStorage::DataCoords(data) => {
Either::Left(data.iter().enumerate().map(move |(i, coords)| {
let grid_x = i as i32 % self.grid_cells_x;
let grid_y = i as i32 / self.grid_cells_x;
let pixel_x = grid_x * self.grid_cell_width;
let pixel_y = grid_y * self.grid_cell_height;
let (grid_coords, pixel_coords) = if coords[0] == -1 {
(None, None)
} else {
let grid_u = coords[0];
let grid_v = coords[1];
let pixel_u = grid_u * self.grid_cell_width;
let pixel_v = grid_v * self.grid_cell_height;
(
Some(Vec2 {
x: grid_u,
y: grid_v,
}),
Some(Vec2 {
x: pixel_u,
y: pixel_v,
}),
)
};
TileCoords {
grid_coords,
pixel_coords,
grid_position: Vec2 {
x: grid_x,
y: grid_y,
},
pixel_position: Vec2 {
x: pixel_x,
y: pixel_y,
},
}
}))
}
TileCoordsLayerStorage::DataCoords2D(data) => {
Either::Right(data.iter().enumerate().flat_map(move |(y, row)| {
row.iter().enumerate().map(move |(x, coords)| {
let grid_x = x as i32;
let grid_y = y as i32;
let pixel_x = grid_x * self.grid_cell_width;
let pixel_y = grid_y * self.grid_cell_height;
let (grid_coords, pixel_coords) = if coords[0] == -1 {
(None, None)
} else {
let grid_u = coords[0];
let grid_v = coords[1];
let pixel_u = grid_u * self.grid_cell_width;
let pixel_v = grid_v * self.grid_cell_height;
(
Some(Vec2 {
x: grid_u,
y: grid_v,
}),
Some(Vec2 {
x: pixel_u,
y: pixel_v,
}),
)
};
TileCoords {
grid_coords,
pixel_coords,
grid_position: Vec2 {
x: grid_x,
y: grid_y,
},
pixel_position: Vec2 {
x: pixel_x,
y: pixel_y,
},
}
})
}))
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct TileCoords {
pub grid_coords: Option<Vec2<i32>>,
pub pixel_coords: Option<Vec2<i32>>,
pub grid_position: Vec2<i32>,
pub pixel_position: Vec2<i32>,
}
#[derive(Clone, Debug, Deserialize)]
pub enum TileCoordsLayerStorage {
#[serde(rename = "dataCoords")]
DataCoords(Vec<Vec<i32>>),
#[serde(rename = "dataCoords2D")]
DataCoords2D(Vec<Vec<Vec<i32>>>),
}
impl Serialize for TileCoordsLayerStorage {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("TileCoordsLayerStorage", 3)?;
match self {
TileCoordsLayerStorage::DataCoords(data) => {
state.serialize_field("dataCoords", &data)?;
state.serialize_field("exportMode", &1)?;
state.serialize_field("arrayMode", &0)?;
}
TileCoordsLayerStorage::DataCoords2D(data) => {
state.serialize_field("dataCoords2D", &data)?;
state.serialize_field("exportMode", &1)?;
state.serialize_field("arrayMode", &1)?;
}
}
state.end()
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GridLayer {
pub name: String,
#[serde(rename = "_eid")]
pub export_id: String,
pub offset_x: f32,
pub offset_y: f32,
pub grid_cell_width: i32,
pub grid_cell_height: i32,
pub grid_cells_x: i32,
pub grid_cells_y: i32,
#[serde(flatten)]
pub data: GridLayerStorage,
}
#[derive(Clone, Debug, Deserialize)]
pub enum GridLayerStorage {
#[serde(rename = "grid")]
Grid(Vec<String>),
#[serde(rename = "grid2D")]
Grid2D(Vec<Vec<String>>),
}
impl Serialize for GridLayerStorage {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("GridLayerStorage", 2)?;
match self {
GridLayerStorage::Grid(data) => {
state.serialize_field("grid", &data)?;
state.serialize_field("arrayMode", &0)?;
}
GridLayerStorage::Grid2D(data) => {
state.serialize_field("grid2D", &data)?;
state.serialize_field("arrayMode", &1)?;
}
}
state.end()
}
}
#[derive(Copy, Clone, Debug)]
pub struct GridCell<'a> {
pub value: &'a str,
pub grid_position: Vec2<i32>,
pub pixel_position: Vec2<i32>,
}
impl GridLayer {
pub fn unpack(&self) -> impl Iterator<Item = GridCell<'_>> + '_ {
match &self.data {
GridLayerStorage::Grid(data) => {
Either::Left(data.iter().enumerate().map(move |(i, value)| {
let grid_x = i as i32 % self.grid_cells_x;
let grid_y = i as i32 / self.grid_cells_x;
let pixel_x = grid_x * self.grid_cell_width;
let pixel_y = grid_y * self.grid_cell_height;
GridCell {
value,
grid_position: Vec2 {
x: grid_x,
y: grid_y,
},
pixel_position: Vec2 {
x: pixel_x,
y: pixel_y,
},
}
}))
}
GridLayerStorage::Grid2D(data) => {
Either::Right(data.iter().enumerate().flat_map(move |(y, row)| {
row.iter().enumerate().map(move |(x, value)| {
let grid_x = x as i32;
let grid_y = y as i32;
let pixel_x = grid_x * self.grid_cell_width;
let pixel_y = grid_y * self.grid_cell_height;
GridCell {
value,
grid_position: Vec2 {
x: grid_x,
y: grid_y,
},
pixel_position: Vec2 {
x: pixel_x,
y: pixel_y,
},
}
})
}))
}
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EntityLayer {
pub name: String,
#[serde(rename = "_eid")]
pub export_id: String,
pub offset_x: f32,
pub offset_y: f32,
pub grid_cell_width: i32,
pub grid_cell_height: i32,
pub grid_cells_x: i32,
pub grid_cells_y: i32,
pub entities: Vec<Entity>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DecalLayer {
pub name: String,
#[serde(rename = "_eid")]
pub export_id: String,
pub offset_x: f32,
pub offset_y: f32,
pub grid_cell_width: i32,
pub grid_cell_height: i32,
pub grid_cells_x: i32,
pub grid_cells_y: i32,
pub decals: Vec<Decal>,
pub folder: PathBuf,
}