poixpixel-discord-bot/src/util/achievementCardGenerator.ts
Ahmad 2f5c3499e7
feat: add achievement system
Signed-off-by: Ahmad <103906421+ahmadk953@users.noreply.github.com>
2025-04-16 16:52:44 -04:00

115 lines
2.8 KiB
TypeScript

import Canvas, { GlobalFonts } from '@napi-rs/canvas';
import { AttachmentBuilder } from 'discord.js';
import path from 'path';
import * as schema from '@/db/schema.js';
import { drawMultilineText, roundRect } from './helpers.js';
const __dirname = path.resolve();
/**
* Generates an achievement card for a user
* TODO: Make this look better
* @param achievement - The achievement to generate a card for
* @returns - The generated card as an AttachmentBuilder
*/
export async function generateAchievementCard(
achievement: schema.achievementDefinitionsTableTypes,
): Promise<AttachmentBuilder> {
GlobalFonts.registerFromPath(
path.join(__dirname, 'assets', 'fonts', 'Manrope-Bold.ttf'),
'Manrope Bold',
);
GlobalFonts.registerFromPath(
path.join(__dirname, 'assets', 'fonts', 'Manrope-Regular.ttf'),
'Manrope',
);
const width = 600;
const height = 180;
const canvas = Canvas.createCanvas(width, height);
const ctx = canvas.getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, width, 0);
gradient.addColorStop(0, '#5865F2');
gradient.addColorStop(1, '#EB459E');
ctx.fillStyle = gradient;
roundRect({ ctx, x: 0, y: 0, width, height, radius: 16, fill: true });
ctx.lineWidth = 4;
ctx.strokeStyle = '#FFFFFF';
roundRect({
ctx,
x: 2,
y: 2,
width: width - 4,
height: height - 4,
radius: 16,
fill: false,
});
const padding = 40;
const iconSize = 72;
const iconX = padding;
const iconY = height / 2 - iconSize / 2;
try {
const iconImage = await Canvas.loadImage(
achievement.imageUrl ||
path.join(__dirname, 'assets', 'images', 'trophy.png'),
);
ctx.save();
ctx.beginPath();
ctx.arc(
iconX + iconSize / 2,
iconY + iconSize / 2,
iconSize / 2,
0,
Math.PI * 2,
);
ctx.clip();
ctx.drawImage(iconImage, iconX, iconY, iconSize, iconSize);
ctx.restore();
ctx.beginPath();
ctx.arc(
iconX + iconSize / 2,
iconY + iconSize / 2,
iconSize / 2 + 4,
0,
Math.PI * 2,
);
ctx.lineWidth = 3;
ctx.strokeStyle = '#FFFFFF';
ctx.stroke();
} catch (e) {
console.error('Error loading icon:', e);
}
const textX = iconX + iconSize + 24;
const titleY = 60;
const nameY = titleY + 35;
const descY = nameY + 34;
ctx.fillStyle = '#FFFFFF';
ctx.font = '22px "Manrope Bold"';
ctx.fillText('Achievement Unlocked!', textX, titleY);
ctx.font = '32px "Manrope Bold"';
ctx.fillText(achievement.name, textX, nameY);
ctx.font = '20px "Manrope"';
drawMultilineText(
ctx,
achievement.description,
textX,
descY,
width - textX - 32,
24,
);
const buffer = canvas.toBuffer('image/png');
return new AttachmentBuilder(buffer, { name: 'achievement.png' });
}