bve 0.0.1

Remaking OpenBVE using a modern architecture, using Unity and Rust.
use crate::parse::function_scripts::ParsedFunctionScript;
use crate::parse::kvp::FromKVPValue;
use bve_derive::{FromKVPFile, FromKVPSection, FromKVPValue};
use cgmath::{Vector2, Vector3};
use num_traits::identities::Zero;

#[derive(Debug, Default, Clone, PartialEq, FromKVPFile)]
pub struct ParsedAnimatedObject {
    #[kvp(rename = "include")]
    pub includes: Vec<Includes>,
    #[kvp(rename = "object")]
    pub objects: Vec<AnimatedObject>,
    #[kvp(rename = "sound")]
    pub sounds: Vec<AnimatedSound>,
    #[kvp(rename = "statechangesound")]
    pub change_state_sounds: Vec<AnimatedStateChangeSound>,
}

#[derive(Debug, Clone, PartialEq, FromKVPSection)]
pub struct Includes {
    #[kvp(bare)]
    pub files: Vec<String>,
    pub position: Vector3<f32>,
}

impl Default for Includes {
    fn default() -> Self {
        Self {
            files: Vec::default(),
            position: Vector3::zero(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, FromKVPSection)]
pub struct AnimatedObject {
    pub position: Vector3<f32>,
    #[kvp(variadic)]
    pub states: Vec<String>,
    pub state_function: Option<ParsedFunctionScript>,

    pub translate_x_direction: Vector3<f32>,
    pub translate_y_direction: Vector3<f32>,
    pub translate_z_direction: Vector3<f32>,

    pub translate_x_function: Option<ParsedFunctionScript>,
    pub translate_y_function: Option<ParsedFunctionScript>,
    pub translate_z_function: Option<ParsedFunctionScript>,

    pub rotate_x_direction: Vector3<f32>,
    pub rotate_y_direction: Vector3<f32>,
    pub rotate_z_direction: Vector3<f32>,

    pub rotate_x_function: Option<ParsedFunctionScript>,
    pub rotate_y_function: Option<ParsedFunctionScript>,
    pub rotate_z_function: Option<ParsedFunctionScript>,

    pub rotate_x_damping: Option<Damping>,
    pub rotate_y_damping: Option<Damping>,
    pub rotate_z_damping: Option<Damping>,

    pub texture_shift_x_direction: Vector2<f32>,
    pub texture_shift_y_direction: Vector2<f32>,

    pub texture_shift_x_function: Option<ParsedFunctionScript>,
    pub texture_shift_y_function: Option<ParsedFunctionScript>,

    pub track_follower_function: Option<ParsedFunctionScript>,

    pub texture_override: TextureOverride,

    pub refresh_rate: RefreshRate,
}

impl Default for AnimatedObject {
    fn default() -> Self {
        Self {
            position: Vector3::zero(),
            states: Vec::default(),
            state_function: None,
            translate_x_direction: Vector3::unit_x(),
            translate_y_direction: Vector3::unit_y(),
            translate_z_direction: Vector3::unit_z(),
            translate_x_function: None,
            translate_y_function: None,
            translate_z_function: None,
            rotate_x_direction: Vector3::unit_x(),
            rotate_y_direction: Vector3::unit_y(),
            rotate_z_direction: Vector3::unit_z(),
            rotate_x_function: None,
            rotate_y_function: None,
            rotate_z_function: None,
            rotate_x_damping: None,
            rotate_y_damping: None,
            rotate_z_damping: None,
            texture_shift_x_direction: Vector2::unit_x(),
            texture_shift_y_direction: Vector2::unit_y(),
            texture_shift_x_function: None,
            texture_shift_y_function: None,
            track_follower_function: None,
            texture_override: TextureOverride::default(),
            refresh_rate: RefreshRate::default(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, FromKVPSection)]
pub struct AnimatedSound {
    filename: String,
    position: Vector3<f32>,
    volume: f32,
    volume_function: Option<ParsedFunctionScript>,
    pitch: f32,
    pitch_function: Option<ParsedFunctionScript>,
    radius: f32,
    track_follower_function: Option<ParsedFunctionScript>,
}

impl Default for AnimatedSound {
    fn default() -> Self {
        Self {
            filename: String::new(),
            position: Vector3::zero(),
            volume: 1.0,
            pitch: 1.0,
            radius: 30.0,
            volume_function: None,
            pitch_function: None,
            track_follower_function: None,
        }
    }
}

#[derive(Debug, Clone, PartialEq, FromKVPSection)]
pub struct AnimatedStateChangeSound {
    filename: String,
    #[kvp(variadic)]
    filenames: Vec<String>,
    position: Vector3<f32>,
    volume: f32,
    pitch: f32,
    radius: f32,
    play_on_show: PlayOn,
    play_on_hide: PlayOn,
}

impl Default for AnimatedStateChangeSound {
    fn default() -> Self {
        Self {
            filename: String::new(),
            filenames: Vec::new(),
            position: Vector3::zero(),
            volume: 1.0,
            pitch: 1.0,
            radius: 30.0,
            play_on_show: PlayOn::Silent,
            play_on_hide: PlayOn::Silent,
        }
    }
}

#[derive(Debug, Clone, PartialEq, FromKVPValue)]
pub struct Damping {
    pub frequency: f32,
    pub damping_ratio: f32,
}

#[derive(Debug, Clone, PartialEq)]
pub enum TextureOverride {
    None,
    Timetable,
}

impl Default for TextureOverride {
    fn default() -> Self {
        Self::None
    }
}

impl FromKVPValue for TextureOverride {
    fn from_kvp_value(value: &str) -> Option<Self> {
        if value == "timetable" {
            Some(Self::Timetable)
        } else {
            None
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum RefreshRate {
    EveryFrame,
    Seconds(f32),
}

impl Default for RefreshRate {
    fn default() -> Self {
        Self::EveryFrame
    }
}

impl FromKVPValue for RefreshRate {
    fn from_kvp_value(value: &str) -> Option<Self> {
        f32::from_kvp_value(value).map(|f| if f == 0.0 { Self::EveryFrame } else { Self::Seconds(f) })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum PlayOn {
    Silent,
    Play,
}

impl Default for PlayOn {
    fn default() -> Self {
        Self::Silent
    }
}

impl FromKVPValue for PlayOn {
    fn from_kvp_value(value: &str) -> Option<Self> {
        i64::from_kvp_value(value).and_then(|i| {
            if i == 0 {
                Some(Self::Silent)
            } else if i == 1 {
                Some(Self::Play)
            } else {
                None
            }
        })
    }
}