mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-05-24 09:10:49 +00:00
Enable pp calculation for all modes
This commit is contained in:
parent
a04fcca1d6
commit
17e59e7135
7 changed files with 325 additions and 167 deletions
|
@ -184,8 +184,7 @@ mod scores {
|
|||
let beatmap = osu.get_beatmap(play.beatmap_id, mode).await?;
|
||||
let info = {
|
||||
let b = beatmap_cache.get_beatmap(beatmap.beatmap_id).await?;
|
||||
(if mode == Mode::Std { Some(mode) } else { None })
|
||||
.and_then(|_| b.get_info_with(play.mods).ok())
|
||||
b.get_info_with(mode, play.mods).ok()
|
||||
};
|
||||
Ok((beatmap, info)) as Result<(Beatmap, Option<BeatmapInfo>)>
|
||||
})
|
||||
|
@ -199,23 +198,22 @@ mod scores {
|
|||
Some(v) => Ok(v),
|
||||
None => {
|
||||
let b = beatmap_cache.get_beatmap(p.beatmap_id).await?;
|
||||
let r: Result<_> =
|
||||
Ok((if mode == Mode::Std { Some(mode) } else { None })
|
||||
.and_then(|_| {
|
||||
b.get_pp_from(
|
||||
Some(p.max_combo as usize),
|
||||
Accuracy::ByCount(
|
||||
p.count_300,
|
||||
p.count_100,
|
||||
p.count_50,
|
||||
p.count_miss,
|
||||
),
|
||||
p.mods,
|
||||
)
|
||||
.ok()
|
||||
.map(|pp| format!("{:.2}pp [?]", pp))
|
||||
})
|
||||
.unwrap_or_else(|| "-".to_owned()));
|
||||
let r: Result<_> = Ok({
|
||||
b.get_pp_from(
|
||||
mode,
|
||||
Some(p.max_combo as usize),
|
||||
Accuracy::ByCount(
|
||||
p.count_300,
|
||||
p.count_100,
|
||||
p.count_50,
|
||||
p.count_miss,
|
||||
),
|
||||
p.mods,
|
||||
)
|
||||
.ok()
|
||||
.map(|pp| format!("{:.2}pp [?]", pp))
|
||||
}
|
||||
.unwrap_or_else(|| "-".to_owned()));
|
||||
r
|
||||
}
|
||||
}
|
||||
|
@ -389,24 +387,20 @@ mod beatmapset {
|
|||
|
||||
struct Paginate {
|
||||
maps: Vec<Beatmap>,
|
||||
infos: Vec<Option<Option<BeatmapInfoWithPP>>>,
|
||||
infos: Vec<Option<BeatmapInfoWithPP>>,
|
||||
mode: Option<Mode>,
|
||||
mods: Mods,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl Paginate {
|
||||
async fn get_beatmap_info(&self, ctx: &Context, b: &Beatmap) -> Option<BeatmapInfoWithPP> {
|
||||
async fn get_beatmap_info(&self, ctx: &Context, b: &Beatmap) -> Result<BeatmapInfoWithPP> {
|
||||
let data = ctx.data.read().await;
|
||||
let cache = data.get::<BeatmapCache>().unwrap();
|
||||
cache
|
||||
.get_beatmap(b.beatmap_id)
|
||||
.map(move |v| {
|
||||
v.ok()
|
||||
.filter(|_| b.mode == Mode::Std)
|
||||
.and_then(move |v| v.get_possible_pp_with(self.mods).ok())
|
||||
})
|
||||
.await
|
||||
.and_then(move |v| v.get_possible_pp_with(self.mode.unwrap_or(b.mode), self.mods))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,7 +434,7 @@ mod beatmapset {
|
|||
let info = match &self.infos[page] {
|
||||
Some(info) => *info,
|
||||
None => {
|
||||
let info = self.get_beatmap_info(ctx, map).await;
|
||||
let info = self.get_beatmap_info(ctx, map).await?;
|
||||
self.infos[page] = Some(info);
|
||||
info
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ pub fn beatmap_offline_embed(
|
|||
) -> Result<Box<dyn FnOnce(&mut CreateEmbed) -> &mut CreateEmbed + Send + Sync>> {
|
||||
let bm = b.content.clone();
|
||||
let metadata = b.metadata.clone();
|
||||
let (info, pp) = b.get_possible_pp_with(mods)?;
|
||||
let (info, pp) = b.get_possible_pp_with(m, mods)?;
|
||||
|
||||
let total_length = if bm.hit_objects.len() >= 1 {
|
||||
Duration::from_millis(
|
||||
|
@ -90,7 +90,7 @@ pub fn beatmap_offline_embed(
|
|||
drain_length: total_length, // It's hard to calculate so maybe just skip...
|
||||
total_length,
|
||||
}
|
||||
.apply_mods(mods, Some(info.stars));
|
||||
.apply_mods(mods, info.stars);
|
||||
Ok(Box::new(move |c: &mut CreateEmbed| {
|
||||
c.title(beatmap_title(
|
||||
&metadata.artist,
|
||||
|
@ -145,12 +145,10 @@ pub fn beatmap_embed<'a>(
|
|||
b: &'_ Beatmap,
|
||||
m: Mode,
|
||||
mods: Mods,
|
||||
info: Option<BeatmapInfoWithPP>,
|
||||
info: BeatmapInfoWithPP,
|
||||
c: &'a mut CreateEmbed,
|
||||
) -> &'a mut CreateEmbed {
|
||||
let diff = b
|
||||
.difficulty
|
||||
.apply_mods(mods, info.map(|(v, _)| v.stars as f64));
|
||||
let diff = b.difficulty.apply_mods(mods, info.0.stars);
|
||||
c.title(beatmap_title(&b.artist, &b.title, &b.difficulty_name, mods))
|
||||
.author(|a| {
|
||||
a.name(&b.creator)
|
||||
|
@ -160,24 +158,19 @@ pub fn beatmap_embed<'a>(
|
|||
.url(b.link())
|
||||
.image(b.cover_url())
|
||||
.color(0xffb6c1)
|
||||
.fields(info.map(|(_, pp)| {
|
||||
(
|
||||
.fields({
|
||||
let pp = info.1;
|
||||
std::iter::once((
|
||||
"Calculated pp",
|
||||
format!(
|
||||
"95%: **{:.2}**pp, 98%: **{:.2}**pp, 99%: **{:.2}**pp, 100%: **{:.2}**pp",
|
||||
pp[0], pp[1], pp[2], pp[3]
|
||||
),
|
||||
false,
|
||||
)
|
||||
}))
|
||||
))
|
||||
})
|
||||
.field("Information", diff.format_info(m, mods, b), false)
|
||||
.description(beatmap_description(b))
|
||||
.footer(|f| {
|
||||
if info.is_none() && mods != Mods::NOMOD {
|
||||
f.text("Star difficulty not reflecting mods applied.");
|
||||
}
|
||||
f
|
||||
})
|
||||
}
|
||||
|
||||
const MAX_DIFFS: usize = 25 - 4;
|
||||
|
@ -283,11 +276,7 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
|||
let content = self.content;
|
||||
let u = self.u;
|
||||
let accuracy = s.accuracy(mode);
|
||||
let info = if mode == Mode::Std {
|
||||
content.get_info_with(s.mods).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let info = content.get_info_with(mode, s.mods).ok();
|
||||
let stars = info
|
||||
.as_ref()
|
||||
.map(|info| info.stars)
|
||||
|
@ -312,34 +301,25 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
|||
),
|
||||
};
|
||||
let pp = s.pp.map(|pp| (pp, format!("{:.2}pp", pp))).or_else(|| {
|
||||
(if mode == Mode::Std { Some(mode) } else { None })
|
||||
.and_then(|_| {
|
||||
content
|
||||
.get_pp_from(
|
||||
Some(s.max_combo as usize),
|
||||
Accuracy::ByCount(s.count_300, s.count_100, s.count_50, s.count_miss),
|
||||
s.mods,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
content
|
||||
.get_pp_from(
|
||||
mode,
|
||||
Some(s.max_combo as usize),
|
||||
Accuracy::ByCount(s.count_300, s.count_100, s.count_50, s.count_miss),
|
||||
s.mods,
|
||||
)
|
||||
.ok()
|
||||
.map(|pp| (pp as f64, format!("{:.2}pp [?]", pp)))
|
||||
});
|
||||
let pp = if !s.perfect {
|
||||
(if mode == Mode::Std { Some(mode) } else { None })
|
||||
.and_then(|_| {
|
||||
content
|
||||
.get_pp_from(
|
||||
None,
|
||||
Accuracy::ByCount(
|
||||
s.count_300 + s.count_miss,
|
||||
s.count_100,
|
||||
s.count_50,
|
||||
0,
|
||||
),
|
||||
s.mods,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
content
|
||||
.get_pp_from(
|
||||
mode,
|
||||
None,
|
||||
Accuracy::ByCount(s.count_300 + s.count_miss, s.count_100, s.count_50, 0),
|
||||
s.mods,
|
||||
)
|
||||
.ok()
|
||||
.filter(|&v| {
|
||||
pp.as_ref()
|
||||
.map(|&(origin, _)| origin < v as f64)
|
||||
|
@ -382,7 +362,7 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
|||
.world_record
|
||||
.map(|v| format!("| #{} on Global Rankings!", v))
|
||||
.unwrap_or_else(|| "".to_owned());
|
||||
let diff = b.difficulty.apply_mods(s.mods, Some(stars));
|
||||
let diff = b.difficulty.apply_mods(s.mods, stars);
|
||||
let creator = if b.difficulty_name.contains("'s") {
|
||||
"".to_owned()
|
||||
} else {
|
||||
|
@ -442,7 +422,7 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
|||
|
||||
pub(crate) fn user_embed(
|
||||
u: User,
|
||||
best: Option<(Score, BeatmapWithMode, Option<BeatmapInfo>)>,
|
||||
best: Option<(Score, BeatmapWithMode, BeatmapInfo)>,
|
||||
m: &mut CreateEmbed,
|
||||
) -> &mut CreateEmbed {
|
||||
m.title(u.username)
|
||||
|
@ -521,7 +501,7 @@ pub(crate) fn user_embed(
|
|||
.push(format!(
|
||||
"> {}",
|
||||
map.difficulty
|
||||
.apply_mods(v.mods, info.map(|i| i.stars as f64))
|
||||
.apply_mods(v.mods, info.stars as f64)
|
||||
.format_info(mode, v.mods, &map)
|
||||
.replace("\n", "\n> ")
|
||||
))
|
||||
|
|
|
@ -163,7 +163,7 @@ pub fn hook<'a>(
|
|||
}
|
||||
|
||||
enum EmbedType {
|
||||
Beatmap(Beatmap, Option<BeatmapInfoWithPP>, Mods),
|
||||
Beatmap(Beatmap, BeatmapInfoWithPP, Mods),
|
||||
Beatmapset(Vec<Beatmap>),
|
||||
}
|
||||
|
||||
|
@ -217,13 +217,12 @@ fn handle_old_links<'a>(
|
|||
.map(|v| Mods::from_str(v.as_str()).pls_ok())
|
||||
.flatten()
|
||||
.unwrap_or(Mods::NOMOD);
|
||||
let info = match mode.unwrap_or(b.mode) {
|
||||
Mode::Std => cache
|
||||
let info = {
|
||||
let mode = mode.unwrap_or(b.mode);
|
||||
cache
|
||||
.get_beatmap(b.beatmap_id)
|
||||
.await
|
||||
.and_then(|b| b.get_possible_pp_with(mods))
|
||||
.pls_ok(),
|
||||
_ => None,
|
||||
.and_then(|b| b.get_possible_pp_with(mode, mods))?
|
||||
};
|
||||
Some(ToPrint {
|
||||
embed: EmbedType::Beatmap(b, info, mods),
|
||||
|
@ -287,13 +286,12 @@ fn handle_new_links<'a>(
|
|||
.name("mods")
|
||||
.and_then(|v| Mods::from_str(v.as_str()).pls_ok())
|
||||
.unwrap_or(Mods::NOMOD);
|
||||
let info = match mode.unwrap_or(beatmap.mode) {
|
||||
Mode::Std => cache
|
||||
let info = {
|
||||
let mode = mode.unwrap_or(beatmap.mode);
|
||||
cache
|
||||
.get_beatmap(beatmap.beatmap_id)
|
||||
.await
|
||||
.and_then(|b| b.get_possible_pp_with(mods))
|
||||
.pls_ok(),
|
||||
_ => None,
|
||||
.and_then(|b| b.get_possible_pp_with(mode, mods))?
|
||||
};
|
||||
Some(ToPrint {
|
||||
embed: EmbedType::Beatmap(beatmap, info, mods),
|
||||
|
@ -353,13 +351,12 @@ fn handle_short_links<'a>(
|
|||
.name("mods")
|
||||
.and_then(|v| Mods::from_str(v.as_str()).pls_ok())
|
||||
.unwrap_or(Mods::NOMOD);
|
||||
let info = match mode.unwrap_or(beatmap.mode) {
|
||||
Mode::Std => cache
|
||||
let info = {
|
||||
let mode = mode.unwrap_or(beatmap.mode);
|
||||
cache
|
||||
.get_beatmap(beatmap.beatmap_id)
|
||||
.await
|
||||
.and_then(|b| b.get_possible_pp_with(mods))
|
||||
.pls_ok(),
|
||||
_ => None,
|
||||
.and_then(|b| b.get_possible_pp_with(mode, mods))?
|
||||
};
|
||||
let r: Result<_> = Ok(ToPrint {
|
||||
embed: EmbedType::Beatmap(beatmap, info, mods),
|
||||
|
@ -383,7 +380,7 @@ fn handle_short_links<'a>(
|
|||
async fn handle_beatmap<'a, 'b>(
|
||||
ctx: &Context,
|
||||
beatmap: &Beatmap,
|
||||
info: Option<BeatmapInfoWithPP>,
|
||||
info: BeatmapInfoWithPP,
|
||||
link: &'_ str,
|
||||
mode: Option<Mode>,
|
||||
mods: Mods,
|
||||
|
|
|
@ -194,9 +194,15 @@ pub async fn save(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
|||
.into_iter()
|
||||
.next()
|
||||
.unwrap();
|
||||
let info = data
|
||||
.get::<BeatmapCache>()
|
||||
.unwrap()
|
||||
.get_beatmap(beatmap.beatmap_id)
|
||||
.await?
|
||||
.get_possible_pp_with(Mode::Std, Mods::NOMOD)?;
|
||||
msg.await?
|
||||
.edit(&ctx, |f| {
|
||||
f.embed(|e| beatmap_embed(&beatmap, Mode::Std, Mods::NOMOD, None, e))
|
||||
f.embed(|e| beatmap_embed(&beatmap, Mode::Std, Mods::NOMOD, info, e))
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
|
@ -478,8 +484,7 @@ pub async fn last(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
|||
.unwrap()
|
||||
.get_beatmap(b.beatmap_id)
|
||||
.await?
|
||||
.get_possible_pp_with(mods)
|
||||
.ok();
|
||||
.get_possible_pp_with(m, mods)?;
|
||||
msg.channel_id
|
||||
.send_message(&ctx, |f| {
|
||||
f.content("Here is the beatmap you requested!")
|
||||
|
@ -646,15 +651,10 @@ async fn get_user(ctx: &Context, msg: &Message, mut args: Args, mode: Mode) -> C
|
|||
{
|
||||
Some(m) => {
|
||||
let beatmap = cache.get_beatmap(m.beatmap_id, mode).await?;
|
||||
let info = match mode {
|
||||
Mode::Std => Some(
|
||||
oppai
|
||||
.get_beatmap(m.beatmap_id)
|
||||
.await?
|
||||
.get_info_with(m.mods)?,
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
let info = oppai
|
||||
.get_beatmap(m.beatmap_id)
|
||||
.await?
|
||||
.get_info_with(mode, m.mods)?;
|
||||
Some((m, BeatmapWithMode(beatmap, mode), info))
|
||||
}
|
||||
None => None,
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use crate::mods::Mods;
|
||||
use crate::{models::Mode, mods::Mods};
|
||||
use osuparse::MetadataSection;
|
||||
use rosu_pp::{Beatmap, BeatmapExt};
|
||||
use rosu_pp::catch::CatchDifficultyAttributes;
|
||||
use rosu_pp::mania::ManiaDifficultyAttributes;
|
||||
use rosu_pp::osu::OsuDifficultyAttributes;
|
||||
use rosu_pp::taiko::TaikoDifficultyAttributes;
|
||||
use rosu_pp::{AttributeProvider, Beatmap, CatchPP, DifficultyAttributes, ManiaPP, OsuPP, TaikoPP};
|
||||
use std::io::Read;
|
||||
use std::sync::Arc;
|
||||
use youmubot_db_sql::{models::osu as models, Pool};
|
||||
|
@ -21,6 +25,16 @@ pub struct BeatmapInfo {
|
|||
pub stars: f64,
|
||||
}
|
||||
|
||||
impl BeatmapInfo {
|
||||
fn extract(beatmap: &Beatmap, attrs: DifficultyAttributes) -> Self {
|
||||
BeatmapInfo {
|
||||
objects: beatmap.hit_objects.len(),
|
||||
max_combo: attrs.max_combo(),
|
||||
stars: attrs.stars(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Accuracy {
|
||||
ByCount(u64, u64, u64, u64), // 300 / 100 / 50 / misses
|
||||
|
@ -52,68 +66,240 @@ impl Accuracy {
|
|||
/// Beatmap Info with attached 95/98/99/100% FC pp.
|
||||
pub type BeatmapInfoWithPP = (BeatmapInfo, [f64; 4]);
|
||||
|
||||
impl BeatmapContent {
|
||||
/// Get pp given the combo and accuracy.
|
||||
pub fn get_pp_from(&self, combo: Option<usize>, accuracy: Accuracy, mods: Mods) -> Result<f64> {
|
||||
let bm = self.content.as_ref();
|
||||
let mut rosu = rosu_pp::OsuPP::new(bm).mods(mods.bits() as u32);
|
||||
if let Some(combo) = combo {
|
||||
rosu = rosu.combo(combo);
|
||||
trait PPCalc<'a>: Sized {
|
||||
type Attrs: rosu_pp::AttributeProvider + Clone;
|
||||
|
||||
fn new(beatmap: &'a Beatmap) -> Self;
|
||||
fn mods(self, mods: u32) -> Self;
|
||||
fn attributes(self, attrs: Self::Attrs) -> Self;
|
||||
|
||||
/* For pp calculation */
|
||||
fn combo(self, combo: usize) -> Self;
|
||||
fn accuracy(self, accuracy: f64) -> Self;
|
||||
fn misses(self, misses: usize) -> Self;
|
||||
fn get_pp(self) -> f64;
|
||||
|
||||
/* For difficulty calculation */
|
||||
fn get_attrs(self) -> Self::Attrs;
|
||||
|
||||
fn combo_opt(self, combo: Option<usize>) -> Self {
|
||||
match combo {
|
||||
Some(c) => self.combo(c),
|
||||
None => self,
|
||||
}
|
||||
if let Accuracy::ByCount(n300, n100, n50, _) = accuracy {
|
||||
rosu = rosu
|
||||
.n300(n300 as usize)
|
||||
.n100(n100 as usize)
|
||||
.n50(n50 as usize);
|
||||
}
|
||||
Ok(rosu
|
||||
.n_misses(accuracy.misses())
|
||||
.accuracy(accuracy.into())
|
||||
.calculate()
|
||||
.pp)
|
||||
}
|
||||
fn accuracy_from(self, accuracy: Accuracy) -> Self {
|
||||
self.misses(accuracy.misses()).accuracy(accuracy.into())
|
||||
}
|
||||
|
||||
/// Get info given mods.
|
||||
pub fn get_info_with(&self, mods: Mods) -> Result<BeatmapInfo> {
|
||||
let stars = self.content.stars().mods(mods.bits() as u32).calculate();
|
||||
Ok(BeatmapInfo {
|
||||
max_combo: stars.max_combo(),
|
||||
objects: self.content.hit_objects.len(),
|
||||
stars: stars.stars(),
|
||||
fn map_attributes(beatmap: &'a Beatmap, mods: Mods) -> Self::Attrs {
|
||||
Self::new(beatmap).mods(mods.bits() as u32).get_attrs()
|
||||
}
|
||||
fn map_pp(beatmap: &'a Beatmap, mods: Mods, combo: Option<usize>, accuracy: Accuracy) -> f64 {
|
||||
Self::new(beatmap)
|
||||
.mods(mods.bits() as u32)
|
||||
.combo_opt(combo)
|
||||
.accuracy_from(accuracy)
|
||||
.get_pp()
|
||||
}
|
||||
fn map_info(beatmap: &'a Beatmap, mods: Mods) -> BeatmapInfo {
|
||||
let attrs = Self::map_attributes(beatmap, mods).attributes();
|
||||
BeatmapInfo::extract(beatmap, attrs)
|
||||
}
|
||||
|
||||
fn map_info_with_pp(beatmap: &'a Beatmap, mods: Mods) -> BeatmapInfoWithPP {
|
||||
let attrs = Self::map_attributes(beatmap, mods);
|
||||
let nw = || {
|
||||
Self::new(beatmap)
|
||||
.mods(mods.bits() as u32)
|
||||
.attributes(attrs.clone())
|
||||
};
|
||||
let pps = [
|
||||
nw().accuracy_from(Accuracy::ByValue(95.0, 0)).get_pp(),
|
||||
nw().accuracy_from(Accuracy::ByValue(98.0, 0)).get_pp(),
|
||||
nw().accuracy_from(Accuracy::ByValue(99.0, 0)).get_pp(),
|
||||
nw().accuracy_from(Accuracy::ByValue(100.0, 0)).get_pp(),
|
||||
];
|
||||
let info = BeatmapInfo::extract(beatmap, attrs.attributes());
|
||||
(info, pps)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PPCalc<'a> for OsuPP<'a> {
|
||||
type Attrs = OsuDifficultyAttributes;
|
||||
|
||||
fn new(beatmap: &'a Beatmap) -> Self {
|
||||
Self::new(beatmap)
|
||||
}
|
||||
fn mods(self, mods: u32) -> Self {
|
||||
self.mods(mods)
|
||||
}
|
||||
|
||||
fn attributes(self, attrs: Self::Attrs) -> Self {
|
||||
self.attributes(attrs)
|
||||
}
|
||||
|
||||
fn combo(self, combo: usize) -> Self {
|
||||
self.combo(combo)
|
||||
}
|
||||
|
||||
fn accuracy(self, accuracy: f64) -> Self {
|
||||
self.accuracy(accuracy)
|
||||
}
|
||||
|
||||
fn misses(self, misses: usize) -> Self {
|
||||
self.n_misses(misses)
|
||||
}
|
||||
|
||||
fn get_pp(self) -> f64 {
|
||||
self.calculate().pp()
|
||||
}
|
||||
|
||||
fn get_attrs(self) -> Self::Attrs {
|
||||
self.calculate().difficulty
|
||||
}
|
||||
}
|
||||
impl<'a> PPCalc<'a> for TaikoPP<'a> {
|
||||
type Attrs = TaikoDifficultyAttributes;
|
||||
|
||||
fn new(beatmap: &'a Beatmap) -> Self {
|
||||
Self::new(beatmap)
|
||||
}
|
||||
fn mods(self, mods: u32) -> Self {
|
||||
self.mods(mods)
|
||||
}
|
||||
|
||||
fn attributes(self, attrs: Self::Attrs) -> Self {
|
||||
self.attributes(attrs)
|
||||
}
|
||||
|
||||
fn combo(self, combo: usize) -> Self {
|
||||
self.combo(combo)
|
||||
}
|
||||
|
||||
fn accuracy(self, accuracy: f64) -> Self {
|
||||
self.accuracy(accuracy)
|
||||
}
|
||||
|
||||
fn misses(self, misses: usize) -> Self {
|
||||
self.n_misses(misses)
|
||||
}
|
||||
|
||||
fn get_pp(self) -> f64 {
|
||||
self.calculate().pp()
|
||||
}
|
||||
|
||||
fn get_attrs(self) -> Self::Attrs {
|
||||
self.calculate().difficulty
|
||||
}
|
||||
}
|
||||
impl<'a> PPCalc<'a> for CatchPP<'a> {
|
||||
type Attrs = CatchDifficultyAttributes;
|
||||
|
||||
fn new(beatmap: &'a Beatmap) -> Self {
|
||||
Self::new(beatmap)
|
||||
}
|
||||
fn mods(self, mods: u32) -> Self {
|
||||
self.mods(mods)
|
||||
}
|
||||
|
||||
fn attributes(self, attrs: Self::Attrs) -> Self {
|
||||
self.attributes(attrs)
|
||||
}
|
||||
|
||||
fn combo(self, combo: usize) -> Self {
|
||||
self.combo(combo)
|
||||
}
|
||||
|
||||
fn accuracy(self, accuracy: f64) -> Self {
|
||||
self.accuracy(accuracy)
|
||||
}
|
||||
|
||||
fn misses(self, misses: usize) -> Self {
|
||||
self.misses(misses)
|
||||
}
|
||||
|
||||
fn get_pp(self) -> f64 {
|
||||
self.calculate().pp()
|
||||
}
|
||||
|
||||
fn get_attrs(self) -> Self::Attrs {
|
||||
self.calculate().difficulty
|
||||
}
|
||||
}
|
||||
impl<'a> PPCalc<'a> for ManiaPP<'a> {
|
||||
type Attrs = ManiaDifficultyAttributes;
|
||||
|
||||
fn new(beatmap: &'a Beatmap) -> Self {
|
||||
Self::new(beatmap)
|
||||
}
|
||||
fn mods(self, mods: u32) -> Self {
|
||||
self.mods(mods)
|
||||
}
|
||||
|
||||
fn attributes(self, attrs: Self::Attrs) -> Self {
|
||||
self.attributes(attrs)
|
||||
}
|
||||
|
||||
fn combo(self, _combo: usize) -> Self {
|
||||
// Mania doesn't seem to care about combo?
|
||||
self
|
||||
}
|
||||
|
||||
fn accuracy(self, accuracy: f64) -> Self {
|
||||
self.accuracy(accuracy)
|
||||
}
|
||||
|
||||
fn misses(self, misses: usize) -> Self {
|
||||
self.n_misses(misses)
|
||||
}
|
||||
|
||||
fn get_pp(self) -> f64 {
|
||||
self.calculate().pp()
|
||||
}
|
||||
|
||||
fn get_attrs(self) -> Self::Attrs {
|
||||
self.calculate().difficulty
|
||||
}
|
||||
}
|
||||
|
||||
impl BeatmapContent {
|
||||
/// Get pp given the combo and accuracy.
|
||||
pub fn get_pp_from(
|
||||
&self,
|
||||
mode: Mode,
|
||||
combo: Option<usize>,
|
||||
accuracy: Accuracy,
|
||||
mods: Mods,
|
||||
) -> Result<f64> {
|
||||
let bm = self.content.as_ref();
|
||||
Ok(match mode {
|
||||
Mode::Std => OsuPP::map_pp(bm, mods, combo, accuracy),
|
||||
Mode::Taiko => TaikoPP::map_pp(bm, mods, combo, accuracy),
|
||||
Mode::Catch => CatchPP::map_pp(bm, mods, combo, accuracy),
|
||||
Mode::Mania => ManiaPP::map_pp(bm, mods, combo, accuracy),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_possible_pp_with(&self, mods: Mods) -> Result<BeatmapInfoWithPP> {
|
||||
let rosu = || self.content.pp().mods(mods.bits() as u32);
|
||||
let pp95 = rosu().accuracy(95.0).calculate();
|
||||
let pp = [
|
||||
pp95.pp(),
|
||||
rosu()
|
||||
.attributes(pp95.clone())
|
||||
.accuracy(98.0)
|
||||
.calculate()
|
||||
.pp(),
|
||||
rosu()
|
||||
.attributes(pp95.clone())
|
||||
.accuracy(99.0)
|
||||
.calculate()
|
||||
.pp(),
|
||||
rosu()
|
||||
.attributes(pp95.clone())
|
||||
.accuracy(100.0)
|
||||
.calculate()
|
||||
.pp(),
|
||||
];
|
||||
let max_combo = pp95.difficulty_attributes().max_combo();
|
||||
let stars = pp95.difficulty_attributes().stars();
|
||||
Ok((
|
||||
BeatmapInfo {
|
||||
objects: self.content.hit_objects.len(),
|
||||
max_combo,
|
||||
stars,
|
||||
},
|
||||
pp,
|
||||
))
|
||||
/// Get info given mods.
|
||||
pub fn get_info_with(&self, mode: Mode, mods: Mods) -> Result<BeatmapInfo> {
|
||||
let bm = self.content.as_ref();
|
||||
Ok(match mode {
|
||||
Mode::Std => OsuPP::map_info(bm, mods),
|
||||
Mode::Taiko => TaikoPP::map_info(bm, mods),
|
||||
Mode::Catch => CatchPP::map_info(bm, mods),
|
||||
Mode::Mania => ManiaPP::map_info(bm, mods),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_possible_pp_with(&self, mode: Mode, mods: Mods) -> Result<BeatmapInfoWithPP> {
|
||||
let bm = self.content.as_ref();
|
||||
Ok(match mode {
|
||||
Mode::Std => OsuPP::map_info_with_pp(bm, mods),
|
||||
Mode::Taiko => TaikoPP::map_info_with_pp(bm, mods),
|
||||
Mode::Catch => CatchPP::map_info_with_pp(bm, mods),
|
||||
Mode::Mania => ManiaPP::map_info_with_pp(bm, mods),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -277,8 +277,9 @@ async fn show_leaderboard(
|
|||
let oppai = data.get::<BeatmapCache>().unwrap();
|
||||
let oppai_map = oppai.get_beatmap(bm.0.beatmap_id).await?;
|
||||
let get_oppai_pp = move |combo: u64, acc: Accuracy, mods: Mods| {
|
||||
(if mode == Mode::Std { Some(mode) } else { None })
|
||||
.and_then(|_| oppai_map.get_pp_from(Some(combo as usize), acc, mods).ok())
|
||||
oppai_map
|
||||
.get_pp_from(mode, Some(combo as usize), acc, mods)
|
||||
.ok()
|
||||
};
|
||||
|
||||
let guild = m.guild_id.expect("Guild-only command");
|
||||
|
|
|
@ -99,9 +99,9 @@ impl Difficulty {
|
|||
}
|
||||
/// Apply mods to the given difficulty.
|
||||
/// Note that `stars`, `aim` and `speed` cannot be calculated from this alone.
|
||||
pub fn apply_mods(&self, mods: Mods, updated_stars: Option<f64>) -> Difficulty {
|
||||
pub fn apply_mods(&self, mods: Mods, updated_stars: f64) -> Difficulty {
|
||||
let mut diff = Difficulty {
|
||||
stars: updated_stars.unwrap_or(self.stars),
|
||||
stars: updated_stars,
|
||||
..self.clone()
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue