Beatsaber Rust Utilities: A Beatsaber V3 parsing library.
beatsaber beatmap
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #10 from AlephCubed/document-everything

Document everything

authored by

AlephCubed and committed by
GitHub
5a0679ec 36d1af0d

+258 -54
+5
src/difficulty.rs
··· 1 + //! Defines the structure of a map's difficulty file(s) (i.e. `ExpertStandard.dat`). 2 + 1 3 pub mod gameplay_event; 2 4 pub mod lightshow; 3 5 pub mod playfield; ··· 8 10 9 11 use serde::{Deserialize, Serialize}; 10 12 13 + /// A map's difficulty file(s) (i.e. `ExpertStandard.dat`). 11 14 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 12 15 #[serde(rename_all = "camelCase")] 13 16 #[cfg_attr( ··· 28 31 /// | 3.2 | Translation events. | Yes | 29 32 /// | 3.3 | More strobe functionality. | No | 30 33 /// | 4.X | New template-like format. | No | 34 + /// 35 + /// [^1]: Not supported by experimental lighting calculation methods. 31 36 pub version: String, 32 37 pub bpm_events: Vec<BpmEvent>, 33 38 #[serde(rename = "rotationEvents")]
+11 -3
src/difficulty/gameplay_event.rs
··· 1 + //! Events that effect gameplay and aren't purely visual. 2 + 1 3 use crate::{impl_timed, loose_enum}; 2 4 use serde::{Deserialize, Serialize}; 3 5 6 + /// Controls the rotation that interactable objects spawn in 90/360 degree difficulties. 4 7 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 5 8 #[cfg_attr( 6 9 feature = "bevy_reflect", ··· 8 11 reflect(Debug, Clone, PartialEq) 9 12 )] 10 13 pub struct LaneRotationEvent { 14 + /// The time the event takes place. 11 15 #[serde(rename = "b")] 12 16 pub beat: f32, 13 17 #[serde(rename = "e")] 14 18 pub execution_time: ExecutionTime, 19 + /// The number of degrees to rotate objects around the player. 15 20 #[serde(rename = "r")] 16 21 pub degrees: f32, 17 22 } ··· 19 24 impl_timed!(LaneRotationEvent::beat); 20 25 21 26 loose_enum!( 22 - /// Determines when a [`LaneRotationEvent`] will be applied to objects placed on the same beat as this event. 27 + /// Determines when a [`LaneRotationEvent`] will be applied to objects. 23 28 #[derive(Default, Copy)] 24 29 ExecutionTime: i32 { 25 - /// The [`LaneRotationEvent`] will affect objects with a beat greater than or equal to the event's beat. 30 + /// The [`LaneRotationEvent`] will affect objects with a beat *greater than or equal to* the event's beat. 26 31 #[default] 27 32 Early = 0, 28 - /// The [`LaneRotationEvent`] will affect objects with a beat greater than the event's beat. 33 + /// The [`LaneRotationEvent`] will affect objects with a beat *greater than* the event's beat. 29 34 Late = 1, 30 35 } 31 36 ); 32 37 38 + /// Changes the BPM of the map at a specific beat. 33 39 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 34 40 #[cfg_attr( 35 41 feature = "bevy_reflect", ··· 37 43 reflect(Debug, Clone, PartialEq) 38 44 )] 39 45 pub struct BpmEvent { 46 + /// The time the event takes place. 40 47 #[serde(rename = "b")] 41 48 pub beat: f32, 49 + /// The BPM to change the map too. 42 50 #[serde(rename = "m")] 43 51 pub bpm: f32, 44 52 }
+11 -21
src/difficulty/lightshow.rs
··· 1 + //! Events that have no effect on gameplay. 2 + 1 3 pub mod basic; 2 - pub mod boxes; 3 4 pub mod easing; 4 5 pub mod filter; 6 + pub mod group; 5 7 6 8 pub use basic::*; 7 - pub use boxes::*; 8 9 pub use easing::*; 9 10 pub use filter::*; 11 + pub use group::*; 10 12 11 13 use crate::loose_enum; 12 14 13 15 loose_enum! { 14 - /// The distribution value does different things depending on the type. 15 - /// 16 - /// # [Beat Distribution](https://bsmg.wiki/mapping/map-format/lightshow.html#light-color-event-boxes-beat-distribution): 17 - /// ### Wave: 18 - /// The value represents the total time for all steps to complete. 19 - /// ### Step: 20 - /// The value represents the time until the next step is completed. 21 - /// 22 - /// # [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): 23 - /// ### Wave: 24 - /// The value represents the total difference between the first and last step. 25 - /// ### Step: 26 - /// The value represents the different between the current and next step. 16 + /// The way that the distribution value is used. 27 17 #[derive(Default, Copy)] 28 18 DistributionType: i32 { 19 + /// The distribution value represents the difference between *the last and first step*. 29 20 #[default] 30 21 Wave = 1, 22 + /// The distribution value represents the difference between *each step*. 31 23 Step = 2, 32 24 } 33 25 } ··· 74 66 } 75 67 76 68 loose_enum! { 77 - /// Controls how the value is changed from the previous event. 78 - /// - Transition: The value will blend from the previous event's value, using the 79 - /// [easing](Easing) value. 80 - /// - Extend: The event's value will be ignored, replaced with the values from the previous event. 81 - /// 82 - /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#light-rotation-events-type). 69 + /// Controls how the state is changed relative to the previous event. 83 70 #[derive(Default, Copy)] 84 71 TransitionType: i32 { 72 + /// The state will blend from the previous event's state, using the events [easing](Easing). 85 73 #[default] 86 74 Transition = 0, 75 + /// The event's state will be ignored, replaced with the state from the previous event. 87 76 Extend = 1, 88 77 } 89 78 } 90 79 91 80 loose_enum! { 81 + /// The axis that a rotation/translation event effects. 92 82 #[derive(Default, Copy)] 93 83 EventAxis: i32 { 94 84 #[default]
+23
src/difficulty/lightshow/basic.rs
··· 1 + //! The non-group events that were inherited from difficulty file V2. 2 + 1 3 use crate::difficulty::playfield::CutDirection; 2 4 use crate::impl_timed; 3 5 use serde::{Deserialize, Serialize}; 4 6 7 + /// The basic V2 event type, which is still used for some elements of V3 environments (for example, the player platform). 5 8 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 6 9 #[cfg_attr( 7 10 feature = "bevy_reflect", ··· 9 12 reflect(Debug, Clone, PartialEq) 10 13 )] 11 14 pub struct BasicEvent { 15 + /// The time the event takes place. 12 16 #[serde(rename = "b")] 13 17 pub beat: f32, 18 + /// Determines the behaviour of the event. The exact behaviour differs depending on the environment. 19 + /// 14 20 /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#basic-events-type). 15 21 #[serde(rename = "et")] 16 22 pub event_type: i32, 23 + /// Determines which effect the event will produce, based on its [type](Self::event_type). 17 24 #[serde(rename = "i")] 18 25 pub value: i32, 26 + /// Modifies the effect. 19 27 #[serde(rename = "f")] 20 28 pub float: f32, 21 29 } 22 30 23 31 impl_timed!(BasicEvent::beat); 24 32 33 + /// Controls the TinyTAN figures on the [BTS environment](crate::info::Environment::BTS). 34 + /// 35 + /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#waypoints). 25 36 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 26 37 #[cfg_attr( 27 38 feature = "bevy_reflect", ··· 29 40 reflect(Debug, Clone, PartialEq) 30 41 )] 31 42 pub struct Waypoint { 43 + /// The time the event takes place. 32 44 #[serde(rename = "b")] 33 45 pub beat: f32, 46 + /// A value representing the vertical position of the event. 47 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 34 48 #[serde(rename = "y")] 35 49 pub row: u8, 50 + /// A value representing the horizontal position of the event. 51 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 36 52 #[serde(rename = "x")] 37 53 pub col: u8, 38 54 #[serde(rename = "d")] ··· 41 57 42 58 impl_timed!(Waypoint::beat); 43 59 60 + /// Controls which lighting colors are used, based on a map or environment's [color scheme](crate::info::color_scheme::ColorScheme). 44 61 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 45 62 #[cfg_attr( 46 63 feature = "bevy_reflect", ··· 48 65 reflect(Debug, Clone, PartialEq) 49 66 )] 50 67 pub struct ColorBoostEvent { 68 + /// The time the event takes place. 51 69 #[serde(rename = "b")] 52 70 pub beat: f32, 71 + /// Whether to enable or disable boost colors. 53 72 #[serde(rename = "o")] 54 73 pub boost: bool, 55 74 } ··· 57 76 impl_timed!(ColorBoostEvent::beat); 58 77 59 78 /// An event containing an array of Special Event Keywords. 79 + /// 60 80 /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#special-event-keywords). 61 81 #[doc(alias = "KeywordEvent")] 62 82 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] ··· 71 91 } 72 92 73 93 /// Allows basic event lanes to be overridden with environment-specific behaviour, using secret keys. 94 + /// 74 95 /// More info [here](https://bsmg.wiki/mapping/map-format/lightshow.html#special-event-keywords). 75 96 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 76 97 #[cfg_attr( ··· 79 100 reflect(Debug, Clone, PartialEq) 80 101 )] 81 102 pub struct Keyword { 103 + /// The secret key of the effect. 82 104 #[serde(rename = "k")] 83 105 pub keyword: String, 106 + /// A list of [event types](BasicEvent::event_type) to effect with the keyword. 84 107 #[serde(rename = "e")] 85 108 pub event_types: Vec<i32>, 86 109 }
+10 -4
src/difficulty/lightshow/boxes.rs src/difficulty/lightshow/group.rs
··· 1 + //! The advanced group lighting system events. 2 + 1 3 pub mod color; 2 4 pub mod rotation; 3 5 pub mod translation; ··· 9 11 use crate::difficulty::lightshow::filter::Filter; 10 12 use crate::timing_traits::Timed; 11 13 14 + /// A collection of [`EventGroup`]s that share the same group ID and beat. 12 15 pub trait EventBox: Timed { 13 16 type Group: EventGroup<Data = Self::Data>; 14 17 type Data: EventData; ··· 20 23 #[doc(hidden)] 21 24 macro_rules! impl_event_box { 22 25 ($ident:ident, $group:ident, $data:ident) => { 23 - impl crate::difficulty::lightshow::boxes::EventBox for $ident { 26 + impl crate::difficulty::lightshow::group::EventBox for $ident { 24 27 type Group = $group; 25 28 type Data = $data; 26 29 ··· 31 34 }; 32 35 } 33 36 37 + /// A collection of [`EventData`] that share the same [`Filter`] and distribution. 34 38 pub trait EventGroup { 35 39 type Data: EventData; 36 40 ··· 45 49 )] 46 50 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32; 47 51 48 - /// Returns the value that the event will be offset for a given light ID (i.e. brightness offset). 52 + /// Returns the value (i.e. brightness) that the event will be offset for a given light ID. 49 53 /// # Panics 50 54 /// Will panic if the light ID is greater than or equal to the group size. 51 55 #[deprecated( ··· 58 62 #[doc(hidden)] 59 63 macro_rules! impl_event_group { 60 64 ($ident:ident::$value_offset:ident, $data:ident) => { 61 - impl crate::difficulty::lightshow::boxes::EventGroup for $ident { 65 + impl crate::difficulty::lightshow::group::EventGroup for $ident { 62 66 type Data = $data; 63 67 64 68 fn get_filter(&self) -> &Filter { ··· 89 93 }; 90 94 } 91 95 96 + /// The lowest-level group event type, which determines the base value of the event. 92 97 pub trait EventData { 98 + /// Returns the number of beats the event will be offset from the [`EventBox`]'s beat. 93 99 fn get_beat_offset(&self) -> f32; 94 100 } 95 101 ··· 97 103 #[doc(hidden)] 98 104 macro_rules! impl_event_data { 99 105 ($ident:ident) => { 100 - impl crate::difficulty::lightshow::boxes::EventData for $ident { 106 + impl crate::difficulty::lightshow::group::EventData for $ident { 101 107 fn get_beat_offset(&self) -> f32 { 102 108 self.beat_offset 103 109 }
+26 -3
src/difficulty/lightshow/boxes/color.rs src/difficulty/lightshow/group/color.rs
··· 1 + //! Events that control the color of lights. 2 + 1 3 use crate::difficulty::lightshow::DistributionType; 2 4 use crate::difficulty::lightshow::easing::Easing; 3 5 use crate::difficulty::lightshow::filter::Filter; ··· 5 7 use crate::{impl_event_box, impl_event_data, impl_event_group, impl_timed, loose_enum}; 6 8 use serde::{Deserialize, Serialize}; 7 9 10 + /// A collection of [`ColorEventGroup`]s that share the same group ID and beat. 8 11 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 9 12 #[cfg_attr( 10 13 feature = "bevy_reflect", ··· 12 15 reflect(Debug, Clone, PartialEq) 13 16 )] 14 17 pub struct ColorEventBox { 18 + /// The time the event takes place. 15 19 #[serde(rename = "b")] 16 20 pub beat: f32, 21 + /// The ID of the collection of objects that this event effects. 17 22 #[serde(rename = "g")] 18 23 pub group_id: i32, 19 24 #[serde(rename = "e")] ··· 33 38 impl_timed!(ColorEventBox::beat); 34 39 impl_event_box!(ColorEventBox, ColorEventGroup, ColorEventData); 35 40 41 + /// A collection of [`ColorEventData`] that share the same [`Filter`] and distribution. 36 42 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 37 43 #[cfg_attr( 38 44 feature = "bevy_reflect", ··· 44 50 pub filter: Filter, 45 51 #[serde(rename = "d")] 46 52 pub beat_dist_type: DistributionType, 53 + /// The strength of the beat distribution. Dependent on the [distribution type](Self::beat_dist_type). 54 + /// 55 + /// A value of zero will have no effect. 47 56 #[serde(rename = "w")] 48 57 pub beat_dist_value: f32, 58 + /// The strength of the brightness distribution. Dependent on the [distribution type](Self::bright_dist_type). 59 + /// 60 + /// A value of zero will have no effect. 49 61 #[serde(rename = "t")] 50 62 pub bright_dist_type: DistributionType, 51 63 #[serde(rename = "r")] 52 64 pub bright_dist_value: f32, 65 + /// Whether the first [`ColorEventData`] of the group will be effected by brightness distribution. 53 66 #[serde(rename = "b")] 54 67 pub bright_dist_effect_first: LooseBool, 55 68 /// > Only present in difficulty file V3.2 or higher. ··· 96 109 } 97 110 } 98 111 112 + /// The lowest-level group event type, which determines the color of the event. 99 113 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 100 114 #[cfg_attr( 101 115 feature = "bevy_reflect", ··· 103 117 reflect(Debug, Clone, PartialEq) 104 118 )] 105 119 pub struct ColorEventData { 120 + /// The number of beats the event will be offset from the [`ColorEventBox`]'s beat. 106 121 #[serde(rename = "b")] 107 122 pub beat_offset: f32, 108 123 #[serde(rename = "i")] ··· 111 126 pub color: LightColor, 112 127 #[serde(rename = "s")] 113 128 pub brightness: f32, 129 + /// Determines the number of strobes that will take place each beat. 130 + /// A value of zero will result in no strobing. 114 131 #[serde(rename = "f")] 115 132 pub strobe_frequency: i32, 116 133 } ··· 130 147 impl_event_data!(ColorEventData); 131 148 132 149 loose_enum! { 150 + /// Controls how the state is changed relative to the previous event. 133 151 #[derive(Default, Copy)] 134 152 ColorTransitionType: i32 { 135 - /// Replaced with `Transition` and [`Easing::None`] in difficulty file V3.2 or higher. 136 - Instant = 0, 153 + /// Unique to color events. 154 + /// Has the same effect as using [`TransitionType::Transition`](crate::lightshow::TransitionType::Transition) 155 + /// and [`Easing::None`] in rotation/translation events. 137 156 #[default] 157 + Instant = 0, 158 + /// The state will blend from the previous event's state, using the events [easing](Easing). 138 159 Transition = 1, 160 + /// The event's state will be ignored, replaced with the state from the previous event. 139 161 Extend = 2, 140 162 } 141 163 } 142 164 143 165 loose_enum! { 166 + /// Controls which color to display, based on a map or environment's [color scheme](crate::info::color_scheme::ColorScheme). 144 167 #[derive(Default, Copy)] 145 168 LightColor: i32 { 146 169 #[default] ··· 154 177 #[cfg(test)] 155 178 mod tests { 156 179 use super::*; 157 - use crate::difficulty::lightshow::boxes::EventGroup; 158 180 use crate::difficulty::lightshow::filter::FilterType; 181 + use crate::difficulty::lightshow::group::EventGroup; 159 182 160 183 #[test] 161 184 fn beat_wave() {
+19 -1
src/difficulty/lightshow/boxes/rotation.rs src/difficulty/lightshow/group/rotation.rs
··· 1 - use crate::difficulty::lightshow::boxes::EventData; 1 + //! Events that control the rotation of objects. 2 + 2 3 use crate::difficulty::lightshow::easing::Easing; 3 4 use crate::difficulty::lightshow::filter::Filter; 5 + use crate::difficulty::lightshow::group::EventData; 4 6 use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType}; 5 7 use crate::utils::LooseBool; 6 8 use crate::{impl_event_box, impl_event_group, impl_timed, loose_enum}; 7 9 use serde::{Deserialize, Serialize}; 8 10 11 + /// A collection of [`RotationEventGroup`]s that share the same group ID and beat. 9 12 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 10 13 #[cfg_attr( 11 14 feature = "bevy_reflect", ··· 13 16 reflect(Debug, Clone, PartialEq) 14 17 )] 15 18 pub struct RotationEventBox { 19 + /// The time the event takes place. 16 20 #[serde(rename = "b")] 17 21 pub beat: f32, 22 + /// The ID of the collection of objects that this event effects. 18 23 #[serde(rename = "g")] 19 24 pub group_id: i32, 20 25 #[serde(rename = "e")] ··· 34 39 impl_timed!(RotationEventBox::beat); 35 40 impl_event_box!(RotationEventBox, RotationEventGroup, RotationEventData); 36 41 42 + /// A collection of [`RotationEventData`] that share the same [`EventAxis`], [`Filter`], and distribution. 37 43 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 38 44 #[cfg_attr( 39 45 feature = "bevy_reflect", ··· 45 51 pub filter: Filter, 46 52 #[serde(rename = "d")] 47 53 pub beat_dist_type: DistributionType, 54 + /// The strength of the beat distribution. Dependent on the [distribution type](Self::beat_dist_type). 55 + /// 56 + /// A value of zero will have no effect. 48 57 #[serde(rename = "w")] 49 58 pub beat_dist_value: f32, 50 59 #[serde(rename = "t")] 51 60 pub rotation_dist_type: DistributionType, 61 + /// The strength of the rotation distribution. Dependent on the [distribution type](Self::rotation_dist_type). 62 + /// 63 + /// A value of zero will have no effect. 52 64 #[serde(rename = "s")] 53 65 pub rotation_dist_value: f32, 66 + /// Whether the first [`RotationEventData`] of the group will be effected by rotation distribution. 54 67 #[serde(rename = "b")] 55 68 pub rotation_dist_effect_first: LooseBool, 56 69 /// > Only present in difficulty file V3.2 or higher. ··· 58 71 pub rotation_dist_easing: Option<Easing>, 59 72 #[serde(rename = "a")] 60 73 pub axis: EventAxis, 74 + /// If true, the rotation will be mirrored. 61 75 #[serde(rename = "r")] 62 76 pub invert_axis: LooseBool, 63 77 #[serde(rename = "l")] ··· 103 117 } 104 118 } 105 119 120 + /// The lowest-level group event type, which determines the base rotation of the event. 106 121 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 107 122 #[cfg_attr( 108 123 feature = "bevy_reflect", ··· 110 125 reflect(Debug, Clone, PartialEq) 111 126 )] 112 127 pub struct RotationEventData { 128 + /// The number of beats the event will be offset from the [`RotationEventBox`]'s beat. 113 129 #[serde(rename = "b")] 114 130 pub beat_offset: f32, 115 131 #[serde(rename = "p")] 116 132 pub transition_type: TransitionType, 117 133 #[serde(rename = "e")] 118 134 pub easing: Easing, 135 + /// The base number of degrees the event will rotate objects by. 119 136 #[serde(rename = "r")] 120 137 pub degrees: f32, 121 138 #[serde(rename = "o")] 122 139 pub direction: RotationDirection, 140 + /// Extends the rotation by 360 degrees in the [`RotationDirection`]. 123 141 #[serde(rename = "l")] 124 142 pub loops: i32, 125 143 }
+18 -1
src/difficulty/lightshow/boxes/translation.rs src/difficulty/lightshow/group/translation.rs
··· 1 - use crate::difficulty::lightshow::boxes::EventData; 1 + //! Events that control the translation/position of objects. 2 + 2 3 use crate::difficulty::lightshow::easing::Easing; 3 4 use crate::difficulty::lightshow::filter::Filter; 5 + use crate::difficulty::lightshow::group::EventData; 4 6 use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType}; 5 7 use crate::utils::LooseBool; 6 8 use crate::{impl_event_box, impl_event_group, impl_timed}; 7 9 use serde::{Deserialize, Serialize}; 8 10 11 + /// A collection of [`TranslationEventGroup`]s that share the same group ID and beat. 9 12 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 10 13 #[cfg_attr( 11 14 feature = "bevy_reflect", ··· 13 16 reflect(Debug, Clone, PartialEq) 14 17 )] 15 18 pub struct TranslationEventBox { 19 + /// The time the event takes place. 16 20 #[serde(rename = "b")] 17 21 pub beat: f32, 22 + /// The ID of the collection of objects that this event effects. 18 23 #[serde(rename = "g")] 19 24 pub group_id: i32, 20 25 #[serde(rename = "e")] ··· 38 43 TranslationEventData 39 44 ); 40 45 46 + /// A collection of [`TranslationEventData`] that share the same [`EventAxis`], [`Filter`], and distribution. 41 47 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 42 48 #[cfg_attr( 43 49 feature = "bevy_reflect", ··· 49 55 pub filter: Filter, 50 56 #[serde(rename = "d")] 51 57 pub beat_dist_type: DistributionType, 58 + /// The strength of the beat distribution. Dependent on the [beat distribution type](Self::beat_dist_type). 59 + /// 60 + /// A value of zero will have no effect. 52 61 #[serde(rename = "w")] 53 62 pub beat_dist_value: f32, 54 63 #[serde(rename = "t")] 55 64 pub translation_dist_type: DistributionType, 65 + /// The strength of the translation distribution. Dependent on the [distribution type](Self::translation_dist_type). 66 + /// 67 + /// A value of zero will have no effect. 56 68 #[serde(rename = "s")] 57 69 pub translation_dist_value: f32, 70 + /// Whether the first [`TranslationEventData`] of the group will be effected by translation distribution. 58 71 #[serde(rename = "b")] 59 72 pub translation_dist_effect_first: LooseBool, 60 73 #[serde(rename = "i")] 61 74 pub translation_dist_easing: Easing, 62 75 #[serde(rename = "a")] 63 76 pub axis: EventAxis, 77 + /// If true, the translation will be mirrored. 64 78 #[serde(rename = "r")] 65 79 pub invert_axis: LooseBool, 66 80 #[serde(rename = "l")] ··· 109 123 } 110 124 } 111 125 126 + /// The lowest-level group event type, which determines the base position of the event. 112 127 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 113 128 #[cfg_attr( 114 129 feature = "bevy_reflect", ··· 116 131 reflect(Debug, Clone, PartialEq) 117 132 )] 118 133 pub struct TranslationEventData { 134 + /// The number of beats the event will be offset from the [`TranslationEventBox`]'s beat. 119 135 #[serde(rename = "b")] 120 136 pub beat_offset: f32, 121 137 #[serde(rename = "p")] 122 138 pub transition_type: TransitionType, 123 139 #[serde(rename = "e")] 124 140 pub easing: Easing, 141 + /// The base number of units the event will offset objects by. 125 142 #[serde(rename = "t")] 126 143 pub value: f32, 127 144 }
+6
src/difficulty/lightshow/easing.rs
··· 1 + //! The easing that a [transition](crate::lightshow::TransitionType::Transition) event will use. 2 + 1 3 use crate::loose_enum; 2 4 use simple_easing::*; 3 5 4 6 loose_enum! { 7 + /// The easing that a [transition](crate::lightshow::TransitionType::Transition) event will use. 5 8 #[derive(Default, Copy)] 6 9 Easing: i32 { 7 10 #[default] ··· 39 42 OutBounce = 29, 40 43 InOutBounce = 30, 41 44 45 + /// Note: For [`Easing::ease`], the result will be the same as [`Easing::InOutBack`]. 42 46 BeatSaberInOutBack = 100, 47 + /// Note: For [`Easing::ease`], the result will be the same as [`Easing::InOutElastic`]. 43 48 BeatSaberInOutElastic = 101, 49 + /// Note: For [`Easing::ease`], the result will be the same as [`Easing::InOutBounce`]. 44 50 BeatSaberInOutBounce = 102, 45 51 } 46 52 }
+16 -16
src/difficulty/lightshow/filter.rs
··· 1 + //! Controls which light IDs are affected by an event. 2 + 1 3 use crate::loose_enum; 2 4 use crate::utils::LooseBool; 3 5 use serde::{Deserialize, Serialize}; 4 6 5 - /// Controls which light indices are affected by event boxes. 7 + /// Controls which light IDs are affected by an event. 6 8 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 7 9 #[cfg_attr( 8 10 feature = "bevy_reflect", ··· 11 13 )] 12 14 pub struct Filter { 13 15 // V3.0: 16 + /// Controls how [`parameter1`](Self::parameter1) and [`parameter2`](Self::parameter2) are used. 14 17 #[serde(rename = "f")] 15 18 pub filter_type: FilterType, 16 - /// Dependent on the [`FilterType`] 19 + /// Dependent on the [`FilterType`]. 17 20 #[serde(rename = "p")] 18 21 pub parameter1: i32, 19 - /// Dependent on the [`FilterType`] 22 + /// Dependent on the [`FilterType`]. 20 23 #[serde(rename = "t")] 21 24 pub parameter2: i32, 25 + /// If true, the filter will start at the end of a group and work backwards. 22 26 #[serde(rename = "r")] 23 27 pub reverse: LooseBool, 24 28 // V3.1: ··· 152 156 } 153 157 154 158 loose_enum! { 155 - /// The parameters of a [Filter] do different things depending on the type. 156 - /// 157 - /// ### [Division](https://bsmg.wiki/mapping/map-format/lightshow.html#index-filters-type-1): 158 - /// Splits the group up into equal sections and selects one. 159 - /// - Parameter 1 determines the number of sections. 160 - /// It will be rounded up to the nearest multiple of the group size. 161 - /// - Parameter 2 determines the section to select, starting at 0. 162 - /// 163 - /// ### [Step and Offset](https://bsmg.wiki/mapping/map-format/lightshow.html#index-filters-type-2): 164 - /// Alternates selecting and not selecting lights. 165 - /// - Parameter 1 is the index of the first light that will be selected, starting at 0. 166 - /// - Parameter 2 determines the number of IDs to move forward before selecting another light. 159 + /// Controls how a [`Filter`]'s [`parameter1`](Filter::parameter1) 160 + /// and [`parameter2`](Filter::parameter2) values are used. 167 161 #[derive(Default, Copy)] 168 162 FilterType: i32 { 163 + /// Splits the group up into equal sections and selects one. 164 + /// - [`parameter1`](Filter::parameter1) determines the number of sections. 165 + /// It will be rounded up to the nearest multiple of the group size. 166 + /// - [`parameter2`](Filter::parameter2) determines the section to select, starting at 0. 169 167 #[default] 170 - //Todo Doesn't match wiki 171 168 Division = 1, 169 + /// Alternates selecting and not selecting lights. 170 + /// - [`parameter1`](Filter::parameter1) is the index of the first light that will be selected, starting at 0. 171 + /// - [`parameter2`](Filter::parameter2) determines the number of IDs to move forward before selecting another light. 172 172 StepAndOffset = 2, 173 173 } 174 174 }
+70 -2
src/difficulty/playfield.rs
··· 1 + //! The interactable objects of a difficulty. 2 + 1 3 use crate::{impl_duration, impl_timed, loose_enum}; 2 4 use serde::{Deserialize, Serialize}; 3 5 6 + /// The standard block/note that a player cuts. 4 7 #[doc(alias = "Block")] 5 8 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 6 9 #[cfg_attr( ··· 9 12 reflect(Debug, Clone, PartialEq) 10 13 )] 11 14 pub struct Note { 15 + /// The position of the object in time. 12 16 #[serde(rename = "b")] 13 17 pub beat: f32, 18 + /// A value representing the vertical position of the object. 19 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 14 20 #[serde(rename = "y")] 15 21 pub row: i32, 22 + /// A value representing the horizontal position of the object. 23 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 16 24 #[serde(rename = "x")] 17 25 pub col: i32, 26 + /// The color that determines which saber should be used to cut the note. 18 27 #[serde(rename = "c")] 19 28 pub color: NoteColor, 29 + /// The direction the note should be cut. 20 30 #[serde(rename = "d")] 21 31 pub direction: CutDirection, 32 + /// The number of degrees counter-clockwise to offset the object by. 22 33 #[serde(rename = "a")] 23 34 pub angle_offset: f32, 24 35 } ··· 26 37 impl_timed!(Note::beat); 27 38 28 39 loose_enum! { 40 + /// The color of a note, which determines which saber should be used to cut it. 29 41 #[derive(Default, Copy)] 30 42 NoteColor: i32 { 31 43 #[default] ··· 35 47 } 36 48 37 49 loose_enum! { 50 + /// The direction a note should be cut. 38 51 #[derive(Default, Copy)] 39 52 CutDirection: i32 { 40 53 #[default] ··· 46 59 UpRight = 5, 47 60 DownLeft = 6, 48 61 DownRight = 7, 62 + #[doc(alias = "Dot")] 49 63 Any = 8, 50 64 } 51 65 } 52 66 53 67 impl CutDirection { 54 - /// Returns the number of degrees a note is rotated, with zero degrees being downward note. 55 - /// Returns zero if the cut direction is unknown. 68 + /// Returns the number of degrees a note is rotated, with zero degrees being a downward note. 69 + /// 70 + /// Returns zero if the cut direction is unknown/any. 56 71 pub fn get_degrees(&self) -> f32 { 57 72 match self { 58 73 CutDirection::Up => 180.0, ··· 69 84 } 70 85 } 71 86 87 + /// The spiked bombs that players avoid hitting with their sabers. 72 88 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 73 89 #[cfg_attr( 74 90 feature = "bevy_reflect", ··· 76 92 reflect(Debug, Clone, PartialEq) 77 93 )] 78 94 pub struct Bomb { 95 + /// The position of the object in time. 79 96 #[serde(rename = "b")] 80 97 pub beat: f32, 98 + /// A value representing the vertical position of the object. 99 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 81 100 #[serde(rename = "y")] 82 101 pub row: i32, 102 + /// A value representing the horizontal position of the object. 103 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 83 104 #[serde(rename = "x")] 84 105 pub col: i32, 85 106 } 86 107 87 108 impl_timed!(Bomb::beat); 88 109 110 + /// A wall/obstacle that players avoid running into. 89 111 #[doc(alias = "Obstacle")] 90 112 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 91 113 #[cfg_attr( ··· 94 116 reflect(Debug, Clone, PartialEq) 95 117 )] 96 118 pub struct Wall { 119 + /// The start position of the object in time. 97 120 #[serde(rename = "b")] 98 121 pub beat: f32, 122 + /// The length (in beats) that an object takes place. 99 123 #[serde(rename = "d")] 100 124 pub duration: f32, 125 + /// A value representing the vertical position of the object. 126 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 101 127 #[serde(rename = "y")] 102 128 pub row: i32, 129 + /// A value representing the horizontal position of the object. 130 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 103 131 #[serde(rename = "x")] 104 132 pub col: i32, 133 + /// The number of columns that the wall will take up. 105 134 #[serde(rename = "w")] 106 135 pub width: i32, 136 + /// The number of rows that the wall will take up. 137 + /// 138 + /// A standard wall has a height of five while a crouch wall has a height of three. 107 139 #[serde(rename = "h")] 108 140 pub height: i32, 109 141 } ··· 123 155 124 156 impl_duration!(Wall::beat, duration: duration); 125 157 158 + /// A glowing line that guides the player's saber. 126 159 #[doc(alias = "Slider")] 127 160 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 128 161 #[cfg_attr( ··· 131 164 reflect(Debug, Clone, PartialEq) 132 165 )] 133 166 pub struct Arc { 167 + /// The start position of the object in time. 134 168 #[serde(rename = "b")] 135 169 pub beat: f32, 170 + /// A value representing the vertical starting position of the object. 171 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 136 172 #[serde(rename = "y")] 137 173 pub row: i32, 174 + /// A value representing the horizontal starting position of the object. 175 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 138 176 #[serde(rename = "x")] 139 177 pub col: i32, 178 + /// The color of the arc. 140 179 #[serde(rename = "c")] 141 180 pub color: NoteColor, 181 + /// The direction the arc moves in at the start. 142 182 #[serde(rename = "d")] 143 183 pub direction: CutDirection, 184 + /// Controls how far away the starting bezier control point is in [cut direction](Self::direction). 144 185 #[serde(rename = "mu")] 145 186 pub control_point: f32, 146 187 188 + /// The end position of the object in time. 147 189 #[serde(rename = "tb")] 148 190 pub tail_beat: f32, 191 + /// A value representing the vertical ending position of the object. 192 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 149 193 #[serde(rename = "ty")] 150 194 pub tail_row: i32, 195 + /// A value representing the horizontal ending position of the object. 196 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 151 197 #[serde(rename = "tx")] 152 198 pub tail_col: i32, 199 + /// The direction the arc moves in at the end. 153 200 #[serde(rename = "tc")] 154 201 pub tail_direction: CutDirection, 202 + /// Controls how far away the ending bezier control point is in [tail cut direction](Self::tail_direction). 155 203 #[serde(rename = "tmu")] 156 204 pub tail_control_point: f32, 157 205 206 + /// Controls how the arc curves from its head to its tail. 158 207 #[serde(rename = "m")] 159 208 pub mid_anchor_mode: MidAnchorMode, 160 209 } ··· 181 230 impl_duration!(Arc::beat, end: tail_beat); 182 231 183 232 loose_enum! { 233 + /// Controls how an arc curves from its head to its tail. 184 234 #[derive(Default, Copy)] 185 235 MidAnchorMode: i32 { 186 236 #[default] ··· 190 240 } 191 241 } 192 242 243 + /// A chain/burst of mini-notes. 193 244 #[doc(alias = "BurstSlider")] 194 245 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 195 246 #[cfg_attr( ··· 198 249 reflect(Debug, Clone, PartialEq) 199 250 )] 200 251 pub struct Chain { 252 + /// The start position of the object in time. 201 253 #[serde(rename = "b")] 202 254 pub beat: f32, 255 + /// A value representing the vertical starting position of the object. 256 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 203 257 #[serde(rename = "y")] 204 258 pub row: i32, 259 + /// A value representing the horizontal starting position of the object. 260 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 205 261 #[serde(rename = "x")] 206 262 pub col: i32, 263 + /// The color that determines which saber should be used to cut the chain links. 207 264 #[serde(rename = "c")] 208 265 pub color: NoteColor, 266 + /// The direction the start of the chain should be cut. 209 267 #[serde(rename = "d")] 210 268 pub direction: CutDirection, 211 269 270 + /// The end position of the object in time. 212 271 #[serde(rename = "tb")] 213 272 pub tail_beat: f32, 273 + /// A value representing the vertical ending position of the object. 274 + /// In the range 0..2 inclusive, with zero being the bottom and two being the top row. 214 275 #[serde(rename = "ty")] 215 276 pub tail_row: i32, 277 + /// A value representing the horizontal ending position of the object. 278 + /// In the range 0..3 inclusive, with zero being the far left and three being the far right column. 216 279 #[serde(rename = "tx")] 217 280 pub tail_col: i32, 218 281 282 + /// The number of links the chain has, including the connected [`Note`]. 219 283 #[serde(rename = "sc")] 220 284 pub link_count: i32, 285 + /// The percent of the path (from head to tail) that will be used, usually in the range 0..1 inclusive. 286 + /// Smaller values will result in the chain links bunching up near the head. 287 + /// 288 + /// Setting this to zero will crash the game. 221 289 #[serde(rename = "s")] 222 290 pub link_squish: f32, 223 291 }
+21 -3
src/info.rs
··· 1 + //! Defines the structure of a map's `Info.dat` file. 2 + 1 3 pub mod color_scheme; 2 4 3 5 pub use color_scheme::*; ··· 5 7 use crate::loose_enum; 6 8 use serde::{Deserialize, Serialize}; 7 9 10 + /// A map's `Info.dat` file. 8 11 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 12 #[cfg_attr( 10 13 feature = "bevy_reflect", ··· 14 17 pub struct Beatmap { 15 18 /// The info file version, in the form of `2.1.0`. 16 19 /// 17 - /// ### Info File 20 + /// ### Version Support 18 21 /// 19 22 /// | Version | Description | Supported | 20 23 /// |---------|-----------------------------------------------|-----------| ··· 49 52 /// The path to the cover image file, relative to the map's folder. 50 53 #[serde(rename = "_coverImageFilename")] 51 54 pub cover_image_file: String, 55 + /// The environment that will be used for 90 and 360 degree difficulties. 56 + /// 57 + /// Starting in info file V2.1, individual difficulties can override this using [environment_index](DifficultyInfo::environment_index). 52 58 #[serde(rename = "_environmentName")] 53 59 pub environment: Environment, 54 60 /// The environment that will be used for 90 and 360 degree difficulties. 55 - /// 56 - /// Starting in info file V2.1, Individual difficulties can override this using [environment_index](DifficultyInfo::environment_index). 57 61 #[serde(rename = "_allDirectionsEnvironmentName")] 58 62 pub all_directions_environment: AllDirectionEnvironment, 59 63 /// > Only present in info file V2.1 or higher. ··· 67 71 } 68 72 69 73 loose_enum! { 74 + /// The world that surrounds the player and defines which lights are available. 75 + /// 76 + /// For 90/360 degree mode, see [`AllDirectionEnvironment`]. 70 77 #[derive(Default)] 71 78 Environment: String { 72 79 #[default] ··· 121 128 } 122 129 123 130 loose_enum! { 131 + /// The world that surrounds the player while playing 90/360 degree mode. 132 + /// 133 + /// For standard mode, see [`Environment`]. 124 134 #[derive(Default)] 125 135 AllDirectionEnvironment: String { 126 136 #[default] ··· 128 138 } 129 139 } 130 140 141 + /// Describes a group of difficulties, all with the same [characteristic/mode](Characteristic). 131 142 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 132 143 #[cfg_attr( 133 144 feature = "bevy_reflect", ··· 142 153 } 143 154 144 155 loose_enum! { 156 + /// Describes the type/game mode of a difficulty. 157 + /// 158 + /// Note that [`Lawless`](Self::Lawless) and [`Lightshow`](Self::Lightshow) are modded characteristics, 159 + /// and may cause problems in un-modded versions of the game. 145 160 #[derive(Default)] 146 161 Characteristic: String { 147 162 #[default] ··· 158 173 } 159 174 } 160 175 176 + /// Describes the settings for a difficulty. 177 + /// 178 + /// Note that a difficulties [characteristic](Characteristic) is defined by its [`DifficultySet`]. 161 179 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 162 180 #[cfg_attr( 163 181 feature = "bevy_reflect",
+14
src/info/color_scheme.rs
··· 1 + //! Describes the colors of objects and lights for an environment/map. 2 + 1 3 pub mod presets; 2 4 3 5 #[allow(unused_imports)] ··· 18 20 pub color_scheme: ColorScheme, 19 21 } 20 22 23 + /// The colors of objects and lights for an environment/map. 24 + /// 25 + /// This does *not* currently support while light color overrides. 21 26 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 22 27 #[cfg_attr( 23 28 feature = "bevy_reflect", ··· 26 31 )] 27 32 #[serde(rename_all = "camelCase")] 28 33 pub struct ColorScheme { 34 + /// The name of the color scheme. 29 35 #[serde(rename = "colorSchemeId")] 30 36 pub id: String, 37 + /// The color for the left saber/notes. Default is red. 31 38 #[doc(alias = "saber_left")] 32 39 #[serde(rename = "saberAColor")] 33 40 pub note_left: Color, 41 + /// The color for the right saber/notes. Default is blue. 34 42 #[doc(alias = "saber_right")] 35 43 #[serde(rename = "saberBColor")] 36 44 pub note_right: Color, 37 45 46 + /// The color of walls/obstacles. 38 47 #[doc(alias = "obstacle")] 39 48 #[serde(rename = "obstaclesColor")] 40 49 pub wall: Color, 41 50 51 + /// The primary light color, often matching the [left note color](Self::note_left). 42 52 #[doc(alias = "environment0")] 43 53 #[serde(rename = "environmentColor0")] 44 54 pub light_primary: Color, 55 + /// The secondary light color, often matching the [right note color](Self::note_right). 45 56 #[doc(alias = "environment1")] 46 57 #[serde(rename = "environmentColor1")] 47 58 pub light_secondary: Color, 48 59 60 + /// The primary light color when [boost colors](crate::ColorBoostEvent) are enabled. 49 61 #[doc(alias = "environment_boost_0")] 50 62 #[serde(rename = "environmentColor0Boost")] 51 63 pub boost_light_primary: Color, 64 + /// The secondary light color when [boost colors](crate::ColorBoostEvent) are enabled. 52 65 #[doc(alias = "environment_boost_1")] 53 66 #[serde(rename = "environmentColor1Boost")] 54 67 pub boost_light_secondary: Color, ··· 94 107 } 95 108 } 96 109 110 + /// The color of an object/light. 97 111 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] 98 112 #[cfg_attr( 99 113 feature = "bevy_reflect",
+6
src/info/color_scheme/presets.rs
··· 1 + //! Defines the color schemes provided by the base game for each environment. 2 + 1 3 use crate::info::color_scheme::{Color, ColorScheme}; 2 4 use crate::info::{AllDirectionEnvironment, Environment}; 3 5 ··· 57 59 } 58 60 59 61 impl Environment { 62 + /// Returns the default color scheme for an environment. 63 + /// 60 64 /// Values taken from [the wiki](https://bsmg.wiki/mapping/lighting-defaults.html#current-colors) 61 65 /// and Kival Evan's [Typescript library](https://github.com/KivalEvan/BeatSaber-JSMap/blob/ef8afc42ab90e2f1100f1a163fa810ec56b6a9f8/src/beatmap/shared/colorScheme.ts). 62 66 /// ··· 404 408 } 405 409 406 410 impl AllDirectionEnvironment { 411 + /// Returns the default color scheme for a 90/360 degree environment. 412 + /// 407 413 /// Value taken from [the wiki](https://bsmg.wiki/mapping/lighting-defaults.html#current-colors). 408 414 /// 409 415 /// ChatGPT was used to help translate between formats, so there could be hallucinations.
+2
src/timing_traits.rs
··· 1 + //! Traits that are used to get an object's position in time and duration. 2 + 1 3 /// Represents any beatmap object that happens at a specific beat. 2 4 pub trait Timed { 3 5 /// Returns the beat that an object takes place.