rosu_map/section/hit_objects/
mod.rs1use std::{
2 num::ParseIntError,
3 ops::{BitAnd, BitAndAssign},
4 str::FromStr,
5};
6
7use self::hit_samples::HitSampleInfo;
8pub use self::{
9 circle::HitObjectCircle,
10 decode::{HitObjects, HitObjectsState, ParseHitObjectsError},
11 hold::HitObjectHold,
12 slider::{
13 curve::{BorrowedCurve, Curve, CurveBuffers},
14 event::{SliderEvent, SliderEventType, SliderEventsIter},
15 path::{PathControlPoint, SliderPath},
16 path_type::{PathType, SplineType},
17 HitObjectSlider,
18 },
19 spinner::HitObjectSpinner,
20};
21
22mod circle;
23pub(crate) mod decode; mod hold;
25mod slider;
26mod spinner;
27
28pub mod hit_samples;
30
31pub(crate) const BASE_SCORING_DIST: f32 = 100.0;
32
33#[derive(Clone, Debug, PartialEq)]
37pub struct HitObject {
38 pub start_time: f64,
39 pub kind: HitObjectKind,
40 pub samples: Vec<HitSampleInfo>,
41}
42
43impl HitObject {
44 pub const fn new_combo(&self) -> bool {
46 self.kind.new_combo()
47 }
48
49 pub fn end_time(&mut self) -> f64 {
59 self.end_time_with_bufs(&mut CurveBuffers::default())
60 }
61
62 pub fn end_time_with_bufs(&mut self, bufs: &mut CurveBuffers) -> f64 {
67 match self.kind {
68 HitObjectKind::Circle(_) => self.start_time,
69 HitObjectKind::Slider(ref mut h) => self.start_time + h.duration_with_bufs(bufs),
70 HitObjectKind::Spinner(ref h) => self.start_time + h.duration,
71 HitObjectKind::Hold(ref h) => self.start_time + h.duration,
72 }
73 }
74}
75
76#[derive(Clone, Debug, PartialEq)]
78pub enum HitObjectKind {
79 Circle(HitObjectCircle),
80 Slider(HitObjectSlider),
81 Spinner(HitObjectSpinner),
82 Hold(HitObjectHold),
83}
84
85impl HitObjectKind {
86 pub const fn new_combo(&self) -> bool {
88 match self {
89 Self::Circle(h) => h.new_combo,
90 Self::Slider(h) => h.new_combo,
91 Self::Spinner(h) => h.new_combo,
92 Self::Hold(_) => false,
93 }
94 }
95}
96
97#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
99pub struct HitObjectType(i32);
100
101impl HitObjectType {
102 pub const CIRCLE: i32 = 1;
103 pub const SLIDER: i32 = 1 << 1;
104 pub const NEW_COMBO: i32 = 1 << 2;
105 pub const SPINNER: i32 = 1 << 3;
106 pub const COMBO_OFFSET: i32 = (1 << 4) | (1 << 5) | (1 << 6);
107 pub const HOLD: i32 = 1 << 7;
108
109 pub const fn has_flag(self, flag: i32) -> bool {
111 (self.0 & flag) != 0
112 }
113}
114
115impl From<&HitObject> for HitObjectType {
116 fn from(hit_object: &HitObject) -> Self {
117 let mut kind = 0;
118
119 match hit_object.kind {
120 HitObjectKind::Circle(ref h) => {
121 kind |= h.combo_offset << 4;
122
123 if h.new_combo {
124 kind |= Self::NEW_COMBO;
125 }
126
127 kind |= Self::CIRCLE;
128 }
129 HitObjectKind::Slider(ref h) => {
130 kind |= h.combo_offset << 4;
131
132 if h.new_combo {
133 kind |= Self::NEW_COMBO;
134 }
135
136 kind |= Self::SLIDER;
137 }
138 HitObjectKind::Spinner(ref h) => {
139 if h.new_combo {
140 kind |= Self::NEW_COMBO;
141 }
142
143 kind |= Self::SPINNER;
144 }
145 HitObjectKind::Hold(_) => kind |= Self::HOLD,
146 }
147
148 Self(kind)
149 }
150}
151
152impl FromStr for HitObjectType {
153 type Err = ParseHitObjectTypeError;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 s.parse().map(Self).map_err(ParseHitObjectTypeError)
157 }
158}
159
160thiserror! {
161 #[error("invalid hit object type")]
162 #[derive(Clone, Debug, PartialEq, Eq)]
164 pub struct ParseHitObjectTypeError(ParseIntError);
165}
166
167impl From<HitObjectType> for i32 {
168 fn from(kind: HitObjectType) -> Self {
169 kind.0
170 }
171}
172
173impl BitAnd<i32> for HitObjectType {
174 type Output = i32;
175
176 fn bitand(self, rhs: i32) -> Self::Output {
177 self.0 & rhs
178 }
179}
180
181impl BitAndAssign<i32> for HitObjectType {
182 fn bitand_assign(&mut self, rhs: i32) {
183 self.0 &= rhs;
184 }
185}