···11+//! Defines the structure of a map's difficulty file(s) (i.e. `ExpertStandard.dat`).
22+13pub mod gameplay_event;
24pub mod lightshow;
35pub mod playfield;
···810911use serde::{Deserialize, Serialize};
10121313+/// A map's difficulty file(s) (i.e. `ExpertStandard.dat`).
1114#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
1215#[serde(rename_all = "camelCase")]
1316#[cfg_attr(
···2831 /// | 3.2 | Translation events. | Yes |
2932 /// | 3.3 | More strobe functionality. | No |
3033 /// | 4.X | New template-like format. | No |
3434+ ///
3535+ /// [^1]: Not supported by experimental lighting calculation methods.
3136 pub version: String,
3237 pub bpm_events: Vec<BpmEvent>,
3338 #[serde(rename = "rotationEvents")]
+11-3
src/difficulty/gameplay_event.rs
···11+//! Events that effect gameplay and aren't purely visual.
22+13use crate::{impl_timed, loose_enum};
24use serde::{Deserialize, Serialize};
3566+/// Controls the rotation that interactable objects spawn in 90/360 degree difficulties.
47#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
58#[cfg_attr(
69 feature = "bevy_reflect",
···811 reflect(Debug, Clone, PartialEq)
912)]
1013pub struct LaneRotationEvent {
1414+ /// The time the event takes place.
1115 #[serde(rename = "b")]
1216 pub beat: f32,
1317 #[serde(rename = "e")]
1418 pub execution_time: ExecutionTime,
1919+ /// The number of degrees to rotate objects around the player.
1520 #[serde(rename = "r")]
1621 pub degrees: f32,
1722}
···1924impl_timed!(LaneRotationEvent::beat);
20252126loose_enum!(
2222- /// Determines when a [`LaneRotationEvent`] will be applied to objects placed on the same beat as this event.
2727+ /// Determines when a [`LaneRotationEvent`] will be applied to objects.
2328 #[derive(Default, Copy)]
2429 ExecutionTime: i32 {
2525- /// The [`LaneRotationEvent`] will affect objects with a beat greater than or equal to the event's beat.
3030+ /// The [`LaneRotationEvent`] will affect objects with a beat *greater than or equal to* the event's beat.
2631 #[default]
2732 Early = 0,
2828- /// The [`LaneRotationEvent`] will affect objects with a beat greater than the event's beat.
3333+ /// The [`LaneRotationEvent`] will affect objects with a beat *greater than* the event's beat.
2934 Late = 1,
3035 }
3136);
32373838+/// Changes the BPM of the map at a specific beat.
3339#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
3440#[cfg_attr(
3541 feature = "bevy_reflect",
···3743 reflect(Debug, Clone, PartialEq)
3844)]
3945pub struct BpmEvent {
4646+ /// The time the event takes place.
4047 #[serde(rename = "b")]
4148 pub beat: f32,
4949+ /// The BPM to change the map too.
4250 #[serde(rename = "m")]
4351 pub bpm: f32,
4452}
+11-21
src/difficulty/lightshow.rs
···11+//! Events that have no effect on gameplay.
22+13pub mod basic;
22-pub mod boxes;
34pub mod easing;
45pub mod filter;
66+pub mod group;
5768pub use basic::*;
77-pub use boxes::*;
89pub use easing::*;
910pub use filter::*;
1111+pub use group::*;
10121113use crate::loose_enum;
12141315loose_enum! {
1414- /// The distribution value does different things depending on the type.
1515- ///
1616- /// # [Beat Distribution](https://bsmg.wiki/mapping/map-format/lightshow.html#light-color-event-boxes-beat-distribution):
1717- /// ### Wave:
1818- /// The value represents the total time for all steps to complete.
1919- /// ### Step:
2020- /// The value represents the time until the next step is completed.
2121- ///
2222- /// # [Brightness](https://bsmg.wiki/mapping/map-format/lightshow.html#light-color-event-boxes-effect-distribution) and [Rotation Distribution](https://bsmg.wiki/mapping/map-format/lightshow.html#light-rotation-event-boxes-effect-distribution):
2323- /// ### Wave:
2424- /// The value represents the total difference between the first and last step.
2525- /// ### Step:
2626- /// The value represents the different between the current and next step.
1616+ /// The way that the distribution value is used.
2717 #[derive(Default, Copy)]
2818 DistributionType: i32 {
1919+ /// The distribution value represents the difference between *the last and first step*.
2920 #[default]
3021 Wave = 1,
2222+ /// The distribution value represents the difference between *each step*.
3123 Step = 2,
3224 }
3325}
···7466}
75677668loose_enum! {
7777- /// Controls how the value is changed from the previous event.
7878- /// - Transition: The value will blend from the previous event's value, using the
7979- /// [easing](Easing) value.
8080- /// - Extend: The event's value will be ignored, replaced with the values from the previous event.
8181- ///
8282- /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#light-rotation-events-type).
6969+ /// Controls how the state is changed relative to the previous event.
8370 #[derive(Default, Copy)]
8471 TransitionType: i32 {
7272+ /// The state will blend from the previous event's state, using the events [easing](Easing).
8573 #[default]
8674 Transition = 0,
7575+ /// The event's state will be ignored, replaced with the state from the previous event.
8776 Extend = 1,
8877 }
8978}
90799180loose_enum! {
8181+ /// The axis that a rotation/translation event effects.
9282 #[derive(Default, Copy)]
9383 EventAxis: i32 {
9484 #[default]
+23
src/difficulty/lightshow/basic.rs
···11+//! The non-group events that were inherited from difficulty file V2.
22+13use crate::difficulty::playfield::CutDirection;
24use crate::impl_timed;
35use serde::{Deserialize, Serialize};
4677+/// The basic V2 event type, which is still used for some elements of V3 environments (for example, the player platform).
58#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
69#[cfg_attr(
710 feature = "bevy_reflect",
···912 reflect(Debug, Clone, PartialEq)
1013)]
1114pub struct BasicEvent {
1515+ /// The time the event takes place.
1216 #[serde(rename = "b")]
1317 pub beat: f32,
1818+ /// Determines the behaviour of the event. The exact behaviour differs depending on the environment.
1919+ ///
1420 /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#basic-events-type).
1521 #[serde(rename = "et")]
1622 pub event_type: i32,
2323+ /// Determines which effect the event will produce, based on its [type](Self::event_type).
1724 #[serde(rename = "i")]
1825 pub value: i32,
2626+ /// Modifies the effect.
1927 #[serde(rename = "f")]
2028 pub float: f32,
2129}
22302331impl_timed!(BasicEvent::beat);
24323333+/// Controls the TinyTAN figures on the [BTS environment](crate::info::Environment::BTS).
3434+///
3535+/// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#waypoints).
2536#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
2637#[cfg_attr(
2738 feature = "bevy_reflect",
···2940 reflect(Debug, Clone, PartialEq)
3041)]
3142pub struct Waypoint {
4343+ /// The time the event takes place.
3244 #[serde(rename = "b")]
3345 pub beat: f32,
4646+ /// A value representing the vertical position of the event.
4747+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
3448 #[serde(rename = "y")]
3549 pub row: u8,
5050+ /// A value representing the horizontal position of the event.
5151+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
3652 #[serde(rename = "x")]
3753 pub col: u8,
3854 #[serde(rename = "d")]
···41574258impl_timed!(Waypoint::beat);
43596060+/// Controls which lighting colors are used, based on a map or environment's [color scheme](crate::info::color_scheme::ColorScheme).
4461#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
4562#[cfg_attr(
4663 feature = "bevy_reflect",
···4865 reflect(Debug, Clone, PartialEq)
4966)]
5067pub struct ColorBoostEvent {
6868+ /// The time the event takes place.
5169 #[serde(rename = "b")]
5270 pub beat: f32,
7171+ /// Whether to enable or disable boost colors.
5372 #[serde(rename = "o")]
5473 pub boost: bool,
5574}
···5776impl_timed!(ColorBoostEvent::beat);
58775978/// An event containing an array of Special Event Keywords.
7979+///
6080/// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#special-event-keywords).
6181#[doc(alias = "KeywordEvent")]
6282#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
···7191}
72927393/// Allows basic event lanes to be overridden with environment-specific behaviour, using secret keys.
9494+///
7495/// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#special-event-keywords).
7596#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
7697#[cfg_attr(
···79100 reflect(Debug, Clone, PartialEq)
80101)]
81102pub struct Keyword {
103103+ /// The secret key of the effect.
82104 #[serde(rename = "k")]
83105 pub keyword: String,
106106+ /// A list of [event types](BasicEvent::event_type) to effect with the keyword.
84107 #[serde(rename = "e")]
85108 pub event_types: Vec<i32>,
86109}
···11+//! The advanced group lighting system events.
22+13pub mod color;
24pub mod rotation;
35pub mod translation;
···911use crate::difficulty::lightshow::filter::Filter;
1012use crate::timing_traits::Timed;
11131414+/// A collection of [`EventGroup`]s that share the same group ID and beat.
1215pub trait EventBox: Timed {
1316 type Group: EventGroup<Data = Self::Data>;
1417 type Data: EventData;
···2023#[doc(hidden)]
2124macro_rules! impl_event_box {
2225 ($ident:ident, $group:ident, $data:ident) => {
2323- impl crate::difficulty::lightshow::boxes::EventBox for $ident {
2626+ impl crate::difficulty::lightshow::group::EventBox for $ident {
2427 type Group = $group;
2528 type Data = $data;
2629···3134 };
3235}
33363737+/// A collection of [`EventData`] that share the same [`Filter`] and distribution.
3438pub trait EventGroup {
3539 type Data: EventData;
3640···4549 )]
4650 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32;
47514848- /// Returns the value that the event will be offset for a given light ID (i.e. brightness offset).
5252+ /// Returns the value (i.e. brightness) that the event will be offset for a given light ID.
4953 /// # Panics
5054 /// Will panic if the light ID is greater than or equal to the group size.
5155 #[deprecated(
···5862#[doc(hidden)]
5963macro_rules! impl_event_group {
6064 ($ident:ident::$value_offset:ident, $data:ident) => {
6161- impl crate::difficulty::lightshow::boxes::EventGroup for $ident {
6565+ impl crate::difficulty::lightshow::group::EventGroup for $ident {
6266 type Data = $data;
63676468 fn get_filter(&self) -> &Filter {
···8993 };
9094}
91959696+/// The lowest-level group event type, which determines the base value of the event.
9297pub trait EventData {
9898+ /// Returns the number of beats the event will be offset from the [`EventBox`]'s beat.
9399 fn get_beat_offset(&self) -> f32;
94100}
95101···97103#[doc(hidden)]
98104macro_rules! impl_event_data {
99105 ($ident:ident) => {
100100- impl crate::difficulty::lightshow::boxes::EventData for $ident {
106106+ impl crate::difficulty::lightshow::group::EventData for $ident {
101107 fn get_beat_offset(&self) -> f32 {
102108 self.beat_offset
103109 }
···11+//! Events that control the color of lights.
22+13use crate::difficulty::lightshow::DistributionType;
24use crate::difficulty::lightshow::easing::Easing;
35use crate::difficulty::lightshow::filter::Filter;
···57use crate::{impl_event_box, impl_event_data, impl_event_group, impl_timed, loose_enum};
68use serde::{Deserialize, Serialize};
791010+/// A collection of [`ColorEventGroup`]s that share the same group ID and beat.
811#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
912#[cfg_attr(
1013 feature = "bevy_reflect",
···1215 reflect(Debug, Clone, PartialEq)
1316)]
1417pub struct ColorEventBox {
1818+ /// The time the event takes place.
1519 #[serde(rename = "b")]
1620 pub beat: f32,
2121+ /// The ID of the collection of objects that this event effects.
1722 #[serde(rename = "g")]
1823 pub group_id: i32,
1924 #[serde(rename = "e")]
···3338impl_timed!(ColorEventBox::beat);
3439impl_event_box!(ColorEventBox, ColorEventGroup, ColorEventData);
35404141+/// A collection of [`ColorEventData`] that share the same [`Filter`] and distribution.
3642#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
3743#[cfg_attr(
3844 feature = "bevy_reflect",
···4450 pub filter: Filter,
4551 #[serde(rename = "d")]
4652 pub beat_dist_type: DistributionType,
5353+ /// The strength of the beat distribution. Dependent on the [distribution type](Self::beat_dist_type).
5454+ ///
5555+ /// A value of zero will have no effect.
4756 #[serde(rename = "w")]
4857 pub beat_dist_value: f32,
5858+ /// The strength of the brightness distribution. Dependent on the [distribution type](Self::bright_dist_type).
5959+ ///
6060+ /// A value of zero will have no effect.
4961 #[serde(rename = "t")]
5062 pub bright_dist_type: DistributionType,
5163 #[serde(rename = "r")]
5264 pub bright_dist_value: f32,
6565+ /// Whether the first [`ColorEventData`] of the group will be effected by brightness distribution.
5366 #[serde(rename = "b")]
5467 pub bright_dist_effect_first: LooseBool,
5568 /// > Only present in difficulty file V3.2 or higher.
···96109 }
97110}
98111112112+/// The lowest-level group event type, which determines the color of the event.
99113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
100114#[cfg_attr(
101115 feature = "bevy_reflect",
···103117 reflect(Debug, Clone, PartialEq)
104118)]
105119pub struct ColorEventData {
120120+ /// The number of beats the event will be offset from the [`ColorEventBox`]'s beat.
106121 #[serde(rename = "b")]
107122 pub beat_offset: f32,
108123 #[serde(rename = "i")]
···111126 pub color: LightColor,
112127 #[serde(rename = "s")]
113128 pub brightness: f32,
129129+ /// Determines the number of strobes that will take place each beat.
130130+ /// A value of zero will result in no strobing.
114131 #[serde(rename = "f")]
115132 pub strobe_frequency: i32,
116133}
···130147impl_event_data!(ColorEventData);
131148132149loose_enum! {
150150+ /// Controls how the state is changed relative to the previous event.
133151 #[derive(Default, Copy)]
134152 ColorTransitionType: i32 {
135135- /// Replaced with `Transition` and [`Easing::None`] in difficulty file V3.2 or higher.
136136- Instant = 0,
153153+ /// Unique to color events.
154154+ /// Has the same effect as using [`TransitionType::Transition`](crate::lightshow::TransitionType::Transition)
155155+ /// and [`Easing::None`] in rotation/translation events.
137156 #[default]
157157+ Instant = 0,
158158+ /// The state will blend from the previous event's state, using the events [easing](Easing).
138159 Transition = 1,
160160+ /// The event's state will be ignored, replaced with the state from the previous event.
139161 Extend = 2,
140162 }
141163}
142164143165loose_enum! {
166166+ /// Controls which color to display, based on a map or environment's [color scheme](crate::info::color_scheme::ColorScheme).
144167 #[derive(Default, Copy)]
145168 LightColor: i32 {
146169 #[default]
···154177#[cfg(test)]
155178mod tests {
156179 use super::*;
157157- use crate::difficulty::lightshow::boxes::EventGroup;
158180 use crate::difficulty::lightshow::filter::FilterType;
181181+ use crate::difficulty::lightshow::group::EventGroup;
159182160183 #[test]
161184 fn beat_wave() {
···11-use crate::difficulty::lightshow::boxes::EventData;
11+//! Events that control the rotation of objects.
22+23use crate::difficulty::lightshow::easing::Easing;
34use crate::difficulty::lightshow::filter::Filter;
55+use crate::difficulty::lightshow::group::EventData;
46use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType};
57use crate::utils::LooseBool;
68use crate::{impl_event_box, impl_event_group, impl_timed, loose_enum};
79use serde::{Deserialize, Serialize};
8101111+/// A collection of [`RotationEventGroup`]s that share the same group ID and beat.
912#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1013#[cfg_attr(
1114 feature = "bevy_reflect",
···1316 reflect(Debug, Clone, PartialEq)
1417)]
1518pub struct RotationEventBox {
1919+ /// The time the event takes place.
1620 #[serde(rename = "b")]
1721 pub beat: f32,
2222+ /// The ID of the collection of objects that this event effects.
1823 #[serde(rename = "g")]
1924 pub group_id: i32,
2025 #[serde(rename = "e")]
···3439impl_timed!(RotationEventBox::beat);
3540impl_event_box!(RotationEventBox, RotationEventGroup, RotationEventData);
36414242+/// A collection of [`RotationEventData`] that share the same [`EventAxis`], [`Filter`], and distribution.
3743#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
3844#[cfg_attr(
3945 feature = "bevy_reflect",
···4551 pub filter: Filter,
4652 #[serde(rename = "d")]
4753 pub beat_dist_type: DistributionType,
5454+ /// The strength of the beat distribution. Dependent on the [distribution type](Self::beat_dist_type).
5555+ ///
5656+ /// A value of zero will have no effect.
4857 #[serde(rename = "w")]
4958 pub beat_dist_value: f32,
5059 #[serde(rename = "t")]
5160 pub rotation_dist_type: DistributionType,
6161+ /// The strength of the rotation distribution. Dependent on the [distribution type](Self::rotation_dist_type).
6262+ ///
6363+ /// A value of zero will have no effect.
5264 #[serde(rename = "s")]
5365 pub rotation_dist_value: f32,
6666+ /// Whether the first [`RotationEventData`] of the group will be effected by rotation distribution.
5467 #[serde(rename = "b")]
5568 pub rotation_dist_effect_first: LooseBool,
5669 /// > Only present in difficulty file V3.2 or higher.
···5871 pub rotation_dist_easing: Option<Easing>,
5972 #[serde(rename = "a")]
6073 pub axis: EventAxis,
7474+ /// If true, the rotation will be mirrored.
6175 #[serde(rename = "r")]
6276 pub invert_axis: LooseBool,
6377 #[serde(rename = "l")]
···103117 }
104118}
105119120120+/// The lowest-level group event type, which determines the base rotation of the event.
106121#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
107122#[cfg_attr(
108123 feature = "bevy_reflect",
···110125 reflect(Debug, Clone, PartialEq)
111126)]
112127pub struct RotationEventData {
128128+ /// The number of beats the event will be offset from the [`RotationEventBox`]'s beat.
113129 #[serde(rename = "b")]
114130 pub beat_offset: f32,
115131 #[serde(rename = "p")]
116132 pub transition_type: TransitionType,
117133 #[serde(rename = "e")]
118134 pub easing: Easing,
135135+ /// The base number of degrees the event will rotate objects by.
119136 #[serde(rename = "r")]
120137 pub degrees: f32,
121138 #[serde(rename = "o")]
122139 pub direction: RotationDirection,
140140+ /// Extends the rotation by 360 degrees in the [`RotationDirection`].
123141 #[serde(rename = "l")]
124142 pub loops: i32,
125143}
···11-use crate::difficulty::lightshow::boxes::EventData;
11+//! Events that control the translation/position of objects.
22+23use crate::difficulty::lightshow::easing::Easing;
34use crate::difficulty::lightshow::filter::Filter;
55+use crate::difficulty::lightshow::group::EventData;
46use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType};
57use crate::utils::LooseBool;
68use crate::{impl_event_box, impl_event_group, impl_timed};
79use serde::{Deserialize, Serialize};
8101111+/// A collection of [`TranslationEventGroup`]s that share the same group ID and beat.
912#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1013#[cfg_attr(
1114 feature = "bevy_reflect",
···1316 reflect(Debug, Clone, PartialEq)
1417)]
1518pub struct TranslationEventBox {
1919+ /// The time the event takes place.
1620 #[serde(rename = "b")]
1721 pub beat: f32,
2222+ /// The ID of the collection of objects that this event effects.
1823 #[serde(rename = "g")]
1924 pub group_id: i32,
2025 #[serde(rename = "e")]
···3843 TranslationEventData
3944);
40454646+/// A collection of [`TranslationEventData`] that share the same [`EventAxis`], [`Filter`], and distribution.
4147#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4248#[cfg_attr(
4349 feature = "bevy_reflect",
···4955 pub filter: Filter,
5056 #[serde(rename = "d")]
5157 pub beat_dist_type: DistributionType,
5858+ /// The strength of the beat distribution. Dependent on the [beat distribution type](Self::beat_dist_type).
5959+ ///
6060+ /// A value of zero will have no effect.
5261 #[serde(rename = "w")]
5362 pub beat_dist_value: f32,
5463 #[serde(rename = "t")]
5564 pub translation_dist_type: DistributionType,
6565+ /// The strength of the translation distribution. Dependent on the [distribution type](Self::translation_dist_type).
6666+ ///
6767+ /// A value of zero will have no effect.
5668 #[serde(rename = "s")]
5769 pub translation_dist_value: f32,
7070+ /// Whether the first [`TranslationEventData`] of the group will be effected by translation distribution.
5871 #[serde(rename = "b")]
5972 pub translation_dist_effect_first: LooseBool,
6073 #[serde(rename = "i")]
6174 pub translation_dist_easing: Easing,
6275 #[serde(rename = "a")]
6376 pub axis: EventAxis,
7777+ /// If true, the translation will be mirrored.
6478 #[serde(rename = "r")]
6579 pub invert_axis: LooseBool,
6680 #[serde(rename = "l")]
···109123 }
110124}
111125126126+/// The lowest-level group event type, which determines the base position of the event.
112127#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
113128#[cfg_attr(
114129 feature = "bevy_reflect",
···116131 reflect(Debug, Clone, PartialEq)
117132)]
118133pub struct TranslationEventData {
134134+ /// The number of beats the event will be offset from the [`TranslationEventBox`]'s beat.
119135 #[serde(rename = "b")]
120136 pub beat_offset: f32,
121137 #[serde(rename = "p")]
122138 pub transition_type: TransitionType,
123139 #[serde(rename = "e")]
124140 pub easing: Easing,
141141+ /// The base number of units the event will offset objects by.
125142 #[serde(rename = "t")]
126143 pub value: f32,
127144}
+6
src/difficulty/lightshow/easing.rs
···11+//! The easing that a [transition](crate::lightshow::TransitionType::Transition) event will use.
22+13use crate::loose_enum;
24use simple_easing::*;
3546loose_enum! {
77+ /// The easing that a [transition](crate::lightshow::TransitionType::Transition) event will use.
58 #[derive(Default, Copy)]
69 Easing: i32 {
710 #[default]
···3942 OutBounce = 29,
4043 InOutBounce = 30,
41444545+ /// Note: For [`Easing::ease`], the result will be the same as [`Easing::InOutBack`].
4246 BeatSaberInOutBack = 100,
4747+ /// Note: For [`Easing::ease`], the result will be the same as [`Easing::InOutElastic`].
4348 BeatSaberInOutElastic = 101,
4949+ /// Note: For [`Easing::ease`], the result will be the same as [`Easing::InOutBounce`].
4450 BeatSaberInOutBounce = 102,
4551 }
4652}
+16-16
src/difficulty/lightshow/filter.rs
···11+//! Controls which light IDs are affected by an event.
22+13use crate::loose_enum;
24use crate::utils::LooseBool;
35use serde::{Deserialize, Serialize};
4655-/// Controls which light indices are affected by event boxes.
77+/// Controls which light IDs are affected by an event.
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79#[cfg_attr(
810 feature = "bevy_reflect",
···1113)]
1214pub struct Filter {
1315 // V3.0:
1616+ /// Controls how [`parameter1`](Self::parameter1) and [`parameter2`](Self::parameter2) are used.
1417 #[serde(rename = "f")]
1518 pub filter_type: FilterType,
1616- /// Dependent on the [`FilterType`]
1919+ /// Dependent on the [`FilterType`].
1720 #[serde(rename = "p")]
1821 pub parameter1: i32,
1919- /// Dependent on the [`FilterType`]
2222+ /// Dependent on the [`FilterType`].
2023 #[serde(rename = "t")]
2124 pub parameter2: i32,
2525+ /// If true, the filter will start at the end of a group and work backwards.
2226 #[serde(rename = "r")]
2327 pub reverse: LooseBool,
2428 // V3.1:
···152156}
153157154158loose_enum! {
155155- /// The parameters of a [Filter] do different things depending on the type.
156156- ///
157157- /// ### [Division](https://bsmg.wiki/mapping/map-format/lightshow.html#index-filters-type-1):
158158- /// Splits the group up into equal sections and selects one.
159159- /// - Parameter 1 determines the number of sections.
160160- /// It will be rounded up to the nearest multiple of the group size.
161161- /// - Parameter 2 determines the section to select, starting at 0.
162162- ///
163163- /// ### [Step and Offset](https://bsmg.wiki/mapping/map-format/lightshow.html#index-filters-type-2):
164164- /// Alternates selecting and not selecting lights.
165165- /// - Parameter 1 is the index of the first light that will be selected, starting at 0.
166166- /// - Parameter 2 determines the number of IDs to move forward before selecting another light.
159159+ /// Controls how a [`Filter`]'s [`parameter1`](Filter::parameter1)
160160+ /// and [`parameter2`](Filter::parameter2) values are used.
167161 #[derive(Default, Copy)]
168162 FilterType: i32 {
163163+ /// Splits the group up into equal sections and selects one.
164164+ /// - [`parameter1`](Filter::parameter1) determines the number of sections.
165165+ /// It will be rounded up to the nearest multiple of the group size.
166166+ /// - [`parameter2`](Filter::parameter2) determines the section to select, starting at 0.
169167 #[default]
170170- //Todo Doesn't match wiki
171168 Division = 1,
169169+ /// Alternates selecting and not selecting lights.
170170+ /// - [`parameter1`](Filter::parameter1) is the index of the first light that will be selected, starting at 0.
171171+ /// - [`parameter2`](Filter::parameter2) determines the number of IDs to move forward before selecting another light.
172172 StepAndOffset = 2,
173173 }
174174}
+70-2
src/difficulty/playfield.rs
···11+//! The interactable objects of a difficulty.
22+13use crate::{impl_duration, impl_timed, loose_enum};
24use serde::{Deserialize, Serialize};
3566+/// The standard block/note that a player cuts.
47#[doc(alias = "Block")]
58#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
69#[cfg_attr(
···912 reflect(Debug, Clone, PartialEq)
1013)]
1114pub struct Note {
1515+ /// The position of the object in time.
1216 #[serde(rename = "b")]
1317 pub beat: f32,
1818+ /// A value representing the vertical position of the object.
1919+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
1420 #[serde(rename = "y")]
1521 pub row: i32,
2222+ /// A value representing the horizontal position of the object.
2323+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
1624 #[serde(rename = "x")]
1725 pub col: i32,
2626+ /// The color that determines which saber should be used to cut the note.
1827 #[serde(rename = "c")]
1928 pub color: NoteColor,
2929+ /// The direction the note should be cut.
2030 #[serde(rename = "d")]
2131 pub direction: CutDirection,
3232+ /// The number of degrees counter-clockwise to offset the object by.
2233 #[serde(rename = "a")]
2334 pub angle_offset: f32,
2435}
···2637impl_timed!(Note::beat);
27382839loose_enum! {
4040+ /// The color of a note, which determines which saber should be used to cut it.
2941 #[derive(Default, Copy)]
3042 NoteColor: i32 {
3143 #[default]
···3547}
36483749loose_enum! {
5050+ /// The direction a note should be cut.
3851 #[derive(Default, Copy)]
3952 CutDirection: i32 {
4053 #[default]
···4659 UpRight = 5,
4760 DownLeft = 6,
4861 DownRight = 7,
6262+ #[doc(alias = "Dot")]
4963 Any = 8,
5064 }
5165}
52665367impl CutDirection {
5454- /// Returns the number of degrees a note is rotated, with zero degrees being downward note.
5555- /// Returns zero if the cut direction is unknown.
6868+ /// Returns the number of degrees a note is rotated, with zero degrees being a downward note.
6969+ ///
7070+ /// Returns zero if the cut direction is unknown/any.
5671 pub fn get_degrees(&self) -> f32 {
5772 match self {
5873 CutDirection::Up => 180.0,
···6984 }
7085}
71868787+/// The spiked bombs that players avoid hitting with their sabers.
7288#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
7389#[cfg_attr(
7490 feature = "bevy_reflect",
···7692 reflect(Debug, Clone, PartialEq)
7793)]
7894pub struct Bomb {
9595+ /// The position of the object in time.
7996 #[serde(rename = "b")]
8097 pub beat: f32,
9898+ /// A value representing the vertical position of the object.
9999+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
81100 #[serde(rename = "y")]
82101 pub row: i32,
102102+ /// A value representing the horizontal position of the object.
103103+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
83104 #[serde(rename = "x")]
84105 pub col: i32,
85106}
8610787108impl_timed!(Bomb::beat);
88109110110+/// A wall/obstacle that players avoid running into.
89111#[doc(alias = "Obstacle")]
90112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
91113#[cfg_attr(
···94116 reflect(Debug, Clone, PartialEq)
95117)]
96118pub struct Wall {
119119+ /// The start position of the object in time.
97120 #[serde(rename = "b")]
98121 pub beat: f32,
122122+ /// The length (in beats) that an object takes place.
99123 #[serde(rename = "d")]
100124 pub duration: f32,
125125+ /// A value representing the vertical position of the object.
126126+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
101127 #[serde(rename = "y")]
102128 pub row: i32,
129129+ /// A value representing the horizontal position of the object.
130130+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
103131 #[serde(rename = "x")]
104132 pub col: i32,
133133+ /// The number of columns that the wall will take up.
105134 #[serde(rename = "w")]
106135 pub width: i32,
136136+ /// The number of rows that the wall will take up.
137137+ ///
138138+ /// A standard wall has a height of five while a crouch wall has a height of three.
107139 #[serde(rename = "h")]
108140 pub height: i32,
109141}
···123155124156impl_duration!(Wall::beat, duration: duration);
125157158158+/// A glowing line that guides the player's saber.
126159#[doc(alias = "Slider")]
127160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128161#[cfg_attr(
···131164 reflect(Debug, Clone, PartialEq)
132165)]
133166pub struct Arc {
167167+ /// The start position of the object in time.
134168 #[serde(rename = "b")]
135169 pub beat: f32,
170170+ /// A value representing the vertical starting position of the object.
171171+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
136172 #[serde(rename = "y")]
137173 pub row: i32,
174174+ /// A value representing the horizontal starting position of the object.
175175+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
138176 #[serde(rename = "x")]
139177 pub col: i32,
178178+ /// The color of the arc.
140179 #[serde(rename = "c")]
141180 pub color: NoteColor,
181181+ /// The direction the arc moves in at the start.
142182 #[serde(rename = "d")]
143183 pub direction: CutDirection,
184184+ /// Controls how far away the starting bezier control point is in [cut direction](Self::direction).
144185 #[serde(rename = "mu")]
145186 pub control_point: f32,
146187188188+ /// The end position of the object in time.
147189 #[serde(rename = "tb")]
148190 pub tail_beat: f32,
191191+ /// A value representing the vertical ending position of the object.
192192+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
149193 #[serde(rename = "ty")]
150194 pub tail_row: i32,
195195+ /// A value representing the horizontal ending position of the object.
196196+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
151197 #[serde(rename = "tx")]
152198 pub tail_col: i32,
199199+ /// The direction the arc moves in at the end.
153200 #[serde(rename = "tc")]
154201 pub tail_direction: CutDirection,
202202+ /// Controls how far away the ending bezier control point is in [tail cut direction](Self::tail_direction).
155203 #[serde(rename = "tmu")]
156204 pub tail_control_point: f32,
157205206206+ /// Controls how the arc curves from its head to its tail.
158207 #[serde(rename = "m")]
159208 pub mid_anchor_mode: MidAnchorMode,
160209}
···181230impl_duration!(Arc::beat, end: tail_beat);
182231183232loose_enum! {
233233+ /// Controls how an arc curves from its head to its tail.
184234 #[derive(Default, Copy)]
185235 MidAnchorMode: i32 {
186236 #[default]
···190240 }
191241}
192242243243+/// A chain/burst of mini-notes.
193244#[doc(alias = "BurstSlider")]
194245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195246#[cfg_attr(
···198249 reflect(Debug, Clone, PartialEq)
199250)]
200251pub struct Chain {
252252+ /// The start position of the object in time.
201253 #[serde(rename = "b")]
202254 pub beat: f32,
255255+ /// A value representing the vertical starting position of the object.
256256+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
203257 #[serde(rename = "y")]
204258 pub row: i32,
259259+ /// A value representing the horizontal starting position of the object.
260260+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
205261 #[serde(rename = "x")]
206262 pub col: i32,
263263+ /// The color that determines which saber should be used to cut the chain links.
207264 #[serde(rename = "c")]
208265 pub color: NoteColor,
266266+ /// The direction the start of the chain should be cut.
209267 #[serde(rename = "d")]
210268 pub direction: CutDirection,
211269270270+ /// The end position of the object in time.
212271 #[serde(rename = "tb")]
213272 pub tail_beat: f32,
273273+ /// A value representing the vertical ending position of the object.
274274+ /// In the range 0..2 inclusive, with zero being the bottom and two being the top row.
214275 #[serde(rename = "ty")]
215276 pub tail_row: i32,
277277+ /// A value representing the horizontal ending position of the object.
278278+ /// In the range 0..3 inclusive, with zero being the far left and three being the far right column.
216279 #[serde(rename = "tx")]
217280 pub tail_col: i32,
218281282282+ /// The number of links the chain has, including the connected [`Note`].
219283 #[serde(rename = "sc")]
220284 pub link_count: i32,
285285+ /// The percent of the path (from head to tail) that will be used, usually in the range 0..1 inclusive.
286286+ /// Smaller values will result in the chain links bunching up near the head.
287287+ ///
288288+ /// Setting this to zero will crash the game.
221289 #[serde(rename = "s")]
222290 pub link_squish: f32,
223291}
+21-3
src/info.rs
···11+//! Defines the structure of a map's `Info.dat` file.
22+13pub mod color_scheme;
2435pub use color_scheme::*;
···57use crate::loose_enum;
68use serde::{Deserialize, Serialize};
791010+/// A map's `Info.dat` file.
811#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
912#[cfg_attr(
1013 feature = "bevy_reflect",
···1417pub struct Beatmap {
1518 /// The info file version, in the form of `2.1.0`.
1619 ///
1717- /// ### Info File
2020+ /// ### Version Support
1821 ///
1922 /// | Version | Description | Supported |
2023 /// |---------|-----------------------------------------------|-----------|
···4952 /// The path to the cover image file, relative to the map's folder.
5053 #[serde(rename = "_coverImageFilename")]
5154 pub cover_image_file: String,
5555+ /// The environment that will be used for 90 and 360 degree difficulties.
5656+ ///
5757+ /// Starting in info file V2.1, individual difficulties can override this using [environment_index](DifficultyInfo::environment_index).
5258 #[serde(rename = "_environmentName")]
5359 pub environment: Environment,
5460 /// The environment that will be used for 90 and 360 degree difficulties.
5555- ///
5656- /// Starting in info file V2.1, Individual difficulties can override this using [environment_index](DifficultyInfo::environment_index).
5761 #[serde(rename = "_allDirectionsEnvironmentName")]
5862 pub all_directions_environment: AllDirectionEnvironment,
5963 /// > Only present in info file V2.1 or higher.
···6771}
68726973loose_enum! {
7474+ /// The world that surrounds the player and defines which lights are available.
7575+ ///
7676+ /// For 90/360 degree mode, see [`AllDirectionEnvironment`].
7077 #[derive(Default)]
7178 Environment: String {
7279 #[default]
···121128}
122129123130loose_enum! {
131131+ /// The world that surrounds the player while playing 90/360 degree mode.
132132+ ///
133133+ /// For standard mode, see [`Environment`].
124134 #[derive(Default)]
125135 AllDirectionEnvironment: String {
126136 #[default]
···128138 }
129139}
130140141141+/// Describes a group of difficulties, all with the same [characteristic/mode](Characteristic).
131142#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
132143#[cfg_attr(
133144 feature = "bevy_reflect",
···142153}
143154144155loose_enum! {
156156+ /// Describes the type/game mode of a difficulty.
157157+ ///
158158+ /// Note that [`Lawless`](Self::Lawless) and [`Lightshow`](Self::Lightshow) are modded characteristics,
159159+ /// and may cause problems in un-modded versions of the game.
145160 #[derive(Default)]
146161 Characteristic: String {
147162 #[default]
···158173 }
159174}
160175176176+/// Describes the settings for a difficulty.
177177+///
178178+/// Note that a difficulties [characteristic](Characteristic) is defined by its [`DifficultySet`].
161179#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
162180#[cfg_attr(
163181 feature = "bevy_reflect",
+14
src/info/color_scheme.rs
···11+//! Describes the colors of objects and lights for an environment/map.
22+13pub mod presets;
2435#[allow(unused_imports)]
···1820 pub color_scheme: ColorScheme,
1921}
20222323+/// The colors of objects and lights for an environment/map.
2424+///
2525+/// This does *not* currently support while light color overrides.
2126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2227#[cfg_attr(
2328 feature = "bevy_reflect",
···2631)]
2732#[serde(rename_all = "camelCase")]
2833pub struct ColorScheme {
3434+ /// The name of the color scheme.
2935 #[serde(rename = "colorSchemeId")]
3036 pub id: String,
3737+ /// The color for the left saber/notes. Default is red.
3138 #[doc(alias = "saber_left")]
3239 #[serde(rename = "saberAColor")]
3340 pub note_left: Color,
4141+ /// The color for the right saber/notes. Default is blue.
3442 #[doc(alias = "saber_right")]
3543 #[serde(rename = "saberBColor")]
3644 pub note_right: Color,
37454646+ /// The color of walls/obstacles.
3847 #[doc(alias = "obstacle")]
3948 #[serde(rename = "obstaclesColor")]
4049 pub wall: Color,
41505151+ /// The primary light color, often matching the [left note color](Self::note_left).
4252 #[doc(alias = "environment0")]
4353 #[serde(rename = "environmentColor0")]
4454 pub light_primary: Color,
5555+ /// The secondary light color, often matching the [right note color](Self::note_right).
4556 #[doc(alias = "environment1")]
4657 #[serde(rename = "environmentColor1")]
4758 pub light_secondary: Color,
48596060+ /// The primary light color when [boost colors](crate::ColorBoostEvent) are enabled.
4961 #[doc(alias = "environment_boost_0")]
5062 #[serde(rename = "environmentColor0Boost")]
5163 pub boost_light_primary: Color,
6464+ /// The secondary light color when [boost colors](crate::ColorBoostEvent) are enabled.
5265 #[doc(alias = "environment_boost_1")]
5366 #[serde(rename = "environmentColor1Boost")]
5467 pub boost_light_secondary: Color,
···94107 }
95108}
96109110110+/// The color of an object/light.
97111#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
98112#[cfg_attr(
99113 feature = "bevy_reflect",
+6
src/info/color_scheme/presets.rs
···11+//! Defines the color schemes provided by the base game for each environment.
22+13use crate::info::color_scheme::{Color, ColorScheme};
24use crate::info::{AllDirectionEnvironment, Environment};
35···5759}
58605961impl Environment {
6262+ /// Returns the default color scheme for an environment.
6363+ ///
6064 /// Values taken from [the wiki](https://bsmg.wiki/mapping/lighting-defaults.html#current-colors)
6165 /// and Kival Evan's [Typescript library](https://github.com/KivalEvan/BeatSaber-JSMap/blob/ef8afc42ab90e2f1100f1a163fa810ec56b6a9f8/src/beatmap/shared/colorScheme.ts).
6266 ///
···404408}
405409406410impl AllDirectionEnvironment {
411411+ /// Returns the default color scheme for a 90/360 degree environment.
412412+ ///
407413 /// Value taken from [the wiki](https://bsmg.wiki/mapping/lighting-defaults.html#current-colors).
408414 ///
409415 /// ChatGPT was used to help translate between formats, so there could be hallucinations.
+2
src/timing_traits.rs
···11+//! Traits that are used to get an object's position in time and duration.
22+13/// Represents any beatmap object that happens at a specific beat.
24pub trait Timed {
35 /// Returns the beat that an object takes place.