/** * Avatar generator API routes */ import { Router, Request, Response } from 'express' import sharp from 'sharp' import fs from 'fs' import CharacterRepository from '#repositories/characterRepository' import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterTypeRepository from '#repositories/characterTypeRepository' import { getPublicPath } from '#application/storage' const router = Router() interface AvatarOptions { characterTypeId: number characterHairId?: number } async function generateAvatar(res: Response, options: AvatarOptions) { try { const characterType = await CharacterTypeRepository.getById(options.characterTypeId) if (!characterType?.sprite?.id) { return res.status(404).json({ message: 'Character type not found' }) } const bodySpritePath = getPublicPath('sprites', characterType.sprite.id, 'idle_right_down.png') if (!fs.existsSync(bodySpritePath)) { console.error(`Body sprite file not found: ${bodySpritePath}`) return res.status(404).json({ message: 'Body sprite file not found' }) } let avatar = sharp(bodySpritePath).extend({ top: 2, bottom: 2, background: { r: 0, g: 0, b: 0, alpha: 0 } }) if (options.characterHairId) { const characterHair = await CharacterHairRepository.getById(options.characterHairId) if (characterHair?.sprite?.id) { const hairSpritePath = getPublicPath('sprites', characterHair.sprite.id, 'front.png') if (fs.existsSync(hairSpritePath)) { avatar = avatar.composite([ { input: hairSpritePath, gravity: 'north' // Top is originY in min @TODO finish me #287 // top: Math.round(Number(characterHair.sprite!.spriteActions.find((action) => action.action === 'front')?.originY ?? 0)), // left: 0 } ]) } else { console.error(`Hair sprite file not found: ${hairSpritePath}`) } } } res.setHeader('Content-Type', 'image/png') return avatar.pipe(res) } catch (error) { console.error('Error generating avatar:', error) return res.status(500).json({ message: 'Error generating avatar' }) } } router.get('/avatar/:characterName', async (req: Request, res: Response) => { const character = await CharacterRepository.getByName(req.params.characterName) if (!character?.characterType) { return res.status(404).json({ message: 'Character or character type not found' }) } return generateAvatar(res, { characterTypeId: character.characterType.id, characterHairId: character.characterHair?.id }) }) router.get('/avatar/s/:characterTypeId/:characterHairId?', async (req: Request, res: Response) => { return generateAvatar(res, { characterTypeId: parseInt(req.params.characterTypeId), characterHairId: req.params.characterHairId ? parseInt(req.params.characterHairId) : undefined }) }) export default router