···11//! The advanced group lighting system events.
2233pub mod color;
44+pub mod fx;
45pub mod rotation;
56pub mod translation;
6778#[doc(hidden)]
89pub use color::*;
1010+#[doc(hidden)]
1111+pub use fx::*;
912#[doc(hidden)]
1013pub use rotation::*;
1114#[doc(hidden)]
+3-2
src/difficulty/lightshow/group/color.rs
···5555 /// A value of zero will have no effect.
5656 #[serde(rename = "w")]
5757 pub beat_dist_value: f32,
5858+5959+ #[serde(rename = "t")]
6060+ pub bright_dist_type: DistributionType,
5861 /// The strength of the brightness distribution. Dependent on the [distribution type](Self::bright_dist_type).
5962 ///
6063 /// A value of zero will have no effect.
6161- #[serde(rename = "t")]
6262- pub bright_dist_type: DistributionType,
6364 #[serde(rename = "r")]
6465 pub bright_dist_value: f32,
6566 /// Whether the first [`ColorEventData`] of the group will be effected by brightness distribution.
+501
src/difficulty/lightshow/group/fx.rs
···11+//! Events that control animations unique to each environment.
22+//!
33+//! Unlike the other V3 group event types, FX events use a template-like JSON syntax.
44+//! In order to have standardized structure across all V3 events, custom serialization has been written in [`FxEventContainer`].
55+//! Because of this, neither [`FxEventBox`] nor [`FxEventGroup`] implement [`Serialize`] nor [`Deserialize`] directly.
66+77+use crate::difficulty::lightshow::DistributionType;
88+use crate::difficulty::lightshow::easing::Easing;
99+use crate::difficulty::lightshow::filter::Filter;
1010+use crate::utils::LooseBool;
1111+use crate::{TransitionType, impl_event_box, impl_event_data, impl_event_group, impl_timed};
1212+use indexmap::IndexSet;
1313+use ordered_float::OrderedFloat;
1414+use serde::ser::SerializeStruct;
1515+use serde::{Deserialize, Deserializer, Serialize, Serializer};
1616+use std::ops::{Deref, DerefMut};
1717+1818+/// Contains a list of [`FxEventBox`] as well as the [`Serialize`] and [`Deserialize`] implementations for FX events.
1919+#[derive(Debug, Clone, PartialEq, Default)]
2020+#[cfg_attr(
2121+ feature = "bevy_reflect",
2222+ derive(bevy_reflect::Reflect),
2323+ reflect(Debug, Clone, PartialEq)
2424+)]
2525+pub struct FxEventContainer {
2626+ pub event_boxes: Vec<FxEventBox>,
2727+}
2828+2929+impl Deref for FxEventContainer {
3030+ type Target = Vec<FxEventBox>;
3131+3232+ fn deref(&self) -> &Self::Target {
3333+ &self.event_boxes
3434+ }
3535+}
3636+3737+impl DerefMut for FxEventContainer {
3838+ fn deref_mut(&mut self) -> &mut Self::Target {
3939+ &mut self.event_boxes
4040+ }
4141+}
4242+4343+/// The format that is actually stored in JSON.
4444+#[derive(Deserialize)]
4545+struct FxEventInput {
4646+ #[serde(rename = "vfxEventBoxGroups")]
4747+ event_boxes: Vec<FxEventBoxRaw>,
4848+ #[serde(rename = "_fxEventsCollection")]
4949+ arrays: FxEventArrays,
5050+}
5151+5252+#[derive(Deserialize, Serialize)]
5353+struct FxEventArrays {
5454+ #[serde(rename = "_fl")]
5555+ event_data: Vec<FxEventData>,
5656+}
5757+5858+impl<'de> Deserialize<'de> for FxEventContainer {
5959+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
6060+ where
6161+ D: Deserializer<'de>,
6262+ {
6363+ let FxEventInput {
6464+ event_boxes,
6565+ arrays,
6666+ } = FxEventInput::deserialize(deserializer)?;
6767+6868+ let event_boxes = event_boxes
6969+ .into_iter()
7070+ .map(|raw_box| {
7171+ let groups = raw_box
7272+ .groups
7373+ .into_iter()
7474+ .map(|raw_group| {
7575+ let data = raw_group
7676+ .data_ids
7777+ .into_iter()
7878+ .map(|id| {
7979+ arrays.event_data.get(id).cloned().ok_or_else(|| {
8080+ serde::de::Error::custom(format!(
8181+ "Missing FxEventData with id {}",
8282+ id
8383+ ))
8484+ })
8585+ })
8686+ .collect::<Result<Vec<FxEventData>, _>>()?;
8787+8888+ Ok(FxEventGroup {
8989+ filter: raw_group.filter,
9090+ beat_dist_type: raw_group.beat_dist_type,
9191+ beat_dist_value: raw_group.beat_dist_value,
9292+ fx_dist_type: raw_group.fx_dist_type,
9393+ fx_dist_value: raw_group.fx_dist_value,
9494+ fx_dist_effect_first: raw_group.fx_dist_effect_first,
9595+ fx_dist_easing: raw_group.fx_dist_easing,
9696+ data,
9797+ })
9898+ })
9999+ .collect::<Result<Vec<FxEventGroup>, _>>()?;
100100+101101+ Ok(FxEventBox {
102102+ beat: raw_box.beat,
103103+ group_id: raw_box.group_id,
104104+ groups,
105105+ })
106106+ })
107107+ .collect::<Result<Vec<FxEventBox>, _>>()?;
108108+109109+ Ok(FxEventContainer { event_boxes })
110110+ }
111111+}
112112+113113+// Todo avoid allocations.
114114+impl Serialize for FxEventContainer {
115115+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116116+ where
117117+ S: Serializer,
118118+ {
119119+ let mut event_data: IndexSet<FxEventDataKey> = IndexSet::new();
120120+121121+ // Todo Deduplicate.
122122+ let raw_boxes = self
123123+ .event_boxes
124124+ .iter()
125125+ .map(|event_box| {
126126+ let groups_raw = event_box
127127+ .groups
128128+ .iter()
129129+ .map(|event_group| {
130130+ let mut ids = Vec::new();
131131+132132+ for data in &event_group.data {
133133+ let data_key: FxEventDataKey = data.into();
134134+135135+ let index = event_data.get_index_of(&data_key).unwrap_or_else(|| {
136136+ let index = event_data.len();
137137+ event_data.insert(data_key);
138138+ index
139139+ });
140140+141141+ ids.push(index);
142142+ }
143143+144144+ FxEventGroupRaw {
145145+ filter: event_group.filter.clone(),
146146+ beat_dist_type: event_group.beat_dist_type.clone(),
147147+ beat_dist_value: event_group.beat_dist_value,
148148+ fx_dist_type: event_group.fx_dist_type.clone(),
149149+ fx_dist_value: event_group.fx_dist_value,
150150+ fx_dist_effect_first: event_group.fx_dist_effect_first,
151151+ fx_dist_easing: event_group.fx_dist_easing.clone(),
152152+ data_ids: ids,
153153+ }
154154+ })
155155+ .collect::<Vec<FxEventGroupRaw>>();
156156+157157+ FxEventBoxRaw {
158158+ beat: event_box.beat,
159159+ group_id: event_box.group_id,
160160+ groups: groups_raw,
161161+ }
162162+ })
163163+ .collect::<Vec<FxEventBoxRaw>>();
164164+165165+ let mut state = serializer.serialize_struct("FxEventContainer", 2)?;
166166+ state.serialize_field("vfxEventBoxGroups", &raw_boxes)?;
167167+ state.serialize_field(
168168+ "_fxEventsCollection",
169169+ &FxEventArrays {
170170+ event_data: event_data.into_iter().map(FxEventData::from).collect(),
171171+ },
172172+ )?;
173173+ state.end()
174174+ }
175175+}
176176+177177+/// A collection of [`FxEventGroup`]s that share the same group ID and beat.
178178+///
179179+/// Does not implement [`Serialize`] nor [`Deserialize`]. For more info, see the [module docs](super::fx).
180180+#[derive(Debug, Clone, PartialEq)]
181181+#[cfg_attr(
182182+ feature = "bevy_reflect",
183183+ derive(bevy_reflect::Reflect),
184184+ reflect(Debug, Clone, PartialEq)
185185+)]
186186+pub struct FxEventBox {
187187+ /// The time the event takes place.
188188+ pub beat: f32,
189189+ /// The ID of the collection of objects that this event effects.
190190+ pub group_id: i32,
191191+ pub groups: Vec<FxEventGroup>,
192192+}
193193+194194+/// The raw JSON structure that uses [data IDs](FxEventGroupRaw::data_ids) rather than actual [event data](FxEventData).
195195+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
196196+struct FxEventBoxRaw {
197197+ #[serde(rename = "b")]
198198+ beat: f32,
199199+ #[serde(rename = "g")]
200200+ group_id: i32,
201201+ #[serde(rename = "e")]
202202+ groups: Vec<FxEventGroupRaw>,
203203+}
204204+205205+impl Default for FxEventBox {
206206+ fn default() -> Self {
207207+ Self {
208208+ beat: 0.0,
209209+ group_id: 0,
210210+ groups: vec![FxEventGroup::default()],
211211+ }
212212+ }
213213+}
214214+215215+impl_timed!(FxEventBox::beat);
216216+impl_event_box!(FxEventBox, FxEventGroup, FxEventData);
217217+218218+/// A collection of [`FxEventData`] that share the same [`Filter`] and distribution.
219219+///
220220+/// Does not implement [`Serialize`] nor [`Deserialize`]. For more info, see the [module docs](super::fx).
221221+#[derive(Debug, Clone, PartialEq)]
222222+#[cfg_attr(
223223+ feature = "bevy_reflect",
224224+ derive(bevy_reflect::Reflect),
225225+ reflect(Debug, Clone, PartialEq)
226226+)]
227227+pub struct FxEventGroup {
228228+ pub filter: Filter,
229229+ pub beat_dist_type: DistributionType,
230230+ /// The strength of the beat distribution. Dependent on the [distribution type](Self::beat_dist_type).
231231+ ///
232232+ /// A value of zero will have no effect.
233233+ pub beat_dist_value: f32,
234234+ pub fx_dist_type: DistributionType,
235235+ /// The strength of the brightness distribution. Dependent on the [distribution type](Self::fx_dist_type).
236236+ ///
237237+ /// A value of zero will have no effect.
238238+ pub fx_dist_value: f32,
239239+ /// Whether the first [`FxEventData`] of the group will be effected by brightness distribution.
240240+ pub fx_dist_effect_first: LooseBool,
241241+ pub fx_dist_easing: Option<Easing>,
242242+ /// In the actual JSON structure, this is a list of indexes to a separate list of event data.
243243+ /// For consistency, this is merged during parsing.
244244+ pub data: Vec<FxEventData>,
245245+}
246246+247247+/// The raw JSON structure that uses [data IDs](self::data_ids) rather than actual [event data](FxEventData).
248248+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
249249+struct FxEventGroupRaw {
250250+ #[serde(rename = "f")]
251251+ filter: Filter,
252252+ #[serde(rename = "d")]
253253+ beat_dist_type: DistributionType,
254254+ #[serde(rename = "w")]
255255+ beat_dist_value: f32,
256256+ #[serde(rename = "t")]
257257+ fx_dist_type: DistributionType,
258258+ #[serde(rename = "s")]
259259+ fx_dist_value: f32,
260260+ #[serde(rename = "b")]
261261+ fx_dist_effect_first: LooseBool,
262262+ #[serde(rename = "i")]
263263+ fx_dist_easing: Option<Easing>,
264264+ #[serde(rename = "l")]
265265+ data_ids: Vec<usize>,
266266+}
267267+268268+impl Default for FxEventGroup {
269269+ fn default() -> Self {
270270+ Self {
271271+ filter: Default::default(),
272272+ beat_dist_type: Default::default(),
273273+ beat_dist_value: 0.0,
274274+ fx_dist_type: Default::default(),
275275+ fx_dist_value: 0.0,
276276+ fx_dist_effect_first: Default::default(),
277277+ fx_dist_easing: Some(Easing::Linear),
278278+ data: vec![FxEventData::default()],
279279+ }
280280+ }
281281+}
282282+283283+impl_event_group!(FxEventGroup::get_fx_offset, FxEventData);
284284+285285+impl FxEventGroup {
286286+ /// Returns the FX value that the event will be offset for a given light ID.
287287+ /// # Panics
288288+ /// Will panic if the light ID is greater than or equal to the group size.
289289+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
290290+ #[allow(deprecated)]
291291+ pub fn get_fx_offset(&self, light_id: i32, group_size: i32) -> f32 {
292292+ self.fx_dist_type.compute_value_offset(
293293+ light_id,
294294+ group_size,
295295+ &self.filter,
296296+ self.fx_dist_value,
297297+ self.data.last().map(|data| data.beat_offset),
298298+ self.fx_dist_easing,
299299+ )
300300+ }
301301+}
302302+303303+/// The lowest-level group event type, which determines the base value of the event.
304304+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
305305+#[cfg_attr(
306306+ feature = "bevy_reflect",
307307+ derive(bevy_reflect::Reflect),
308308+ reflect(Debug, Clone, PartialEq)
309309+)]
310310+pub struct FxEventData {
311311+ /// The number of beats the event will be offset from the [`FxEventBox`]'s beat.
312312+ #[serde(rename = "b")]
313313+ pub beat_offset: f32,
314314+ #[serde(rename = "p")]
315315+ pub transition_type: TransitionType,
316316+ #[serde(rename = "i")]
317317+ pub easing: Easing,
318318+ /// The base value of the effect.
319319+ #[serde(rename = "v")]
320320+ pub value: f32,
321321+}
322322+323323+/// A `PartialEq` and `Hash` version of [`FxEventData`], allowing for deduplication.
324324+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
325325+struct FxEventDataKey {
326326+ beat_offset: OrderedFloat<f32>,
327327+ transition_type: TransitionType,
328328+ easing: Easing,
329329+ value: OrderedFloat<f32>,
330330+}
331331+332332+impl From<&FxEventData> for FxEventDataKey {
333333+ fn from(value: &FxEventData) -> Self {
334334+ Self {
335335+ beat_offset: value.beat_offset.into(),
336336+ transition_type: value.transition_type,
337337+ easing: value.easing,
338338+ value: value.value.into(),
339339+ }
340340+ }
341341+}
342342+343343+impl From<FxEventDataKey> for FxEventData {
344344+ fn from(value: FxEventDataKey) -> Self {
345345+ Self {
346346+ beat_offset: value.beat_offset.into(),
347347+ transition_type: value.transition_type,
348348+ easing: value.easing,
349349+ value: value.value.into(),
350350+ }
351351+ }
352352+}
353353+354354+impl Default for FxEventData {
355355+ fn default() -> Self {
356356+ Self {
357357+ beat_offset: 0.0,
358358+ transition_type: Default::default(),
359359+ easing: Easing::default(),
360360+ value: 1.0,
361361+ }
362362+ }
363363+}
364364+365365+impl_event_data!(FxEventData);
366366+367367+#[cfg(test)]
368368+mod tests {
369369+ use super::*;
370370+ use serde_json::{Value, json};
371371+372372+ fn get_test_container() -> FxEventContainer {
373373+ FxEventContainer {
374374+ event_boxes: vec![FxEventBox {
375375+ beat: 2.0,
376376+ group_id: 0,
377377+ groups: vec![get_test_group(), get_test_group()],
378378+ }],
379379+ }
380380+ }
381381+382382+ fn get_test_group() -> FxEventGroup {
383383+ FxEventGroup {
384384+ filter: Default::default(),
385385+ beat_dist_type: DistributionType::Wave,
386386+ beat_dist_value: 1.0,
387387+ fx_dist_type: DistributionType::Wave,
388388+ fx_dist_value: 1.0,
389389+ fx_dist_effect_first: LooseBool::True,
390390+ fx_dist_easing: Some(Easing::None),
391391+ data: vec![FxEventData {
392392+ beat_offset: 0.0,
393393+ transition_type: TransitionType::Transition,
394394+ easing: Easing::Linear,
395395+ value: 100.0,
396396+ }],
397397+ }
398398+ }
399399+400400+ fn get_test_json() -> Value {
401401+ json!(
402402+ {
403403+ "vfxEventBoxGroups":
404404+ [
405405+ {
406406+ "b": 2.0,
407407+ "g": 0,
408408+ "e":
409409+ [
410410+ {
411411+ "f":
412412+ {
413413+ "c": 0,
414414+ "f": 1,
415415+ "p": 1,
416416+ "t": 0,
417417+ "r": 0,
418418+ "n": 0,
419419+ "s": 0,
420420+ "l": 1.0,
421421+ "d": 0
422422+ },
423423+ "w": 1.0,
424424+ "d": 1,
425425+ "s": 1.0,
426426+ "t": 1,
427427+ "b": 1,
428428+ "i": -1,
429429+ "l":
430430+ [
431431+ 0
432432+ ]
433433+ },
434434+ {
435435+ "f":
436436+ {
437437+ "c": 0,
438438+ "f": 1,
439439+ "p": 1,
440440+ "t": 0,
441441+ "r": 0,
442442+ "n": 0,
443443+ "s": 0,
444444+ "l": 1.0,
445445+ "d": 0
446446+ },
447447+ "w": 1.0,
448448+ "d": 1,
449449+ "s": 1.0,
450450+ "t": 1,
451451+ "b": 1,
452452+ "i": -1,
453453+ "l":
454454+ [
455455+ 0
456456+ ]
457457+ }
458458+ ]
459459+ }
460460+ ],
461461+ "_fxEventsCollection":
462462+ {
463463+ "_fl":
464464+ [
465465+ {
466466+ "b": 0.0,
467467+ "p": 0,
468468+ "i": 0,
469469+ "v": 100.0
470470+ }
471471+ ]
472472+ }
473473+ }
474474+ )
475475+ }
476476+477477+ #[test]
478478+ fn test_deserialize() {
479479+ let container: FxEventContainer = serde_json::from_value(get_test_json()).unwrap();
480480+481481+ assert_eq!(container, get_test_container());
482482+ }
483483+484484+ #[test]
485485+ fn test_serialize() {
486486+ let out_json = serde_json::to_value(&get_test_container()).unwrap();
487487+488488+ assert_eq!(out_json, get_test_json());
489489+ }
490490+491491+ #[test]
492492+ fn test_round_trip() {
493493+ let container: FxEventContainer = serde_json::from_value(get_test_json()).unwrap();
494494+495495+ let out_json = serde_json::to_string_pretty(&container).unwrap();
496496+497497+ let round_trip: FxEventContainer = serde_json::from_str(&out_json).unwrap();
498498+499499+ assert_eq!(container, round_trip);
500500+ }
501501+}