mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-21 01:38:55 +00:00
136 lines
4 KiB
Rust
136 lines
4 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use std::fmt;
|
|
|
|
bitflags::bitflags! {
|
|
/// The mods available to osu!
|
|
#[derive(std::default::Default, Serialize, Deserialize)]
|
|
pub struct Mods: u64 {
|
|
const NOMOD = 0;
|
|
const NF = 1 << 0;
|
|
const EZ = 1 << 1;
|
|
const TD = 1 << 2;
|
|
const HD = 1 << 3;
|
|
const HR = 1 << 4;
|
|
const SD = 1 << 5;
|
|
const DT = 1 << 6;
|
|
const RX = 1 << 7;
|
|
const HT = 1 << 8;
|
|
const NC = 1 << 9;
|
|
const FL = 1 << 10;
|
|
const AT = 1 << 11;
|
|
const SO = 1 << 12;
|
|
const AP = 1 << 13;
|
|
const PF = 1 << 14;
|
|
const KEY4 = 1 << 15; /* TODO: what are these abbreviated to? */
|
|
const KEY5 = 1 << 16;
|
|
const KEY6 = 1 << 17;
|
|
const KEY7 = 1 << 18;
|
|
const KEY8 = 1 << 19;
|
|
const FADEIN = 1 << 20;
|
|
const RANDOM = 1 << 21;
|
|
const CINEMA = 1 << 22;
|
|
const TARGET = 1 << 23;
|
|
const KEY9 = 1 << 24;
|
|
const KEYCOOP = 1 << 25;
|
|
const KEY1 = 1 << 26;
|
|
const KEY3 = 1 << 27;
|
|
const KEY2 = 1 << 28;
|
|
const SCOREV2 = 1 << 29;
|
|
const TOUCH_DEVICE = Self::TD.bits;
|
|
const NOVIDEO = Self::TD.bits; /* never forget */
|
|
const SPEED_CHANGING = Self::DT.bits | Self::HT.bits | Self::NC.bits;
|
|
const MAP_CHANGING = Self::HR.bits | Self::EZ.bits | Self::SPEED_CHANGING.bits;
|
|
}
|
|
}
|
|
|
|
const MODS_WITH_NAMES: &[(Mods, &'static str)] = &[
|
|
(Mods::NF, "NF"),
|
|
(Mods::EZ, "EZ"),
|
|
(Mods::TD, "TD"),
|
|
(Mods::HD, "HD"),
|
|
(Mods::HR, "HR"),
|
|
(Mods::SD, "SD"),
|
|
(Mods::DT, "DT"),
|
|
(Mods::RX, "RX"),
|
|
(Mods::HT, "HT"),
|
|
(Mods::NC, "NC"),
|
|
(Mods::FL, "FL"),
|
|
(Mods::AT, "AT"),
|
|
(Mods::SO, "SO"),
|
|
(Mods::AP, "AP"),
|
|
(Mods::PF, "PF"),
|
|
(Mods::KEY1, "1K"),
|
|
(Mods::KEY2, "2K"),
|
|
(Mods::KEY3, "3K"),
|
|
(Mods::KEY4, "4K"),
|
|
(Mods::KEY5, "5K"),
|
|
(Mods::KEY6, "6K"),
|
|
(Mods::KEY7, "7K"),
|
|
(Mods::KEY8, "8K"),
|
|
(Mods::KEY9, "9K"),
|
|
];
|
|
|
|
impl std::str::FromStr for Mods {
|
|
type Err = String;
|
|
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
|
|
let mut res = Self::default();
|
|
while s.len() >= 2 {
|
|
let (m, nw) = s.split_at(2);
|
|
s = nw;
|
|
match &m.to_uppercase()[..] {
|
|
"NF" => res |= Mods::NF,
|
|
"EZ" => res |= Mods::EZ,
|
|
"TD" => res |= Mods::TD,
|
|
"HD" => res |= Mods::HD,
|
|
"HR" => res |= Mods::HR,
|
|
"SD" => res |= Mods::SD,
|
|
"DT" => res |= Mods::DT,
|
|
"RX" => res |= Mods::RX,
|
|
"HT" => res |= Mods::HT,
|
|
"NC" => res |= Mods::NC,
|
|
"FL" => res |= Mods::FL,
|
|
"AT" => res |= Mods::AT,
|
|
"SO" => res |= Mods::SO,
|
|
"AP" => res |= Mods::AP,
|
|
"PF" => res |= Mods::PF,
|
|
"1K" => res |= Mods::KEY1,
|
|
"2K" => res |= Mods::KEY2,
|
|
"3K" => res |= Mods::KEY3,
|
|
"4K" => res |= Mods::KEY4,
|
|
"5K" => res |= Mods::KEY5,
|
|
"6K" => res |= Mods::KEY6,
|
|
"7K" => res |= Mods::KEY7,
|
|
"8K" => res |= Mods::KEY8,
|
|
"9K" => res |= Mods::KEY9,
|
|
v => return Err(format!("{} is not a valid mod", v)),
|
|
}
|
|
}
|
|
if s.len() > 0 {
|
|
Err("String of odd length is not a mod string".to_owned())
|
|
} else {
|
|
Ok(res)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Mods {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
if self.is_empty() {
|
|
// Return an empty string
|
|
return Ok(());
|
|
}
|
|
write!(f, "+")?;
|
|
for p in MODS_WITH_NAMES.iter() {
|
|
if self.contains(p.0) {
|
|
write!(f, "{}", p.1)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl From<Mods> for oppai_rs::Mods {
|
|
fn from(m: Mods) -> Self {
|
|
oppai_rs::Mods::from_bits_truncate(m.bits() as i32)
|
|
}
|
|
}
|