1
Fork 0

So much progress on b30

Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
prescientmoon 2024-07-18 20:17:39 +02:00
parent 373e54c55e
commit 3dc320d524
Signed by: prescientmoon
SSH key fingerprint: SHA256:UUF9JT2s8Xfyv76b8ZuVL7XrmimH4o49p4b+iexbVH4
15 changed files with 745 additions and 39 deletions
src/commands

View file

@ -19,8 +19,8 @@ pub async fn chart(
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
let attachement_name = "chart.png";
let icon_attachement = match chart.cached_jacket {
Some(bytes) => Some(CreateAttachment::bytes(bytes, attachement_name)),
let icon_attachement = match chart.cached_jacket.as_ref() {
Some(jacket) => Some(CreateAttachment::bytes(jacket.raw, attachement_name)),
None => None,
};

View file

@ -17,8 +17,11 @@ use poise::{
use sqlx::query_as;
use crate::{
bitmap::{BitmapCanvas, LayoutDrawer, LayoutManager},
chart::{Chart, Song},
context::{Context, Error},
score::{guess_song_and_chart, DbPlay, Score},
jacket::BITMAP_IMAGE_SIZE,
score::{guess_song_and_chart, DbPlay, Play, Score},
user::{discord_it_to_discord_user, User},
};
@ -27,7 +30,7 @@ use crate::{
#[poise::command(
prefix_command,
slash_command,
subcommands("chart"),
subcommands("chart", "b30"),
subcommand_required
)]
pub async fn stats(_ctx: Context<'_>) -> Result<(), Error> {
@ -212,7 +215,6 @@ pub async fn plot(
.iter()
.map(|(t, s)| Circle::new((*t, *s), 3, BLUE.filled())),
)?;
root.present()?;
}
@ -228,3 +230,226 @@ pub async fn plot(
Ok(())
}
// }}}
// {{{ B30
/// Show the 30 best scores
#[poise::command(prefix_command, slash_command)]
pub async fn b30(ctx: Context<'_>) -> Result<(), Error> {
let user = match User::from_context(&ctx).await {
Ok(user) => user,
Err(_) => {
ctx.say("You are not an user in my database, sorry!")
.await?;
return Ok(());
}
};
let plays: Vec<DbPlay> = query_as(
"
SELECT id, chart_id, user_id,
created_at, MAX(score) as score, zeta_score,
creation_ptt, creation_zeta_ptt, far_notes, max_recall, discord_attachment_id
FROM plays p
WHERE user_id = ?
GROUP BY chart_id
ORDER BY score DESC
",
)
.bind(user.id)
.fetch_all(&ctx.data().db)
.await?;
if plays.len() < 30 {
ctx.reply("Not enough plays found").await?;
return Ok(());
}
// TODO: consider not reallocating everything here
let mut plays: Vec<(Play, &Song, &Chart)> = plays
.into_iter()
.map(|play| {
let play = play.to_play();
// TODO: change the .lookup to perform binary search or something
let (song, chart) = ctx.data().song_cache.lookup_chart(play.chart_id)?;
Ok((play, song, chart))
})
.collect::<Result<Vec<_>, Error>>()?;
plays.sort_by_key(|(play, _, chart)| -play.score.play_rating(chart.chart_constant));
plays.truncate(30);
let mut layout = LayoutManager::default();
let jacket_area = layout.make_box(BITMAP_IMAGE_SIZE, BITMAP_IMAGE_SIZE);
let jacket_margin = 10;
let jacket_with_margin =
layout.margin(jacket_area, jacket_margin, jacket_margin, 5, jacket_margin);
let top_left_area = layout.make_box(90, layout.height(jacket_with_margin));
let top_area = layout.glue_vertically(top_left_area, jacket_with_margin);
let bottom_area = layout.make_box(layout.width(top_area), 40);
let item_area = layout.glue_horizontally(top_area, bottom_area);
let item_with_margin = layout.margin_xy(item_area, 25, 20);
let (item_grid, item_origins) = layout.repeated_evenly(item_with_margin, (5, 6));
let root = item_grid;
// layout.normalize(root);
let width = layout.width(root);
let height = layout.height(root);
let canvas = BitmapCanvas::new(width, height);
let mut drawer = LayoutDrawer::new(layout, canvas);
let asset_cache = &ctx.data().jacket_cache;
let bg = &asset_cache.b30_background;
drawer.blit_rbg(
root,
(
-((bg.width() - width) as i32) / 2,
-((bg.height() - height) as i32) / 2,
),
bg.dimensions(),
bg.as_raw(),
);
for (i, origin) in item_origins.enumerate() {
drawer
.layout
.edit_to_relative(item_with_margin, item_grid, origin.0, origin.1);
drawer.fill(top_area, (59, 78, 102, 255));
let (_play, song, chart) = &plays[i];
// {{{ Display jacket
let jacket = chart.cached_jacket.as_ref().ok_or_else(|| {
format!(
"Cannot find jacket for chart {} [{:?}]",
song.title, chart.difficulty
)
})?;
drawer.blit_rbg(
jacket_area,
(0, 0),
jacket.bitmap.dimensions(),
&jacket.bitmap.as_raw(),
);
// }}}
// {{{ Display difficulty background
let diff_bg = &asset_cache.diff_backgrounds[chart.difficulty.to_index()];
drawer.blit_rbga(
jacket_area,
(
BITMAP_IMAGE_SIZE as i32 - (diff_bg.width() as i32) / 2,
-(diff_bg.height() as i32) / 2,
),
diff_bg.dimensions(),
&diff_bg.as_raw(),
);
// }}}
// {{{ Display difficulty text
let x_offset = if chart.level.ends_with("+") {
3
} else if chart.level == "11" {
-2
} else {
0
};
// jacket_area.draw_text(
// &chart.level,
// &TextStyle::from(("Exo", 30).into_font())
// .color(&WHITE)
// .with_anchor::<RGBAColor>(Pos {
// h_pos: HPos::Center,
// v_pos: VPos::Center,
// })
// .into_text_style(&jacket_area),
// (BITMAP_IMAGE_SIZE as i32 + x_offset, 2),
// )?;
// }}}
// {{{ Display chart name
// Draw background
drawer.fill(bottom_area, (0x82, 0x71, 0xA7, 255));
let tx = 10;
let ty = drawer.layout.height(bottom_area) as i32 / 2;
// let text = &song.title;
// let mut size = 30;
// let mut text_style = TextStyle::from(("Exo", size).into_font().style(FontStyle::Bold))
// .with_anchor::<RGBAColor>(Pos {
// h_pos: HPos::Left,
// v_pos: VPos::Center,
// })
// .into_text_style(&bottom_area);
//
// while text_style.font.layout_box(text).unwrap().1 .0 >= item_area.0 as i32 - 20 {
// size -= 3;
// text_style.font = ("Exo", size).into_font();
// }
//
// Draw drop shadow
// bottom_area.draw_text(
// &song.title,
// &text_style.color(&RGBAColor(0, 0, 0, 0.2)),
// (tx + 3, ty + 3),
// )?;
// bottom_area.draw_text(
// &song.title,
// &text_style.color(&RGBAColor(0, 0, 0, 0.2)),
// (tx - 3, ty + 3),
// )?;
// bottom_area.draw_text(
// &song.title,
// &text_style.color(&RGBAColor(0, 0, 0, 0.2)),
// (tx + 3, ty - 3),
// )?;
// bottom_area.draw_text(
// &song.title,
// &text_style.color(&RGBAColor(0, 0, 0, 0.2)),
// (tx - 3, ty - 3),
// )?;
// Draw text
// bottom_area.draw_text(&song.title, &text_style.color(&WHITE), (tx, ty))?;
// }}}
// {{{ Display index
let bg = &asset_cache.count_background;
// Draw background
drawer.blit_rbga(item_area, (-8, jacket_margin as i32), bg.dimensions(), bg);
// let text_style = TextStyle::from(("Exo", 30).into_font().style(FontStyle::Bold))
// .with_anchor::<RGBAColor>(Pos {
// h_pos: HPos::Left,
// v_pos: VPos::Center,
// })
// .into_text_style(&area);
let tx = 7;
let ty = (jacket_margin + bg.height() as i32 / 2) - 3;
// Draw drop shadow
// area.draw_text(
// &format!("#{}", i + 1),
// &text_style.color(&BLACK),
// (tx + 2, ty + 2),
// )?;
// Draw main text
// area.draw_text(&format!("#{}", i + 1), &text_style.color(&WHITE), (tx, ty))?;
// }}}
}
let mut out_buffer = Vec::new();
let image: ImageBuffer<Rgb<u8>, _> =
ImageBuffer::from_raw(width, height, drawer.canvas.buffer).unwrap();
let mut cursor = Cursor::new(&mut out_buffer);
image.write_to(&mut cursor, image::ImageFormat::Png)?;
let reply = CreateReply::default().attachment(CreateAttachment::bytes(out_buffer, "b30.png"));
ctx.send(reply).await?;
Ok(())
}
// }}}