1use crate::datafile;
2
3use bitflags::bitflags;
4use fixed::types::{I17F15, I22F10, I27F5};
5use image::RgbaImage;
6use ndarray::Array2;
7use serde::{Deserialize, Serialize};
8use structview::View;
9use thiserror::Error;
10use vek::{Disk, Extent2, Rect, Rgba, Uv, Vec2};
11
12use std::io;
13
14mod checks;
15pub mod edit;
17mod impls;
19mod load;
20mod map_dir;
22mod parse;
23mod save;
24
25pub use checks::MapError;
26pub use load::{Load, LoadMultiple};
27pub use map_dir::MapDirParseError;
28pub use parse::MapParseError;
29
30#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
31#[serde(rename_all = "lowercase", tag = "type")]
32pub enum Version {
33 DDNet06,
34 Teeworlds07,
35}
36
37#[derive(Debug, Clone, Eq, PartialEq)]
78pub struct TwMap {
79 pub version: Version,
81 pub info: Info,
83 pub images: Vec<Image>,
85 pub envelopes: Vec<Envelope>,
87 pub groups: Vec<Group>,
89 pub sounds: Vec<Sound>,
91}
92
93#[derive(Error, Debug)]
94#[non_exhaustive]
95pub enum Error {
96 #[error("Map - {0}")]
97 Map(#[from] MapError),
98 #[error("Map from Datafile - {0}")]
99 MapParse(#[from] parse::MapParseError),
100 #[error("Datafile saving - {0}")]
101 DatafileSave(#[from] datafile::DatafileSaveError),
102 #[error("Datafile parsing - {0}")]
103 DatafileParse(#[from] datafile::DatafileParseError),
104 #[error("IO - {0}")]
105 Io(#[from] io::Error),
106 #[error("MapDir - {0}")]
107 MapDirParse(#[from] map_dir::MapDirParseError),
108}
109
110#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
111pub enum LayerKind {
113 Game,
114 Tiles,
115 Quads,
116 Front,
117 Tele,
118 Speedup,
119 Switch,
120 Tune,
121 Sounds,
122 Invalid(InvalidLayerKind),
123}
124
125#[derive(Debug, Eq, PartialOrd, PartialEq, Copy, Clone, Hash)]
126pub enum InvalidLayerKind {
127 Unknown(i32), UnknownTilemap(i32), NoType, NoTypeTilemap, }
132
133#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
134pub enum CompressedData<T, U> {
137 Compressed(Vec<u8>, usize, U),
138 Loaded(T),
139}
140
141#[derive(Debug, Default, Eq, PartialEq, Clone, Serialize, Deserialize)]
143pub struct Info {
144 pub author: String,
145 pub version: String,
146 pub credits: String,
147 pub license: String,
148 pub settings: Vec<String>,
149}
150
151#[derive(Debug, Eq, PartialEq, Clone)]
152pub enum Image {
153 External(ExternalImage),
155 Embedded(EmbeddedImage),
157}
158
159#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
160pub struct ExternalImage {
161 #[serde(skip)]
163 pub name: String,
164
165 pub size: Extent2<u32>,
166}
167
168#[derive(Debug, Eq, PartialEq, Copy, Clone)]
169pub struct ImageLoadInfo {
170 pub size: Extent2<u32>,
171}
172
173#[derive(Debug, Eq, PartialEq, Clone)]
174pub struct EmbeddedImage {
175 pub name: String,
177
178 pub image: CompressedData<RgbaImage, ImageLoadInfo>, }
180
181const PARALLAX_DIVISOR: Vec2<i32> = Vec2::new(100, 100);
182
183#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
184pub struct Group {
185 pub name: String,
186 pub offset: Vec2<I27F5>,
188 pub parallax: Vec2<i32>,
190 #[serde(skip)]
191 pub layers: Vec<Layer>,
192 pub clipping: bool,
194 pub clip: Rect<I27F5, I27F5>,
197}
198
199#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
200pub struct Sound {
201 pub name: String,
204 pub data: CompressedData<Vec<u8>, ()>,
205}
206
207#[derive(Default, Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
208pub struct AutomapperConfig {
209 pub config: Option<u16>,
212 pub seed: u32,
214 pub automatic: bool,
216}
217
218#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Serialize, Deserialize)]
219pub struct BezierCurve<T> {
220 pub handle_l: Vec2<T>,
221 pub handle_r: Vec2<T>,
222}
223
224#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize, Default)]
225#[serde(rename_all = "snake_case", tag = "type")]
226pub enum CurveKind<T> {
232 Step,
234 #[default]
237 Linear,
238 Slow,
241 Fast,
244 Smooth,
247 Bezier(BezierCurve<T>),
251 Unknown(i32),
253}
254
255#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
256pub struct EnvPoint<T> {
257 pub time: i32,
259 pub content: T,
260 #[serde(flatten)]
262 pub curve: CurveKind<T>,
263}
264
265pub trait BezierDefault: Default {
269 fn bezier_default() -> Self {
270 Default::default()
271 }
272}
273
274#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize, Default)]
275pub struct Position {
276 #[serde(flatten)]
277 pub offset: Vec2<I17F15>,
278 pub rotation: I22F10,
280}
281
282#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
283pub struct Volume(pub I22F10);
284
285#[derive(Default, Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
286pub struct Env<T: Copy> {
287 pub name: String,
288 pub synchronized: bool,
291 pub points: Vec<EnvPoint<T>>,
293}
294
295#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
296#[serde(rename_all = "snake_case", tag = "type")]
297pub enum Envelope {
298 Position(Env<Position>),
299 Color(Env<Rgba<I22F10>>),
300 Sound(Env<Volume>),
302}
303
304#[derive(Debug, Clone, Eq, PartialEq)]
305pub struct TilesLoadInfo {
306 pub size: Extent2<u32>,
307 pub compression: bool,
308}
309
310unsafe impl View for TileFlags {} bitflags! {
312 #[repr(C)]
321 #[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, Hash, Eq, PartialEq)]
322 #[serde(into = "map_dir::DirTileFlags", try_from = "map_dir::DirTileFlags")]
323 pub struct TileFlags: u8 {
324 const FLIP_X = 0b0001;
328 const FLIP_Y = 0b0010;
332 const OPAQUE = 0b0100;
334 const ROTATE = 0b1000;
337 }
338}
339
340#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
341#[repr(C)]
342pub struct Tile {
343 pub id: u8,
345 #[serde(flatten)]
346 pub flags: TileFlags,
347 #[serde(skip)]
348 pub(crate) skip: u8, #[serde(skip)]
350 pub(crate) unused: u8,
351}
352
353#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
354#[repr(C)]
355pub struct GameTile {
356 pub id: u8,
358 #[serde(flatten)]
359 pub flags: TileFlags,
360 #[serde(skip)]
361 pub(crate) skip: u8, #[serde(skip)]
363 pub(crate) unused: u8,
364}
365
366#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
367#[repr(C)]
368pub struct Tele {
369 pub number: u8,
370 pub id: u8,
371}
372
373#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
374#[repr(C)]
375#[serde(into = "i16", from = "i16")]
376pub struct I16 {
378 pub(crate) bytes: [u8; 2],
379}
380
381#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
382#[repr(C)]
383pub struct Speedup {
384 pub force: u8,
385 pub max_speed: u8,
386 pub id: u8,
387 #[serde(skip)]
388 pub(crate) unused_padding: u8,
389 pub angle: I16,
392}
393
394#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
395#[repr(C)]
396pub struct Switch {
397 pub number: u8,
398 pub id: u8,
399 #[serde(flatten)]
400 pub flags: TileFlags,
401 pub delay: u8,
402}
403
404#[derive(Debug, Copy, Clone, View, Eq, PartialEq, Default, Serialize, Deserialize)]
405#[repr(C)]
406pub struct Tune {
407 pub number: u8,
408 pub id: u8,
409}
410
411#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
412pub struct GameLayer {
413 #[serde(flatten, with = "map_dir::tiles_serialization")]
414 pub tiles: CompressedData<Array2<GameTile>, TilesLoadInfo>,
415}
416
417#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
418pub struct FrontLayer {
419 #[serde(flatten, with = "map_dir::tiles_serialization")]
420 pub tiles: CompressedData<Array2<GameTile>, TilesLoadInfo>,
421}
422
423#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
424pub struct TeleLayer {
425 #[serde(flatten, with = "map_dir::tiles_serialization")]
426 pub tiles: CompressedData<Array2<Tele>, TilesLoadInfo>,
427}
428
429#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
430pub struct SpeedupLayer {
431 #[serde(flatten, with = "map_dir::tiles_serialization")]
432 pub tiles: CompressedData<Array2<Speedup>, TilesLoadInfo>,
433}
434
435#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
436pub struct SwitchLayer {
437 #[serde(flatten, with = "map_dir::tiles_serialization")]
438 pub tiles: CompressedData<Array2<Switch>, TilesLoadInfo>,
439}
440
441#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
442pub struct TuneLayer {
443 #[serde(flatten, with = "map_dir::tiles_serialization")]
444 pub tiles: CompressedData<Array2<Tune>, TilesLoadInfo>,
445}
446
447#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
448pub struct TilesLayer {
449 pub name: String,
450 pub detail: bool,
451 pub color: Rgba<u8>,
452 #[serde(with = "map_dir::envelope_index_serialization")]
453 pub color_env: Option<u16>,
454 pub color_env_offset: i32,
455 #[serde(with = "map_dir::image_index_serialization")]
456 pub image: Option<u16>,
457 #[serde(flatten, with = "map_dir::tiles_serialization")]
458 pub tiles: CompressedData<Array2<Tile>, TilesLoadInfo>,
459 pub automapper_config: AutomapperConfig,
460}
461
462#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
463pub struct Quad {
464 pub corners: [Vec2<I17F15>; 4],
467 pub position: Vec2<I17F15>,
469 pub colors: [Rgba<u8>; 4],
470 pub texture_coords: [Uv<I22F10>; 4], #[serde(with = "map_dir::envelope_index_serialization")]
472 pub position_env: Option<u16>,
473 pub position_env_offset: i32,
474 #[serde(with = "map_dir::envelope_index_serialization")]
475 pub color_env: Option<u16>,
476 pub color_env_offset: i32,
477}
478
479#[derive(Default, Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
480pub struct QuadsLayer {
481 pub name: String,
482 pub detail: bool,
483 pub quads: Vec<Quad>,
484 #[serde(with = "map_dir::image_index_serialization")]
485 pub image: Option<u16>, }
487
488#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
489#[serde(rename_all = "snake_case", tag = "type")]
490pub enum SoundArea {
491 Rectangle(Rect<I17F15, I17F15>),
492 Circle(Disk<I17F15, I27F5>),
493}
494
495#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
496pub struct SoundSource {
497 pub area: SoundArea,
498 pub looping: bool,
499 pub panning: bool,
500 pub delay: i32,
501 pub falloff: u8,
502 #[serde(with = "map_dir::envelope_index_serialization")]
503 pub position_env: Option<u16>,
504 pub position_env_offset: i32,
505 #[serde(with = "map_dir::envelope_index_serialization")]
506 pub sound_env: Option<u16>,
507 pub sound_env_offset: i32,
508}
509
510#[derive(Default, Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
511pub struct SoundsLayer {
512 pub name: String,
513 pub detail: bool,
514 pub sources: Vec<SoundSource>,
515 #[serde(with = "map_dir::sound_index_serialization")]
516 pub sound: Option<u16>,
517}
518
519#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
520#[serde(rename_all = "snake_case", tag = "type")]
521pub enum Layer {
522 Game(GameLayer),
523 Tiles(TilesLayer),
524 Quads(QuadsLayer),
525 Front(FrontLayer),
526 Tele(TeleLayer),
527 Speedup(SpeedupLayer),
528 Switch(SwitchLayer),
529 Tune(TuneLayer),
530 Sounds(SoundsLayer),
531 #[serde(skip)]
532 Invalid(InvalidLayerKind),
533}
534
535pub trait TilemapLayer: AnyLayer {
536 type TileType: AnyTile;
537
538 fn tiles(&self) -> &CompressedData<Array2<Self::TileType>, TilesLoadInfo>;
539
540 fn tiles_mut(&mut self) -> &mut CompressedData<Array2<Self::TileType>, TilesLoadInfo>;
541}
542
543pub trait AnyTile: Default + PartialEq + Copy + Clone + checks::TileChecking + View {
544 fn id(&self) -> u8;
545
546 fn id_mut(&mut self) -> &mut u8;
547
548 fn flags(&self) -> Option<TileFlags>;
549
550 fn flags_mut(&mut self) -> Option<&mut TileFlags>;
551}
552
553pub trait AnyLayer: Sized {
554 fn kind() -> LayerKind;
555
556 fn get(layer: &Layer) -> Option<&Self>;
557
558 fn get_mut(layer: &mut Layer) -> Option<&mut Self>;
559}
560
561pub trait PhysicsLayer: TilemapLayer {}