···6060 /// Applies the relevant easing function.
6161 ///
6262 /// The Beatsaber specific easing use the standard equivalent instead.
6363- /// If the easing is [`None`](Self::None) or [`Unknown`](Self::Unknown), then the result will be zero.
6363+ /// If the easing is [`None`](Self::None) or [`Undefined`](Self::Undefined), then the result will be zero.
6464 pub fn ease(&self, num: f32) -> f32 {
6565 match self {
6666 Easing::None => 0.0,
···100100 Easing::BeatSaberInOutBack => back_in_out(num),
101101 Easing::BeatSaberInOutElastic => elastic_in_out(num),
102102 Easing::BeatSaberInOutBounce => bounce_in_out(num),
103103- Easing::Unknown(_) => 0.0,
103103+ Easing::Undefined(_) => 0.0,
104104 }
105105 }
106106}
+14-13
src/difficulty/lightshow/filter.rs
···11//! Controls which light IDs are affected by an event.
2233-use loose_enum::{LooseBool, loose_enum};
33+use crate::loose_bool::LooseBool;
44+use loose_enum::loose_enum;
45use serde::{Deserialize, Serialize};
5667/// Controls which light IDs are affected by an event.
···2324 pub parameter2: i32,
2425 /// If true, the filter will start at the end of a group and work backwards.
2526 #[serde(rename = "r")]
2626- pub reverse: LooseBool<i32>,
2727+ pub reverse: LooseBool,
2728 // V3.1:
2829 /// > Only present in difficulty file V3.1 or higher.
2930 ///
···71727273impl Filter {
7374 /// Returns true if the light ID is in the filter.
7474- /// # Unknown
7575- /// If the [`FilterType`] is `Unknown` then the result will be `true`.
7575+ /// # Undefined
7676+ /// If the [`FilterType`] is `Undefined` then the result will be `true`.
7677 /// # Panics
7778 /// Will panic if the light ID is greater than or equal to the group size.
7879 #[must_use]
···110111 let offset_light_id = light_id - self.parameter1;
111112 offset_light_id % self.parameter2.max(1) == 0 && offset_light_id >= 0
112113 }
113113- FilterType::Unknown(_) => true,
114114+ FilterType::Undefined(_) => true,
114115 }
115116 }
116117···119120 /// This is required for distribution calculations.
120121 ///
121122 /// Also see [`count_filtered`](Self::count_filtered).
122122- /// # Unknown
123123- /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`.
123123+ /// # Undefined
124124+ /// If the [`FilterType`] is `Undefined` then the result will be the same as `group_size`.
124125 #[must_use]
125126 #[inline]
126127 #[deprecated(note = "Experimental. Does not consider random in calculations.")]
···141142 FilterType::StepAndOffset => {
142143 group_size / self.parameter2.max(1) - self.parameter1 / self.parameter2.max(1)
143144 }
144144- FilterType::Unknown(_) => group_size,
145145+ FilterType::Undefined(_) => group_size,
145146 }
146147 }
147148···149150 /// Returns the number of light chunks effected by the filter.
150151 ///
151152 /// Also see [`count_filtered_without_limit`](Self::count_filtered_without_limit).
152152- /// # Unknown
153153- /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`.
153153+ /// # Undefined
154154+ /// If the [`FilterType`] is `Undefined` then the result will be the same as `group_size`.
154155 #[must_use]
155156 #[inline]
156157 #[deprecated(note = "Experimental. Does not consider random in calculations.")]
···167168168169 #[allow(deprecated)]
169170 /// Returns the light chunk ID relative to the [filtered count](Self::count_filtered).
170170- /// # Unknown
171171- /// If the [`FilterType`] is `Unknown` then the result will be the same as `light_id`.
171171+ /// # Undefined
172172+ /// If the [`FilterType`] is `Undefined` then the result will be the same as `light_id`.
172173 /// # Panics
173174 /// Will panic if the light ID is greater than or equal to the group size.
174175 // Todo what is the behaviour when the light ID is not in the filter?
···199200 let offset_light_id = light_id - self.parameter1;
200201 offset_light_id / self.parameter2.max(1)
201202 }
202202- FilterType::Unknown(_) => group_size,
203203+ FilterType::Undefined(_) => group_size,
203204 }
204205 }
205206}
+4-3
src/difficulty/lightshow/group/color.rs
···33use crate::difficulty::lightshow::DistributionType;
44use crate::difficulty::lightshow::easing::Easing;
55use crate::difficulty::lightshow::filter::Filter;
66+use crate::loose_bool::LooseBool;
67use crate::{impl_event_box, impl_event_data, impl_event_group, impl_timed};
77-use loose_enum::{LooseBool, loose_enum};
88+use loose_enum::loose_enum;
89use serde::{Deserialize, Serialize};
9101011/// A collection of [`ColorEventGroup`]s that share the same group ID and beat.
···6566 pub bright_dist_value: f32,
6667 /// Whether the first [`ColorEventData`] of the group will be effected by brightness distribution.
6768 #[serde(rename = "b")]
6868- pub bright_dist_effect_first: LooseBool<i32>,
6969+ pub bright_dist_effect_first: LooseBool,
6970 /// > Only present in difficulty file V3.2 or higher.
7071 #[serde(rename = "i")]
7172 pub bright_dist_easing: Option<Easing>,
···140141 ///
141142 /// Whether to fade between strobe states or not.
142143 #[serde(rename = "sf")]
143143- pub strobe_fade: Option<LooseBool<i32>>,
144144+ pub strobe_fade: Option<LooseBool>,
144145}
145146146147impl Default for ColorEventData {
+4-4
src/difficulty/lightshow/group/fx.rs
···77use crate::difficulty::lightshow::DistributionType;
88use crate::difficulty::lightshow::easing::Easing;
99use crate::difficulty::lightshow::filter::Filter;
1010+use crate::loose_bool::LooseBool;
1011use crate::{TransitionType, impl_event_box, impl_event_data, impl_event_group, impl_timed};
1112use indexmap::IndexSet;
1212-use loose_enum::LooseBool;
1313use ordered_float::OrderedFloat;
1414use serde::ser::SerializeStruct;
1515use serde::{Deserialize, Deserializer, Serialize, Serializer};
···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<i32>,
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.
···258258 #[serde(rename = "s")]
259259 fx_dist_value: f32,
260260 #[serde(rename = "b")]
261261- fx_dist_effect_first: LooseBool<i32>,
261261+ fx_dist_effect_first: LooseBool,
262262 #[serde(rename = "i")]
263263 fx_dist_easing: Option<Easing>,
264264 #[serde(rename = "l")]
···367367#[cfg(test)]
368368mod tests {
369369 use super::*;
370370- use loose_enum::LooseBool;
370370+ use crate::loose_bool::LooseBool;
371371 use serde_json::{Value, json};
372372373373 fn get_test_container() -> FxEventContainer {
+4-3
src/difficulty/lightshow/group/rotation.rs
···44use crate::difficulty::lightshow::filter::Filter;
55use crate::difficulty::lightshow::group::EventData;
66use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType};
77+use crate::loose_bool::LooseBool;
78use crate::{impl_event_box, impl_event_group, impl_timed};
88-use loose_enum::{LooseBool, loose_enum};
99+use loose_enum::loose_enum;
910use serde::{Deserialize, Serialize};
10111112/// A collection of [`RotationEventGroup`]s that share the same group ID and beat.
···6566 pub rotation_dist_value: f32,
6667 /// Whether the first [`RotationEventData`] of the group will be effected by rotation distribution.
6768 #[serde(rename = "b")]
6868- pub rotation_dist_effect_first: LooseBool<i32>,
6969+ pub rotation_dist_effect_first: LooseBool,
6970 /// > Only present in difficulty file V3.2 or higher.
7071 #[serde(rename = "i")]
7172 pub rotation_dist_easing: Option<Easing>,
···7374 pub axis: EventAxis,
7475 /// If true, the rotation will be mirrored.
7576 #[serde(rename = "r")]
7676- pub invert_axis: LooseBool<i32>,
7777+ pub invert_axis: LooseBool,
7778 #[serde(rename = "l")]
7879 pub data: Vec<RotationEventData>,
7980}
+3-3
src/difficulty/lightshow/group/translation.rs
···44use crate::difficulty::lightshow::filter::Filter;
55use crate::difficulty::lightshow::group::EventData;
66use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType};
77+use crate::loose_bool::LooseBool;
78use crate::{impl_event_box, impl_event_group, impl_timed};
88-use loose_enum::LooseBool;
99use serde::{Deserialize, Serialize};
10101111/// A collection of [`TranslationEventGroup`]s that share the same group ID and beat.
···6969 pub translation_dist_value: f32,
7070 /// Whether the first [`TranslationEventData`] of the group will be effected by translation distribution.
7171 #[serde(rename = "b")]
7272- pub translation_dist_effect_first: LooseBool<i32>,
7272+ pub translation_dist_effect_first: LooseBool,
7373 #[serde(rename = "i")]
7474 pub translation_dist_easing: Easing,
7575 #[serde(rename = "a")]
7676 pub axis: EventAxis,
7777 /// If true, the translation will be mirrored.
7878 #[serde(rename = "r")]
7979- pub invert_axis: LooseBool<i32>,
7979+ pub invert_axis: LooseBool,
8080 #[serde(rename = "l")]
8181 pub data: Vec<TranslationEventData>,
8282}
+2-2
src/difficulty/playfield.rs
···7878impl CutDirection {
7979 /// Returns the number of degrees a note is rotated, with zero degrees being a downward note.
8080 ///
8181- /// Returns zero if the cut direction is unknown/any.
8181+ /// Returns zero if the cut direction is undefined/any.
8282 pub fn get_degrees(&self) -> f32 {
8383 match self {
8484 CutDirection::Up => 180.0,
···9090 CutDirection::DownLeft => -45.0,
9191 CutDirection::DownRight => 45.0,
9292 CutDirection::Any => 0.0,
9393- CutDirection::Unknown(_) => 0.0,
9393+ CutDirection::Undefined(_) => 0.0,
9494 }
9595 }
9696}
+2-2
src/info/color_scheme/presets.rs
···6969 /// White light colors are not currently supported, and are therefor ignored.
7070 pub fn get_color_scheme(&self) -> ColorScheme {
7171 match self {
7272- Environment::Unknown(_)
7272+ Environment::Undefined(_)
7373 | Environment::TheFirst
7474 | Environment::Triangle
7575 | Environment::Nice
···423423 rgb!(0.32222217, 0.6111111, 0.75),
424424 rgb!(0.03844783, 0.62239975, 0.90566039)
425425 ),
426426- AllDirectionEnvironment::Unknown(_) => ColorScheme::default(),
426426+ AllDirectionEnvironment::Undefined(_) => ColorScheme::default(),
427427 }
428428 }
429429}
+1
src/lib.rs
···2233pub mod difficulty;
44pub mod info;
55+mod loose_bool;
56pub mod timing_traits;
6778#[doc(hidden)]
+92
src/loose_bool.rs
···11+//! Taken from [this example](https://github.com/AlephCubed/loose_enum/blob/main/examples/loose_bool.rs).
22+33+use core::error::Error;
44+use core::fmt::{Display, Formatter};
55+use loose_enum::loose_enum;
66+77+loose_enum! {
88+ /// An integer repr bool, with 0 being false and 1 being true. Any other value will be saved as Undefined.
99+ #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)]
1010+ #[cfg_attr(
1111+ feature = "bevy_reflect",
1212+ derive(bevy_reflect::Reflect),
1313+ reflect(Debug, Clone, PartialEq)
1414+ )]
1515+ pub enum LooseBool: i32 {
1616+ /// A falsy value of zero.
1717+ #[default]
1818+ False = 0,
1919+ /// A truthy value of one.
2020+ True = 1,
2121+ }
2222+}
2323+2424+impl LooseBool {
2525+ /// Returns true if the value is [`True`](Self::True).
2626+ pub fn is_true(&self) -> bool {
2727+ matches!(self, Self::True)
2828+ }
2929+3030+ /// Returns true if the value is [`False`](Self::False).
3131+ pub fn is_false(&self) -> bool {
3232+ matches!(self, Self::False)
3333+ }
3434+}
3535+3636+impl From<bool> for LooseBool {
3737+ fn from(value: bool) -> Self {
3838+ match value {
3939+ true => Self::True,
4040+ false => Self::False,
4141+ }
4242+ }
4343+}
4444+4545+impl TryFrom<LooseBool> for bool {
4646+ type Error = UndefinedBoolError;
4747+4848+ fn try_from(value: LooseBool) -> Result<Self, Self::Error> {
4949+ match value {
5050+ LooseBool::False => Ok(false),
5151+ LooseBool::True => Ok(true),
5252+ LooseBool::Undefined(_) => Err(UndefinedBoolError),
5353+ }
5454+ }
5555+}
5656+5757+/// Error returned when attempting to convert a [`LooseBool::Undefined`] into a `bool`.
5858+#[derive(Debug, Eq, PartialEq, Hash)]
5959+pub struct UndefinedBoolError;
6060+6161+impl Display for UndefinedBoolError {
6262+ fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
6363+ write!(f, "Cannot convert `LooseBool::Undefined` into `bool`.")
6464+ }
6565+}
6666+6767+impl Error for UndefinedBoolError {}
6868+6969+#[cfg(test)]
7070+mod tests {
7171+ use super::*;
7272+7373+ #[test]
7474+ fn loose_to_bool() {
7575+ assert_eq!(bool::try_from(LooseBool::True), Ok(true));
7676+ assert_eq!(bool::try_from(LooseBool::False), Ok(false));
7777+7878+ for i in 2..256 {
7979+ assert_eq!(
8080+ bool::try_from(LooseBool::Undefined(i)),
8181+ Err(UndefinedBoolError),
8282+ "Failed for i={i}"
8383+ );
8484+ }
8585+ }
8686+8787+ #[test]
8888+ fn bool_to_loose() {
8989+ assert_eq!(LooseBool::from(false), LooseBool::False);
9090+ assert_eq!(LooseBool::from(true), LooseBool::True);
9191+ }
9292+}