use super::{
brush::Brush,
color::Color,
layer::Layer,
novascript::{variable::Variable, NovaScript},
object::Object,
pattern::Pattern,
prefab::Prefab,
vec2::Vec2,
};
use crate::{error::Error, Read, ReadVersioned, Write};
use uuid::Uuid;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default)]
#[allow(clippy::struct_excessive_bools)]
pub struct LevelData {
pub level_id: Uuid,
pub level_version: i32,
pub nova_level: bool,
pub under_decoration_tiles: Vec<i32>,
pub background_decoration_tiles: Vec<i32>,
pub terrain_tiles: Vec<i32>,
pub floating_zone_tiles: Vec<i32>,
pub object_tiles: Vec<i32>,
pub foreground_decoration_tiles: Vec<i32>,
pub objects: Vec<Object>,
pub layers: Vec<Layer>,
pub prefabs: Vec<Prefab>,
pub brushes: Vec<Brush>,
pub patterns: Vec<Pattern>,
pub color_palette: Option<Vec<Color>>,
pub author_time: i64,
pub author_lap_times: Vec<i64>,
pub silver_medal_time: i64,
pub gold_medal_time: i64,
pub laps: i32,
pub center_camera: bool,
pub scripts: Vec<i32>,
pub nova_scripts: Vec<NovaScript>,
pub global_variables: Vec<Variable>,
pub theme: String,
pub custom_background_color: Color,
pub(crate) unknown1: [u8; 4],
pub custom_terrain_pattern_id: i32,
pub custom_terrain_pattern_tiling: Vec2,
pub custom_terrain_pattern_offset: Vec2,
pub custom_terrain_color: Color,
pub custom_terrain_secondary_color: Color,
pub custom_terrain_blend_mode: i32,
pub custom_terrain_border_color: Color,
pub custom_terrain_border_thickness: f32,
pub custom_terrain_border_corner_radius: f32,
pub custom_terrain_round_reflex_angles: bool,
pub custom_terrain_round_collider: bool,
pub custom_terrain_friction: f32,
pub default_music: bool,
pub music_ids: Vec<String>,
pub allow_direction_change: bool,
pub disable_replays: bool,
pub disable_revive_pads: bool,
pub disable_start_animation: bool,
pub gravity: Vec2,
}
impl LevelData {
#[must_use]
pub fn default_with_id(level_id: Uuid) -> Self {
Self {
level_id,
level_version: 1,
nova_level: true,
color_palette: Some(Vec::default()),
laps: 1,
theme: "mountains".to_string(),
default_music: true,
gravity: Vec2 { x: 0.0, y: -75.0 },
..Default::default()
}
}
}
impl ReadVersioned for LevelData {
fn read(input: &mut impl std::io::Read, version: i32) -> Result<Self, Error> {
Ok(Self {
level_id: Read::read(input)?,
level_version: Read::read(input)?,
nova_level: Read::read(input)?,
under_decoration_tiles: Read::read(input)?,
background_decoration_tiles: Read::read(input)?,
terrain_tiles: Read::read(input)?,
floating_zone_tiles: Read::read(input)?,
object_tiles: Read::read(input)?,
foreground_decoration_tiles: Read::read(input)?,
objects: Read::read(input)?,
layers: Read::read(input)?,
prefabs: Read::read(input)?,
brushes: Read::read(input)?,
patterns: Read::read(input)?,
color_palette: if version >= 17 {
Some(Read::read(input)?)
} else {
None
},
author_time: Read::read(input)?,
author_lap_times: Read::read(input)?,
silver_medal_time: Read::read(input)?,
gold_medal_time: Read::read(input)?,
laps: Read::read(input)?,
center_camera: Read::read(input)?,
scripts: Read::read(input)?,
nova_scripts: Read::read(input)?,
global_variables: Read::read(input)?,
theme: Read::read(input)?,
custom_background_color: Read::read(input)?,
unknown1: Read::read(input)?,
custom_terrain_pattern_id: Read::read(input)?,
custom_terrain_pattern_tiling: Read::read(input)?,
custom_terrain_pattern_offset: Read::read(input)?,
custom_terrain_color: Read::read(input)?,
custom_terrain_secondary_color: Read::read(input)?,
custom_terrain_blend_mode: Read::read(input)?,
custom_terrain_border_color: Read::read(input)?,
custom_terrain_border_thickness: Read::read(input)?,
custom_terrain_border_corner_radius: Read::read(input)?,
custom_terrain_round_reflex_angles: Read::read(input)?,
custom_terrain_round_collider: Read::read(input)?,
custom_terrain_friction: Read::read(input)?,
default_music: Read::read(input)?,
music_ids: Read::read(input)?,
allow_direction_change: Read::read(input)?,
disable_replays: Read::read(input)?,
disable_revive_pads: Read::read(input)?,
disable_start_animation: Read::read(input)?,
gravity: Read::read(input)?,
})
}
}
impl Write for LevelData {
fn write(&self, output: &mut impl std::io::Write) -> Result<(), Error> {
self.level_id.write(output)?;
self.level_version.write(output)?;
self.nova_level.write(output)?;
self.under_decoration_tiles.write(output)?;
self.background_decoration_tiles.write(output)?;
self.terrain_tiles.write(output)?;
self.floating_zone_tiles.write(output)?;
self.object_tiles.write(output)?;
self.foreground_decoration_tiles.write(output)?;
self.objects.write(output)?;
self.layers.write(output)?;
self.prefabs.write(output)?;
self.brushes.write(output)?;
self.patterns.write(output)?;
if let Some(color_palette) = &self.color_palette {
color_palette.write(output)?;
}
self.author_time.write(output)?;
self.author_lap_times.write(output)?;
self.silver_medal_time.write(output)?;
self.gold_medal_time.write(output)?;
self.laps.write(output)?;
self.center_camera.write(output)?;
self.scripts.write(output)?;
self.nova_scripts.write(output)?;
self.global_variables.write(output)?;
self.theme.write(output)?;
self.custom_background_color.write(output)?;
self.unknown1.write(output)?;
self.custom_terrain_pattern_id.write(output)?;
self.custom_terrain_pattern_tiling.write(output)?;
self.custom_terrain_pattern_offset.write(output)?;
self.custom_terrain_color.write(output)?;
self.custom_terrain_secondary_color.write(output)?;
self.custom_terrain_blend_mode.write(output)?;
self.custom_terrain_border_color.write(output)?;
self.custom_terrain_border_thickness.write(output)?;
self.custom_terrain_border_corner_radius.write(output)?;
self.custom_terrain_round_reflex_angles.write(output)?;
self.custom_terrain_round_collider.write(output)?;
self.custom_terrain_friction.write(output)?;
self.default_music.write(output)?;
self.music_ids.write(output)?;
self.allow_direction_change.write(output)?;
self.disable_replays.write(output)?;
self.disable_revive_pads.write(output)?;
self.disable_start_animation.write(output)?;
self.gravity.write(output)
}
}