bms_rs/bms/model/
obj.rs

1//! Definitions of the note object.
2
3use std::num::NonZeroU64;
4
5use crate::{
6    bms::{
7        Decimal,
8        command::{
9            JudgeLevel, ObjId,
10            channel::Channel,
11            time::{ObjTime, Track},
12        },
13    },
14    command::channel::NoteChannelId,
15};
16
17use crate::bms::command::{graphics::Argb, minor_command::SwBgaEvent};
18
19/// An object playing sound on the score.
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct WavObj {
23    /// The time offset in the track.
24    pub offset: ObjTime,
25    /// The key, or lane, where the object is placed.
26    pub channel_id: NoteChannelId,
27    /// The `#WAVxx` id to be rung on play.
28    pub wav_id: ObjId,
29}
30
31impl PartialOrd for WavObj {
32    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
33        Some(self.cmp(other))
34    }
35}
36
37impl Ord for WavObj {
38    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
39        self.offset
40            .cmp(&other.offset)
41            .then(self.wav_id.cmp(&other.wav_id))
42    }
43}
44
45impl WavObj {
46    pub(crate) fn dangling() -> Self {
47        Self {
48            offset: ObjTime::new(
49                1,
50                0,
51                NonZeroU64::new(1).expect("1 should be a valid NonZeroU64"),
52            ),
53            channel_id: NoteChannelId::bgm(),
54            wav_id: ObjId::null(),
55        }
56    }
57}
58
59/// An object to change the BPM of the score.
60#[derive(Debug, Clone)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62pub struct BpmChangeObj {
63    /// The time to begin the change of BPM.
64    pub time: ObjTime,
65    /// The BPM to be.
66    pub bpm: Decimal,
67}
68
69impl PartialEq for BpmChangeObj {
70    fn eq(&self, other: &Self) -> bool {
71        self.time == other.time
72    }
73}
74
75impl Eq for BpmChangeObj {}
76
77impl PartialOrd for BpmChangeObj {
78    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
79        Some(self.cmp(other))
80    }
81}
82
83impl Ord for BpmChangeObj {
84    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
85        self.time.cmp(&other.time)
86    }
87}
88
89/// An object to change its section length of the score.
90#[derive(Debug, Clone)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92pub struct SectionLenChangeObj {
93    /// The target track to change.
94    pub track: Track,
95    /// The length to be.
96    pub length: Decimal,
97}
98
99impl PartialEq for SectionLenChangeObj {
100    fn eq(&self, other: &Self) -> bool {
101        self.track == other.track
102    }
103}
104
105impl Eq for SectionLenChangeObj {}
106
107impl PartialOrd for SectionLenChangeObj {
108    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
109        Some(self.cmp(other))
110    }
111}
112
113impl Ord for SectionLenChangeObj {
114    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
115        self.track.cmp(&other.track)
116    }
117}
118
119/// An object to stop scrolling of score.
120#[derive(Debug, Clone)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct StopObj {
123    /// Time to start the stop.
124    pub time: ObjTime,
125    /// Object duration how long stops scrolling of score.
126    ///
127    /// Note that the duration of stopping will not be changed by a current measure length but BPM.
128    pub duration: Decimal,
129}
130
131impl PartialEq for StopObj {
132    fn eq(&self, other: &Self) -> bool {
133        self.time == other.time
134    }
135}
136
137impl Eq for StopObj {}
138
139impl PartialOrd for StopObj {
140    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
141        Some(self.cmp(other))
142    }
143}
144
145impl Ord for StopObj {
146    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
147        self.time.cmp(&other.time)
148    }
149}
150
151/// An object to change the image for BGA (background animation).
152#[derive(Debug, Clone, Copy)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub struct BgaObj {
155    /// Time to start to display the image.
156    pub time: ObjTime,
157    /// Identifier represents the image/video file registered in [`Header`].
158    pub id: ObjId,
159    /// Layer to display.
160    pub layer: BgaLayer,
161}
162
163impl PartialEq for BgaObj {
164    fn eq(&self, other: &Self) -> bool {
165        self.time == other.time
166    }
167}
168
169impl Eq for BgaObj {}
170
171impl PartialOrd for BgaObj {
172    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
173        Some(self.cmp(other))
174    }
175}
176
177impl Ord for BgaObj {
178    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
179        self.time.cmp(&other.time)
180    }
181}
182
183/// A layer where the image for BGA to be displayed.
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
185#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186#[non_exhaustive]
187pub enum BgaLayer {
188    /// The lowest layer.
189    Base,
190    /// Layer which is displayed only if a player missed to play notes.
191    Poor,
192    /// An overlaying layer.
193    Overlay,
194    /// An overlaying layer.
195    ///
196    /// This layer is layered over [`BgaLayer::Overlay`].
197    Overlay2,
198}
199
200impl BgaLayer {
201    /// Convert a [`crate::bms::command::channel::Channel`] to a [`BgaLayer`].
202    #[must_use]
203    pub const fn from_channel(channel: Channel) -> Option<Self> {
204        match channel {
205            Channel::BgaBase => Some(Self::Base),
206
207            Channel::BgaBaseArgb | Channel::BgaBaseOpacity => Some(Self::Base),
208            Channel::BgaLayer => Some(Self::Overlay),
209
210            Channel::BgaLayerArgb | Channel::BgaLayerOpacity => Some(Self::Overlay),
211            Channel::BgaLayer2 => Some(Self::Overlay2),
212
213            Channel::BgaLayer2Argb | Channel::BgaLayer2Opacity => Some(Self::Overlay2),
214            Channel::BgaPoor => Some(Self::Poor),
215
216            Channel::BgaPoorArgb | Channel::BgaPoorOpacity => Some(Self::Poor),
217            _ => None,
218        }
219    }
220
221    /// Convert a [`BgaLayer`] to a [`crate::bms::command::channel::Channel`].
222    #[must_use]
223    pub const fn to_channel(self) -> Channel {
224        match self {
225            Self::Base => Channel::BgaBase,
226            Self::Overlay => Channel::BgaLayer,
227            Self::Overlay2 => Channel::BgaLayer2,
228            Self::Poor => Channel::BgaPoor,
229        }
230    }
231}
232
233/// An object to change scrolling factor of the score.
234#[derive(Debug, Clone)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236pub struct ScrollingFactorObj {
237    /// The time to begin the change of BPM.
238    pub time: ObjTime,
239    /// The scrolling factor to be.
240    pub factor: Decimal,
241}
242
243impl PartialEq for ScrollingFactorObj {
244    fn eq(&self, other: &Self) -> bool {
245        self.time == other.time
246    }
247}
248
249impl Eq for ScrollingFactorObj {}
250
251impl PartialOrd for ScrollingFactorObj {
252    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
253        Some(self.cmp(other))
254    }
255}
256
257impl Ord for ScrollingFactorObj {
258    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
259        self.time.cmp(&other.time)
260    }
261}
262
263/// An object to change spacing factor among notes with linear interpolation.
264#[derive(Debug, Clone)]
265#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
266pub struct SpeedObj {
267    /// The time to begin the change of BPM.
268    pub time: ObjTime,
269    /// The spacing factor to be.
270    pub factor: Decimal,
271}
272
273impl PartialEq for SpeedObj {
274    fn eq(&self, other: &Self) -> bool {
275        self.time == other.time
276    }
277}
278
279impl Eq for SpeedObj {}
280
281impl PartialOrd for SpeedObj {
282    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
283        Some(self.cmp(other))
284    }
285}
286
287impl Ord for SpeedObj {
288    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
289        self.time.cmp(&other.time)
290    }
291}
292
293/// An object to change the opacity of BGA layers.
294#[derive(Debug, Clone, Eq, PartialEq)]
295#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
296pub struct BgaOpacityObj {
297    /// The time which the opacity change is on.
298    pub time: ObjTime,
299    /// The BGA layer to change opacity for.
300    pub layer: BgaLayer,
301    /// The opacity value (0x01-0xFF, where 0x01 is transparent and 0xFF is opaque).
302    pub opacity: u8,
303}
304
305/// An object to change the ARGB color of BGA layers.
306#[derive(Debug, Clone, Eq, PartialEq)]
307#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
308pub struct BgaArgbObj {
309    /// The time which the ARGB change is on.
310    pub time: ObjTime,
311    /// The BGA layer to change ARGB color for.
312    pub layer: BgaLayer,
313    /// The ARGB color value (A,R,G,B each [0-255]).
314    pub argb: Argb,
315}
316
317/// An object to change the volume of BGM sounds.
318#[derive(Debug, Clone, Eq, PartialEq)]
319#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
320pub struct BgmVolumeObj {
321    /// The time which the volume change is on.
322    pub time: ObjTime,
323    /// The volume value (0x01-0xFF, where 0x01 is minimum and 0xFF is maximum).
324    pub volume: u8,
325}
326
327/// An object to change the volume of KEY sounds.
328#[derive(Debug, Clone, Eq, PartialEq)]
329#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
330pub struct KeyVolumeObj {
331    /// The time which the volume change is on.
332    pub time: ObjTime,
333    /// The volume value (0x01-0xFF, where 0x01 is minimum and 0xFF is maximum).
334    pub volume: u8,
335}
336
337/// An object to seek video position.
338#[derive(Debug, Clone, Eq, PartialEq)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub struct SeekObj {
341    /// The time which the seek event is on.
342    pub time: ObjTime,
343    /// The seek position value.
344    pub position: Decimal,
345}
346
347/// An object to display text.
348#[derive(Debug, Clone, Eq, PartialEq)]
349#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
350pub struct TextObj {
351    /// The time which the text is displayed.
352    pub time: ObjTime,
353    /// The text content.
354    pub text: String,
355}
356
357/// An object to change judge level.
358#[derive(Debug, Clone, Eq, PartialEq)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
360pub struct JudgeObj {
361    /// The time which the judge change is on.
362    pub time: ObjTime,
363    /// The judge level.
364    pub judge_level: JudgeLevel,
365}
366
367/// An object to change BGA keybound.
368#[derive(Debug, Clone, Eq, PartialEq)]
369#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
370pub struct BgaKeyboundObj {
371    /// The time which the BGA keybound change is on.
372    pub time: ObjTime,
373    /// The BGA keybound event.
374    pub event: SwBgaEvent,
375}
376
377/// An object to change option.
378#[derive(Debug, Clone, Eq, PartialEq)]
379#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
380pub struct OptionObj {
381    /// The time which the option change is on.
382    pub time: ObjTime,
383    /// The option content.
384    pub option: String,
385}