forked from noxious/server
Stash
This commit is contained in:
parent
d6681f9af7
commit
c59b391a6a
Binary file not shown.
@ -489,7 +489,7 @@ export default class InitCommand extends BaseCommand {
|
|||||||
|
|
||||||
private async createCharacterHair(): Promise<void> {
|
private async createCharacterHair(): Promise<void> {
|
||||||
const hairSprite = new Sprite()
|
const hairSprite = new Sprite()
|
||||||
hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1')
|
hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1').setWidth(30).setHeight(40)
|
||||||
await hairSprite.save()
|
await hairSprite.save()
|
||||||
|
|
||||||
const frontAction = new SpriteAction()
|
const frontAction = new SpriteAction()
|
||||||
|
@ -65,9 +65,12 @@ export class AvatarController extends BaseController {
|
|||||||
return this.sendError(res, 'Body sprite file not found', 404)
|
return this.sendError(res, 'Body sprite file not found', 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get body sprite metadata
|
||||||
|
const bodyMetadata = await sharp(bodySpritePath).metadata()
|
||||||
|
|
||||||
let avatar = sharp(bodySpritePath).extend({
|
let avatar = sharp(bodySpritePath).extend({
|
||||||
top: 2,
|
top: 0,
|
||||||
bottom: 2,
|
bottom: 0,
|
||||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -76,7 +79,21 @@ export class AvatarController extends BaseController {
|
|||||||
if (characterHair?.sprite?.id) {
|
if (characterHair?.sprite?.id) {
|
||||||
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
||||||
if (fs.existsSync(hairSpritePath)) {
|
if (fs.existsSync(hairSpritePath)) {
|
||||||
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
|
// Resize hair sprite to match body dimensions
|
||||||
|
const resizedHair = await sharp(hairSpritePath)
|
||||||
|
.resize(bodyMetadata.width, bodyMetadata.height, {
|
||||||
|
fit: 'contain',
|
||||||
|
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||||
|
})
|
||||||
|
.toBuffer()
|
||||||
|
|
||||||
|
avatar = avatar.composite([
|
||||||
|
{
|
||||||
|
input: resizedHair,
|
||||||
|
left: 0,
|
||||||
|
top: -27 // Apply vertical offset
|
||||||
|
}
|
||||||
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +101,7 @@ export class AvatarController extends BaseController {
|
|||||||
res.setHeader('Content-Type', 'image/png')
|
res.setHeader('Content-Type', 'image/png')
|
||||||
return avatar.pipe(res)
|
return avatar.pipe(res)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Avatar generation error:', error)
|
||||||
return this.sendError(res, 'Error generating avatar', 500)
|
return this.sendError(res, 'Error generating avatar', 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ interface EffectiveDimensions {
|
|||||||
type Payload = {
|
type Payload = {
|
||||||
id: UUID
|
id: UUID
|
||||||
name: string
|
name: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
spriteActions: Array<{
|
spriteActions: Array<{
|
||||||
action: string
|
action: string
|
||||||
sprites: SpriteImage[]
|
sprites: SpriteImage[]
|
||||||
@ -56,7 +58,7 @@ export default class SpriteUpdateEvent extends BaseEvent {
|
|||||||
await spriteRepository.getEntityManager().populate(sprite, ['spriteActions'])
|
await spriteRepository.getEntityManager().populate(sprite, ['spriteActions'])
|
||||||
|
|
||||||
// Update sprite in database
|
// Update sprite in database
|
||||||
await sprite.setName(data.name).setUpdatedAt(new Date()).save()
|
await sprite.setName(data.name).setWidth(data.width).setHeight(data.height).setUpdatedAt(new Date()).save()
|
||||||
|
|
||||||
// First verify all sprite sheets can be generated
|
// First verify all sprite sheets can be generated
|
||||||
for (const actionData of data.spriteActions) {
|
for (const actionData of data.spriteActions) {
|
||||||
@ -89,13 +91,13 @@ export default class SpriteUpdateEvent extends BaseEvent {
|
|||||||
sprite.getSpriteActions().add(spriteAction)
|
sprite.getSpriteActions().add(spriteAction)
|
||||||
|
|
||||||
spriteAction
|
spriteAction
|
||||||
.setAction(actionData.action)
|
.setAction(actionData.action)
|
||||||
.setSprites(actionData.sprites)
|
.setSprites(actionData.sprites)
|
||||||
.setOriginX(actionData.originX)
|
.setOriginX(actionData.originX)
|
||||||
.setOriginY(actionData.originY)
|
.setOriginY(actionData.originY)
|
||||||
.setFrameWidth(await this.calculateMaxWidth(actionData.sprites))
|
.setFrameWidth(await this.calculateMaxWidth(actionData.sprites))
|
||||||
.setFrameHeight(totalHeight)
|
.setFrameHeight(totalHeight)
|
||||||
.setFrameRate(actionData.frameRate)
|
.setFrameRate(actionData.frameRate)
|
||||||
|
|
||||||
await spriteRepository.getEntityManager().persistAndFlush(spriteAction)
|
await spriteRepository.getEntityManager().persistAndFlush(spriteAction)
|
||||||
}
|
}
|
||||||
@ -126,27 +128,27 @@ export default class SpriteUpdateEvent extends BaseEvent {
|
|||||||
|
|
||||||
// Process images and create sprite sheet
|
// Process images and create sprite sheet
|
||||||
const processedImages = await Promise.all(
|
const processedImages = await Promise.all(
|
||||||
sprites.map(async (sprite, index) => {
|
sprites.map(async (sprite, index) => {
|
||||||
const { width, height, offsetX, offsetY } = await this.processImage(sprite)
|
const { width, height, offsetX, offsetY } = await this.processImage(sprite)
|
||||||
const uri = sprite.url.split(';base64,').pop()
|
const uri = sprite.url.split(';base64,').pop()
|
||||||
if (!uri) throw new Error('Invalid base64 image')
|
if (!uri) throw new Error('Invalid base64 image')
|
||||||
const buffer = Buffer.from(uri, 'base64')
|
const buffer = Buffer.from(uri, 'base64')
|
||||||
|
|
||||||
// Create individual frame
|
// Create individual frame
|
||||||
const left = offsetX >= 0 ? offsetX : 0
|
const left = offsetX >= 0 ? offsetX : 0
|
||||||
const verticalOffset = totalHeight - height - (offsetY >= 0 ? offsetY : 0)
|
const verticalOffset = totalHeight - height - (offsetY >= 0 ? offsetY : 0)
|
||||||
return sharp({
|
return sharp({
|
||||||
create: {
|
create: {
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: totalHeight,
|
height: totalHeight,
|
||||||
channels: 4,
|
channels: 4,
|
||||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.composite([{ input: buffer, left, top: verticalOffset }])
|
||||||
|
.png()
|
||||||
|
.toBuffer()
|
||||||
})
|
})
|
||||||
.composite([{ input: buffer, left, top: verticalOffset }])
|
|
||||||
.png()
|
|
||||||
.toBuffer()
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combine frames into sprite sheet
|
// Combine frames into sprite sheet
|
||||||
@ -158,15 +160,15 @@ export default class SpriteUpdateEvent extends BaseEvent {
|
|||||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.composite(
|
.composite(
|
||||||
processedImages.map((buffer, index) => ({
|
processedImages.map((buffer, index) => ({
|
||||||
input: buffer,
|
input: buffer,
|
||||||
left: index * maxWidth,
|
left: index * maxWidth,
|
||||||
top: 0
|
top: 0
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
.png()
|
.png()
|
||||||
.toBuffer()
|
.toBuffer()
|
||||||
|
|
||||||
// Ensure directory exists
|
// Ensure directory exists
|
||||||
const dir = `public/sprites/${spriteId}`
|
const dir = `public/sprites/${spriteId}`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user