forked from noxious/server
86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
/**
|
|
* 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
|