forked from syntaxbullet/AuroraBot-discord
136 lines
5.1 KiB
TypeScript
136 lines
5.1 KiB
TypeScript
import { GlobalFonts, createCanvas, loadImage } from '@napi-rs/canvas';
|
|
import path from 'path';
|
|
|
|
// Register Fonts (same as studentID.ts)
|
|
const fontDir = path.join(process.cwd(), 'bot', 'assets', 'fonts');
|
|
GlobalFonts.registerFromPath(path.join(fontDir, 'IBMPlexSansCondensed-SemiBold.ttf'), 'IBMPlexSansCondensed-SemiBold');
|
|
GlobalFonts.registerFromPath(path.join(fontDir, 'IBMPlexMono-Bold.ttf'), 'IBMPlexMono-Bold');
|
|
|
|
export async function generateLootdropCard(amount: number, currency: string): Promise<Buffer> {
|
|
const templatePath = path.join(process.cwd(), 'bot', 'assets', 'graphics', 'lootdrop', 'template.png');
|
|
const template = await loadImage(templatePath);
|
|
|
|
const canvas = createCanvas(template.width, template.height);
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// Draw Template
|
|
ctx.drawImage(template, 0, 0);
|
|
|
|
// Draw Lootdrop Text (Title-ish)
|
|
ctx.save();
|
|
ctx.font = '48px IBMPlexSansCondensed-SemiBold';
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.textAlign = 'center';
|
|
ctx.shadowBlur = 10;
|
|
ctx.shadowColor = 'rgba(255, 255, 255, 0.5)';
|
|
// Center of lower half (512-1024) is roughly 768
|
|
ctx.fillText('A STAR IS FALLING', canvas.width / 2, 660);
|
|
ctx.restore();
|
|
|
|
// Draw Reward Amount
|
|
ctx.save();
|
|
ctx.font = '72px IBMPlexMono-Bold';
|
|
ctx.fillStyle = '#DAC7A1';
|
|
ctx.textAlign = 'center';
|
|
//ctx.shadowBlur = 15;
|
|
//ctx.shadowColor = 'rgba(255, 215, 0, 0.8)';
|
|
ctx.fillText(`${amount} ${currency}`, canvas.width / 2, 760); // Below title
|
|
ctx.restore();
|
|
|
|
// Crop the image by 64px on all sides
|
|
const croppedWidth = template.width - 128;
|
|
const croppedHeight = template.height - 128;
|
|
const croppedCanvas = createCanvas(croppedWidth, croppedHeight);
|
|
const croppedCtx = croppedCanvas.getContext('2d');
|
|
|
|
// Draw the original canvas onto the cropped canvas, shifted by -64
|
|
croppedCtx.drawImage(canvas, -64, -64);
|
|
|
|
return croppedCanvas.toBuffer('image/png');
|
|
}
|
|
|
|
export async function generateClaimedLootdropCard(amount: number, currency: string, username: string, avatarUrl: string): Promise<Buffer> {
|
|
const templatePath = path.join(process.cwd(), 'bot', 'assets', 'graphics', 'lootdrop', 'template.png');
|
|
const template = await loadImage(templatePath);
|
|
|
|
const canvas = createCanvas(template.width, template.height);
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// Draw Template
|
|
ctx.drawImage(template, 0, 0);
|
|
|
|
// Add a colored overlay to signify "claimed"
|
|
ctx.fillStyle = 'rgba(10, 10, 20, 0.85)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Draw Claimed Text (Title-ish)
|
|
ctx.save();
|
|
ctx.font = '48px IBMPlexSansCondensed-SemiBold';
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.textAlign = 'center';
|
|
ctx.shadowBlur = 10;
|
|
ctx.shadowColor = 'rgba(255, 255, 255, 0.5)';
|
|
ctx.fillText('STAR CLAIMED', canvas.width / 2, 660);
|
|
ctx.restore();
|
|
|
|
// Draw "by username" with Avatar
|
|
ctx.save();
|
|
ctx.font = '36px IBMPlexSansCondensed-SemiBold';
|
|
ctx.fillStyle = '#AAAAAA';
|
|
|
|
// Calculate layout for centering Group (Avatar + Text)
|
|
const text = `by ${username}`;
|
|
const textMetrics = ctx.measureText(text);
|
|
const textWidth = textMetrics.width;
|
|
const avatarSize = 50;
|
|
const gap = 15;
|
|
const totalWidth = avatarSize + gap + textWidth;
|
|
|
|
const startX = (canvas.width - totalWidth) / 2;
|
|
const baselineY = 830;
|
|
|
|
// Draw Avatar
|
|
try {
|
|
const avatar = await loadImage(avatarUrl);
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
// Center avatar vertically relative to text roughly (baseline - ~half cap height)
|
|
// 36px text ~ 27px cap height. Center roughly at baselineY - 14
|
|
const avatarCenterY = baselineY - 14;
|
|
ctx.arc(startX + avatarSize / 2, avatarCenterY, avatarSize / 2, 0, Math.PI * 2);
|
|
ctx.closePath();
|
|
ctx.clip();
|
|
ctx.drawImage(avatar, startX, avatarCenterY - avatarSize / 2, avatarSize, avatarSize);
|
|
ctx.restore();
|
|
} catch (e) {
|
|
// Fallback if avatar fails to load, just don't draw it (or maybe shift text?)
|
|
// For now, let's just proceed, the text will be off-center if avatar is missing but that's acceptable edge case
|
|
console.error("Failed to load avatar", e);
|
|
}
|
|
|
|
// Draw Text
|
|
ctx.textAlign = 'left';
|
|
ctx.fillText(text, startX + avatarSize + gap, baselineY);
|
|
ctx.restore();
|
|
|
|
ctx.save();
|
|
ctx.font = '72px IBMPlexMono-Bold'; // Match Amount size
|
|
ctx.fillStyle = '#E6D2B5'; // Lighter gold/beige for better contrast
|
|
ctx.textAlign = 'center';
|
|
ctx.shadowBlur = 10;
|
|
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)'; // Dark shadow for contrast
|
|
ctx.fillText(`${amount} ${currency}`, canvas.width / 2, 760); // Same position as Unclaimed Amount
|
|
ctx.restore();
|
|
|
|
// Crop the image by 64px on all sides
|
|
const croppedWidth = template.width - 128;
|
|
const croppedHeight = template.height - 128;
|
|
const croppedCanvas = createCanvas(croppedWidth, croppedHeight);
|
|
const croppedCtx = croppedCanvas.getContext('2d');
|
|
|
|
// Draw the original canvas onto the cropped canvas, shifted by -64
|
|
croppedCtx.drawImage(canvas, -64, -64);
|
|
|
|
return croppedCanvas.toBuffer('image/png');
|
|
}
|