2026-03-06 22:13:19 +00:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
2026-03-07 08:52:24 +00:00
|
|
|
import { generateSpriteSheet, getBotColors, FRAME_SIZE, ANIMATIONS, type SpriteCustomization } from '../game/sprites'
|
2026-03-06 22:13:19 +00:00
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
seed: string
|
|
|
|
|
archetype?: string
|
2026-03-07 08:21:20 +00:00
|
|
|
tier?: number
|
2026-03-06 22:13:19 +00:00
|
|
|
size?: number
|
2026-03-07 08:52:24 +00:00
|
|
|
customization?: SpriteCustomization
|
2026-03-06 22:13:19 +00:00
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
const canvasRef = ref<HTMLCanvasElement>()
|
|
|
|
|
let img: HTMLImageElement | null = null
|
|
|
|
|
let frame = 0
|
|
|
|
|
let animHandle: ReturnType<typeof setTimeout> | null = null
|
|
|
|
|
|
|
|
|
|
function render() {
|
|
|
|
|
if (!canvasRef.value || !img?.complete) return
|
|
|
|
|
const ctx = canvasRef.value.getContext('2d')!
|
|
|
|
|
const displaySize = props.size || 64
|
|
|
|
|
canvasRef.value.width = displaySize
|
|
|
|
|
canvasRef.value.height = displaySize
|
|
|
|
|
ctx.clearRect(0, 0, displaySize, displaySize)
|
|
|
|
|
ctx.imageSmoothingEnabled = false
|
|
|
|
|
|
|
|
|
|
const idleAnim = ANIMATIONS.idle
|
|
|
|
|
const f = frame % idleAnim.frames
|
|
|
|
|
ctx.drawImage(
|
|
|
|
|
img,
|
|
|
|
|
f * FRAME_SIZE, idleAnim.row * FRAME_SIZE, FRAME_SIZE, FRAME_SIZE,
|
|
|
|
|
0, 0, displaySize, displaySize,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
frame++
|
|
|
|
|
animHandle = setTimeout(() => render(), 180)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadSprite() {
|
|
|
|
|
const colors = getBotColors(props.seed)
|
2026-03-07 08:52:24 +00:00
|
|
|
const dataUrl = generateSpriteSheet(props.seed, props.tier || 0, colors.primary, colors.secondary, props.archetype, props.customization)
|
2026-03-06 22:13:19 +00:00
|
|
|
img = new Image()
|
|
|
|
|
img.onload = () => render()
|
|
|
|
|
img.src = dataUrl
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => loadSprite())
|
|
|
|
|
|
2026-03-07 08:52:24 +00:00
|
|
|
watch(() => [props.seed, props.archetype, props.customization], () => {
|
2026-03-06 22:13:19 +00:00
|
|
|
if (animHandle) clearTimeout(animHandle)
|
|
|
|
|
frame = 0
|
|
|
|
|
loadSprite()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (animHandle) clearTimeout(animHandle)
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<canvas
|
|
|
|
|
ref="canvasRef"
|
|
|
|
|
:style="{ width: `${size || 64}px`, height: `${size || 64}px`, imageRendering: 'pixelated' }"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|