1use std::fmt;
2use crate::{def_varied_type_enum, wasm_bindgen};
3
4def_varied_type_enum!(pub ChartDefaults {
5 TITLE: &'static str => "Unknown Title",
6 ALT_TITLE: &'static str => "Unknown Title",
7 ARTIST: &'static str => "Unknown Artist",
8 ALT_ARTIST: &'static str => "Unknown Artist",
9 CREATOR: &'static str => "Unknown Creator",
10 GENRE: &'static str => "Unknown Genre",
11 SOURCE: &'static str => "Unknown Source",
12 TAGS: Vec<String> => Vec::<String>::new(),
13
14 BPM: &'static f32 => &0.0,
15 DIFFICULTY_NAME: &'static str => "Unknown Difficulty",
16 BG_PATH: &'static str => "Unknown Background Path",
17 SONG_PATH: &'static str => "Unknown Song File Path",
18 AUDIO_OFFSET: &'static i32 => &0,
19 PREVIEW_TIME: &'static i32 => &0,
20 OVERALL_DIFFICULTY: &'static f32 => &7.2,
21 KEY_COUNT: &'static u8 => &4,
22
23 RAW_NOTES: &'static str => "No Note Data",
24 RAW_BPMS: &'static str => "No BPM Data",
25 RAW_STOPS: &'static str => "No STOPS Data",
26 RAW_SV: &'static str => "No SV Data",
27
28 HITSOUND: [u8; 4] => [0, 0, 0, 0],
29});
30
31#[derive(Clone)]
32pub struct HitObjectRow {
33 pub time: i32,
34 pub beat: f32,
35 pub keys: Vec<Key>
36}
37
38impl HitObjectRow {
39 pub fn empty(time: i32, beat: f32, key_count: usize) -> Self {
40 HitObjectRow {
41 time,
42 beat,
43 keys: vec![Key::empty(); key_count]
44 }
45 }
46}
47
48pub type Measure = Vec<HitObjectRow>;
49
50#[wasm_bindgen]
51#[derive(Debug, Clone, Copy, PartialEq)]
52pub enum TimingChangeType {
53 Bpm,
54 Sv,
55 Stop
56}
57
58#[allow(unused)]
59#[non_exhaustive]
60#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
61pub enum GameMode {
62 Mania,
63 Taiko,
64 Catch,
65 OsuStandard
66}
67
68impl fmt::Display for GameMode {
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 match self {
71 Self::Mania => write!(f, "mania"),
72 Self::Taiko => write!(f, "taiko"),
73 Self::Catch => write!(f, "catch"),
74 Self::OsuStandard => write!(f, "osu! standard"),
75 }
76 }
77}
78
79pub trait HitObject {
80 fn game_mode(&self) -> GameMode;
81}
82
83#[wasm_bindgen]
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum KeyType {
86 Empty,
87 Normal,
88 SliderStart,
89 SliderEnd,
90 Mine,
91 Fake,
92 Unknown,
93}
94
95#[wasm_bindgen]
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct Key {
98 #[wasm_bindgen(getter_with_clone)]
99 pub key_type: KeyType,
100 #[wasm_bindgen(getter_with_clone)]
101 pub slider_end_time: Option<i32>,
102}
103
104impl HitObject for Key {
105 fn game_mode(&self) -> GameMode {
106 GameMode::Mania
107 }
108}
109
110#[wasm_bindgen]
111impl Key {
112 #[wasm_bindgen]
113 pub fn empty() -> Self {
114 Self {
115 key_type: KeyType::Empty,
116 slider_end_time: None,
117 }
118 }
119
120 #[wasm_bindgen]
121 pub fn normal() -> Self {
122 Self {
123 key_type: KeyType::Normal,
124 slider_end_time: None,
125 }
126 }
127
128 #[wasm_bindgen]
129 pub fn slider_start(value: Option<i32>) -> Self {
130 Self {
131 key_type: KeyType::SliderStart,
132 slider_end_time: value,
133 }
134 }
135
136 #[wasm_bindgen]
137 pub fn slider_end() -> Self {
138 Self {
139 key_type: KeyType::SliderEnd,
140 slider_end_time: None,
141 }
142 }
143
144 #[wasm_bindgen]
145 pub fn mine() -> Self {
146 Self {
147 key_type: KeyType::Mine,
148 slider_end_time: None,
149 }
150 }
151
152 #[wasm_bindgen]
153 pub fn fake() -> Self {
154 Self {
155 key_type: KeyType::Fake,
156 slider_end_time: None,
157 }
158 }
159
160 #[wasm_bindgen]
161 pub fn unknown() -> Self {
162 Self {
163 key_type: KeyType::Unknown,
164 slider_end_time: None,
165 }
166 }
167
168 #[wasm_bindgen]
169 pub fn slider_end_time(&self) -> Option<i32> {
170 self.slider_end_time
171 }
172}
173
174#[wasm_bindgen]
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum TaikoHitobjectType {
177 Empty,
178 Don,
179 Kat,
180 BonusDon,
181 BonusKat,
182 DrumRoll,
183 BonusDrumRoll,
184 Balloon,
185 Unknown,
186}
187
188#[wasm_bindgen]
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub struct TaikoHitobject {
191 #[wasm_bindgen(getter_with_clone)]
192 pub note_type: TaikoHitobjectType,
193 #[wasm_bindgen(getter_with_clone)]
194 pub end_time: Option<i32>,
195}
196
197impl HitObject for TaikoHitobject {
198 fn game_mode(&self) -> GameMode {
199 GameMode::Taiko
200 }
201}
202
203#[wasm_bindgen]
204impl TaikoHitobject {
205 #[wasm_bindgen]
206 pub fn empty() -> Self {
207 Self {
208 note_type: TaikoHitobjectType::Empty,
209 end_time: None,
210 }
211 }
212
213 #[wasm_bindgen]
214 pub fn don() -> Self {
215 Self {
216 note_type: TaikoHitobjectType::Don,
217 end_time: None,
218 }
219 }
220
221 #[wasm_bindgen]
222 pub fn kat() -> Self {
223 Self {
224 note_type: TaikoHitobjectType::Kat,
225 end_time: None,
226 }
227 }
228
229 #[wasm_bindgen]
230 pub fn bonus_don() -> Self {
231 Self {
232 note_type: TaikoHitobjectType::BonusDon,
233 end_time: None,
234 }
235 }
236
237 #[wasm_bindgen]
238 pub fn bonus_kat() -> Self {
239 Self {
240 note_type: TaikoHitobjectType::BonusKat,
241 end_time: None,
242 }
243 }
244
245 #[wasm_bindgen]
246 pub fn drum_roll(end_time: i32) -> Self {
247 Self {
248 note_type: TaikoHitobjectType::DrumRoll,
249 end_time: Some(end_time),
250 }
251 }
252
253 #[wasm_bindgen]
254 pub fn bonus_drum_roll(end_time: i32) -> Self {
255 Self {
256 note_type: TaikoHitobjectType::BonusDrumRoll,
257 end_time: Some(end_time),
258 }
259 }
260
261 #[wasm_bindgen]
262 pub fn balloon(end_time: i32) -> Self {
263 Self {
264 note_type: TaikoHitobjectType::Balloon,
265 end_time: Some(end_time),
266 }
267 }
268
269 #[wasm_bindgen]
270 pub fn unknown() -> Self {
271 Self {
272 note_type: TaikoHitobjectType::Unknown,
273 end_time: None,
274 }
275 }
276
277 #[wasm_bindgen]
278 pub fn end_time(&self) -> Option<i32> {
279 self.end_time
280 }
281}
282
283#[wasm_bindgen]
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum CatchHitobjectType {
286 Empty,
287 Fruit,
288 Juice,
289 Banana,
290 Hyperfruit,
291 Unknown,
292}
293
294#[wasm_bindgen]
295#[derive(Debug, Clone, Copy, PartialEq)]
296pub struct CatchHitobject {
297 #[wasm_bindgen(getter_with_clone)]
298 pub object_type: CatchHitobjectType,
299 #[wasm_bindgen(getter_with_clone)]
300 pub x_position: i32,
301 #[wasm_bindgen(getter_with_clone)]
302 pub end_time: Option<i32>,
303 #[wasm_bindgen(getter_with_clone)]
304 pub hyperdash: bool,
305}
306
307impl HitObject for CatchHitobject {
308 fn game_mode(&self) -> GameMode {
309 GameMode::Catch
310 }
311}
312
313#[wasm_bindgen]
314impl CatchHitobject {
315 #[wasm_bindgen]
316 pub fn empty() -> Self {
317 Self {
318 object_type: CatchHitobjectType::Empty,
319 x_position: 0,
320 end_time: None,
321 hyperdash: false,
322 }
323 }
324
325 #[wasm_bindgen]
326 pub fn fruit(x_position: i32) -> Self {
327 Self {
328 object_type: CatchHitobjectType::Fruit,
329 x_position,
330 end_time: None,
331 hyperdash: false,
332 }
333 }
334
335 #[wasm_bindgen]
336 pub fn juice(x_position: i32) -> Self {
337 Self {
338 object_type: CatchHitobjectType::Juice,
339 x_position,
340 end_time: None,
341 hyperdash: false,
342 }
343 }
344
345 #[wasm_bindgen]
346 pub fn banana(x_position: i32, end_time: i32) -> Self {
347 Self {
348 object_type: CatchHitobjectType::Banana,
349 x_position,
350 end_time: Some(end_time),
351 hyperdash: false,
352 }
353 }
354
355 #[wasm_bindgen]
356 pub fn hyperfruit(x_position: i32) -> Self {
357 Self {
358 object_type: CatchHitobjectType::Hyperfruit,
359 x_position,
360 end_time: None,
361 hyperdash: true,
362 }
363 }
364
365 #[wasm_bindgen]
366 pub fn unknown() -> Self {
367 Self {
368 object_type: CatchHitobjectType::Unknown,
369 x_position: 0,
370 end_time: None,
371 hyperdash: false,
372 }
373 }
374
375 #[wasm_bindgen]
376 pub fn end_time(&self) -> Option<i32> {
377 self.end_time
378 }
379
380 #[wasm_bindgen]
381 pub fn is_hyperdash(&self) -> bool {
382 self.hyperdash
383 }
384}
385
386
387#[wasm_bindgen]
389#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub enum OsuHitobjectType {
391 Empty,
392 HitCircle,
393 Slider,
394 Spinner,
395 Unknown,
396}
397
398#[wasm_bindgen]
399#[derive(Debug, Clone, Copy, PartialEq)]
400pub struct OsuHitobject {
401 #[wasm_bindgen(getter_with_clone)]
402 pub object_type: OsuHitobjectType,
403 #[wasm_bindgen(getter_with_clone)]
404 pub x: i32,
405 #[wasm_bindgen(getter_with_clone)]
406 pub y: i32,
407 #[wasm_bindgen(getter_with_clone)]
408 pub end_time: Option<i32>,
409 #[wasm_bindgen(getter_with_clone)]
410 pub new_combo: bool,
411 #[wasm_bindgen(getter_with_clone)]
412 pub combo_skip: u8,
413}
414
415impl HitObject for OsuHitobject {
416 fn game_mode(&self) -> GameMode {
417 GameMode::OsuStandard
418 }
419}
420
421#[wasm_bindgen]
422impl OsuHitobject {
423 #[wasm_bindgen]
424 pub fn empty() -> Self {
425 Self {
426 object_type: OsuHitobjectType::Empty,
427 x: 0,
428 y: 0,
429 end_time: None,
430 new_combo: false,
431 combo_skip: 0,
432 }
433 }
434
435 #[wasm_bindgen]
436 pub fn hit_circle(x: i32, y: i32) -> Self {
437 Self {
438 object_type: OsuHitobjectType::HitCircle,
439 x,
440 y,
441 end_time: None,
442 new_combo: false,
443 combo_skip: 0,
444 }
445 }
446
447 #[wasm_bindgen]
448 pub fn slider(x: i32, y: i32) -> Self {
449 Self {
450 object_type: OsuHitobjectType::Slider,
451 x,
452 y,
453 end_time: None,
454 new_combo: false,
455 combo_skip: 0,
456 }
457 }
458
459 #[wasm_bindgen]
460 pub fn spinner(end_time: i32) -> Self {
461 Self {
462 object_type: OsuHitobjectType::Spinner,
463 x: 256,
464 y: 192,
465 end_time: Some(end_time),
466 new_combo: false,
467 combo_skip: 0,
468 }
469 }
470
471 #[wasm_bindgen]
472 pub fn unknown() -> Self {
473 Self {
474 object_type: OsuHitobjectType::Unknown,
475 x: 0,
476 y: 0,
477 end_time: None,
478 new_combo: false,
479 combo_skip: 0,
480 }
481 }
482
483 #[wasm_bindgen]
484 pub fn with_new_combo(mut self) -> Self {
485 self.new_combo = true;
486 self
487 }
488
489 #[wasm_bindgen]
490 pub fn with_combo_skip(mut self, skip: u8) -> Self {
491 self.combo_skip = skip;
492 self
493 }
494
495 #[wasm_bindgen]
496 pub fn end_time(&self) -> Option<i32> {
497 self.end_time
498 }
499
500 #[wasm_bindgen]
501 pub fn is_new_combo(&self) -> bool {
502 self.new_combo
503 }
504
505 #[wasm_bindgen]
506 pub fn combo_skip(&self) -> u8 {
507 self.combo_skip
508 }
509}