/* ============================================================ DSP ARCADE — shared UI: load screen, title, HUD, pause, game over, high-score entry. Draws on top of the game canvas. ============================================================ */ (function () { 'use strict'; const { STATE } = window.DSP; function text(ctx, str, x, y, size, color, align = 'center') { ctx.font = `${size}px "Press Start 2P", monospace`; ctx.textAlign = align; ctx.textBaseline = 'middle'; ctx.fillStyle = color; ctx.fillText(str, x, y); } function glowText(ctx, str, x, y, size, color, glow) { ctx.save(); ctx.shadowColor = glow || color; ctx.shadowBlur = 12; text(ctx, str, x, y, size, color); ctx.restore(); } function blink(on) { return Math.floor(performance.now() / 400) % 2 === on; } function draw(game) { const ctx = game.ctx; const W = game.W, H = game.H; const cx = W / 2; switch (game.state) { case STATE.LOADING: { ctx.fillStyle = '#05010d'; ctx.fillRect(0, 0, W, H); for (let i = 0; i < 40; i++) { const sx = (i * 97 + game.time * 0.3) % W; const sy = (i * 53) % H; ctx.fillStyle = i % 3 === 0 ? '#ff2d95' : '#7df9ff'; ctx.globalAlpha = 0.3 + (i % 5) * 0.12; ctx.fillRect(sx, sy, 2, 2); } ctx.globalAlpha = 1; glowText(ctx, game.title, cx, H * 0.3, 20, '#ff2d95'); const step = game.loadSteps[Math.min(game.loadStep, game.loadSteps.length - 1)]; text(ctx, step + '...', cx, H * 0.5, 8, '#7df9ff'); const bw = W * 0.5, bh = 14; const bx = cx - bw / 2, by = H * 0.6; ctx.strokeStyle = '#7df9ff'; ctx.lineWidth = 2; ctx.strokeRect(bx, by, bw, bh); ctx.fillStyle = '#ff2d95'; ctx.fillRect(bx + 2, by + 2, (bw - 4) * Math.min(1, game.loadProgress), bh - 4); text(ctx, Math.floor(Math.min(1, game.loadProgress) * 100) + '%', cx, by + bh + 16, 8, '#ffffff'); break; } case STATE.TITLE: { ctx.fillStyle = 'rgba(5,1,13,0.75)'; ctx.fillRect(0, 0, W, H); glowText(ctx, game.title, cx, H * 0.28, 22, '#ff2d95'); text(ctx, 'DIGITAL SPACEPORT ARCADE', cx, H * 0.28 + 30, 7, '#7df9ff'); if (blink(0)) text(ctx, 'PRESS SPACE TO START', cx, H * 0.55, 10, '#ffe14d'); text(ctx, 'HI-SCORE: ' + String(game.best).padStart(6, '0'), cx, H * 0.68, 8, '#7dff5e'); text(ctx, 'M: MUTE P: PAUSE', cx, H * 0.85, 7, '#8a7fb5'); break; } case STATE.PLAYING: case STATE.PAUSED: { ctx.fillStyle = 'rgba(5,1,13,0.55)'; ctx.fillRect(0, 0, W, 26); text(ctx, 'SCORE ' + String(game.score).padStart(6, '0'), 8, 13, 8, '#ffffff', 'left'); text(ctx, 'HI ' + String(game.best).padStart(6, '0'), W / 2, 13, 8, '#7dff5e'); text(ctx, 'LV ' + game.level, W - 8, 13, 8, '#ffe14d', 'right'); const hearts = Math.min(game.lives, 5); for (let i = 0; i < hearts; i++) { const img = window.DSP_SPRITES.get('heart', 2); ctx.drawImage(img, 8 + i * 18, 32); } if (game.mouseCaptured !== undefined) { if (game.mouseCaptured) { text(ctx, 'MOUS FANGEN - ESC FREIGIBT', W - 8, 44, 7, '#ff7de9', 'right'); } else if (game.state === STATE.PLAYING) { text(ctx, 'KLICK IN CANVAS = MAUS FANGEN', W - 8, 44, 7, '#8a7fb5', 'right'); } } if (game.state === STATE.PAUSED) { ctx.fillStyle = 'rgba(5,1,13,0.7)'; ctx.fillRect(0, 0, W, H); glowText(ctx, 'PAUSED', cx, H * 0.45, 20, '#7df9ff'); text(ctx, 'PRESS P TO RESUME', cx, H * 0.58, 8, '#ffffff'); } break; } case STATE.GAMEOVER: { ctx.fillStyle = 'rgba(5,1,13,0.8)'; ctx.fillRect(0, 0, W, H); glowText(ctx, 'GAME OVER', cx, H * 0.3, 22, '#ff4d6d'); text(ctx, 'SCORE ' + String(game.score).padStart(6, '0'), cx, H * 0.45, 10, '#ffffff'); if (game.gameOverRank > 0) { text(ctx, 'RANK #' + game.gameOverRank + ' ON THE BOARD', cx, H * 0.55, 8, '#ffe14d'); } if (blink(0)) text(ctx, 'PRESS SPACE', cx, H * 0.7, 9, '#7df9ff'); break; } case STATE.HIGHSCORE: { ctx.fillStyle = 'rgba(5,1,13,0.85)'; ctx.fillRect(0, 0, W, H); glowText(ctx, 'NEW HIGH SCORE!', cx, H * 0.25, 14, '#ffe14d'); text(ctx, 'SCORE ' + String(game.score).padStart(6, '0'), cx, H * 0.38, 10, '#ffffff'); text(ctx, 'ENTER PILOT NAME', cx, H * 0.5, 8, '#7df9ff'); const name = (game._hsName || '').toUpperCase(); const cursor = blink(0) ? '_' : ' '; text(ctx, (name + cursor).padStart(9, ' '), cx, H * 0.6, 14, '#7dff5e'); text(ctx, 'TYPE NAME + ENTER', cx, H * 0.72, 7, '#8a7fb5'); break; } } } window.addEventListener('keydown', (e) => { const g = window.DSP_ACTIVE_GAME; if (!g || g.state !== STATE.HIGHSCORE) return; if (e.code === 'Enter') return; if (e.code === 'Backspace') { g._hsName = (g._hsName || '').slice(0, -1); e.preventDefault(); return; } if (e.key && e.key.length === 1 && /[a-zA-Z]/.test(e.key)) { g._hsName = ((g._hsName || '') + e.key).slice(0, 8); e.preventDefault(); } }); window.DSP_UI = { draw }; })();