use super::vertex2d::Vertex2D;
use crate::impl_shared_attributes;
use crate::vpx::gameitem::select::{TimerData, WriteSharedAttributes};
use crate::vpx::{
biff::{self, BiffRead, BiffReader, BiffWrite},
color::Color,
};
use log::warn;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(fake::Dummy))]
pub struct Reel {
pub ver1: Vertex2D, pub ver2: Vertex2D, pub back_color: Color,
pub is_transparent: bool, pub image: String,
pub sound: String, pub name: String,
pub width: f32, pub height: f32, pub reel_count: f32, pub reel_spacing: f32, pub motor_steps: f32, pub digit_range: f32, pub update_interval: u32,
pub use_image_grid: bool,
pub is_visible: bool,
pub images_per_grid_row: u32,
pub timer: TimerData,
pub is_locked: bool,
pub editor_layer: Option<u32>,
pub editor_layer_name: Option<String>,
pub editor_layer_visibility: Option<bool>,
pub part_group_name: Option<String>,
}
impl_shared_attributes!(Reel);
#[derive(Debug, Serialize, Deserialize)]
struct ReelJson {
ver1: Vertex2D,
ver2: Vertex2D,
back_color: Color,
#[serde(flatten)]
pub timer: TimerData,
is_transparent: bool,
image: String,
sound: String,
name: String,
width: f32,
height: f32,
reel_count: f32,
reel_spacing: f32,
motor_steps: f32,
digit_range: f32,
update_interval: u32,
use_image_grid: bool,
is_visible: bool,
images_per_grid_row: u32,
#[serde(skip_serializing_if = "Option::is_none")]
part_group_name: Option<String>,
}
impl ReelJson {
pub fn from_reel(reel: &Reel) -> Self {
Self {
ver1: reel.ver1,
ver2: reel.ver2,
back_color: reel.back_color,
timer: reel.timer.clone(),
is_transparent: reel.is_transparent,
image: reel.image.clone(),
sound: reel.sound.clone(),
name: reel.name.clone(),
width: reel.width,
height: reel.height,
reel_count: reel.reel_count,
reel_spacing: reel.reel_spacing,
motor_steps: reel.motor_steps,
digit_range: reel.digit_range,
update_interval: reel.update_interval,
use_image_grid: reel.use_image_grid,
is_visible: reel.is_visible,
images_per_grid_row: reel.images_per_grid_row,
part_group_name: reel.part_group_name.clone(),
}
}
pub fn to_reel(&self) -> Reel {
Reel {
ver1: self.ver1,
ver2: self.ver2,
back_color: self.back_color,
timer: self.timer.clone(),
is_transparent: self.is_transparent,
image: self.image.clone(),
sound: self.sound.clone(),
name: self.name.clone(),
width: self.width,
height: self.height,
reel_count: self.reel_count,
reel_spacing: self.reel_spacing,
motor_steps: self.motor_steps,
digit_range: self.digit_range,
update_interval: self.update_interval,
use_image_grid: self.use_image_grid,
is_visible: self.is_visible,
images_per_grid_row: self.images_per_grid_row,
is_locked: false,
editor_layer: None,
editor_layer_name: None,
editor_layer_visibility: None,
part_group_name: self.part_group_name.clone(),
}
}
}
impl Default for Reel {
fn default() -> Self {
Self {
ver1: Vertex2D::default(),
ver2: Vertex2D::default(),
back_color: Color::rgb(64, 64, 64),
timer: TimerData::default(),
is_transparent: false,
image: Default::default(),
sound: Default::default(),
name: Default::default(),
width: 30.0,
height: 40.0,
reel_count: 5.0,
reel_spacing: 4.0,
motor_steps: 2.0,
digit_range: 9.0,
update_interval: 50,
use_image_grid: false,
is_visible: true,
images_per_grid_row: 1,
is_locked: false,
editor_layer: Default::default(),
editor_layer_name: None,
editor_layer_visibility: None,
part_group_name: None,
}
}
}
impl Serialize for Reel {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
ReelJson::from_reel(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Reel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let reel_json = ReelJson::deserialize(deserializer)?;
Ok(reel_json.to_reel())
}
}
impl BiffRead for Reel {
fn biff_read(reader: &mut BiffReader<'_>) -> Self {
let mut reel = Reel::default();
loop {
reader.next(biff::WARN);
if reader.is_eof() {
break;
}
let tag = reader.tag();
let tag_str = tag.as_str();
match tag_str {
"VER1" => {
reel.ver1 = Vertex2D::biff_read(reader);
}
"VER2" => {
reel.ver2 = Vertex2D::biff_read(reader);
}
"CLRB" => {
reel.back_color = Color::biff_read(reader);
}
"TRNS" => {
reel.is_transparent = reader.get_bool();
}
"IMAG" => {
reel.image = reader.get_string();
}
"SOUN" => {
reel.sound = reader.get_string();
}
"NAME" => {
reel.name = reader.get_wide_string();
}
"WDTH" => {
reel.width = reader.get_f32();
}
"HIGH" => {
reel.height = reader.get_f32();
}
"RCNT" => {
reel.reel_count = reader.get_f32();
}
"RSPC" => {
reel.reel_spacing = reader.get_f32();
}
"MSTP" => {
reel.motor_steps = reader.get_f32();
}
"RANG" => {
reel.digit_range = reader.get_f32();
}
"UPTM" => {
reel.update_interval = reader.get_u32();
}
"UGRD" => {
reel.use_image_grid = reader.get_bool();
}
"VISI" => {
reel.is_visible = reader.get_bool();
}
"GIPR" => {
reel.images_per_grid_row = reader.get_u32();
}
_ => {
if !reel.timer.biff_read_tag(tag_str, reader)
&& !reel.read_shared_attribute(tag_str, reader)
{
warn!(
"Unknown tag {} for {}",
tag_str,
std::any::type_name::<Self>()
);
reader.skip_tag();
}
}
}
}
reel
}
}
impl BiffWrite for Reel {
fn biff_write(&self, writer: &mut biff::BiffWriter) {
writer.write_tagged("VER1", &self.ver1);
writer.write_tagged("VER2", &self.ver2);
writer.write_tagged_with("CLRB", &self.back_color, Color::biff_write);
self.timer.biff_write(writer);
writer.write_tagged_bool("TRNS", self.is_transparent);
writer.write_tagged_string("IMAG", &self.image);
writer.write_tagged_string("SOUN", &self.sound);
writer.write_tagged_wide_string("NAME", &self.name);
writer.write_tagged_f32("WDTH", self.width);
writer.write_tagged_f32("HIGH", self.height);
writer.write_tagged_f32("RCNT", self.reel_count);
writer.write_tagged_f32("RSPC", self.reel_spacing);
writer.write_tagged_f32("MSTP", self.motor_steps);
writer.write_tagged_f32("RANG", self.digit_range);
writer.write_tagged_u32("UPTM", self.update_interval);
writer.write_tagged_bool("UGRD", self.use_image_grid);
writer.write_tagged_bool("VISI", self.is_visible);
writer.write_tagged_u32("GIPR", self.images_per_grid_row);
self.write_shared_attributes(writer);
writer.close(true);
}
}
#[cfg(test)]
mod tests {
use crate::vpx::biff::BiffWriter;
use fake::{Fake, Faker};
use super::*;
use crate::vpx::gameitem::tests::RandomOption;
use pretty_assertions::assert_eq;
use rand::RngExt;
#[test]
fn test_write_read() {
let mut rng = rand::rng();
let reel = Reel {
ver1: Vertex2D::new(rng.random(), rng.random()),
ver2: Vertex2D::new(rng.random(), rng.random()),
back_color: Faker.fake(),
timer: TimerData {
is_enabled: rng.random(),
interval: rng.random(),
},
is_transparent: rng.random(),
image: "test image".to_string(),
sound: "test sound".to_string(),
name: "test name".to_string(),
width: rng.random(),
height: rng.random(),
reel_count: rng.random(),
reel_spacing: rng.random(),
motor_steps: rng.random(),
digit_range: rng.random(),
update_interval: rng.random(),
use_image_grid: rng.random(),
is_visible: rng.random(),
images_per_grid_row: rng.random(),
is_locked: rng.random(),
editor_layer: Some(rng.random()),
editor_layer_name: Some("test layer name".to_string()),
editor_layer_visibility: rng.random_option(),
part_group_name: Some("test part group name".to_string()),
};
let mut writer = BiffWriter::new();
Reel::biff_write(&reel, &mut writer);
let reel_read = Reel::biff_read(&mut BiffReader::new(writer.get_data()));
assert_eq!(reel, reel_read);
}
}