forked from syntaxbullet/AuroraBot-discord
refactor: initial moves
This commit is contained in:
135
bot/graphics/lootdrop.ts
Normal file
135
bot/graphics/lootdrop.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { GlobalFonts, createCanvas, loadImage } from '@napi-rs/canvas';
|
||||
import path from 'path';
|
||||
|
||||
// Register Fonts (same as studentID.ts)
|
||||
const fontDir = path.join(process.cwd(), 'src', '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(), 'src', '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(), 'src', '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');
|
||||
}
|
||||
Reference in New Issue
Block a user