map_parser/maps/
qua.rs

1use crate::enums::GameMode;
2use bitflags::bitflags;
3use serde::{Deserialize, Serialize};
4
5fn constant_1() -> f32 {
6    1.0
7}
8
9#[derive(Serialize, Deserialize, Default, Clone, Debug)]
10#[serde(rename_all = "PascalCase")]
11#[serde(default)]
12pub struct QuaverMap {
13    pub audio_file: String,
14    pub song_preview_time: i32,
15    pub background_file: String,
16    pub banner_file: String,
17    pub map_id: i32,
18    pub map_set_id: i32,
19    pub mode: GameMode,
20    pub title: String,
21    pub artist: String,
22    pub source: String,
23    pub tags: String,
24    pub creator: String,
25    pub difficulty_name: String,
26    pub description: String,
27    pub genre: String,
28    pub bpm_does_not_affect_scroll_velocity: bool,
29    #[serde(default = "constant_1")]
30    pub initial_scroll_velocity: f32,
31    pub has_scratch_key: bool,
32    pub editor_layers: Vec<EditorLayerInfo>,
33    pub custom_audio_samples: Vec<CustomAudioSampleInfo>,
34    pub sound_effects: Vec<SoundEffectInfo>,
35    pub timing_points: Vec<TimingPointInfo>,
36    pub slider_velocities: Vec<SliderVelocityInfo>,
37    pub hit_objects: Vec<HitObjectInfo>,
38    pub file_path: String,
39}
40
41impl QuaverMap {
42    pub fn from_path(path: &str) -> Self {
43        let file = std::fs::File::open(path).unwrap();
44        serde_yaml::from_reader(file).unwrap()
45    }
46
47    pub fn from_string(input: &str) -> Self {
48        serde_yaml::from_str(input).unwrap()
49    }
50
51    pub fn sort(&mut self) {
52        self.hit_objects.sort_by_key(|x| x.start_time);
53        self.timing_points.sort_by_key(|x| x.start_time as i32);
54        self.slider_velocities.sort_by_key(|x| x.start_time as i32);
55        self.sound_effects.sort_by_key(|x| x.start_time as i32);
56    }
57
58    pub fn to_file(&self) -> Result<(), serde_yaml::Error> {
59        let w = std::fs::OpenOptions::new()
60            .write(true)
61            .create(true)
62            .open(format!("{}.QuaverMap", self.title))
63            .expect("unable to open file");
64
65        serde_yaml::to_writer(w, self)
66    }
67
68    pub fn get_actions_per_second(&self, rate: Option<f32>) -> f32 {
69        let rate = rate.unwrap_or(1.);
70        let mut actions: Vec<i32> = Vec::new();
71
72        for &info in self.hit_objects.iter() {
73            actions.push(info.start_time);
74
75            if info.end_time > 0 {
76                actions.push(info.end_time);
77            }
78        }
79
80        if actions.is_empty() {
81            return 0.;
82        }
83
84        actions.sort();
85
86        let mut length = actions.last().unwrap() - actions.first().unwrap();
87
88        for i in 0..actions.len() {
89            if i == 0 {
90                continue;
91            }
92
93            let action = actions[i];
94            let previous_action = actions[i - 1];
95            let difference = action - previous_action;
96
97            if difference >= 1000 {
98                length -= difference;
99            }
100        }
101
102        actions.len() as f32 / (length as f32 / (1000. * rate))
103    }
104}
105
106#[derive(Serialize, Deserialize, Default, Clone, Debug)]
107#[serde(rename_all = "PascalCase")]
108#[serde(default)]
109pub struct EditorLayerInfo {
110    pub name: String,
111    pub hidden: bool,
112    pub color_rgb: String,
113}
114
115#[derive(Serialize, Deserialize, Clone, Debug)]
116#[serde(rename_all = "PascalCase")]
117pub struct CustomAudioSampleInfo {
118    pub path: String,
119    pub unaffected_by_rate: bool,
120}
121
122#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug)]
123#[serde(rename_all = "PascalCase")]
124#[serde(default)]
125pub struct SoundEffectInfo {
126    pub start_time: f32,
127    pub sample: i32,
128    pub volume: i32,
129}
130
131#[derive(Serialize, Deserialize, Default, Clone, Debug)]
132#[serde(rename_all = "PascalCase")]
133#[serde(default)]
134pub struct TimingPointInfo {
135    pub start_time: f32,
136    pub bpm: f32,
137    // pub signature: TimeSignature,
138    pub hidden: bool,
139}
140
141#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy)]
142#[serde(rename_all = "PascalCase")]
143#[serde(default)]
144pub struct SliderVelocityInfo {
145    pub start_time: f32,
146    pub multiplier: f32,
147}
148
149#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)]
150#[serde(rename_all = "PascalCase")]
151#[serde(default)]
152pub struct HitObjectInfo {
153    pub start_time: i32,
154    pub lane: i32,
155    pub end_time: i32,
156    // pub hit_sound: HitSounds,
157}
158
159bitflags! {
160    #[derive(Serialize, Deserialize, Default)]
161    #[serde(rename_all = "PascalCase")]
162    #[serde(default)]
163    pub struct HitSounds: i32 {
164        const NORMAL = 1 << 0;
165        const WHISTLE = 1 << 1;
166        const FINISH = 1 << 2;
167        const CLAP = 1 << 3;
168    }
169}