use crate::difficulty::lightshow::DistributionType;
use crate::difficulty::lightshow::easing::Easing;
use crate::difficulty::lightshow::filter::Filter;
use crate::loose_bool::LooseBool;
use crate::{TransitionType, impl_event_box, impl_event_data, impl_event_group, impl_timed};
use indexmap::IndexSet;
use ordered_float::OrderedFloat;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
reflect(Debug, Clone, PartialEq)
)]
pub struct FxEventContainer {
pub event_boxes: Vec<FxEventBox>,
}
impl Deref for FxEventContainer {
type Target = Vec<FxEventBox>;
fn deref(&self) -> &Self::Target {
&self.event_boxes
}
}
impl DerefMut for FxEventContainer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.event_boxes
}
}
#[derive(Deserialize)]
struct FxEventInput {
#[serde(rename = "vfxEventBoxGroups")]
event_boxes: Vec<FxEventBoxRaw>,
#[serde(rename = "_fxEventsCollection")]
arrays: FxEventArrays,
}
#[derive(Deserialize, Serialize)]
struct FxEventArrays {
#[serde(rename = "_fl")]
event_data: Vec<FxEventData>,
}
impl<'de> Deserialize<'de> for FxEventContainer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let FxEventInput {
event_boxes,
arrays,
} = FxEventInput::deserialize(deserializer)?;
let event_boxes = event_boxes
.into_iter()
.map(|raw_box| {
let groups = raw_box
.groups
.into_iter()
.map(|raw_group| {
let data = raw_group
.data_ids
.into_iter()
.map(|id| {
arrays.event_data.get(id).cloned().ok_or_else(|| {
serde::de::Error::custom(format!(
"Missing FxEventData with id {}",
id
))
})
})
.collect::<Result<Vec<FxEventData>, _>>()?;
Ok(FxEventGroup {
filter: raw_group.filter,
beat_dist_type: raw_group.beat_dist_type,
beat_dist_value: raw_group.beat_dist_value,
fx_dist_type: raw_group.fx_dist_type,
fx_dist_value: raw_group.fx_dist_value,
fx_dist_effect_first: raw_group.fx_dist_effect_first,
fx_dist_easing: raw_group.fx_dist_easing,
data,
})
})
.collect::<Result<Vec<FxEventGroup>, _>>()?;
Ok(FxEventBox {
beat: raw_box.beat,
group_id: raw_box.group_id,
groups,
})
})
.collect::<Result<Vec<FxEventBox>, _>>()?;
Ok(FxEventContainer { event_boxes })
}
}
impl Serialize for FxEventContainer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut event_data: IndexSet<FxEventDataKey> = IndexSet::new();
let raw_boxes = self
.event_boxes
.iter()
.map(|event_box| {
let groups_raw = event_box
.groups
.iter()
.map(|event_group| {
let mut ids = Vec::new();
for data in &event_group.data {
let data_key: FxEventDataKey = data.into();
let index = event_data.get_index_of(&data_key).unwrap_or_else(|| {
let index = event_data.len();
event_data.insert(data_key);
index
});
ids.push(index);
}
FxEventGroupRaw {
filter: event_group.filter.clone(),
beat_dist_type: event_group.beat_dist_type.clone(),
beat_dist_value: event_group.beat_dist_value,
fx_dist_type: event_group.fx_dist_type.clone(),
fx_dist_value: event_group.fx_dist_value,
fx_dist_effect_first: event_group.fx_dist_effect_first,
fx_dist_easing: event_group.fx_dist_easing.clone(),
data_ids: ids,
}
})
.collect::<Vec<FxEventGroupRaw>>();
FxEventBoxRaw {
beat: event_box.beat,
group_id: event_box.group_id,
groups: groups_raw,
}
})
.collect::<Vec<FxEventBoxRaw>>();
let mut state = serializer.serialize_struct("FxEventContainer", 2)?;
state.serialize_field("vfxEventBoxGroups", &raw_boxes)?;
state.serialize_field(
"_fxEventsCollection",
&FxEventArrays {
event_data: event_data.into_iter().map(FxEventData::from).collect(),
},
)?;
state.end()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
reflect(Debug, Clone, PartialEq)
)]
pub struct FxEventBox {
pub beat: f32,
pub group_id: i32,
pub groups: Vec<FxEventGroup>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct FxEventBoxRaw {
#[serde(rename = "b")]
beat: f32,
#[serde(rename = "g")]
group_id: i32,
#[serde(rename = "e")]
groups: Vec<FxEventGroupRaw>,
}
impl Default for FxEventBox {
fn default() -> Self {
Self {
beat: 0.0,
group_id: 0,
groups: vec![FxEventGroup::default()],
}
}
}
impl_timed!(FxEventBox::beat);
impl_event_box!(FxEventBox, FxEventGroup, FxEventData);
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
reflect(Debug, Clone, PartialEq)
)]
pub struct FxEventGroup {
pub filter: Filter,
pub beat_dist_type: DistributionType,
pub beat_dist_value: f32,
pub fx_dist_type: DistributionType,
pub fx_dist_value: f32,
pub fx_dist_effect_first: LooseBool,
pub fx_dist_easing: Option<Easing>,
pub data: Vec<FxEventData>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct FxEventGroupRaw {
#[serde(rename = "f")]
filter: Filter,
#[serde(rename = "d")]
beat_dist_type: DistributionType,
#[serde(rename = "w")]
beat_dist_value: f32,
#[serde(rename = "t")]
fx_dist_type: DistributionType,
#[serde(rename = "s")]
fx_dist_value: f32,
#[serde(rename = "b")]
fx_dist_effect_first: LooseBool,
#[serde(rename = "i")]
fx_dist_easing: Option<Easing>,
#[serde(rename = "l")]
data_ids: Vec<usize>,
}
impl Default for FxEventGroup {
fn default() -> Self {
Self {
filter: Default::default(),
beat_dist_type: Default::default(),
beat_dist_value: 0.0,
fx_dist_type: Default::default(),
fx_dist_value: 0.0,
fx_dist_effect_first: Default::default(),
fx_dist_easing: Some(Easing::Linear),
data: vec![FxEventData::default()],
}
}
}
impl_event_group!(FxEventGroup::get_fx_offset, FxEventData);
impl FxEventGroup {
#[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
#[allow(deprecated)]
pub fn get_fx_offset(&self, light_id: i32, group_size: i32) -> f32 {
self.fx_dist_type.compute_value_offset(
light_id,
group_size,
&self.filter,
self.fx_dist_value,
self.data.last().map(|data| data.beat_offset),
self.fx_dist_easing,
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
reflect(Debug, Clone, PartialEq)
)]
pub struct FxEventData {
#[serde(rename = "b")]
pub beat_offset: f32,
#[serde(rename = "p")]
pub transition_type: TransitionType,
#[serde(rename = "i")]
pub easing: Easing,
#[serde(rename = "v")]
pub value: f32,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
struct FxEventDataKey {
beat_offset: OrderedFloat<f32>,
transition_type: TransitionType,
easing: Easing,
value: OrderedFloat<f32>,
}
impl From<&FxEventData> for FxEventDataKey {
fn from(value: &FxEventData) -> Self {
Self {
beat_offset: value.beat_offset.into(),
transition_type: value.transition_type,
easing: value.easing,
value: value.value.into(),
}
}
}
impl From<FxEventDataKey> for FxEventData {
fn from(value: FxEventDataKey) -> Self {
Self {
beat_offset: value.beat_offset.into(),
transition_type: value.transition_type,
easing: value.easing,
value: value.value.into(),
}
}
}
impl Default for FxEventData {
fn default() -> Self {
Self {
beat_offset: 0.0,
transition_type: Default::default(),
easing: Easing::default(),
value: 1.0,
}
}
}
impl_event_data!(FxEventData);
#[cfg(test)]
mod tests {
use super::*;
use crate::loose_bool::LooseBool;
use serde_json::{Value, json};
fn get_test_container() -> FxEventContainer {
FxEventContainer {
event_boxes: vec![FxEventBox {
beat: 2.0,
group_id: 0,
groups: vec![get_test_group(), get_test_group()],
}],
}
}
fn get_test_group() -> FxEventGroup {
FxEventGroup {
filter: Default::default(),
beat_dist_type: DistributionType::Wave,
beat_dist_value: 1.0,
fx_dist_type: DistributionType::Wave,
fx_dist_value: 1.0,
fx_dist_effect_first: LooseBool::True,
fx_dist_easing: Some(Easing::None),
data: vec![FxEventData {
beat_offset: 0.0,
transition_type: TransitionType::Transition,
easing: Easing::Linear,
value: 100.0,
}],
}
}
fn get_test_json() -> Value {
json!(
{
"vfxEventBoxGroups":
[
{
"b": 2.0,
"g": 0,
"e":
[
{
"f":
{
"c": 0,
"f": 1,
"p": 1,
"t": 0,
"r": 0,
"n": 0,
"s": 0,
"l": 1.0,
"d": 0
},
"w": 1.0,
"d": 1,
"s": 1.0,
"t": 1,
"b": 1,
"i": -1,
"l":
[
0
]
},
{
"f":
{
"c": 0,
"f": 1,
"p": 1,
"t": 0,
"r": 0,
"n": 0,
"s": 0,
"l": 1.0,
"d": 0
},
"w": 1.0,
"d": 1,
"s": 1.0,
"t": 1,
"b": 1,
"i": -1,
"l":
[
0
]
}
]
}
],
"_fxEventsCollection":
{
"_fl":
[
{
"b": 0.0,
"p": 0,
"i": 0,
"v": 100.0
}
]
}
}
)
}
#[test]
fn test_deserialize() {
let container: FxEventContainer = serde_json::from_value(get_test_json()).unwrap();
assert_eq!(container, get_test_container());
}
#[test]
fn test_serialize() {
let out_json = serde_json::to_value(&get_test_container()).unwrap();
assert_eq!(out_json, get_test_json());
}
#[test]
fn test_round_trip() {
let container: FxEventContainer = serde_json::from_value(get_test_json()).unwrap();
let out_json = serde_json::to_string_pretty(&container).unwrap();
let round_trip: FxEventContainer = serde_json::from_str(&out_json).unwrap();
assert_eq!(container, round_trip);
}
}