1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use bevy::{math::Vec4, reflect::Reflect, render::color::Color};
use serde::{de::Visitor, Deserialize, Serialize};
use self::{
definitions::Definitions,
level::{EntityRef, Level},
};
pub mod definitions;
pub mod level;
pub mod macros;
#[derive(Serialize, Debug, Clone, Copy, Reflect)]
pub struct LdtkColor {
pub r: f32,
pub g: f32,
pub b: f32,
}
impl From<String> for LdtkColor {
fn from(value: String) -> Self {
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 { r, g, b }
}
}
impl Into<Color> for LdtkColor {
fn into(self) -> Color {
Color::rgb(self.r, self.g, self.b)
}
}
impl<'de> Deserialize<'de> for LdtkColor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(LdtkColorVisitor)
}
}
impl Into<Vec4> for LdtkColor {
fn into(self) -> Vec4 {
Vec4::new(self.r, self.g, self.b, 1.)
}
}
pub struct LdtkColorVisitor;
impl<'de> Visitor<'de> for LdtkColorVisitor {
type Value = LdtkColor;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a color in the format #RRGGBB")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(LdtkColor::from(value.to_string()))
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct LdtkJson {
/// Project background color
pub bg_color: LdtkColor,
/// A structure containing all the definitions of this project
pub defs: Definitions,
/// If TRUE, one file will be saved for the project (incl. all its definitions)
/// and one file in a sub-folder for each level.
pub external_levels: bool,
/// Unique project identifier
pub iid: String,
/// File format version
pub json_version: String,
/// All levels. The order of this array is only relevant in
/// `LinearHorizontal` and `linearVertical` world layouts (see `worldLayout` value).
///
/// Otherwise, you should refer to the `worldX`,`worldY` coordinates of each Level.
pub levels: Vec<Level>,
/// All instances of entities that have their `exportToToc`flag enabled
/// are listed in this array.
pub toc: Vec<Toc>,
/// ## WARNING:
/// this field will move to the `worlds` array after the "multi-worlds" update.
/// It will then be `null`. You can enable the Multi-worlds
/// advanced project option to enable the change immediately.
///
/// Height of the world grid in pixels.
pub world_grid_height: Option<i32>,
/// ## WARNING:
/// this field will move to the `worlds` array after the "multi-worlds" update.
/// It will then be `null`. You can enable the Multi-worlds
/// advanced project option to enable the change immediately.
///
/// Width of the world grid in pixels.
pub world_grid_width: Option<i32>,
/// ## WARNING:
/// this field will move to the `worlds` array after the "multi-worlds" update.
/// It will then be `null`. You can enable the Multi-worlds
/// advanced project option to enable the change immediately.
///
/// An enum that describes how levels are organized in this project (ie. linearly or in a 2D space).
pub world_layout: Option<WorldLayout>,
/// This array will be empty, unless you enable the Multi-Worlds in the project advanced settings.
/// - in current version, a LDtk project file can only contain a single world with
/// multiple levels in it. In this case, levels and world layout related settings
/// are stored in the root of the JSON.
/// - with "Multi-worlds" enabled, there will be a `worlds` array in root, each world
/// containing levels and layout settings. Basically, it's pretty much only about
/// moving the `levels` array to the `worlds` array, along with world layout related values
/// (eg. `worldGridWidth` etc).
///
/// If you want to start supporting this future update easily,
/// please refer to this documentation: https://github.com/deepnight/ldtk/issues/231
pub worlds: Vec<World>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Toc {
pub identifier: String,
pub instances: Vec<EntityRef>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum WorldLayout {
Free,
GridVania,
LinearHorizontal,
LinearVertical,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct World {
/// Width of the world grid in pixels.
pub world_grid_width: i32,
/// Unique instance identifer
pub iid: String,
/// Height of the world grid in pixels.
pub world_grid_height: i32,
/// An enum that describes how levels are organized in this project
/// (ie. linearly or in a 2D space).
/// Possible values: `Free`, `GridVania`, `LinearHorizontal`, `LinearVertical`
pub world_layout: Option<WorldLayout>,
/// All levels from this world.
/// The order of this array is only relevant in `LinearHorizontal` and
/// `linearVertical` world layouts (see `worldLayout` value). Otherwise,
/// you should refer to the `worldX`,`worldY` coordinates of each Level.
pub levels: Vec<Level>,
/// User defined unique identifier
pub identifier: String,
}