rosu_pp/any/
score_state.rs

1use rosu_map::section::general::GameMode;
2
3use crate::{
4    catch::CatchScoreState, mania::ManiaScoreState, osu::OsuScoreState, taiko::TaikoScoreState,
5};
6
7/// Aggregation for a score's current state.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct ScoreState {
10    /// Maximum combo that the score has had so far. **Not** the maximum
11    /// possible combo of the map so far.
12    ///
13    /// Note that for osu!catch only fruits and droplets are considered for
14    /// combo.
15    ///
16    /// Irrelevant for osu!mania.
17    pub max_combo: u32,
18    /// "Large tick" hits for osu!standard.
19    ///
20    /// The meaning depends on the kind of score:
21    /// - if set on osu!stable, this field is irrelevant and can be `0`
22    /// - if set on osu!lazer *with* slider accuracy, this field is the amount
23    ///   of hit slider ticks and repeats
24    /// - if set on osu!lazer *without* slider accuracy, this field is the
25    ///   amount of hit slider heads, ticks, and repeats
26    ///
27    /// Only relevant for osu!lazer.
28    pub osu_large_tick_hits: u32,
29    /// "Small ticks" hits for osu!standard.
30    ///
31    /// These are essentially the slider end hits for lazer scores without
32    /// slider accuracy.
33    ///
34    /// Only relevant for osu!lazer.
35    pub osu_small_tick_hits: u32,
36    /// Amount of successfully hit slider ends.
37    ///
38    /// Only relevant for osu!standard in lazer.
39    pub slider_end_hits: u32,
40    /// Amount of current gekis (n320 for osu!mania).
41    pub n_geki: u32,
42    /// Amount of current katus (tiny droplet misses for osu!catch / n200 for
43    /// osu!mania).
44    pub n_katu: u32,
45    /// Amount of current 300s (fruits for osu!catch).
46    pub n300: u32,
47    /// Amount of current 100s (droplets for osu!catch).
48    pub n100: u32,
49    /// Amount of current 50s (tiny droplets for osu!catch).
50    pub n50: u32,
51    /// Amount of current misses (fruits + droplets for osu!catch).
52    pub misses: u32,
53}
54
55impl ScoreState {
56    /// Create a new empty score state.
57    pub const fn new() -> Self {
58        Self {
59            max_combo: 0,
60            osu_large_tick_hits: 0,
61            osu_small_tick_hits: 0,
62            slider_end_hits: 0,
63            n_geki: 0,
64            n_katu: 0,
65            n300: 0,
66            n100: 0,
67            n50: 0,
68            misses: 0,
69        }
70    }
71
72    /// Return the total amount of hits by adding everything up based on the
73    /// mode.
74    pub fn total_hits(&self, mode: GameMode) -> u32 {
75        let mut amount = self.n300 + self.n100 + self.misses;
76
77        if mode != GameMode::Taiko {
78            amount += self.n50;
79
80            if mode != GameMode::Osu {
81                amount += self.n_katu;
82                amount += u32::from(mode != GameMode::Catch) * self.n_geki;
83            }
84        }
85
86        amount
87    }
88}
89
90impl From<ScoreState> for OsuScoreState {
91    fn from(state: ScoreState) -> Self {
92        Self {
93            max_combo: state.max_combo,
94            large_tick_hits: state.osu_large_tick_hits,
95            small_tick_hits: state.osu_small_tick_hits,
96            slider_end_hits: state.slider_end_hits,
97            n300: state.n300,
98            n100: state.n100,
99            n50: state.n50,
100            misses: state.misses,
101        }
102    }
103}
104
105impl From<ScoreState> for TaikoScoreState {
106    fn from(state: ScoreState) -> Self {
107        Self {
108            max_combo: state.max_combo,
109            n300: state.n300,
110            n100: state.n100,
111            misses: state.misses,
112        }
113    }
114}
115
116impl From<ScoreState> for CatchScoreState {
117    fn from(state: ScoreState) -> Self {
118        Self {
119            max_combo: state.max_combo,
120            fruits: state.n300,
121            droplets: state.n100,
122            tiny_droplets: state.n50,
123            tiny_droplet_misses: state.n_katu,
124            misses: state.misses,
125        }
126    }
127}
128
129impl From<ScoreState> for ManiaScoreState {
130    fn from(state: ScoreState) -> Self {
131        Self {
132            n320: state.n_geki,
133            n300: state.n300,
134            n200: state.n_katu,
135            n100: state.n100,
136            n50: state.n50,
137            misses: state.misses,
138        }
139    }
140}
141
142impl From<OsuScoreState> for ScoreState {
143    fn from(state: OsuScoreState) -> Self {
144        Self {
145            max_combo: state.max_combo,
146            osu_large_tick_hits: state.large_tick_hits,
147            osu_small_tick_hits: state.small_tick_hits,
148            slider_end_hits: state.slider_end_hits,
149            n_geki: 0,
150            n_katu: 0,
151            n300: state.n300,
152            n100: state.n100,
153            n50: state.n50,
154            misses: state.misses,
155        }
156    }
157}
158
159impl From<TaikoScoreState> for ScoreState {
160    fn from(state: TaikoScoreState) -> Self {
161        Self {
162            max_combo: state.max_combo,
163            osu_large_tick_hits: 0,
164            osu_small_tick_hits: 0,
165            slider_end_hits: 0,
166            n_geki: 0,
167            n_katu: 0,
168            n300: state.n300,
169            n100: state.n100,
170            n50: 0,
171            misses: state.misses,
172        }
173    }
174}
175
176impl From<CatchScoreState> for ScoreState {
177    fn from(state: CatchScoreState) -> Self {
178        Self {
179            max_combo: state.max_combo,
180            osu_large_tick_hits: 0,
181            osu_small_tick_hits: 0,
182            slider_end_hits: 0,
183            n_geki: 0,
184            n_katu: state.tiny_droplet_misses,
185            n300: state.fruits,
186            n100: state.droplets,
187            n50: state.tiny_droplets,
188            misses: state.misses,
189        }
190    }
191}
192
193impl From<ManiaScoreState> for ScoreState {
194    fn from(state: ManiaScoreState) -> Self {
195        Self {
196            max_combo: 0,
197            osu_large_tick_hits: 0,
198            osu_small_tick_hits: 0,
199            slider_end_hits: 0,
200            n_geki: state.n320,
201            n_katu: state.n200,
202            n300: state.n300,
203            n100: state.n100,
204            n50: state.n50,
205            misses: state.misses,
206        }
207    }
208}
209
210impl Default for ScoreState {
211    fn default() -> Self {
212        Self::new()
213    }
214}