79 lines
3.1 KiB
Vue
79 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|||
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||
|
|
import { generateSpriteSheet, getBotColors, FRAME_SIZE, MAX_FRAMES, TOTAL_ROWS, ANIMATIONS } from '../game/sprites'
|
||
|
|
|
||
|
|
const currentFrame = ref(0)
|
||
|
|
let intervalId: number | null = null
|
||
|
|
|
||
|
|
const tiers = [
|
||
|
|
{ tier: 0, seed: 'lorem', label: 'Tier 0 - Unranked', desc: 'Clawbot' },
|
||
|
|
{ tier: 1, seed: 'null', label: 'Tier 1 - Rookie', desc: 'Basic bot' },
|
||
|
|
{ tier: 2, seed: 'deep', label: 'Tier 2 - Rising', desc: 'Belt + feet' },
|
||
|
|
{ tier: 3, seed: 'quantum', label: 'Tier 3 - Contender', desc: 'Gloves + shoulders' },
|
||
|
|
{ tier: 4, seed: 'skull', label: 'Tier 4 - Champion', desc: 'Headband + aura' },
|
||
|
|
{ tier: 5, seed: 'architect', label: 'Tier 5 - Legend', desc: 'Crown + gold' },
|
||
|
|
]
|
||
|
|
|
||
|
|
const animNames = Object.keys(ANIMATIONS) as (keyof typeof ANIMATIONS)[]
|
||
|
|
const loadedImages: HTMLImageElement[] = []
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
for (const t of tiers) {
|
||
|
|
const colors = getBotColors(t.seed)
|
||
|
|
const dataUrl = generateSpriteSheet(t.seed, t.tier, colors.primary, colors.secondary)
|
||
|
|
const img = new Image()
|
||
|
|
img.src = dataUrl
|
||
|
|
img.onload = () => renderAll()
|
||
|
|
loadedImages.push(img)
|
||
|
|
}
|
||
|
|
|
||
|
|
intervalId = window.setInterval(() => {
|
||
|
|
currentFrame.value = (currentFrame.value + 1) % MAX_FRAMES
|
||
|
|
renderAll()
|
||
|
|
}, 180)
|
||
|
|
})
|
||
|
|
|
||
|
|
onUnmounted(() => { if (intervalId) clearInterval(intervalId) })
|
||
|
|
|
||
|
|
function renderAll() {
|
||
|
|
const canvases = document.querySelectorAll<HTMLCanvasElement>('.tier-preview')
|
||
|
|
canvases.forEach((canvas, idx) => {
|
||
|
|
if (idx >= loadedImages.length || !loadedImages[idx].complete) return
|
||
|
|
const ctx = canvas.getContext('2d')!
|
||
|
|
const displaySize = 128
|
||
|
|
canvas.width = displaySize * animNames.length
|
||
|
|
canvas.height = displaySize
|
||
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||
|
|
ctx.imageSmoothingEnabled = false
|
||
|
|
|
||
|
|
animNames.forEach((anim, animIdx) => {
|
||
|
|
const row = ANIMATIONS[anim].row
|
||
|
|
const frames = ANIMATIONS[anim].frames
|
||
|
|
const frame = currentFrame.value % frames
|
||
|
|
ctx.drawImage(loadedImages[idx], frame * FRAME_SIZE, row * FRAME_SIZE, FRAME_SIZE, FRAME_SIZE, animIdx * displaySize, 0, displaySize, displaySize)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div class="p-4 max-w-7xl mx-auto space-y-4">
|
||
|
|
<h1 class="font-display font-black text-2xl text-neon-pink glow-pink tracking-wider">SPRITE TIER PREVIEW</h1>
|
||
|
|
|
||
|
|
<div class="flex gap-0">
|
||
|
|
<div class="w-[110px] flex-shrink-0" />
|
||
|
|
<div v-for="anim in animNames" :key="anim" class="flex-1 text-center font-pixel text-[8px] text-neon-cyan uppercase tracking-wider">{{ anim }}</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div v-for="(t, idx) in tiers" :key="t.tier" class="border border-border rounded-lg bg-surface-raised/50 p-2 flex items-center gap-3">
|
||
|
|
<div class="w-[110px] flex-shrink-0">
|
||
|
|
<p class="font-display font-black text-xs tracking-wider" :class="`tier-${t.tier}`">{{ t.label }}</p>
|
||
|
|
<p class="font-mono text-[8px] text-text-muted">{{ t.desc }}</p>
|
||
|
|
</div>
|
||
|
|
<div class="flex-1 overflow-hidden">
|
||
|
|
<canvas class="tier-preview block h-[128px]" style="image-rendering: pixelated;" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|