use std::fs;
use std::path::{Path, PathBuf};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::{Error, Vec2};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Project {
pub name: String,
pub ogmo_version: String,
pub level_paths: Vec<PathBuf>,
pub background_color: String,
pub grid_color: String,
pub angles_radians: bool,
pub directory_depth: i32,
pub layer_grid_default_size: Vec2<i32>,
pub level_default_size: Vec2<i32>,
pub level_min_size: Vec2<i32>,
pub level_max_size: Vec2<i32>,
pub level_values: Vec<ValueTemplate>,
pub default_export_mode: String,
pub compact_export: bool,
pub entity_tags: Vec<String>,
pub layers: Vec<LayerTemplate>,
pub entities: Vec<EntityTemplate>,
pub tilesets: Vec<Tileset>,
}
impl Project {
pub fn from_json(s: &str) -> Result<Project, Error> {
serde_json::from_str(s).map_err(Error::Json)
}
pub fn from_file(path: impl AsRef<Path>) -> Result<Project, Error> {
let json = fs::read_to_string(path).map_err(Error::Io)?;
Project::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(tag = "definition")]
pub enum ValueTemplate {
Boolean(BooleanValueTemplate),
Color(ColorValueTemplate),
Enum(EnumValueTemplate),
Integer(IntegerValueTemplate),
Float(FloatValueTemplate),
String(StringValueTemplate),
Text(TextValueTemplate),
}
impl ValueTemplate {
pub fn name(&self) -> &str {
match self {
ValueTemplate::Boolean(data) => &data.name,
ValueTemplate::Color(data) => &data.name,
ValueTemplate::Enum(data) => &data.name,
ValueTemplate::Integer(data) => &data.name,
ValueTemplate::Float(data) => &data.name,
ValueTemplate::String(data) => &data.name,
ValueTemplate::Text(data) => &data.name,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BooleanValueTemplate {
pub name: String,
pub defaults: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ColorValueTemplate {
pub name: String,
pub defaults: String,
pub include_alpha: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EnumValueTemplate {
pub name: String,
pub defaults: i32,
pub choices: Vec<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IntegerValueTemplate {
pub name: String,
pub defaults: i32,
pub bounded: bool,
pub min: i32,
pub max: i32,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FloatValueTemplate {
pub name: String,
pub defaults: f32,
pub bounded: bool,
pub min: f32,
pub max: f32,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StringValueTemplate {
pub name: String,
pub defaults: String,
pub max_length: i32,
pub trim_whitespace: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TextValueTemplate {
pub name: String,
pub defaults: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum LayerTemplate {
Tile(TileLayerTemplate),
Grid(GridLayerTemplate),
Entity(EntityLayerTemplate),
Decal(DecalLayerTemplate),
}
impl LayerTemplate {
pub fn name(&self) -> &str {
match self {
LayerTemplate::Tile(data) => &data.name,
LayerTemplate::Grid(data) => &data.name,
LayerTemplate::Entity(data) => &data.name,
LayerTemplate::Decal(data) => &data.name,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "definition", rename = "tile")]
pub struct TileLayerTemplate {
pub name: String,
pub grid_size: Vec2<i32>,
#[serde(rename = "exportID")]
pub export_id: String,
pub export_mode: ExportMode,
pub array_mode: ArrayMode,
pub default_tileset: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "definition", rename = "grid")]
pub struct GridLayerTemplate {
pub name: String,
pub grid_size: Vec2<i32>,
#[serde(rename = "exportID")]
pub export_id: String,
pub array_mode: ArrayMode,
pub legend: HashMap<String, String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "definition", rename = "entity")]
pub struct EntityLayerTemplate {
pub name: String,
pub grid_size: Vec2<i32>,
#[serde(rename = "exportID")]
pub export_id: String,
pub required_tags: Vec<String>,
pub excluded_tags: Vec<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "definition", rename = "decal")]
pub struct DecalLayerTemplate {
pub name: String,
pub grid_size: Vec2<i32>,
#[serde(rename = "exportID")]
pub export_id: String,
pub folder: PathBuf,
pub include_image_sequence: bool,
pub scaleable: bool,
pub rotatable: bool,
pub values: Vec<ValueTemplate>,
}
#[derive(Clone, Debug, Deserialize_repr, Serialize_repr)]
#[repr(u8)]
pub enum ExportMode {
Ids = 0,
Coords = 1,
}
#[derive(Clone, Debug, Deserialize_repr, Serialize_repr)]
#[repr(u8)]
pub enum ArrayMode {
One = 0,
Two = 1,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EntityTemplate {
pub name: String,
#[serde(rename = "exportID")]
pub export_id: String,
pub limit: i32,
pub size: Vec2<f32>,
pub origin: Vec2<f32>,
pub origin_anchored: bool,
pub shape: Shape,
pub color: String,
pub tile_x: bool,
pub tile_y: bool,
pub tile_size: Vec2<f32>,
pub resizeable_x: bool,
pub resizeable_y: bool,
pub rotatable: bool,
pub rotation_degrees: f32,
pub can_flip_x: bool,
pub can_flip_y: bool,
pub can_set_color: bool,
pub has_nodes: bool,
pub node_limit: i32,
pub node_display: i32,
pub node_ghost: bool,
pub tags: Vec<String>,
pub values: Vec<ValueTemplate>,
#[serde(skip_serializing_if = "Option::is_none")]
pub texture: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub texture_image: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Shape {
pub label: String,
pub points: Vec<Vec2<f32>>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Tileset {
pub label: String,
pub path: PathBuf,
pub image: String,
pub tile_width: i32,
pub tile_height: i32,
pub tile_separation_x: i32,
pub tile_separation_y: i32,
}
impl Tileset {
pub fn tile_coords(
&self,
texture_width: i32,
texture_height: i32,
) -> impl Iterator<Item = Vec2<i32>> + '_ {
let step_x = self.tile_width + self.tile_separation_x;
let step_y = self.tile_height + self.tile_separation_y;
let tiles_x = texture_width / step_x;
let tiles_y = texture_height / step_y;
(0..tiles_y).flat_map(move |tile_y| {
(0..tiles_x).map(move |tile_x| {
let x = tile_x * step_x;
let y = tile_y * step_y;
Vec2 { x, y }
})
})
}
}