Back up everything before history rewrite
Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
parent
c035ecbb52
commit
3f922fcf6c
|
@ -1,4 +1,4 @@
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, io::Cursor, path::PathBuf};
|
||||||
|
|
||||||
use image::{imageops::FilterType, GenericImageView, Rgba};
|
use image::{imageops::FilterType, GenericImageView, Rgba};
|
||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
@ -153,9 +153,17 @@ impl JacketCache {
|
||||||
image.resize(BITMAP_IMAGE_SIZE, BITMAP_IMAGE_SIZE, FilterType::Nearest);
|
image.resize(BITMAP_IMAGE_SIZE, BITMAP_IMAGE_SIZE, FilterType::Nearest);
|
||||||
|
|
||||||
if should_blur_jacket_art() {
|
if should_blur_jacket_art() {
|
||||||
image = image.blur(20.0);
|
image = image.blur(40.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let encoded_pic = {
|
||||||
|
let mut processed_pic = Vec::new();
|
||||||
|
image.write_to(
|
||||||
|
&mut Cursor::new(&mut processed_pic),
|
||||||
|
image::ImageFormat::Jpeg,
|
||||||
|
)?;
|
||||||
|
processed_pic.leak()
|
||||||
|
};
|
||||||
let bitmap: &'static _ = Box::leak(Box::new(image.into_rgb8()));
|
let bitmap: &'static _ = Box::leak(Box::new(image.into_rgb8()));
|
||||||
|
|
||||||
if name == "base" {
|
if name == "base" {
|
||||||
|
@ -163,7 +171,7 @@ impl JacketCache {
|
||||||
for chart in song_cache.charts_mut() {
|
for chart in song_cache.charts_mut() {
|
||||||
if chart.song_id == song_id && chart.cached_jacket.is_none() {
|
if chart.song_id == song_id && chart.cached_jacket.is_none() {
|
||||||
chart.cached_jacket = Some(Jacket {
|
chart.cached_jacket = Some(Jacket {
|
||||||
raw: contents,
|
raw: encoded_pic,
|
||||||
bitmap,
|
bitmap,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -171,7 +179,7 @@ impl JacketCache {
|
||||||
} else if difficulty.is_some() {
|
} else if difficulty.is_some() {
|
||||||
let chart = song_cache.lookup_chart_mut(chart_id).unwrap();
|
let chart = song_cache.lookup_chart_mut(chart_id).unwrap();
|
||||||
chart.cached_jacket = Some(Jacket {
|
chart.cached_jacket = Some(Jacket {
|
||||||
raw: contents,
|
raw: encoded_pic,
|
||||||
bitmap,
|
bitmap,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::context::{Error, UserContext};
|
||||||
use crate::user::User;
|
use crate::user::User;
|
||||||
|
|
||||||
use super::chart::SongCache;
|
use super::chart::SongCache;
|
||||||
use super::score::Score;
|
use super::score::{Score, ScoringSystem};
|
||||||
|
|
||||||
// {{{ Create play
|
// {{{ Create play
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -127,7 +127,7 @@ pub struct DbPlay {
|
||||||
|
|
||||||
impl DbPlay {
|
impl DbPlay {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_play(self) -> Play {
|
pub fn into_play(self) -> Play {
|
||||||
Play {
|
Play {
|
||||||
id: self.id as u32,
|
id: self.id as u32,
|
||||||
chart_id: self.chart_id as u32,
|
chart_id: self.chart_id as u32,
|
||||||
|
@ -175,6 +175,20 @@ pub struct Play {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Play {
|
impl Play {
|
||||||
|
// {{{ Query the underlying score
|
||||||
|
#[inline]
|
||||||
|
pub fn score(&self, system: ScoringSystem) -> Score {
|
||||||
|
match system {
|
||||||
|
ScoringSystem::Standard => self.score,
|
||||||
|
ScoringSystem::EX => self.zeta_score,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn play_rating(&self, system: ScoringSystem, chart_constant: u32) -> i32 {
|
||||||
|
self.score(system).play_rating(chart_constant)
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
// {{{ Play => distribution
|
// {{{ Play => distribution
|
||||||
pub fn distribution(&self, note_count: u32) -> Option<(u32, u32, u32, u32)> {
|
pub fn distribution(&self, note_count: u32) -> Option<(u32, u32, u32, u32)> {
|
||||||
if let Some(fars) = self.far_notes {
|
if let Some(fars) = self.far_notes {
|
||||||
|
@ -278,7 +292,8 @@ impl Play {
|
||||||
song.title, chart.difficulty
|
song.title, chart.difficulty
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.map(|p| p.to_play());
|
.map(|p| p.into_play());
|
||||||
|
|
||||||
let prev_score = prev_play.as_ref().map(|p| p.score);
|
let prev_score = prev_play.as_ref().map(|p| p.score);
|
||||||
let prev_zeta_score = prev_play.as_ref().map(|p| p.zeta_score);
|
let prev_zeta_score = prev_play.as_ref().map(|p| p.zeta_score);
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -360,9 +375,10 @@ pub async fn get_best_plays<'a>(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
song_cache: &'a SongCache,
|
song_cache: &'a SongCache,
|
||||||
user: &User,
|
user: &User,
|
||||||
|
scoring_system: ScoringSystem,
|
||||||
min_amount: usize,
|
min_amount: usize,
|
||||||
max_amount: usize,
|
max_amount: usize,
|
||||||
) -> Result<Result<PlayCollection<'a>, &'static str>, Error> {
|
) -> Result<Result<PlayCollection<'a>, String>, Error> {
|
||||||
// {{{ DB data fetching
|
// {{{ DB data fetching
|
||||||
let plays: Vec<DbPlay> = query_as(
|
let plays: Vec<DbPlay> = query_as(
|
||||||
"
|
"
|
||||||
|
@ -381,7 +397,10 @@ pub async fn get_best_plays<'a>(
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
if plays.len() < min_amount {
|
if plays.len() < min_amount {
|
||||||
return Ok(Err("Not enough plays found"));
|
return Ok(Err(format!(
|
||||||
|
"Not enough plays found ({} out of a minimum of {min_amount})",
|
||||||
|
plays.len()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{{ B30 computation
|
// {{{ B30 computation
|
||||||
|
@ -390,13 +409,13 @@ pub async fn get_best_plays<'a>(
|
||||||
let mut plays: Vec<(Play, &Song, &Chart)> = plays
|
let mut plays: Vec<(Play, &Song, &Chart)> = plays
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|play| {
|
.map(|play| {
|
||||||
let play = play.to_play();
|
let play = play.into_play();
|
||||||
let (song, chart) = song_cache.lookup_chart(play.chart_id)?;
|
let (song, chart) = song_cache.lookup_chart(play.chart_id)?;
|
||||||
Ok((play, song, chart))
|
Ok((play, song, chart))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, Error>>()?;
|
.collect::<Result<Vec<_>, Error>>()?;
|
||||||
|
|
||||||
plays.sort_by_key(|(play, _, chart)| -play.score.play_rating(chart.chart_constant));
|
plays.sort_by_key(|(play, _, chart)| -play.play_rating(scoring_system, chart.chart_constant));
|
||||||
plays.truncate(max_amount);
|
plays.truncate(max_amount);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
@ -404,11 +423,12 @@ pub async fn get_best_plays<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn compute_b30_ptt(plays: &PlayCollection<'_>) -> i32 {
|
pub fn compute_b30_ptt(scoring_system: ScoringSystem, plays: &PlayCollection<'_>) -> i32 {
|
||||||
plays
|
plays
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(play, _, chart)| play.score.play_rating(chart.chart_constant))
|
.map(|(play, _, chart)| play.play_rating(scoring_system, chart.chart_constant))
|
||||||
.sum::<i32>()
|
.sum::<i32>()
|
||||||
/ plays.len() as i32
|
.checked_div(plays.len() as i32)
|
||||||
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|
|
@ -6,6 +6,21 @@ use crate::context::Error;
|
||||||
|
|
||||||
use super::chart::Chart;
|
use super::chart::Chart;
|
||||||
|
|
||||||
|
// {{{ Scoring system
|
||||||
|
#[derive(Debug, Clone, Copy, poise::ChoiceParameter)]
|
||||||
|
pub enum ScoringSystem {
|
||||||
|
Standard,
|
||||||
|
|
||||||
|
// Inspired by sdvx's EX-scoring
|
||||||
|
EX,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ScoringSystem {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
// {{{ Grade
|
// {{{ Grade
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Grade {
|
pub enum Grade {
|
||||||
|
@ -140,7 +155,7 @@ impl Score {
|
||||||
if play_rating >= prev_play_rating {
|
if play_rating >= prev_play_rating {
|
||||||
write!(buffer, " (+{:.2})", play_rating - prev_play_rating)?;
|
write!(buffer, " (+{:.2})", play_rating - prev_play_rating)?;
|
||||||
} else {
|
} else {
|
||||||
write!(buffer, " (-{:.2})", play_rating - prev_play_rating)?;
|
write!(buffer, " ({:.2})", play_rating - prev_play_rating)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{cell::RefCell, env::var, path::PathBuf, str::FromStr, sync::OnceLock, thread::LocalKey};
|
use std::{cell::RefCell, env::var, path::PathBuf, str::FromStr, sync::OnceLock, thread::LocalKey};
|
||||||
|
|
||||||
use freetype::{Face, Library};
|
use freetype::{Face, Library};
|
||||||
use image::{imageops::FilterType, ImageBuffer, Rgb, Rgba};
|
use image::{ImageBuffer, Rgb, Rgba};
|
||||||
|
|
||||||
use crate::{arcaea::chart::Difficulty, timed};
|
use crate::{arcaea::chart::Difficulty, timed};
|
||||||
|
|
||||||
|
@ -55,94 +55,101 @@ pub fn with_font<T>(
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn should_skip_jacket_art() -> bool {
|
pub fn should_skip_jacket_art() -> bool {
|
||||||
static CELL: OnceLock<bool> = OnceLock::new();
|
var("SHIMMERING_NO_JACKETS").unwrap_or_default() == "1"
|
||||||
*CELL.get_or_init(|| var("SHIMMERING_NO_JACKETS").unwrap_or_default() == "1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn should_blur_jacket_art() -> bool {
|
pub fn should_blur_jacket_art() -> bool {
|
||||||
static CELL: OnceLock<bool> = OnceLock::new();
|
var("SHIMMERING_BLUR_JACKETS").unwrap_or_default() == "1"
|
||||||
*CELL.get_or_init(|| var("SHIMMERING_BLUR_JACKETS").unwrap_or_default() == "1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_b30_background() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
|
pub fn get_b30_background() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_b30_background", {
|
||||||
let raw_b30_background = image::open(get_assets_dir().join("b30_background.jpg"))
|
let raw_b30_background = image::open(get_assets_dir().join("b30_background.jpg"))
|
||||||
.expect("Could not open b30 background");
|
.expect("Could not open b30 background");
|
||||||
|
|
||||||
raw_b30_background
|
raw_b30_background.blur(7.0).into_rgb8()
|
||||||
.resize(
|
})
|
||||||
8 * raw_b30_background.width(),
|
|
||||||
8 * raw_b30_background.height(),
|
|
||||||
FilterType::Lanczos3,
|
|
||||||
)
|
|
||||||
.blur(7.0)
|
|
||||||
.into_rgb8()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_count_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
pub fn get_count_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_count_backound", {
|
||||||
image::open(get_assets_dir().join("count_background.png"))
|
image::open(get_assets_dir().join("count_background.png"))
|
||||||
.expect("Could not open count background")
|
.expect("Could not open count background")
|
||||||
.into_rgba8()
|
.into_rgba8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_score_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
pub fn get_score_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_score_background", {
|
||||||
image::open(get_assets_dir().join("score_background.png"))
|
image::open(get_assets_dir().join("score_background.png"))
|
||||||
.expect("Could not open score background")
|
.expect("Could not open score background")
|
||||||
.into_rgba8()
|
.into_rgba8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_status_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
pub fn get_status_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_status_background", {
|
||||||
image::open(get_assets_dir().join("status_background.png"))
|
image::open(get_assets_dir().join("status_background.png"))
|
||||||
.expect("Could not open status background")
|
.expect("Could not open status background")
|
||||||
.into_rgba8()
|
.into_rgba8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_grade_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
pub fn get_grade_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_grade_background", {
|
||||||
image::open(get_assets_dir().join("grade_background.png"))
|
image::open(get_assets_dir().join("grade_background.png"))
|
||||||
.expect("Could not open grade background")
|
.expect("Could not open grade background")
|
||||||
.into_rgba8()
|
.into_rgba8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_top_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
|
pub fn get_top_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_top_background", {
|
||||||
image::open(get_assets_dir().join("top_background.png"))
|
image::open(get_assets_dir().join("top_background.png"))
|
||||||
.expect("Could not open top background")
|
.expect("Could not open top background")
|
||||||
.into_rgb8()
|
.into_rgb8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_name_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
|
pub fn get_name_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_name_background", {
|
||||||
image::open(get_assets_dir().join("name_background.png"))
|
image::open(get_assets_dir().join("name_background.png"))
|
||||||
.expect("Could not open name background")
|
.expect("Could not open name background")
|
||||||
.into_rgb8()
|
.into_rgb8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ptt_emblem() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
pub fn get_ptt_emblem() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
|
||||||
CELL.get_or_init(|| {
|
CELL.get_or_init(|| {
|
||||||
|
timed!("load_ptt_emblem", {
|
||||||
image::open(get_assets_dir().join("ptt_emblem.png"))
|
image::open(get_assets_dir().join("ptt_emblem.png"))
|
||||||
.expect("Could not open ptt emblem")
|
.expect("Could not open ptt emblem")
|
||||||
.into_rgba8()
|
.into_rgba8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_difficulty_background(
|
pub fn get_difficulty_background(
|
||||||
|
@ -150,6 +157,7 @@ pub fn get_difficulty_background(
|
||||||
) -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
) -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
|
||||||
static CELL: OnceLock<[ImageBuffer<Rgba<u8>, Vec<u8>>; 5]> = OnceLock::new();
|
static CELL: OnceLock<[ImageBuffer<Rgba<u8>, Vec<u8>>; 5]> = OnceLock::new();
|
||||||
&CELL.get_or_init(|| {
|
&CELL.get_or_init(|| {
|
||||||
|
timed!("load_difficulty_background", {
|
||||||
let assets_dir = get_assets_dir();
|
let assets_dir = get_assets_dir();
|
||||||
Difficulty::DIFFICULTY_SHORTHANDS.map(|shorthand| {
|
Difficulty::DIFFICULTY_SHORTHANDS.map(|shorthand| {
|
||||||
image::open(assets_dir.join(format!("diff_{}.png", shorthand.to_lowercase())))
|
image::open(assets_dir.join(format!("diff_{}.png", shorthand.to_lowercase())))
|
||||||
|
@ -159,5 +167,6 @@ pub fn get_difficulty_background(
|
||||||
))
|
))
|
||||||
.into_rgba8()
|
.into_rgba8()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
})[difficulty.to_index()]
|
})[difficulty.to_index()]
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
arcaea::{
|
arcaea::{
|
||||||
jacket::BITMAP_IMAGE_SIZE,
|
jacket::BITMAP_IMAGE_SIZE,
|
||||||
play::{compute_b30_ptt, get_best_plays, DbPlay},
|
play::{compute_b30_ptt, get_best_plays, DbPlay},
|
||||||
score::Score,
|
score::{Score, ScoringSystem},
|
||||||
},
|
},
|
||||||
assert_is_pookie,
|
assert_is_pookie,
|
||||||
assets::{
|
assets::{
|
||||||
|
@ -92,7 +92,7 @@ pub async fn best(
|
||||||
song.title, chart.difficulty
|
song.title, chart.difficulty
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.to_play();
|
.into_play();
|
||||||
|
|
||||||
let (embed, attachment) = play
|
let (embed, attachment) = play
|
||||||
.to_embed(
|
.to_embed(
|
||||||
|
@ -117,11 +117,13 @@ pub async fn best(
|
||||||
#[poise::command(prefix_command, slash_command)]
|
#[poise::command(prefix_command, slash_command)]
|
||||||
pub async fn plot(
|
pub async fn plot(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
|
scoring_system: Option<ScoringSystem>,
|
||||||
#[rest]
|
#[rest]
|
||||||
#[description = "Name of chart to show (difficulty at the end)"]
|
#[description = "Name of chart to show (difficulty at the end)"]
|
||||||
name: String,
|
name: String,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let user = get_user!(&ctx);
|
let user = get_user!(&ctx);
|
||||||
|
let scoring_system = scoring_system.unwrap_or_default();
|
||||||
|
|
||||||
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
||||||
|
|
||||||
|
@ -152,7 +154,12 @@ pub async fn plot(
|
||||||
|
|
||||||
let min_time = plays.iter().map(|p| p.created_at).min().unwrap();
|
let min_time = plays.iter().map(|p| p.created_at).min().unwrap();
|
||||||
let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
|
let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
|
||||||
let mut min_score = plays.iter().map(|p| p.score).min().unwrap();
|
let mut min_score = plays
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.clone().into_play().score(scoring_system))
|
||||||
|
.min()
|
||||||
|
.unwrap()
|
||||||
|
.0 as i64;
|
||||||
|
|
||||||
if min_score > 9_900_000 {
|
if min_score > 9_900_000 {
|
||||||
min_score = 9_800_000;
|
min_score = 9_800_000;
|
||||||
|
@ -202,20 +209,26 @@ pub async fn plot(
|
||||||
.draw()?;
|
.draw()?;
|
||||||
|
|
||||||
let mut points: Vec<_> = plays
|
let mut points: Vec<_> = plays
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|play| (play.created_at.and_utc().timestamp_millis(), play.score))
|
.map(|play| {
|
||||||
|
(
|
||||||
|
play.created_at.and_utc().timestamp_millis(),
|
||||||
|
play.into_play().score(scoring_system),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
points.sort();
|
points.sort();
|
||||||
points.dedup();
|
points.dedup();
|
||||||
|
|
||||||
chart.draw_series(LineSeries::new(points.iter().map(|(t, s)| (*t, *s)), &BLUE))?;
|
chart.draw_series(LineSeries::new(
|
||||||
|
points.iter().map(|(t, s)| (*t, s.0 as i64)),
|
||||||
|
&BLUE,
|
||||||
|
))?;
|
||||||
|
|
||||||
chart.draw_series(
|
chart.draw_series(points.iter().map(|(t, s)| {
|
||||||
points
|
Circle::new((*t, s.0 as i64), 3, plotters::style::Color::filled(&BLUE))
|
||||||
.iter()
|
}))?;
|
||||||
.map(|(t, s)| Circle::new((*t, *s), 3, plotters::style::Color::filled(&BLUE))),
|
|
||||||
)?;
|
|
||||||
root.present()?;
|
root.present()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +248,7 @@ pub async fn plot(
|
||||||
async fn best_plays(
|
async fn best_plays(
|
||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
user: &User,
|
user: &User,
|
||||||
|
scoring_system: ScoringSystem,
|
||||||
grid_size: (u32, u32),
|
grid_size: (u32, u32),
|
||||||
require_full: bool,
|
require_full: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
@ -245,10 +259,11 @@ async fn best_plays(
|
||||||
&user_ctx.db,
|
&user_ctx.db,
|
||||||
&user_ctx.song_cache,
|
&user_ctx.song_cache,
|
||||||
&user,
|
&user,
|
||||||
|
scoring_system,
|
||||||
if require_full {
|
if require_full {
|
||||||
grid_size.0 * grid_size.1
|
grid_size.0 * grid_size.1
|
||||||
} else {
|
} else {
|
||||||
grid_size.0 * (grid_size.1.max(1) - 1)
|
grid_size.0 * (grid_size.1.max(1) - 1) + 1
|
||||||
} as usize,
|
} as usize,
|
||||||
(grid_size.0 * grid_size.1) as usize
|
(grid_size.0 * grid_size.1) as usize
|
||||||
)
|
)
|
||||||
|
@ -471,7 +486,7 @@ async fn best_plays(
|
||||||
stroke: Some((Color::BLACK, 1.5)),
|
stroke: Some((Color::BLACK, 1.5)),
|
||||||
drop_shadow: None,
|
drop_shadow: None,
|
||||||
},
|
},
|
||||||
&format!("{:0>10}", format!("{}", play.score)),
|
&format!("{:0>10}", format!("{}", play.score(scoring_system))),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -494,9 +509,12 @@ async fn best_plays(
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Display status text
|
// {{{ Display status text
|
||||||
with_font(&EXO_FONT, |faces| {
|
with_font(&EXO_FONT, |faces| {
|
||||||
let status = play
|
let status = play.short_status(chart).ok_or_else(|| {
|
||||||
.short_status(chart)
|
format!(
|
||||||
.ok_or_else(|| format!("Could not get status for score {}", play.score))?;
|
"Could not get status for score {}",
|
||||||
|
play.score(scoring_system)
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let x_offset = match status {
|
let x_offset = match status {
|
||||||
'P' => 2,
|
'P' => 2,
|
||||||
|
@ -540,7 +558,7 @@ async fn best_plays(
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Display grade text
|
// {{{ Display grade text
|
||||||
with_font(&EXO_FONT, |faces| {
|
with_font(&EXO_FONT, |faces| {
|
||||||
let grade = play.score.grade();
|
let grade = play.score(scoring_system).grade();
|
||||||
let center = grade_bg_area.center();
|
let center = grade_bg_area.center();
|
||||||
|
|
||||||
drawer.text(
|
drawer.text(
|
||||||
|
@ -586,7 +604,10 @@ async fn best_plays(
|
||||||
(top_left_center, 94),
|
(top_left_center, 94),
|
||||||
faces,
|
faces,
|
||||||
style,
|
style,
|
||||||
&format!("{:.2}", play.score.play_rating_f32(chart.chart_constant)),
|
&format!(
|
||||||
|
"{:.2}",
|
||||||
|
play.play_rating(scoring_system, chart.chart_constant) as f32 / 100.0
|
||||||
|
),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -622,7 +643,7 @@ async fn best_plays(
|
||||||
.attachment(CreateAttachment::bytes(out_buffer, "b30.png"))
|
.attachment(CreateAttachment::bytes(out_buffer, "b30.png"))
|
||||||
.content(format!(
|
.content(format!(
|
||||||
"Your ptt is {:.2}",
|
"Your ptt is {:.2}",
|
||||||
compute_b30_ptt(&plays) as f32 / 100.0
|
compute_b30_ptt(scoring_system, &plays) as f32 / 100.0
|
||||||
));
|
));
|
||||||
ctx.send(reply).await?;
|
ctx.send(reply).await?;
|
||||||
|
|
||||||
|
@ -632,15 +653,34 @@ async fn best_plays(
|
||||||
// {{{ B30
|
// {{{ B30
|
||||||
/// Show the 30 best scores
|
/// Show the 30 best scores
|
||||||
#[poise::command(prefix_command, slash_command, user_cooldown = 30)]
|
#[poise::command(prefix_command, slash_command, user_cooldown = 30)]
|
||||||
pub async fn b30(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn b30(ctx: Context<'_>, scoring_system: Option<ScoringSystem>) -> Result<(), Error> {
|
||||||
let user = get_user!(&ctx);
|
let user = get_user!(&ctx);
|
||||||
best_plays(&ctx, &user, (5, 6), true).await
|
best_plays(
|
||||||
|
&ctx,
|
||||||
|
&user,
|
||||||
|
scoring_system.unwrap_or_default(),
|
||||||
|
(5, 6),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[poise::command(prefix_command, slash_command, hide_in_help, global_cooldown = 5)]
|
#[poise::command(prefix_command, slash_command, hide_in_help, global_cooldown = 5)]
|
||||||
pub async fn bany(ctx: Context<'_>, width: u32, height: u32) -> Result<(), Error> {
|
pub async fn bany(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
scoring_system: Option<ScoringSystem>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let user = get_user!(&ctx);
|
let user = get_user!(&ctx);
|
||||||
assert_is_pookie!(ctx, user);
|
assert_is_pookie!(ctx, user);
|
||||||
best_plays(&ctx, &user, (width, height), false).await
|
best_plays(
|
||||||
|
&ctx,
|
||||||
|
&user,
|
||||||
|
scoring_system.unwrap_or_default(),
|
||||||
|
(width, height),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|
|
@ -333,7 +333,7 @@ impl CharMeasurements {
|
||||||
.ok_or_else(|| "No chars in cache")?;
|
.ok_or_else(|| "No chars in cache")?;
|
||||||
|
|
||||||
println!("char '{}', distance {}", best_match.1, best_match.0);
|
println!("char '{}', distance {}", best_match.1, best_match.0);
|
||||||
if best_match.0 <= 1.0 {
|
if best_match.0 <= 0.75 {
|
||||||
result.push(best_match.1);
|
result.push(best_match.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue