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