rosu_map/section/
difficulty.rs1use crate::{
2 decode::{DecodeBeatmap, DecodeState},
3 util::{KeyValue, ParseNumber, ParseNumberError, StrExt},
4 Beatmap,
5};
6
7#[derive(Clone, Debug, PartialEq)]
9pub struct Difficulty {
10 pub hp_drain_rate: f32,
11 pub circle_size: f32,
12 pub overall_difficulty: f32,
13 pub approach_rate: f32,
14 pub slider_multiplier: f64,
15 pub slider_tick_rate: f64,
16}
17
18impl Default for Difficulty {
19 fn default() -> Self {
20 Self {
21 hp_drain_rate: 5.0,
22 circle_size: 5.0,
23 overall_difficulty: 5.0,
24 approach_rate: 5.0,
25 slider_multiplier: 1.4,
26 slider_tick_rate: 1.0,
27 }
28 }
29}
30
31impl From<Difficulty> for Beatmap {
32 fn from(difficulty: Difficulty) -> Self {
33 Self {
34 hp_drain_rate: difficulty.hp_drain_rate,
35 circle_size: difficulty.circle_size,
36 overall_difficulty: difficulty.overall_difficulty,
37 approach_rate: difficulty.approach_rate,
38 slider_multiplier: difficulty.slider_multiplier,
39 slider_tick_rate: difficulty.slider_tick_rate,
40 ..Self::default()
41 }
42 }
43}
44
45section_keys! {
46 pub enum DifficultyKey {
48 HPDrainRate,
49 CircleSize,
50 OverallDifficulty,
51 ApproachRate,
52 SliderMultiplier,
53 SliderTickRate,
54 }
55}
56
57thiserror! {
58 #[derive(Debug)]
60 pub enum ParseDifficultyError {
61 #[error("failed to parse number")]
62 Number(#[from] ParseNumberError),
63 }
64}
65
66pub struct DifficultyState {
68 pub has_approach_rate: bool,
69 pub difficulty: Difficulty,
70}
71
72impl DecodeState for DifficultyState {
73 fn create(_: i32) -> Self {
74 Self {
75 has_approach_rate: false,
76 difficulty: Difficulty::default(),
77 }
78 }
79}
80
81impl From<DifficultyState> for Difficulty {
82 fn from(state: DifficultyState) -> Self {
83 state.difficulty
84 }
85}
86
87impl DecodeBeatmap for Difficulty {
88 type Error = ParseDifficultyError;
89 type State = DifficultyState;
90
91 fn parse_general(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
92 Ok(())
93 }
94
95 fn parse_editor(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
96 Ok(())
97 }
98
99 fn parse_metadata(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
100 Ok(())
101 }
102
103 fn parse_difficulty(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
104 let Ok(KeyValue { key, value }) = KeyValue::parse(line.trim_comment()) else {
105 return Ok(());
106 };
107
108 match key {
109 DifficultyKey::HPDrainRate => state.difficulty.hp_drain_rate = value.parse_num()?,
110 DifficultyKey::CircleSize => state.difficulty.circle_size = value.parse_num()?,
111 DifficultyKey::OverallDifficulty => {
112 state.difficulty.overall_difficulty = value.parse_num()?;
113
114 if !state.has_approach_rate {
115 state.difficulty.approach_rate = state.difficulty.overall_difficulty;
116 }
117 }
118 DifficultyKey::ApproachRate => {
119 state.difficulty.approach_rate = value.parse_num()?;
120 state.has_approach_rate = true;
121 }
122 DifficultyKey::SliderMultiplier => {
123 state.difficulty.slider_multiplier = f64::parse(value)?.clamp(0.4, 3.6);
124 }
125 DifficultyKey::SliderTickRate => {
126 state.difficulty.slider_tick_rate = f64::parse(value)?.clamp(0.5, 8.0);
127 }
128 }
129
130 Ok(())
131 }
132
133 fn parse_events(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
134 Ok(())
135 }
136
137 fn parse_timing_points(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
138 Ok(())
139 }
140
141 fn parse_colors(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
142 Ok(())
143 }
144
145 fn parse_hit_objects(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
146 Ok(())
147 }
148
149 fn parse_variables(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
150 Ok(())
151 }
152
153 fn parse_catch_the_beat(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
154 Ok(())
155 }
156
157 fn parse_mania(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
158 Ok(())
159 }
160}