diff --git a/Cargo.lock b/Cargo.lock index 0420ed5..8efe5f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,6 +3087,7 @@ dependencies = [ "osuparse", "rand", "regex", + "reqwest", "rosu-pp", "rosu-v2", "serde", diff --git a/youmubot-osu/src/discord/display.rs b/youmubot-osu/src/discord/display.rs index 7aa0f6d..ef9491c 100644 --- a/youmubot-osu/src/discord/display.rs +++ b/youmubot-osu/src/discord/display.rs @@ -273,12 +273,7 @@ mod scores { let pw = pp.iter().map(|v| v.len()).max().unwrap_or(2); /*mods width*/ - let mw = plays - .iter() - .map(|v| v.mods.to_string().len()) - .max() - .unwrap() - .max(4); + let mw = plays.iter().map(|v| v.mods.str_len()).max().unwrap().max(4); /*beatmap names*/ let bw = beatmaps.iter().map(|v| v.len()).max().unwrap().max(7); /* ranks width */ @@ -311,16 +306,15 @@ mod scores { // Each row for (id, (play, beatmap)) in plays.iter().zip(beatmaps.iter()).enumerate() { m.push_line(format!( - "{:>3} | {:>pw$} | {:>8} | {:>rw$} | {:mw$} | {:bw$}", + "{:>3} | {:>pw$} | {:>8} | {:>rw$} | {} | {:bw$}", id + start + 1, pp[id], format!("{:.2}%", play.accuracy(self.mode)), ranks[id], - play.mods.to_string(), + play.mods.to_string_padded(mw), beatmap, rw = rw, pw = pw, - mw = mw, bw = bw )); } diff --git a/youmubot-osu/src/discord/server_rank.rs b/youmubot-osu/src/discord/server_rank.rs index 3b76066..c0c000d 100644 --- a/youmubot-osu/src/discord/server_rank.rs +++ b/youmubot-osu/src/discord/server_rank.rs @@ -461,7 +461,7 @@ async fn show_leaderboard( /*mods width*/ let mdw = scores .iter() - .map(|(_, _, v)| v.mods.to_string().len()) + .map(|(_, _, v)| v.mods.str_len()) .max() .unwrap() .max(4); @@ -514,17 +514,16 @@ async fn show_leaderboard( )); for (id, (_, member, p)) in scores.iter().enumerate() { content.push_line_safe(format!( - "{:>4} | {:>pw$} | {:>mdw$} | {:>rw$} | {:>aw$} | {:>cw$} | {:>mw$} | {:uw$}", + "{:>4} | {:>pw$} | {} | {:>rw$} | {:>aw$} | {:>cw$} | {:>mw$} | {:uw$}", format!("#{}", 1 + id + start), pp[id], - p.mods.to_string(), + p.mods.to_string_padded(mdw), ranks[id], accuracies[id], combos[id], misses[id], member, pw = pw, - mdw = mdw, rw = rw, aw = aw, cw = cw, diff --git a/youmubot-osu/src/models/mods.rs b/youmubot-osu/src/models/mods.rs index ffa01bb..2a100f5 100644 --- a/youmubot-osu/src/models/mods.rs +++ b/youmubot-osu/src/models/mods.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; use std::fmt; +const LAZER_TEXT: &'static str = "⚡"; + bitflags::bitflags! { /// The mods available to osu! #[derive(std::default::Default, Serialize, Deserialize)] @@ -42,7 +44,7 @@ bitflags::bitflags! { const MAP_CHANGING = Self::HR.bits | Self::EZ.bits | Self::SPEED_CHANGING.bits; // Made up flags - const CLASSIC = 1 << 59; + const LAZER = 1 << 59; const UNKNOWN = 1 << 60; } } @@ -72,10 +74,24 @@ const MODS_WITH_NAMES: &[(Mods, &str)] = &[ (Mods::KEY7, "7K"), (Mods::KEY8, "8K"), (Mods::KEY9, "9K"), - (Mods::CLASSIC, "CL"), (Mods::UNKNOWN, "??"), ]; +impl Mods { + // Return the string length of the string representation of the mods. + pub fn str_len(&self) -> usize { + let s = format!("{}", self); + s.len() + if s.contains(LAZER_TEXT) { 1 } else { 0 } + } + + // Format the mods into a string with padded size. + pub fn to_string_padded(&self, size: usize) -> String { + let s = format!("{}", self); + let real_padded = size - if s.contains(LAZER_TEXT) { 1 } else { 0 }; + format!("{:>mw$}", s, mw = real_padded) + } +} + impl std::str::FromStr for Mods { type Err = String; fn from_str(mut s: &str) -> Result { @@ -112,7 +128,6 @@ impl std::str::FromStr for Mods { "7K" => res |= Mods::KEY7, "8K" => res |= Mods::KEY8, "9K" => res |= Mods::KEY9, - "CL" => res |= Mods::CLASSIC, "??" => res |= Mods::UNKNOWN, v => return Err(format!("{} is not a valid mod", v)), } @@ -127,19 +142,22 @@ impl std::str::FromStr for Mods { impl fmt::Display for Mods { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.is_empty() { - // Return an empty string - return Ok(()); + if !(*self & (Mods::all() ^ Mods::LAZER)).is_empty() { + write!(f, "+")?; + for p in MODS_WITH_NAMES.iter() { + if !self.contains(p.0) { + continue; + } + match p.0 { + Mods::DT if self.contains(Mods::NC) => continue, + Mods::SD if self.contains(Mods::PF) => continue, + _ => (), + }; + write!(f, "{}", p.1)?; + } } - write!(f, "+")?; - for p in MODS_WITH_NAMES.iter() { - if !self.contains(p.0) { - continue; - } - if p.0 == Mods::DT && self.contains(Mods::NC) { - continue; - } - write!(f, "{}", p.1)?; + if self.contains(Mods::LAZER) { + write!(f, "{}", LAZER_TEXT)?; } Ok(()) } diff --git a/youmubot-osu/src/models/rosu.rs b/youmubot-osu/src/models/rosu.rs index c42dc73..002a719 100644 --- a/youmubot-osu/src/models/rosu.rs +++ b/youmubot-osu/src/models/rosu.rs @@ -302,12 +302,20 @@ impl From for rosu::mods::GameModsIntermode { res.insert(*m2); } } + if !value.contains(Mods::LAZER) { + res.insert(GameModIntermode::Classic); + } res } } impl From for Mods { fn from(value: rosu_v2::prelude::GameModsIntermode) -> Self { + let init = if value.contains(GameModIntermode::Classic) { + Mods::NOMOD + } else { + Mods::LAZER + }; value .into_iter() .map(|m| match m { @@ -335,10 +343,10 @@ impl From for Mods { GameModIntermode::SevenKeys => Mods::KEY7, GameModIntermode::EightKeys => Mods::KEY8, GameModIntermode::NineKeys => Mods::KEY9, - GameModIntermode::Classic => Mods::CLASSIC, + GameModIntermode::Classic => Mods::NOMOD, _ => Mods::UNKNOWN, }) - .fold(Mods::NOMOD, |a, b| a | b) + .fold(init, |a, b| a | b) // Mods::from_bits_truncate(value.bits() as u64) }