| |
| |
| |
| |
|
|
| import fs from 'fs'; |
| import path from 'path'; |
| import { fileURLToPath } from 'url'; |
|
|
| const __filename = fileURLToPath(import.meta.url); |
| const __dirname = path.dirname(__filename); |
| const projectRoot = path.join(__dirname, '..'); |
|
|
| |
| const assetsDir = path.join(projectRoot, 'public', 'assets'); |
| const backdropsDir = path.join(assetsDir, 'backdrops'); |
| const musicDir = path.join(assetsDir, 'music'); |
|
|
| |
| fs.mkdirSync(backdropsDir, { recursive: true }); |
| fs.mkdirSync(musicDir, { recursive: true }); |
|
|
| |
| const levelPalettes = [ |
| ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', '#F7DC6F', '#BB8FCE'], |
| ['#3498DB', '#E74C3C', '#2ECC71', '#F39C12', '#9B59B6', '#1ABC9C', '#E67E22'], |
| ['#FF1744', '#00E676', '#2979FF', '#FFEA00', '#D500F9', '#00E5FF', '#FF9100'], |
| ['#8E44AD', '#16A085', '#C0392B', '#F39C12', '#2980B9', '#27AE60', '#D35400'], |
| ['#FF6F61', '#6B5B95', '#88B04B', '#F7CAC9', '#92A8D1', '#955251', '#B565A7'], |
| ['#34495E', '#E74C3C', '#ECF0F1', '#3498DB', '#2ECC71', '#F39C12', '#9B59B6'], |
| ['#FF4500', '#FFD700', '#00CED1', '#FF1493', '#00FF00', '#1E90FF', '#FF69B4'], |
| ['#8B4513', '#DAA520', '#CD853F', '#D2691E', '#B8860B', '#A0522D', '#DEB887'], |
| ['#000080', '#4B0082', '#8B008B', '#9400D3', '#9932CC', '#BA55D3', '#DA70D6'], |
| ['#FF0000', '#FF4500', '#FF6347', '#FF7F50', '#FFA500', '#FFD700', '#FFFF00'] |
| ]; |
|
|
| |
| async function generateBackdrop(level, palette) { |
| const { createCanvas } = await import('canvas'); |
| const canvas = createCanvas(256, 224); |
| const ctx = canvas.getContext('2d'); |
|
|
| |
| const gradient = ctx.createLinearGradient(0, 0, 256, 224); |
| gradient.addColorStop(0, palette[0]); |
| gradient.addColorStop(0.5, palette[1]); |
| gradient.addColorStop(1, palette[2]); |
| ctx.fillStyle = gradient; |
| ctx.fillRect(0, 0, 256, 224); |
|
|
| |
| ctx.fillStyle = palette[3]; |
| ctx.globalAlpha = 0.1; |
| for (let i = 0; i < 50; i++) { |
| const x = Math.random() * 256; |
| const y = Math.random() * 224; |
| const size = Math.random() * 20 + 5; |
| ctx.fillRect(x, y, size, size); |
| } |
|
|
| |
| ctx.globalAlpha = 0.3; |
| ctx.fillStyle = palette[4]; |
| ctx.font = 'bold 48px monospace'; |
| ctx.fillText(`L${level}`, 10, 50); |
|
|
| |
| |
|
|
| |
| ctx.globalAlpha = 0.4; |
| ctx.fillStyle = '#000000'; |
| ctx.fillRect(88, 32, 80, 160); |
|
|
| |
| ctx.globalAlpha = 1.0; |
| ctx.strokeStyle = '#FFFF00'; |
| ctx.lineWidth = 3; |
| ctx.strokeRect(88, 32, 80, 160); |
|
|
| |
| ctx.fillStyle = '#FFFF00'; |
| const markerSize = 8; |
| |
| ctx.fillRect(88 - markerSize, 32 - markerSize, markerSize, markerSize); |
| |
| ctx.fillRect(88 + 80, 32 - markerSize, markerSize, markerSize); |
| |
| ctx.fillRect(88 - markerSize, 32 + 160, markerSize, markerSize); |
| |
| ctx.fillRect(88 + 80, 32 + 160, markerSize, markerSize); |
|
|
| |
| ctx.globalAlpha = 0.8; |
| ctx.fillStyle = '#FFFFFF'; |
| ctx.font = 'bold 10px monospace'; |
| ctx.fillText('PLAY AREA', 92, 28); |
| ctx.fillText('80x160px', 95, 200); |
| ctx.fillText(`(${88},${32})`, 92, 44); |
|
|
| |
| ctx.globalAlpha = 0.15; |
| ctx.strokeStyle = '#FFFFFF'; |
| ctx.lineWidth = 1; |
| |
| for (let x = 88; x <= 168; x += 8) { |
| ctx.beginPath(); |
| ctx.moveTo(x, 32); |
| ctx.lineTo(x, 192); |
| ctx.stroke(); |
| } |
| |
| for (let y = 32; y <= 192; y += 8) { |
| ctx.beginPath(); |
| ctx.moveTo(88, y); |
| ctx.lineTo(168, y); |
| ctx.stroke(); |
| } |
|
|
| return canvas.toBuffer('image/png'); |
| } |
|
|
| |
| function generateSilentMP3() { |
| |
| |
| const mp3Header = Buffer.from([ |
| 0xFF, 0xFB, 0x90, 0x00, |
| ]); |
| |
| |
| const frames = 38; |
| const frameSize = 417; |
| const buffer = Buffer.alloc(frames * frameSize); |
| |
| for (let i = 0; i < frames; i++) { |
| mp3Header.copy(buffer, i * frameSize); |
| } |
| |
| return buffer; |
| } |
|
|
| |
| async function generateAllAssets() { |
| console.log('Generating placeholder assets...\n'); |
| |
| |
| let canvasAvailable = false; |
| try { |
| await import('canvas'); |
| canvasAvailable = true; |
| } catch (e) { |
| console.log('⚠️ Canvas module not available. Install with: npm install canvas'); |
| console.log(' Skipping backdrop generation. You can add your own PNG files.\n'); |
| } |
| |
| for (let level = 1; level <= 10; level++) { |
| const levelBackdropDir = path.join(backdropsDir, `level-${level}`); |
| const levelMusicDir = path.join(musicDir, `level-${level}`); |
| |
| fs.mkdirSync(levelBackdropDir, { recursive: true }); |
| fs.mkdirSync(levelMusicDir, { recursive: true }); |
| |
| |
| if (canvasAvailable) { |
| const backdropPath = path.join(levelBackdropDir, 'backdrop.png'); |
| const backdropBuffer = await generateBackdrop(level, levelPalettes[level - 1]); |
| fs.writeFileSync(backdropPath, backdropBuffer); |
| console.log(`✓ Generated backdrop for level ${level}`); |
| } else { |
| console.log(`⊘ Skipped backdrop for level ${level} (canvas not available)`); |
| } |
| |
| |
| const musicPath = path.join(levelMusicDir, 'track.mp3'); |
| const mp3Buffer = generateSilentMP3(); |
| fs.writeFileSync(musicPath, mp3Buffer); |
| console.log(`✓ Generated music placeholder for level ${level}`); |
| } |
| |
| console.log('\n✅ All placeholder assets generated!'); |
| console.log('\nYou can now replace these files with your own:'); |
| console.log(' - Backdrops: public/assets/backdrops/level-X/backdrop.png (256x224 pixels)'); |
| console.log(' - Music: public/assets/music/level-X/track.mp3'); |
| } |
|
|
| generateAllAssets().catch(console.error); |
|
|
|
|