404game 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/404game.js +571 -0
- package/dist/404game.js.map +1 -0
- package/dist/404game.umd.cjs +31 -0
- package/dist/404game.umd.cjs.map +1 -0
- package/dist/core/Engine.d.ts +31 -0
- package/dist/core/Input.d.ts +21 -0
- package/dist/core/Renderer.d.ts +16 -0
- package/dist/core/Storage.d.ts +4 -0
- package/dist/games/Breakout.d.ts +2 -0
- package/dist/games/Dino.d.ts +2 -0
- package/dist/games/Flappy.d.ts +2 -0
- package/dist/games/Snake.d.ts +2 -0
- package/dist/index.d.ts +21 -0
- package/dist/types.d.ts +76 -0
- package/dist/ui/Overlay.d.ts +29 -0
- package/dist/ui/themes.d.ts +3 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"404game.js","sources":["../src/core/Input.ts","../src/core/Renderer.ts","../src/core/Engine.ts","../src/ui/Overlay.ts","../src/core/Storage.ts","../src/ui/themes.ts","../src/games/Snake.ts","../src/games/Flappy.ts","../src/games/Breakout.ts","../src/games/Dino.ts","../src/index.ts"],"sourcesContent":["import type { InputState } from '../types';\n\nconst ACTION_KEYS = new Set([' ', 'Spacebar', 'Enter']);\nconst UP_KEYS = new Set(['ArrowUp', 'w', 'W']);\nconst DOWN_KEYS = new Set(['ArrowDown', 's', 'S']);\nconst LEFT_KEYS = new Set(['ArrowLeft', 'a', 'A']);\nconst RIGHT_KEYS = new Set(['ArrowRight', 'd', 'D']);\n\nconst SWIPE_THRESHOLD = 30;\n\nexport class Input {\n state: InputState = {\n up: false,\n down: false,\n left: false,\n right: false,\n action: false,\n actionPressed: false,\n swipe: null,\n tap: false,\n };\n\n private actionWasPressed = false;\n private nextSwipe: InputState['swipe'] = null;\n private nextTap = false;\n\n private touchStartX = 0;\n private touchStartY = 0;\n private touchStartTime = 0;\n\n private target: HTMLElement;\n private boundHandlers: { type: string; fn: EventListener; target: EventTarget }[] = [];\n\n constructor(target: HTMLElement) {\n this.target = target;\n this.attach();\n }\n\n private attach(): void {\n const onKeyDown = (e: KeyboardEvent) => {\n if (this.handleKey(e.key, true)) e.preventDefault();\n };\n const onKeyUp = (e: KeyboardEvent) => {\n this.handleKey(e.key, false);\n };\n\n const onTouchStart = (e: TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n this.touchStartX = t.clientX;\n this.touchStartY = t.clientY;\n this.touchStartTime = performance.now();\n e.preventDefault();\n };\n const onTouchEnd = (e: TouchEvent) => {\n const t = e.changedTouches[0];\n if (!t) return;\n const dx = t.clientX - this.touchStartX;\n const dy = t.clientY - this.touchStartY;\n const elapsed = performance.now() - this.touchStartTime;\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (absDx < SWIPE_THRESHOLD && absDy < SWIPE_THRESHOLD && elapsed < 400) {\n this.nextTap = true;\n this.nextSwipe = null;\n this.actionWasPressed = true;\n } else if (absDx > absDy) {\n this.nextSwipe = dx > 0 ? 'right' : 'left';\n } else {\n this.nextSwipe = dy > 0 ? 'down' : 'up';\n }\n e.preventDefault();\n };\n\n const onMouseDown = (e: MouseEvent) => {\n this.nextTap = true;\n this.actionWasPressed = true;\n e.preventDefault();\n };\n\n this.addListener(window, 'keydown', onKeyDown as EventListener);\n this.addListener(window, 'keyup', onKeyUp as EventListener);\n this.addListener(this.target, 'touchstart', onTouchStart as EventListener, { passive: false });\n this.addListener(this.target, 'touchend', onTouchEnd as EventListener, { passive: false });\n this.addListener(this.target, 'mousedown', onMouseDown as EventListener);\n }\n\n private addListener(\n target: EventTarget,\n type: string,\n fn: EventListener,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, fn, options);\n this.boundHandlers.push({ type, fn, target });\n }\n\n private handleKey(key: string, down: boolean): boolean {\n let handled = false;\n if (UP_KEYS.has(key)) {\n this.state.up = down;\n handled = true;\n if (down) this.nextSwipe = 'up';\n } else if (DOWN_KEYS.has(key)) {\n this.state.down = down;\n handled = true;\n if (down) this.nextSwipe = 'down';\n } else if (LEFT_KEYS.has(key)) {\n this.state.left = down;\n handled = true;\n if (down) this.nextSwipe = 'left';\n } else if (RIGHT_KEYS.has(key)) {\n this.state.right = down;\n handled = true;\n if (down) this.nextSwipe = 'right';\n } else if (ACTION_KEYS.has(key)) {\n const wasDown = this.state.action;\n this.state.action = down;\n if (down && !wasDown) this.actionWasPressed = true;\n handled = true;\n }\n return handled;\n }\n\n /** Called by Engine once per frame BEFORE update. */\n beginFrame(): void {\n this.state.actionPressed = this.actionWasPressed;\n this.state.swipe = this.nextSwipe;\n this.state.tap = this.nextTap;\n }\n\n /** Called by Engine once per frame AFTER update. */\n endFrame(): void {\n this.actionWasPressed = false;\n this.nextSwipe = null;\n this.nextTap = false;\n }\n\n destroy(): void {\n for (const { target, type, fn } of this.boundHandlers) {\n target.removeEventListener(type, fn);\n }\n this.boundHandlers = [];\n }\n}\n","import type { Theme } from '../types';\n\n/** Helpers for drawing on a 2D canvas with theme support. */\nexport const Renderer = {\n clear(ctx: CanvasRenderingContext2D, w: number, h: number, theme: Theme): void {\n ctx.fillStyle = theme.bg;\n ctx.fillRect(0, 0, w, h);\n },\n\n rect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n color: string,\n ): void {\n ctx.fillStyle = color;\n ctx.fillRect(x, y, w, h);\n },\n\n circle(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n r: number,\n color: string,\n ): void {\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.arc(x, y, r, 0, Math.PI * 2);\n ctx.fill();\n },\n\n text(\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number,\n y: number,\n options: {\n color?: string;\n size?: number;\n font?: string;\n align?: CanvasTextAlign;\n baseline?: CanvasTextBaseline;\n bold?: boolean;\n } = {},\n ): void {\n const size = options.size ?? 16;\n const weight = options.bold ? 'bold ' : '';\n ctx.fillStyle = options.color ?? '#fff';\n ctx.font = `${weight}${size}px ${options.font ?? 'monospace'}`;\n ctx.textAlign = options.align ?? 'left';\n ctx.textBaseline = options.baseline ?? 'alphabetic';\n ctx.fillText(text, x, y);\n },\n\n glow(\n ctx: CanvasRenderingContext2D,\n color: string,\n blur: number,\n draw: () => void,\n ): void {\n ctx.save();\n ctx.shadowColor = color;\n ctx.shadowBlur = blur;\n draw();\n ctx.restore();\n },\n};\n","import type { GameContext, GameModule, Theme, InputState } from '../types';\nimport { Input } from './Input';\nimport { Renderer } from './Renderer';\n\nexport interface EngineOptions {\n canvas: HTMLCanvasElement;\n theme: Theme;\n width: number;\n height: number;\n game: GameModule;\n onScore: (score: number) => void;\n onGameOver: (score: number) => void;\n onWin: (score: number) => void;\n}\n\ntype State = 'idle' | 'running' | 'gameover' | 'won';\n\nexport class Engine {\n private opts: EngineOptions;\n private ctx2d: CanvasRenderingContext2D;\n private input: Input;\n private rafId: number | null = null;\n private state: State = 'idle';\n private lastTime = 0;\n private startTime = 0;\n private score = 0;\n private gameCtx: GameContext;\n\n constructor(opts: EngineOptions) {\n this.opts = opts;\n const ctx = opts.canvas.getContext('2d');\n if (!ctx) throw new Error('404game: 2D canvas context unavailable');\n this.ctx2d = ctx;\n\n // Handle high-DPI: scale backing store but draw in CSS pixels.\n const dpr = window.devicePixelRatio || 1;\n opts.canvas.width = opts.width * dpr;\n opts.canvas.height = opts.height * dpr;\n opts.canvas.style.width = `${opts.width}px`;\n opts.canvas.style.height = `${opts.height}px`;\n this.ctx2d.setTransform(dpr, 0, 0, dpr, 0, 0);\n\n this.input = new Input(opts.canvas);\n\n this.gameCtx = {\n ctx: this.ctx2d,\n width: opts.width,\n height: opts.height,\n theme: opts.theme,\n input: this.input.state,\n dt: 0,\n time: 0,\n setScore: (s: number) => {\n this.score = s;\n this.opts.onScore(s);\n },\n gameOver: () => this.handleGameOver(),\n win: () => this.handleWin(),\n };\n }\n\n start(): void {\n if (this.state === 'running') return;\n this.score = 0;\n this.opts.onScore(0);\n this.startTime = performance.now();\n this.lastTime = this.startTime;\n this.state = 'running';\n this.opts.game.init(this.gameCtx);\n this.loop();\n }\n\n stop(): void {\n if (this.rafId != null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.state = 'idle';\n }\n\n isRunning(): boolean {\n return this.state === 'running';\n }\n\n private loop = (): void => {\n if (this.state !== 'running') return;\n const now = performance.now();\n const dt = Math.min(0.05, (now - this.lastTime) / 1000); // cap at 50ms\n this.lastTime = now;\n\n this.gameCtx.dt = dt;\n this.gameCtx.time = (now - this.startTime) / 1000;\n\n this.input.beginFrame();\n Renderer.clear(this.ctx2d, this.opts.width, this.opts.height, this.opts.theme);\n this.opts.game.update(this.gameCtx);\n if (this.state === 'running') {\n this.opts.game.draw(this.gameCtx);\n }\n this.input.endFrame();\n\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n private handleGameOver(): void {\n if (this.state !== 'running') return;\n this.state = 'gameover';\n if (this.rafId != null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.opts.onGameOver(this.score);\n }\n\n private handleWin(): void {\n if (this.state !== 'running') return;\n this.state = 'won';\n if (this.rafId != null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.opts.onWin(this.score);\n }\n\n getInputState(): InputState {\n return this.input.state;\n }\n\n destroy(): void {\n this.stop();\n this.input.destroy();\n this.opts.game.destroy?.();\n }\n}\n","import type { Theme } from '../types';\n\nexport interface OverlayOptions {\n message: string;\n subMessage: string;\n theme: Theme;\n showHighscore: boolean;\n onStart: () => void;\n onRestart: () => void;\n}\n\nexport class Overlay {\n private root: HTMLElement;\n private banner: HTMLElement;\n private startScreen: HTMLElement;\n private gameOverScreen: HTMLElement;\n private hud: HTMLElement;\n private scoreEl: HTMLElement;\n private highEl: HTMLElement;\n private finalScoreEl: HTMLElement;\n private opts: OverlayOptions;\n\n constructor(container: HTMLElement, opts: OverlayOptions) {\n this.opts = opts;\n this.root = container;\n\n // Banner\n this.banner = document.createElement('div');\n this.banner.className = 'g404-banner';\n this.banner.innerHTML = `\n <div class=\"g404-banner-title\">${escape(opts.message)}</div>\n <div class=\"g404-banner-sub\">${escape(opts.subMessage)}</div>\n `;\n\n // HUD (score)\n this.hud = document.createElement('div');\n this.hud.className = 'g404-hud';\n this.scoreEl = document.createElement('span');\n this.scoreEl.className = 'g404-score';\n this.scoreEl.textContent = 'Score: 0';\n this.highEl = document.createElement('span');\n this.highEl.className = 'g404-high';\n this.highEl.textContent = 'Best: 0';\n this.hud.appendChild(this.scoreEl);\n if (opts.showHighscore) this.hud.appendChild(this.highEl);\n\n // Start screen\n this.startScreen = document.createElement('div');\n this.startScreen.className = 'g404-screen g404-start';\n this.startScreen.innerHTML = `\n <div class=\"g404-screen-title\">READY?</div>\n <div class=\"g404-screen-sub\">Press SPACE or TAP to start</div>\n <button class=\"g404-btn\" type=\"button\">▶ START</button>\n `;\n this.startScreen.querySelector('button')!.addEventListener('click', (e) => {\n e.stopPropagation();\n opts.onStart();\n });\n\n // Game-over screen\n this.gameOverScreen = document.createElement('div');\n this.gameOverScreen.className = 'g404-screen g404-gameover';\n this.gameOverScreen.style.display = 'none';\n this.gameOverScreen.innerHTML = `\n <div class=\"g404-screen-title\">GAME OVER</div>\n <div class=\"g404-final-score\">Score: <span class=\"g404-final\">0</span></div>\n <button class=\"g404-btn\" type=\"button\">↻ PLAY AGAIN</button>\n `;\n this.finalScoreEl = this.gameOverScreen.querySelector('.g404-final')!;\n this.gameOverScreen.querySelector('button')!.addEventListener('click', (e) => {\n e.stopPropagation();\n opts.onRestart();\n });\n\n this.injectStyles(opts.theme);\n this.root.appendChild(this.banner);\n this.root.appendChild(this.hud);\n this.root.appendChild(this.startScreen);\n this.root.appendChild(this.gameOverScreen);\n }\n\n private injectStyles(theme: Theme): void {\n if (document.getElementById('g404-styles')) return;\n const style = document.createElement('style');\n style.id = 'g404-styles';\n style.textContent = `\n .g404-root { position: relative; display: inline-block; font-family: ${theme.font}; }\n .g404-banner { text-align: center; padding: 12px 8px 8px; color: ${theme.fg}; font-family: ${theme.font}; }\n .g404-banner-title { font-size: 22px; font-weight: bold; letter-spacing: 2px; color: ${theme.accent}; text-shadow: 0 0 12px ${theme.accent}55; }\n .g404-banner-sub { font-size: 13px; color: ${theme.muted}; margin-top: 4px; }\n .g404-hud { display: flex; justify-content: space-between; padding: 6px 12px; color: ${theme.fg}; font-family: ${theme.font}; font-size: 14px; }\n .g404-score { color: ${theme.fg}; }\n .g404-high { color: ${theme.muted}; }\n .g404-canvas-wrap { position: relative; line-height: 0; }\n .g404-canvas { display: block; border-radius: 6px; touch-action: none; cursor: pointer; }\n .g404-screen { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; background: ${theme.bg}cc; backdrop-filter: blur(4px); color: ${theme.fg}; font-family: ${theme.font}; border-radius: 6px; }\n .g404-screen-title { font-size: 32px; font-weight: bold; letter-spacing: 3px; color: ${theme.accent}; text-shadow: 0 0 16px ${theme.accent}88; }\n .g404-screen-sub { font-size: 14px; color: ${theme.muted}; }\n .g404-final-score { font-size: 18px; color: ${theme.fg}; }\n .g404-final { color: ${theme.accent}; font-weight: bold; }\n .g404-btn { background: ${theme.accent}; color: ${theme.bg}; border: none; padding: 12px 28px; font-size: 16px; font-weight: bold; letter-spacing: 1px; border-radius: 6px; cursor: pointer; font-family: ${theme.font}; transition: transform 0.1s, box-shadow 0.2s; }\n .g404-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 16px ${theme.accent}66; }\n .g404-btn:active { transform: translateY(0); }\n `;\n document.head.appendChild(style);\n }\n\n setScore(score: number): void {\n this.scoreEl.textContent = `Score: ${score}`;\n }\n\n setHighscore(hs: number): void {\n this.highEl.textContent = `Best: ${hs}`;\n }\n\n showStart(): void {\n this.startScreen.style.display = 'flex';\n this.gameOverScreen.style.display = 'none';\n }\n\n hideStart(): void {\n this.startScreen.style.display = 'none';\n }\n\n showGameOver(score: number): void {\n this.finalScoreEl.textContent = String(score);\n this.gameOverScreen.style.display = 'flex';\n this.startScreen.style.display = 'none';\n }\n\n hideGameOver(): void {\n this.gameOverScreen.style.display = 'none';\n }\n\n destroy(): void {\n this.banner.remove();\n this.hud.remove();\n this.startScreen.remove();\n this.gameOverScreen.remove();\n }\n}\n\nfunction escape(s: string): string {\n const div = document.createElement('div');\n div.textContent = s;\n return div.innerHTML;\n}\n","const PREFIX = '404game.hs.';\n\nexport const Storage = {\n getHighscore(gameName: string): number {\n try {\n const v = localStorage.getItem(PREFIX + gameName);\n return v ? parseInt(v, 10) || 0 : 0;\n } catch {\n return 0;\n }\n },\n\n setHighscore(gameName: string, score: number): boolean {\n try {\n const prev = Storage.getHighscore(gameName);\n if (score > prev) {\n localStorage.setItem(PREFIX + gameName, String(score));\n return true;\n }\n } catch {\n /* ignore */\n }\n return false;\n },\n};\n","import type { Theme, ThemeName } from '../types';\n\nexport const themes: Record<ThemeName, Theme> = {\n neon: {\n bg: '#0a0a18',\n fg: '#f0f0ff',\n accent: '#00ffd5',\n danger: '#ff3b6b',\n muted: '#5a5a8a',\n font: '\"Courier New\", monospace',\n },\n retro: {\n bg: '#2b1d0e',\n fg: '#ffe1a8',\n accent: '#ffaa00',\n danger: '#d62828',\n muted: '#8a6b3b',\n font: '\"Courier New\", monospace',\n },\n minimal: {\n bg: '#fafafa',\n fg: '#111111',\n accent: '#0066ff',\n danger: '#e63946',\n muted: '#999999',\n font: 'system-ui, -apple-system, sans-serif',\n },\n dark: {\n bg: '#1a1a1a',\n fg: '#e8e8e8',\n accent: '#7c5cff',\n danger: '#ff5252',\n muted: '#666666',\n font: 'system-ui, -apple-system, sans-serif',\n },\n};\n\nexport function getTheme(name: ThemeName | undefined): Theme {\n return themes[name ?? 'neon'] ?? themes.neon;\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Vec { x: number; y: number; }\ntype Dir = 'up' | 'down' | 'left' | 'right';\n\nconst CELL = 16;\nconst TICK = 0.12; // seconds per move\n\nexport function createSnake(): GameModule {\n let cols = 0;\n let rows = 0;\n let snake: Vec[] = [];\n let dir: Dir = 'right';\n let queuedDir: Dir = 'right';\n let food: Vec = { x: 0, y: 0 };\n let acc = 0;\n let score = 0;\n\n function spawnFood(): void {\n while (true) {\n const f = { x: Math.floor(Math.random() * cols), y: Math.floor(Math.random() * rows) };\n if (!snake.some((s) => s.x === f.x && s.y === f.y)) {\n food = f;\n return;\n }\n }\n }\n\n function turn(d: Dir): void {\n const opposites: Record<Dir, Dir> = { up: 'down', down: 'up', left: 'right', right: 'left' };\n if (opposites[d] !== dir) queuedDir = d;\n }\n\n return {\n name: 'snake',\n init(ctx: GameContext) {\n cols = Math.floor(ctx.width / CELL);\n rows = Math.floor(ctx.height / CELL);\n const cx = Math.floor(cols / 2);\n const cy = Math.floor(rows / 2);\n snake = [{ x: cx, y: cy }, { x: cx - 1, y: cy }, { x: cx - 2, y: cy }];\n dir = 'right';\n queuedDir = 'right';\n acc = 0;\n score = 0;\n spawnFood();\n ctx.setScore(0);\n },\n\n update(ctx: GameContext) {\n const { input } = ctx;\n if (input.swipe === 'up' || input.up) turn('up');\n else if (input.swipe === 'down' || input.down) turn('down');\n else if (input.swipe === 'left' || input.left) turn('left');\n else if (input.swipe === 'right' || input.right) turn('right');\n\n acc += ctx.dt;\n while (acc >= TICK) {\n acc -= TICK;\n dir = queuedDir;\n const head = snake[0]!;\n const nh: Vec = { x: head.x, y: head.y };\n if (dir === 'up') nh.y -= 1;\n else if (dir === 'down') nh.y += 1;\n else if (dir === 'left') nh.x -= 1;\n else if (dir === 'right') nh.x += 1;\n\n if (nh.x < 0 || nh.x >= cols || nh.y < 0 || nh.y >= rows) {\n ctx.gameOver();\n return;\n }\n if (snake.some((s) => s.x === nh.x && s.y === nh.y)) {\n ctx.gameOver();\n return;\n }\n snake.unshift(nh);\n if (nh.x === food.x && nh.y === food.y) {\n score += 10;\n ctx.setScore(score);\n spawnFood();\n } else {\n snake.pop();\n }\n }\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme } = ctx;\n\n // grid hint\n c.strokeStyle = theme.muted + '22';\n c.lineWidth = 1;\n for (let x = 0; x <= cols; x++) {\n c.beginPath(); c.moveTo(x * CELL, 0); c.lineTo(x * CELL, rows * CELL); c.stroke();\n }\n for (let y = 0; y <= rows; y++) {\n c.beginPath(); c.moveTo(0, y * CELL); c.lineTo(cols * CELL, y * CELL); c.stroke();\n }\n\n // food\n Renderer.glow(c, theme.danger, 12, () => {\n Renderer.circle(c, food.x * CELL + CELL / 2, food.y * CELL + CELL / 2, CELL / 2 - 2, theme.danger);\n });\n\n // snake\n Renderer.glow(c, theme.accent, 8, () => {\n for (let i = 0; i < snake.length; i++) {\n const s = snake[i]!;\n Renderer.rect(c, s.x * CELL + 1, s.y * CELL + 1, CELL - 2, CELL - 2, i === 0 ? theme.accent : theme.accent + 'cc');\n }\n });\n },\n };\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Pipe { x: number; gapY: number; passed: boolean; }\n\nconst GRAVITY = 1400;\nconst FLAP_VELOCITY = -380;\nconst PIPE_WIDTH = 50;\nconst PIPE_GAP = 130;\nconst PIPE_SPACING = 200;\nconst PIPE_SPEED = 160;\nconst BIRD_RADIUS = 12;\n\nexport function createFlappy(): GameModule {\n let birdY = 0;\n let birdX = 0;\n let velocity = 0;\n let pipes: Pipe[] = [];\n let score = 0;\n let spawnAcc = 0;\n\n return {\n name: 'flappy',\n init(ctx: GameContext) {\n birdX = ctx.width * 0.25;\n birdY = ctx.height / 2;\n velocity = 0;\n pipes = [];\n score = 0;\n spawnAcc = PIPE_SPACING; // spawn first immediately\n ctx.setScore(0);\n },\n\n update(ctx: GameContext) {\n const { input, dt, width, height } = ctx;\n\n if (input.actionPressed || input.tap || input.swipe === 'up') {\n velocity = FLAP_VELOCITY;\n }\n\n velocity += GRAVITY * dt;\n birdY += velocity * dt;\n\n if (birdY > height - BIRD_RADIUS || birdY < BIRD_RADIUS) {\n ctx.gameOver();\n return;\n }\n\n // spawn pipes by distance\n spawnAcc += PIPE_SPEED * dt;\n if (spawnAcc >= PIPE_SPACING) {\n spawnAcc -= PIPE_SPACING;\n const gapY = 60 + Math.random() * (height - 120 - PIPE_GAP);\n pipes.push({ x: width + PIPE_WIDTH, gapY, passed: false });\n }\n\n for (const p of pipes) {\n p.x -= PIPE_SPEED * dt;\n if (!p.passed && p.x + PIPE_WIDTH < birdX) {\n p.passed = true;\n score += 1;\n ctx.setScore(score);\n }\n // collision\n if (birdX + BIRD_RADIUS > p.x && birdX - BIRD_RADIUS < p.x + PIPE_WIDTH) {\n if (birdY - BIRD_RADIUS < p.gapY || birdY + BIRD_RADIUS > p.gapY + PIPE_GAP) {\n ctx.gameOver();\n return;\n }\n }\n }\n pipes = pipes.filter((p) => p.x + PIPE_WIDTH > -10);\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme, height } = ctx;\n\n // pipes\n Renderer.glow(c, theme.accent, 8, () => {\n for (const p of pipes) {\n Renderer.rect(c, p.x, 0, PIPE_WIDTH, p.gapY, theme.accent);\n Renderer.rect(c, p.x, p.gapY + PIPE_GAP, PIPE_WIDTH, height - (p.gapY + PIPE_GAP), theme.accent);\n }\n });\n\n // bird\n Renderer.glow(c, theme.danger, 12, () => {\n Renderer.circle(c, birdX, birdY, BIRD_RADIUS, theme.danger);\n });\n // eye\n Renderer.circle(c, birdX + 4, birdY - 3, 2, theme.bg);\n },\n };\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Brick { x: number; y: number; w: number; h: number; alive: boolean; color: string; }\n\nconst PADDLE_W = 80;\nconst PADDLE_H = 10;\nconst PADDLE_Y_OFFSET = 30;\nconst PADDLE_SPEED = 420;\nconst BALL_RADIUS = 6;\nconst BALL_SPEED = 280;\nconst BRICK_ROWS = 5;\nconst BRICK_COLS = 8;\n\nexport function createBreakout(): GameModule {\n let paddleX = 0;\n let ballX = 0, ballY = 0;\n let vx = 0, vy = 0;\n let bricks: Brick[] = [];\n let score = 0;\n let launched = false;\n\n return {\n name: 'breakout',\n init(ctx: GameContext) {\n paddleX = ctx.width / 2 - PADDLE_W / 2;\n ballX = ctx.width / 2;\n ballY = ctx.height - PADDLE_Y_OFFSET - PADDLE_H - BALL_RADIUS - 1;\n vx = 0;\n vy = 0;\n launched = false;\n score = 0;\n ctx.setScore(0);\n\n bricks = [];\n const pad = 6;\n const offsetY = 40;\n const brickW = (ctx.width - pad * (BRICK_COLS + 1)) / BRICK_COLS;\n const brickH = 16;\n const palette = [ctx.theme.danger, ctx.theme.accent, ctx.theme.fg, ctx.theme.accent, ctx.theme.danger];\n for (let r = 0; r < BRICK_ROWS; r++) {\n for (let col = 0; col < BRICK_COLS; col++) {\n bricks.push({\n x: pad + col * (brickW + pad),\n y: offsetY + r * (brickH + pad),\n w: brickW,\n h: brickH,\n alive: true,\n color: palette[r] ?? ctx.theme.accent,\n });\n }\n }\n },\n\n update(ctx: GameContext) {\n const { input, dt, width, height } = ctx;\n\n // Paddle controls: keyboard, swipe (treated as direction hold via input.left/right),\n // or follow tap position on touch (use last touch X via tap fallback - simple: arrows only).\n if (input.left) paddleX -= PADDLE_SPEED * dt;\n if (input.right) paddleX += PADDLE_SPEED * dt;\n if (input.swipe === 'left') paddleX -= 40;\n if (input.swipe === 'right') paddleX += 40;\n paddleX = Math.max(0, Math.min(width - PADDLE_W, paddleX));\n\n const paddleY = height - PADDLE_Y_OFFSET - PADDLE_H;\n\n if (!launched) {\n ballX = paddleX + PADDLE_W / 2;\n ballY = paddleY - BALL_RADIUS - 1;\n if (input.actionPressed || input.tap) {\n launched = true;\n const angle = -Math.PI / 2 + (Math.random() - 0.5) * 0.6;\n vx = Math.cos(angle) * BALL_SPEED;\n vy = Math.sin(angle) * BALL_SPEED;\n }\n return;\n }\n\n ballX += vx * dt;\n ballY += vy * dt;\n\n // walls\n if (ballX < BALL_RADIUS) { ballX = BALL_RADIUS; vx = -vx; }\n if (ballX > width - BALL_RADIUS) { ballX = width - BALL_RADIUS; vx = -vx; }\n if (ballY < BALL_RADIUS) { ballY = BALL_RADIUS; vy = -vy; }\n\n // paddle collision\n if (\n ballY + BALL_RADIUS >= paddleY &&\n ballY - BALL_RADIUS <= paddleY + PADDLE_H &&\n ballX >= paddleX && ballX <= paddleX + PADDLE_W && vy > 0\n ) {\n vy = -Math.abs(vy);\n const offset = (ballX - (paddleX + PADDLE_W / 2)) / (PADDLE_W / 2);\n vx = offset * BALL_SPEED * 0.9;\n // normalize\n const speed = Math.sqrt(vx * vx + vy * vy);\n const target = BALL_SPEED;\n vx = (vx / speed) * target;\n vy = (vy / speed) * target;\n }\n\n // brick collisions\n for (const b of bricks) {\n if (!b.alive) continue;\n if (ballX + BALL_RADIUS > b.x && ballX - BALL_RADIUS < b.x + b.w &&\n ballY + BALL_RADIUS > b.y && ballY - BALL_RADIUS < b.y + b.h) {\n b.alive = false;\n score += 10;\n ctx.setScore(score);\n // simple bounce: flip whichever side has more penetration\n const overlapX = Math.min(ballX + BALL_RADIUS - b.x, b.x + b.w - (ballX - BALL_RADIUS));\n const overlapY = Math.min(ballY + BALL_RADIUS - b.y, b.y + b.h - (ballY - BALL_RADIUS));\n if (overlapX < overlapY) vx = -vx;\n else vy = -vy;\n break;\n }\n }\n\n if (bricks.every((b) => !b.alive)) {\n ctx.win();\n return;\n }\n\n // bottom\n if (ballY > height + 20) {\n ctx.gameOver();\n return;\n }\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme, height } = ctx;\n\n // bricks\n for (const b of bricks) {\n if (!b.alive) continue;\n Renderer.glow(c, b.color, 6, () => {\n Renderer.rect(c, b.x, b.y, b.w, b.h, b.color);\n });\n }\n\n // paddle\n const paddleY = height - PADDLE_Y_OFFSET - PADDLE_H;\n Renderer.glow(c, theme.accent, 8, () => {\n Renderer.rect(c, paddleX, paddleY, PADDLE_W, PADDLE_H, theme.accent);\n });\n\n // ball\n Renderer.glow(c, theme.fg, 10, () => {\n Renderer.circle(c, ballX, ballY, BALL_RADIUS, theme.fg);\n });\n\n if (!launched) {\n Renderer.text(c, 'Press SPACE / TAP to launch', ctx.width / 2, ctx.height - 8, {\n color: theme.muted, size: 11, font: theme.font, align: 'center',\n });\n }\n },\n };\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Obstacle { x: number; w: number; h: number; }\n\nconst GRAVITY = 2200;\nconst JUMP_VELOCITY = -780;\nconst DINO_W = 24;\nconst DINO_H = 28;\nconst GROUND_OFFSET = 30;\nconst BASE_SPEED = 240;\n\nexport function createDino(): GameModule {\n let dinoX = 0;\n let dinoY = 0;\n let velocity = 0;\n let groundY = 0;\n let obstacles: Obstacle[] = [];\n let spawnTimer = 0;\n let score = 0;\n let scoreAcc = 0;\n let speed = BASE_SPEED;\n\n return {\n name: 'dino',\n init(ctx: GameContext) {\n dinoX = 60;\n groundY = ctx.height - GROUND_OFFSET;\n dinoY = groundY - DINO_H;\n velocity = 0;\n obstacles = [];\n spawnTimer = 0.8;\n score = 0;\n scoreAcc = 0;\n speed = BASE_SPEED;\n ctx.setScore(0);\n },\n\n update(ctx: GameContext) {\n const { input, dt, width } = ctx;\n const onGround = dinoY >= groundY - DINO_H;\n\n if ((input.actionPressed || input.tap || input.swipe === 'up') && onGround) {\n velocity = JUMP_VELOCITY;\n }\n\n velocity += GRAVITY * dt;\n dinoY += velocity * dt;\n if (dinoY > groundY - DINO_H) {\n dinoY = groundY - DINO_H;\n velocity = 0;\n }\n\n // speed up over time\n speed += dt * 8;\n\n // score\n scoreAcc += dt * 10;\n if (scoreAcc >= 1) {\n const inc = Math.floor(scoreAcc);\n scoreAcc -= inc;\n score += inc;\n ctx.setScore(score);\n }\n\n // spawn obstacles\n spawnTimer -= dt;\n if (spawnTimer <= 0) {\n const h = 22 + Math.random() * 28;\n const w = 12 + Math.random() * 16;\n obstacles.push({ x: width + 20, w, h });\n spawnTimer = 0.7 + Math.random() * 0.9 - Math.min(0.4, (speed - BASE_SPEED) / 1000);\n if (spawnTimer < 0.4) spawnTimer = 0.4;\n }\n\n for (const o of obstacles) o.x -= speed * dt;\n obstacles = obstacles.filter((o) => o.x + o.w > -10);\n\n // collision (axis-aligned)\n for (const o of obstacles) {\n const ox = o.x;\n const oy = groundY - o.h;\n if (\n dinoX + DINO_W > ox && dinoX < ox + o.w &&\n dinoY + DINO_H > oy && dinoY < oy + o.h\n ) {\n ctx.gameOver();\n return;\n }\n }\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme, width } = ctx;\n\n // ground line\n c.strokeStyle = theme.muted;\n c.lineWidth = 1;\n c.beginPath();\n c.moveTo(0, groundY + 0.5);\n c.lineTo(width, groundY + 0.5);\n c.stroke();\n\n // ground tick marks (parallax-ish)\n for (let i = 0; i < 20; i++) {\n const tx = ((i * 40) - (ctx.time * 80) % 40);\n Renderer.rect(c, tx, groundY + 4, 16, 2, theme.muted);\n }\n\n // obstacles (cacti)\n Renderer.glow(c, theme.danger, 6, () => {\n for (const o of obstacles) {\n Renderer.rect(c, o.x, groundY - o.h, o.w, o.h, theme.danger);\n }\n });\n\n // dino\n Renderer.glow(c, theme.accent, 10, () => {\n Renderer.rect(c, dinoX, dinoY, DINO_W, DINO_H, theme.accent);\n });\n // eye\n Renderer.rect(c, dinoX + DINO_W - 7, dinoY + 6, 3, 3, theme.bg);\n },\n };\n}\n","/**\n * 404game.js — Turn boring 404 pages into playable mini-games.\n *\n * Quick start:\n * <script src=\"404game.js\"></script>\n * <div id=\"game\"></div>\n * <script>Game404.mount('#game', { game: 'snake' });</script>\n */\n\nimport type {\n Game404Options,\n Game404Instance,\n GameModule,\n GameName,\n} from './types';\nimport { Engine } from './core/Engine';\nimport { Overlay } from './ui/Overlay';\nimport { Storage } from './core/Storage';\nimport { getTheme } from './ui/themes';\nimport { createSnake } from './games/Snake';\nimport { createFlappy } from './games/Flappy';\nimport { createBreakout } from './games/Breakout';\nimport { createDino } from './games/Dino';\n\nconst VERSION = '0.1.0';\n\nconst REGISTRY: Record<Exclude<GameName, 'random'>, () => GameModule> = {\n snake: createSnake,\n flappy: createFlappy,\n breakout: createBreakout,\n dino: createDino,\n};\n\nconst instances = new WeakMap<Element, Game404Instance>();\n\nfunction resolveTarget(selector: string | Element): HTMLElement {\n const el = typeof selector === 'string' ? document.querySelector(selector) : selector;\n if (!el) throw new Error(`404game: target not found: ${String(selector)}`);\n return el as HTMLElement;\n}\n\nfunction pickGame(name: GameName | undefined): { name: string; module: GameModule } {\n if (!name || name === 'random') {\n const keys = Object.keys(REGISTRY) as Array<Exclude<GameName, 'random'>>;\n const k = keys[Math.floor(Math.random() * keys.length)]!;\n return { name: k, module: REGISTRY[k]() };\n }\n const factory = REGISTRY[name as Exclude<GameName, 'random'>];\n if (!factory) throw new Error(`404game: unknown game \"${name}\"`);\n return { name, module: factory() };\n}\n\nexport function mount(\n selector: string | Element,\n options: Game404Options = {},\n): Game404Instance {\n const target = resolveTarget(selector);\n // If already mounted, destroy previous instance first.\n const existing = instances.get(target);\n if (existing) existing.destroy();\n\n const theme = getTheme(options.theme);\n const width = options.width ?? (Math.min(target.clientWidth || 480, 720) || 480);\n const height = options.height ?? 360;\n\n // Wrap container so we own the DOM inside.\n target.classList.add('g404-root');\n target.innerHTML = '';\n\n const canvasWrap = document.createElement('div');\n canvasWrap.className = 'g404-canvas-wrap';\n const canvas = document.createElement('canvas');\n canvas.className = 'g404-canvas';\n canvas.style.background = theme.bg;\n canvasWrap.appendChild(canvas);\n\n // Order: banner, hud, canvas (overlays appended into canvasWrap)\n // Overlay constructor appends banner+hud+screens to `target` directly.\n // We want layout: [banner][hud][canvasWrap (with absolute overlays)]\n target.appendChild(canvasWrap);\n\n let currentGameName = '';\n let currentScore = 0;\n\n // Build initial game module\n let picked = pickGame(options.game);\n currentGameName = picked.name;\n\n const overlay = new Overlay(target, {\n message: options.message ?? '404 — Page Not Found',\n subMessage: options.subMessage ?? 'But hey, want to play instead?',\n theme,\n showHighscore: options.showHighscore !== false,\n onStart: () => {\n overlay.hideStart();\n overlay.hideGameOver();\n engine.start();\n options.onStart?.();\n },\n onRestart: () => {\n overlay.hideGameOver();\n // re-create game module so internal state resets cleanly\n picked = pickGame(options.game);\n currentGameName = picked.name;\n engine.destroy();\n buildEngine();\n engine.start();\n options.onStart?.();\n },\n });\n\n // Move overlays into canvas wrap so they sit on top of the canvas.\n const startScreen = target.querySelector('.g404-start') as HTMLElement | null;\n const gameOverScreen = target.querySelector('.g404-gameover') as HTMLElement | null;\n if (startScreen) canvasWrap.appendChild(startScreen);\n if (gameOverScreen) canvasWrap.appendChild(gameOverScreen);\n canvasWrap.style.position = 'relative';\n\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n\n let engine!: Engine;\n\n function buildEngine(): void {\n engine = new Engine({\n canvas,\n theme,\n width,\n height,\n game: picked.module,\n onScore: (s: number) => {\n currentScore = s;\n overlay.setScore(s);\n options.onScore?.(s);\n },\n onGameOver: (s: number) => {\n Storage.setHighscore(currentGameName, s);\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n overlay.showGameOver(s);\n options.onGameOver?.(s);\n },\n onWin: (s: number) => {\n Storage.setHighscore(currentGameName, s);\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n overlay.showGameOver(s);\n options.onWin?.(s);\n },\n });\n }\n\n buildEngine();\n\n // Start flow\n if (options.autoStart) {\n overlay.hideStart();\n engine.start();\n } else {\n overlay.showStart();\n // Allow tap/space anywhere on canvas to start.\n const startOnAction = (e: Event) => {\n e.preventDefault();\n if (!engine.isRunning() && target.querySelector('.g404-start')?.checkVisibility?.() !== false) {\n const startVisible = (startScreen?.style.display !== 'none');\n if (startVisible) {\n overlay.hideStart();\n engine.start();\n options.onStart?.();\n }\n }\n };\n canvas.addEventListener('click', startOnAction);\n window.addEventListener('keydown', (e) => {\n if ((e.key === ' ' || e.key === 'Enter') && !engine.isRunning()) {\n const startVisible = (startScreen?.style.display !== 'none');\n if (startVisible) {\n e.preventDefault();\n overlay.hideStart();\n engine.start();\n options.onStart?.();\n }\n }\n });\n }\n\n const instance: Game404Instance = {\n destroy() {\n engine.destroy();\n overlay.destroy();\n target.innerHTML = '';\n target.classList.remove('g404-root');\n instances.delete(target);\n },\n restart() {\n engine.destroy();\n picked = pickGame(options.game);\n currentGameName = picked.name;\n buildEngine();\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n overlay.hideGameOver();\n overlay.hideStart();\n engine.start();\n },\n getScore() {\n return currentScore;\n },\n };\n\n instances.set(target, instance);\n return instance;\n}\n\nexport function unmount(selector: string | Element): void {\n const target = resolveTarget(selector);\n const instance = instances.get(target);\n if (instance) instance.destroy();\n}\n\nexport const games: ReadonlyArray<Exclude<GameName, 'random'>> = ['snake', 'flappy', 'breakout', 'dino'];\n\nexport const version = VERSION;\n\n// Re-export public types\nexport type {\n Game404Options,\n Game404Instance,\n GameName,\n ThemeName,\n Theme,\n} from './types';\n\n// Default export so UMD build exposes `window.Game404` with all members.\nconst Game404 = { mount, unmount, games, version };\nexport default Game404;\n"],"names":["ACTION_KEYS","UP_KEYS","DOWN_KEYS","LEFT_KEYS","RIGHT_KEYS","SWIPE_THRESHOLD","Input","target","onKeyDown","e","onKeyUp","onTouchStart","t","onTouchEnd","dx","dy","elapsed","absDx","absDy","onMouseDown","type","fn","options","key","down","handled","wasDown","Renderer","ctx","w","h","theme","x","y","color","r","text","size","weight","blur","draw","Engine","opts","now","dt","dpr","s","_a","_b","Overlay","container","escape","style","score","hs","div","PREFIX","Storage","gameName","v","prev","themes","getTheme","name","CELL","TICK","createSnake","cols","rows","snake","dir","queuedDir","food","acc","spawnFood","f","turn","d","cx","cy","input","head","nh","c","i","GRAVITY","FLAP_VELOCITY","PIPE_WIDTH","PIPE_GAP","PIPE_SPACING","PIPE_SPEED","BIRD_RADIUS","createFlappy","birdY","birdX","velocity","pipes","spawnAcc","width","height","gapY","p","PADDLE_W","PADDLE_H","PADDLE_Y_OFFSET","PADDLE_SPEED","BALL_RADIUS","BALL_SPEED","BRICK_ROWS","BRICK_COLS","createBreakout","paddleX","ballX","ballY","vx","vy","bricks","launched","pad","offsetY","brickW","brickH","palette","col","paddleY","angle","speed","b","overlapX","overlapY","JUMP_VELOCITY","DINO_W","DINO_H","GROUND_OFFSET","BASE_SPEED","createDino","dinoX","dinoY","groundY","obstacles","spawnTimer","scoreAcc","onGround","inc","o","ox","oy","tx","VERSION","REGISTRY","instances","resolveTarget","selector","el","pickGame","keys","k","factory","mount","existing","canvasWrap","canvas","currentGameName","currentScore","picked","overlay","engine","buildEngine","startScreen","gameOverScreen","startOnAction","_c","instance","unmount","games","version","Game404"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
(function(x,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(x=typeof globalThis<"u"?globalThis:x||self,k(x.Game404={}))})(this,function(x){"use strict";const k=new Set([" ","Spacebar","Enter"]),ne=new Set(["ArrowUp","w","W"]),se=new Set(["ArrowDown","s","S"]),ie=new Set(["ArrowLeft","a","A"]),ae=new Set(["ArrowRight","d","D"]),F=30;class re{constructor(e){this.state={up:!1,down:!1,left:!1,right:!1,action:!1,actionPressed:!1,swipe:null,tap:!1},this.actionWasPressed=!1,this.nextSwipe=null,this.nextTap=!1,this.touchStartX=0,this.touchStartY=0,this.touchStartTime=0,this.boundHandlers=[],this.target=e,this.attach()}attach(){const e=f=>{this.handleKey(f.key,!0)&&f.preventDefault()},t=f=>{this.handleKey(f.key,!1)},s=f=>{const g=f.touches[0];g&&(this.touchStartX=g.clientX,this.touchStartY=g.clientY,this.touchStartTime=performance.now(),f.preventDefault())},o=f=>{const g=f.changedTouches[0];if(!g)return;const h=g.clientX-this.touchStartX,c=g.clientY-this.touchStartY,a=performance.now()-this.touchStartTime,i=Math.abs(h),d=Math.abs(c);i<F&&d<F&&a<400?(this.nextTap=!0,this.nextSwipe=null,this.actionWasPressed=!0):i>d?this.nextSwipe=h>0?"right":"left":this.nextSwipe=c>0?"down":"up",f.preventDefault()},u=f=>{this.nextTap=!0,this.actionWasPressed=!0,f.preventDefault()};this.addListener(window,"keydown",e),this.addListener(window,"keyup",t),this.addListener(this.target,"touchstart",s,{passive:!1}),this.addListener(this.target,"touchend",o,{passive:!1}),this.addListener(this.target,"mousedown",u)}addListener(e,t,s,o){e.addEventListener(t,s,o),this.boundHandlers.push({type:t,fn:s,target:e})}handleKey(e,t){let s=!1;if(ne.has(e))this.state.up=t,s=!0,t&&(this.nextSwipe="up");else if(se.has(e))this.state.down=t,s=!0,t&&(this.nextSwipe="down");else if(ie.has(e))this.state.left=t,s=!0,t&&(this.nextSwipe="left");else if(ae.has(e))this.state.right=t,s=!0,t&&(this.nextSwipe="right");else if(k.has(e)){const o=this.state.action;this.state.action=t,t&&!o&&(this.actionWasPressed=!0),s=!0}return s}beginFrame(){this.state.actionPressed=this.actionWasPressed,this.state.swipe=this.nextSwipe,this.state.tap=this.nextTap}endFrame(){this.actionWasPressed=!1,this.nextSwipe=null,this.nextTap=!1}destroy(){for(const{target:e,type:t,fn:s}of this.boundHandlers)e.removeEventListener(t,s);this.boundHandlers=[]}}const p={clear(n,e,t,s){n.fillStyle=s.bg,n.fillRect(0,0,e,t)},rect(n,e,t,s,o,u){n.fillStyle=u,n.fillRect(e,t,s,o)},circle(n,e,t,s,o){n.fillStyle=o,n.beginPath(),n.arc(e,t,s,0,Math.PI*2),n.fill()},text(n,e,t,s,o={}){const u=o.size??16,f=o.bold?"bold ":"";n.fillStyle=o.color??"#fff",n.font=`${f}${u}px ${o.font??"monospace"}`,n.textAlign=o.align??"left",n.textBaseline=o.baseline??"alphabetic",n.fillText(e,t,s)},glow(n,e,t,s){n.save(),n.shadowColor=e,n.shadowBlur=t,s(),n.restore()}};class oe{constructor(e){this.rafId=null,this.state="idle",this.lastTime=0,this.startTime=0,this.score=0,this.loop=()=>{if(this.state!=="running")return;const o=performance.now(),u=Math.min(.05,(o-this.lastTime)/1e3);this.lastTime=o,this.gameCtx.dt=u,this.gameCtx.time=(o-this.startTime)/1e3,this.input.beginFrame(),p.clear(this.ctx2d,this.opts.width,this.opts.height,this.opts.theme),this.opts.game.update(this.gameCtx),this.state==="running"&&this.opts.game.draw(this.gameCtx),this.input.endFrame(),this.rafId=requestAnimationFrame(this.loop)},this.opts=e;const t=e.canvas.getContext("2d");if(!t)throw new Error("404game: 2D canvas context unavailable");this.ctx2d=t;const s=window.devicePixelRatio||1;e.canvas.width=e.width*s,e.canvas.height=e.height*s,e.canvas.style.width=`${e.width}px`,e.canvas.style.height=`${e.height}px`,this.ctx2d.setTransform(s,0,0,s,0,0),this.input=new re(e.canvas),this.gameCtx={ctx:this.ctx2d,width:e.width,height:e.height,theme:e.theme,input:this.input.state,dt:0,time:0,setScore:o=>{this.score=o,this.opts.onScore(o)},gameOver:()=>this.handleGameOver(),win:()=>this.handleWin()}}start(){this.state!=="running"&&(this.score=0,this.opts.onScore(0),this.startTime=performance.now(),this.lastTime=this.startTime,this.state="running",this.opts.game.init(this.gameCtx),this.loop())}stop(){this.rafId!=null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.state="idle"}isRunning(){return this.state==="running"}handleGameOver(){this.state==="running"&&(this.state="gameover",this.rafId!=null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.opts.onGameOver(this.score))}handleWin(){this.state==="running"&&(this.state="won",this.rafId!=null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.opts.onWin(this.score))}getInputState(){return this.input.state}destroy(){var e,t;this.stop(),this.input.destroy(),(t=(e=this.opts.game).destroy)==null||t.call(e)}}class ce{constructor(e,t){this.opts=t,this.root=e,this.banner=document.createElement("div"),this.banner.className="g404-banner",this.banner.innerHTML=`
|
|
2
|
+
<div class="g404-banner-title">${B(t.message)}</div>
|
|
3
|
+
<div class="g404-banner-sub">${B(t.subMessage)}</div>
|
|
4
|
+
`,this.hud=document.createElement("div"),this.hud.className="g404-hud",this.scoreEl=document.createElement("span"),this.scoreEl.className="g404-score",this.scoreEl.textContent="Score: 0",this.highEl=document.createElement("span"),this.highEl.className="g404-high",this.highEl.textContent="Best: 0",this.hud.appendChild(this.scoreEl),t.showHighscore&&this.hud.appendChild(this.highEl),this.startScreen=document.createElement("div"),this.startScreen.className="g404-screen g404-start",this.startScreen.innerHTML=`
|
|
5
|
+
<div class="g404-screen-title">READY?</div>
|
|
6
|
+
<div class="g404-screen-sub">Press SPACE or TAP to start</div>
|
|
7
|
+
<button class="g404-btn" type="button">▶ START</button>
|
|
8
|
+
`,this.startScreen.querySelector("button").addEventListener("click",s=>{s.stopPropagation(),t.onStart()}),this.gameOverScreen=document.createElement("div"),this.gameOverScreen.className="g404-screen g404-gameover",this.gameOverScreen.style.display="none",this.gameOverScreen.innerHTML=`
|
|
9
|
+
<div class="g404-screen-title">GAME OVER</div>
|
|
10
|
+
<div class="g404-final-score">Score: <span class="g404-final">0</span></div>
|
|
11
|
+
<button class="g404-btn" type="button">↻ PLAY AGAIN</button>
|
|
12
|
+
`,this.finalScoreEl=this.gameOverScreen.querySelector(".g404-final"),this.gameOverScreen.querySelector("button").addEventListener("click",s=>{s.stopPropagation(),t.onRestart()}),this.injectStyles(t.theme),this.root.appendChild(this.banner),this.root.appendChild(this.hud),this.root.appendChild(this.startScreen),this.root.appendChild(this.gameOverScreen)}injectStyles(e){if(document.getElementById("g404-styles"))return;const t=document.createElement("style");t.id="g404-styles",t.textContent=`
|
|
13
|
+
.g404-root { position: relative; display: inline-block; font-family: ${e.font}; }
|
|
14
|
+
.g404-banner { text-align: center; padding: 12px 8px 8px; color: ${e.fg}; font-family: ${e.font}; }
|
|
15
|
+
.g404-banner-title { font-size: 22px; font-weight: bold; letter-spacing: 2px; color: ${e.accent}; text-shadow: 0 0 12px ${e.accent}55; }
|
|
16
|
+
.g404-banner-sub { font-size: 13px; color: ${e.muted}; margin-top: 4px; }
|
|
17
|
+
.g404-hud { display: flex; justify-content: space-between; padding: 6px 12px; color: ${e.fg}; font-family: ${e.font}; font-size: 14px; }
|
|
18
|
+
.g404-score { color: ${e.fg}; }
|
|
19
|
+
.g404-high { color: ${e.muted}; }
|
|
20
|
+
.g404-canvas-wrap { position: relative; line-height: 0; }
|
|
21
|
+
.g404-canvas { display: block; border-radius: 6px; touch-action: none; cursor: pointer; }
|
|
22
|
+
.g404-screen { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; background: ${e.bg}cc; backdrop-filter: blur(4px); color: ${e.fg}; font-family: ${e.font}; border-radius: 6px; }
|
|
23
|
+
.g404-screen-title { font-size: 32px; font-weight: bold; letter-spacing: 3px; color: ${e.accent}; text-shadow: 0 0 16px ${e.accent}88; }
|
|
24
|
+
.g404-screen-sub { font-size: 14px; color: ${e.muted}; }
|
|
25
|
+
.g404-final-score { font-size: 18px; color: ${e.fg}; }
|
|
26
|
+
.g404-final { color: ${e.accent}; font-weight: bold; }
|
|
27
|
+
.g404-btn { background: ${e.accent}; color: ${e.bg}; border: none; padding: 12px 28px; font-size: 16px; font-weight: bold; letter-spacing: 1px; border-radius: 6px; cursor: pointer; font-family: ${e.font}; transition: transform 0.1s, box-shadow 0.2s; }
|
|
28
|
+
.g404-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 16px ${e.accent}66; }
|
|
29
|
+
.g404-btn:active { transform: translateY(0); }
|
|
30
|
+
`,document.head.appendChild(t)}setScore(e){this.scoreEl.textContent=`Score: ${e}`}setHighscore(e){this.highEl.textContent=`Best: ${e}`}showStart(){this.startScreen.style.display="flex",this.gameOverScreen.style.display="none"}hideStart(){this.startScreen.style.display="none"}showGameOver(e){this.finalScoreEl.textContent=String(e),this.gameOverScreen.style.display="flex",this.startScreen.style.display="none"}hideGameOver(){this.gameOverScreen.style.display="none"}destroy(){this.banner.remove(),this.hud.remove(),this.startScreen.remove(),this.gameOverScreen.remove()}}function B(n){const e=document.createElement("div");return e.textContent=n,e.innerHTML}const K="404game.hs.",E={getHighscore(n){try{const e=localStorage.getItem(K+n);return e&&parseInt(e,10)||0}catch{return 0}},setHighscore(n,e){try{const t=E.getHighscore(n);if(e>t)return localStorage.setItem(K+n,String(e)),!0}catch{}return!1}},V={neon:{bg:"#0a0a18",fg:"#f0f0ff",accent:"#00ffd5",danger:"#ff3b6b",muted:"#5a5a8a",font:'"Courier New", monospace'},retro:{bg:"#2b1d0e",fg:"#ffe1a8",accent:"#ffaa00",danger:"#d62828",muted:"#8a6b3b",font:'"Courier New", monospace'},minimal:{bg:"#fafafa",fg:"#111111",accent:"#0066ff",danger:"#e63946",muted:"#999999",font:"system-ui, -apple-system, sans-serif"},dark:{bg:"#1a1a1a",fg:"#e8e8e8",accent:"#7c5cff",danger:"#ff5252",muted:"#666666",font:"system-ui, -apple-system, sans-serif"}};function le(n){return V[n??"neon"]??V.neon}const S=16,X=.12;function he(){let n=0,e=0,t=[],s="right",o="right",u={x:0,y:0},f=0,g=0;function h(){for(;;){const a={x:Math.floor(Math.random()*n),y:Math.floor(Math.random()*e)};if(!t.some(i=>i.x===a.x&&i.y===a.y)){u=a;return}}}function c(a){({up:"down",down:"up",left:"right",right:"left"})[a]!==s&&(o=a)}return{name:"snake",init(a){n=Math.floor(a.width/S),e=Math.floor(a.height/S);const i=Math.floor(n/2),d=Math.floor(e/2);t=[{x:i,y:d},{x:i-1,y:d},{x:i-2,y:d}],s="right",o="right",f=0,g=0,h(),a.setScore(0)},update(a){const{input:i}=a;for(i.swipe==="up"||i.up?c("up"):i.swipe==="down"||i.down?c("down"):i.swipe==="left"||i.left?c("left"):(i.swipe==="right"||i.right)&&c("right"),f+=a.dt;f>=X;){f-=X,s=o;const d=t[0],r={x:d.x,y:d.y};if(s==="up"?r.y-=1:s==="down"?r.y+=1:s==="left"?r.x-=1:s==="right"&&(r.x+=1),r.x<0||r.x>=n||r.y<0||r.y>=e){a.gameOver();return}if(t.some(l=>l.x===r.x&&l.y===r.y)){a.gameOver();return}t.unshift(r),r.x===u.x&&r.y===u.y?(g+=10,a.setScore(g),h()):t.pop()}},draw(a){const{ctx:i,theme:d}=a;i.strokeStyle=d.muted+"22",i.lineWidth=1;for(let r=0;r<=n;r++)i.beginPath(),i.moveTo(r*S,0),i.lineTo(r*S,e*S),i.stroke();for(let r=0;r<=e;r++)i.beginPath(),i.moveTo(0,r*S),i.lineTo(n*S,r*S),i.stroke();p.glow(i,d.danger,12,()=>{p.circle(i,u.x*S+S/2,u.y*S+S/2,S/2-2,d.danger)}),p.glow(i,d.accent,8,()=>{for(let r=0;r<t.length;r++){const l=t[r];p.rect(i,l.x*S+1,l.y*S+1,S-2,S-2,r===0?d.accent:d.accent+"cc")}})}}}const de=1400,fe=-380,M=50,A=130,H=200,q=160,T=12;function ge(){let n=0,e=0,t=0,s=[],o=0,u=0;return{name:"flappy",init(f){e=f.width*.25,n=f.height/2,t=0,s=[],o=0,u=H,f.setScore(0)},update(f){const{input:g,dt:h,width:c,height:a}=f;if((g.actionPressed||g.tap||g.swipe==="up")&&(t=fe),t+=de*h,n+=t*h,n>a-T||n<T){f.gameOver();return}if(u+=q*h,u>=H){u-=H;const i=60+Math.random()*(a-120-A);s.push({x:c+M,gapY:i,passed:!1})}for(const i of s)if(i.x-=q*h,!i.passed&&i.x+M<e&&(i.passed=!0,o+=1,f.setScore(o)),e+T>i.x&&e-T<i.x+M&&(n-T<i.gapY||n+T>i.gapY+A)){f.gameOver();return}s=s.filter(i=>i.x+M>-10)},draw(f){const{ctx:g,theme:h,height:c}=f;p.glow(g,h.accent,8,()=>{for(const a of s)p.rect(g,a.x,0,M,a.gapY,h.accent),p.rect(g,a.x,a.gapY+A,M,c-(a.gapY+A),h.accent)}),p.glow(g,h.danger,12,()=>{p.circle(g,e,n,T,h.danger)}),p.circle(g,e+4,n-3,2,h.bg)}}}const P=80,C=10,Y=30,z=420,w=6,D=280,ue=5,$=8;function me(){let n=0,e=0,t=0,s=0,o=0,u=[],f=0,g=!1;return{name:"breakout",init(h){n=h.width/2-P/2,e=h.width/2,t=h.height-Y-C-w-1,s=0,o=0,g=!1,f=0,h.setScore(0),u=[];const c=6,a=40,i=(h.width-c*($+1))/$,d=16,r=[h.theme.danger,h.theme.accent,h.theme.fg,h.theme.accent,h.theme.danger];for(let l=0;l<ue;l++)for(let m=0;m<$;m++)u.push({x:c+m*(i+c),y:a+l*(d+c),w:i,h:d,alive:!0,color:r[l]??h.theme.accent})},update(h){const{input:c,dt:a,width:i,height:d}=h;c.left&&(n-=z*a),c.right&&(n+=z*a),c.swipe==="left"&&(n-=40),c.swipe==="right"&&(n+=40),n=Math.max(0,Math.min(i-P,n));const r=d-Y-C;if(!g){if(e=n+P/2,t=r-w-1,c.actionPressed||c.tap){g=!0;const l=-Math.PI/2+(Math.random()-.5)*.6;s=Math.cos(l)*D,o=Math.sin(l)*D}return}if(e+=s*a,t+=o*a,e<w&&(e=w,s=-s),e>i-w&&(e=i-w,s=-s),t<w&&(t=w,o=-o),t+w>=r&&t-w<=r+C&&e>=n&&e<=n+P&&o>0){o=-Math.abs(o),s=(e-(n+P/2))/(P/2)*D*.9;const m=Math.sqrt(s*s+o*o),b=D;s=s/m*b,o=o/m*b}for(const l of u)if(l.alive&&e+w>l.x&&e-w<l.x+l.w&&t+w>l.y&&t-w<l.y+l.h){l.alive=!1,f+=10,h.setScore(f);const m=Math.min(e+w-l.x,l.x+l.w-(e-w)),b=Math.min(t+w-l.y,l.y+l.h-(t-w));m<b?s=-s:o=-o;break}if(u.every(l=>!l.alive)){h.win();return}if(t>d+20){h.gameOver();return}},draw(h){const{ctx:c,theme:a,height:i}=h;for(const r of u)r.alive&&p.glow(c,r.color,6,()=>{p.rect(c,r.x,r.y,r.w,r.h,r.color)});const d=i-Y-C;p.glow(c,a.accent,8,()=>{p.rect(c,n,d,P,C,a.accent)}),p.glow(c,a.fg,10,()=>{p.circle(c,e,t,w,a.fg)}),g||p.text(c,"Press SPACE / TAP to launch",h.width/2,h.height-8,{color:a.muted,size:11,font:a.font,align:"center"})}}}const pe=2200,we=-780,R=24,I=28,ye=30,_=240;function Se(){let n=0,e=0,t=0,s=0,o=[],u=0,f=0,g=0,h=_;return{name:"dino",init(c){n=60,s=c.height-ye,e=s-I,t=0,o=[],u=.8,f=0,g=0,h=_,c.setScore(0)},update(c){const{input:a,dt:i,width:d}=c,r=e>=s-I;if((a.actionPressed||a.tap||a.swipe==="up")&&r&&(t=we),t+=pe*i,e+=t*i,e>s-I&&(e=s-I,t=0),h+=i*8,g+=i*10,g>=1){const l=Math.floor(g);g-=l,f+=l,c.setScore(f)}if(u-=i,u<=0){const l=22+Math.random()*28,m=12+Math.random()*16;o.push({x:d+20,w:m,h:l}),u=.7+Math.random()*.9-Math.min(.4,(h-_)/1e3),u<.4&&(u=.4)}for(const l of o)l.x-=h*i;o=o.filter(l=>l.x+l.w>-10);for(const l of o){const m=l.x,b=s-l.h;if(n+R>m&&n<m+l.w&&e+I>b&&e<b+l.h){c.gameOver();return}}},draw(c){const{ctx:a,theme:i,width:d}=c;a.strokeStyle=i.muted,a.lineWidth=1,a.beginPath(),a.moveTo(0,s+.5),a.lineTo(d,s+.5),a.stroke();for(let r=0;r<20;r++){const l=r*40-c.time*80%40;p.rect(a,l,s+4,16,2,i.muted)}p.glow(a,i.danger,6,()=>{for(const r of o)p.rect(a,r.x,s-r.h,r.w,r.h,i.danger)}),p.glow(a,i.accent,10,()=>{p.rect(a,n,e,R,I,i.accent)}),p.rect(a,n+R-7,e+6,3,3,i.bg)}}}const ve="0.1.0",G={snake:he,flappy:ge,breakout:me,dino:Se},L=new WeakMap;function j(n){const e=typeof n=="string"?document.querySelector(n):n;if(!e)throw new Error(`404game: target not found: ${String(n)}`);return e}function W(n){if(!n||n==="random"){const t=Object.keys(G),s=t[Math.floor(Math.random()*t.length)];return{name:s,module:G[s]()}}const e=G[n];if(!e)throw new Error(`404game: unknown game "${n}"`);return{name:n,module:e()}}function U(n,e={}){const t=j(n),s=L.get(t);s&&s.destroy();const o=le(e.theme),u=e.width??(Math.min(t.clientWidth||480,720)||480),f=e.height??360;t.classList.add("g404-root"),t.innerHTML="";const g=document.createElement("div");g.className="g404-canvas-wrap";const h=document.createElement("canvas");h.className="g404-canvas",h.style.background=o.bg,g.appendChild(h),t.appendChild(g);let c="",a=0,i=W(e.game);c=i.name;const d=new ce(t,{message:e.message??"404 — Page Not Found",subMessage:e.subMessage??"But hey, want to play instead?",theme:o,showHighscore:e.showHighscore!==!1,onStart:()=>{var y;d.hideStart(),d.hideGameOver(),m.start(),(y=e.onStart)==null||y.call(e)},onRestart:()=>{var y;d.hideGameOver(),i=W(e.game),c=i.name,m.destroy(),b(),m.start(),(y=e.onStart)==null||y.call(e)}}),r=t.querySelector(".g404-start"),l=t.querySelector(".g404-gameover");r&&g.appendChild(r),l&&g.appendChild(l),g.style.position="relative",d.setHighscore(E.getHighscore(c));let m;function b(){m=new oe({canvas:h,theme:o,width:u,height:f,game:i.module,onScore:y=>{var v;a=y,d.setScore(y),(v=e.onScore)==null||v.call(e,y)},onGameOver:y=>{var v;E.setHighscore(c,y),d.setHighscore(E.getHighscore(c)),d.showGameOver(y),(v=e.onGameOver)==null||v.call(e,y)},onWin:y=>{var v;E.setHighscore(c,y),d.setHighscore(E.getHighscore(c)),d.showGameOver(y),(v=e.onWin)==null||v.call(e,y)}})}if(b(),e.autoStart)d.hideStart(),m.start();else{d.showStart();const y=v=>{var O,N,te;v.preventDefault(),!m.isRunning()&&((N=(O=t.querySelector(".g404-start"))==null?void 0:O.checkVisibility)==null?void 0:N.call(O))!==!1&&(r==null?void 0:r.style.display)!=="none"&&(d.hideStart(),m.start(),(te=e.onStart)==null||te.call(e))};h.addEventListener("click",y),window.addEventListener("keydown",v=>{var O;(v.key===" "||v.key==="Enter")&&!m.isRunning()&&(r==null?void 0:r.style.display)!=="none"&&(v.preventDefault(),d.hideStart(),m.start(),(O=e.onStart)==null||O.call(e))})}const ee={destroy(){m.destroy(),d.destroy(),t.innerHTML="",t.classList.remove("g404-root"),L.delete(t)},restart(){m.destroy(),i=W(e.game),c=i.name,b(),d.setHighscore(E.getHighscore(c)),d.hideGameOver(),d.hideStart(),m.start()},getScore(){return a}};return L.set(t,ee),ee}function J(n){const e=j(n),t=L.get(e);t&&t.destroy()}const Q=["snake","flappy","breakout","dino"],Z=ve,be={mount:U,unmount:J,games:Q,version:Z};x.default=be,x.games=Q,x.mount=U,x.unmount=J,x.version=Z,Object.defineProperties(x,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
31
|
+
//# sourceMappingURL=404game.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"404game.umd.cjs","sources":["../src/core/Input.ts","../src/core/Renderer.ts","../src/core/Engine.ts","../src/ui/Overlay.ts","../src/core/Storage.ts","../src/ui/themes.ts","../src/games/Snake.ts","../src/games/Flappy.ts","../src/games/Breakout.ts","../src/games/Dino.ts","../src/index.ts"],"sourcesContent":["import type { InputState } from '../types';\n\nconst ACTION_KEYS = new Set([' ', 'Spacebar', 'Enter']);\nconst UP_KEYS = new Set(['ArrowUp', 'w', 'W']);\nconst DOWN_KEYS = new Set(['ArrowDown', 's', 'S']);\nconst LEFT_KEYS = new Set(['ArrowLeft', 'a', 'A']);\nconst RIGHT_KEYS = new Set(['ArrowRight', 'd', 'D']);\n\nconst SWIPE_THRESHOLD = 30;\n\nexport class Input {\n state: InputState = {\n up: false,\n down: false,\n left: false,\n right: false,\n action: false,\n actionPressed: false,\n swipe: null,\n tap: false,\n };\n\n private actionWasPressed = false;\n private nextSwipe: InputState['swipe'] = null;\n private nextTap = false;\n\n private touchStartX = 0;\n private touchStartY = 0;\n private touchStartTime = 0;\n\n private target: HTMLElement;\n private boundHandlers: { type: string; fn: EventListener; target: EventTarget }[] = [];\n\n constructor(target: HTMLElement) {\n this.target = target;\n this.attach();\n }\n\n private attach(): void {\n const onKeyDown = (e: KeyboardEvent) => {\n if (this.handleKey(e.key, true)) e.preventDefault();\n };\n const onKeyUp = (e: KeyboardEvent) => {\n this.handleKey(e.key, false);\n };\n\n const onTouchStart = (e: TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n this.touchStartX = t.clientX;\n this.touchStartY = t.clientY;\n this.touchStartTime = performance.now();\n e.preventDefault();\n };\n const onTouchEnd = (e: TouchEvent) => {\n const t = e.changedTouches[0];\n if (!t) return;\n const dx = t.clientX - this.touchStartX;\n const dy = t.clientY - this.touchStartY;\n const elapsed = performance.now() - this.touchStartTime;\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (absDx < SWIPE_THRESHOLD && absDy < SWIPE_THRESHOLD && elapsed < 400) {\n this.nextTap = true;\n this.nextSwipe = null;\n this.actionWasPressed = true;\n } else if (absDx > absDy) {\n this.nextSwipe = dx > 0 ? 'right' : 'left';\n } else {\n this.nextSwipe = dy > 0 ? 'down' : 'up';\n }\n e.preventDefault();\n };\n\n const onMouseDown = (e: MouseEvent) => {\n this.nextTap = true;\n this.actionWasPressed = true;\n e.preventDefault();\n };\n\n this.addListener(window, 'keydown', onKeyDown as EventListener);\n this.addListener(window, 'keyup', onKeyUp as EventListener);\n this.addListener(this.target, 'touchstart', onTouchStart as EventListener, { passive: false });\n this.addListener(this.target, 'touchend', onTouchEnd as EventListener, { passive: false });\n this.addListener(this.target, 'mousedown', onMouseDown as EventListener);\n }\n\n private addListener(\n target: EventTarget,\n type: string,\n fn: EventListener,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, fn, options);\n this.boundHandlers.push({ type, fn, target });\n }\n\n private handleKey(key: string, down: boolean): boolean {\n let handled = false;\n if (UP_KEYS.has(key)) {\n this.state.up = down;\n handled = true;\n if (down) this.nextSwipe = 'up';\n } else if (DOWN_KEYS.has(key)) {\n this.state.down = down;\n handled = true;\n if (down) this.nextSwipe = 'down';\n } else if (LEFT_KEYS.has(key)) {\n this.state.left = down;\n handled = true;\n if (down) this.nextSwipe = 'left';\n } else if (RIGHT_KEYS.has(key)) {\n this.state.right = down;\n handled = true;\n if (down) this.nextSwipe = 'right';\n } else if (ACTION_KEYS.has(key)) {\n const wasDown = this.state.action;\n this.state.action = down;\n if (down && !wasDown) this.actionWasPressed = true;\n handled = true;\n }\n return handled;\n }\n\n /** Called by Engine once per frame BEFORE update. */\n beginFrame(): void {\n this.state.actionPressed = this.actionWasPressed;\n this.state.swipe = this.nextSwipe;\n this.state.tap = this.nextTap;\n }\n\n /** Called by Engine once per frame AFTER update. */\n endFrame(): void {\n this.actionWasPressed = false;\n this.nextSwipe = null;\n this.nextTap = false;\n }\n\n destroy(): void {\n for (const { target, type, fn } of this.boundHandlers) {\n target.removeEventListener(type, fn);\n }\n this.boundHandlers = [];\n }\n}\n","import type { Theme } from '../types';\n\n/** Helpers for drawing on a 2D canvas with theme support. */\nexport const Renderer = {\n clear(ctx: CanvasRenderingContext2D, w: number, h: number, theme: Theme): void {\n ctx.fillStyle = theme.bg;\n ctx.fillRect(0, 0, w, h);\n },\n\n rect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n color: string,\n ): void {\n ctx.fillStyle = color;\n ctx.fillRect(x, y, w, h);\n },\n\n circle(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n r: number,\n color: string,\n ): void {\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.arc(x, y, r, 0, Math.PI * 2);\n ctx.fill();\n },\n\n text(\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number,\n y: number,\n options: {\n color?: string;\n size?: number;\n font?: string;\n align?: CanvasTextAlign;\n baseline?: CanvasTextBaseline;\n bold?: boolean;\n } = {},\n ): void {\n const size = options.size ?? 16;\n const weight = options.bold ? 'bold ' : '';\n ctx.fillStyle = options.color ?? '#fff';\n ctx.font = `${weight}${size}px ${options.font ?? 'monospace'}`;\n ctx.textAlign = options.align ?? 'left';\n ctx.textBaseline = options.baseline ?? 'alphabetic';\n ctx.fillText(text, x, y);\n },\n\n glow(\n ctx: CanvasRenderingContext2D,\n color: string,\n blur: number,\n draw: () => void,\n ): void {\n ctx.save();\n ctx.shadowColor = color;\n ctx.shadowBlur = blur;\n draw();\n ctx.restore();\n },\n};\n","import type { GameContext, GameModule, Theme, InputState } from '../types';\nimport { Input } from './Input';\nimport { Renderer } from './Renderer';\n\nexport interface EngineOptions {\n canvas: HTMLCanvasElement;\n theme: Theme;\n width: number;\n height: number;\n game: GameModule;\n onScore: (score: number) => void;\n onGameOver: (score: number) => void;\n onWin: (score: number) => void;\n}\n\ntype State = 'idle' | 'running' | 'gameover' | 'won';\n\nexport class Engine {\n private opts: EngineOptions;\n private ctx2d: CanvasRenderingContext2D;\n private input: Input;\n private rafId: number | null = null;\n private state: State = 'idle';\n private lastTime = 0;\n private startTime = 0;\n private score = 0;\n private gameCtx: GameContext;\n\n constructor(opts: EngineOptions) {\n this.opts = opts;\n const ctx = opts.canvas.getContext('2d');\n if (!ctx) throw new Error('404game: 2D canvas context unavailable');\n this.ctx2d = ctx;\n\n // Handle high-DPI: scale backing store but draw in CSS pixels.\n const dpr = window.devicePixelRatio || 1;\n opts.canvas.width = opts.width * dpr;\n opts.canvas.height = opts.height * dpr;\n opts.canvas.style.width = `${opts.width}px`;\n opts.canvas.style.height = `${opts.height}px`;\n this.ctx2d.setTransform(dpr, 0, 0, dpr, 0, 0);\n\n this.input = new Input(opts.canvas);\n\n this.gameCtx = {\n ctx: this.ctx2d,\n width: opts.width,\n height: opts.height,\n theme: opts.theme,\n input: this.input.state,\n dt: 0,\n time: 0,\n setScore: (s: number) => {\n this.score = s;\n this.opts.onScore(s);\n },\n gameOver: () => this.handleGameOver(),\n win: () => this.handleWin(),\n };\n }\n\n start(): void {\n if (this.state === 'running') return;\n this.score = 0;\n this.opts.onScore(0);\n this.startTime = performance.now();\n this.lastTime = this.startTime;\n this.state = 'running';\n this.opts.game.init(this.gameCtx);\n this.loop();\n }\n\n stop(): void {\n if (this.rafId != null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.state = 'idle';\n }\n\n isRunning(): boolean {\n return this.state === 'running';\n }\n\n private loop = (): void => {\n if (this.state !== 'running') return;\n const now = performance.now();\n const dt = Math.min(0.05, (now - this.lastTime) / 1000); // cap at 50ms\n this.lastTime = now;\n\n this.gameCtx.dt = dt;\n this.gameCtx.time = (now - this.startTime) / 1000;\n\n this.input.beginFrame();\n Renderer.clear(this.ctx2d, this.opts.width, this.opts.height, this.opts.theme);\n this.opts.game.update(this.gameCtx);\n if (this.state === 'running') {\n this.opts.game.draw(this.gameCtx);\n }\n this.input.endFrame();\n\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n private handleGameOver(): void {\n if (this.state !== 'running') return;\n this.state = 'gameover';\n if (this.rafId != null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.opts.onGameOver(this.score);\n }\n\n private handleWin(): void {\n if (this.state !== 'running') return;\n this.state = 'won';\n if (this.rafId != null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.opts.onWin(this.score);\n }\n\n getInputState(): InputState {\n return this.input.state;\n }\n\n destroy(): void {\n this.stop();\n this.input.destroy();\n this.opts.game.destroy?.();\n }\n}\n","import type { Theme } from '../types';\n\nexport interface OverlayOptions {\n message: string;\n subMessage: string;\n theme: Theme;\n showHighscore: boolean;\n onStart: () => void;\n onRestart: () => void;\n}\n\nexport class Overlay {\n private root: HTMLElement;\n private banner: HTMLElement;\n private startScreen: HTMLElement;\n private gameOverScreen: HTMLElement;\n private hud: HTMLElement;\n private scoreEl: HTMLElement;\n private highEl: HTMLElement;\n private finalScoreEl: HTMLElement;\n private opts: OverlayOptions;\n\n constructor(container: HTMLElement, opts: OverlayOptions) {\n this.opts = opts;\n this.root = container;\n\n // Banner\n this.banner = document.createElement('div');\n this.banner.className = 'g404-banner';\n this.banner.innerHTML = `\n <div class=\"g404-banner-title\">${escape(opts.message)}</div>\n <div class=\"g404-banner-sub\">${escape(opts.subMessage)}</div>\n `;\n\n // HUD (score)\n this.hud = document.createElement('div');\n this.hud.className = 'g404-hud';\n this.scoreEl = document.createElement('span');\n this.scoreEl.className = 'g404-score';\n this.scoreEl.textContent = 'Score: 0';\n this.highEl = document.createElement('span');\n this.highEl.className = 'g404-high';\n this.highEl.textContent = 'Best: 0';\n this.hud.appendChild(this.scoreEl);\n if (opts.showHighscore) this.hud.appendChild(this.highEl);\n\n // Start screen\n this.startScreen = document.createElement('div');\n this.startScreen.className = 'g404-screen g404-start';\n this.startScreen.innerHTML = `\n <div class=\"g404-screen-title\">READY?</div>\n <div class=\"g404-screen-sub\">Press SPACE or TAP to start</div>\n <button class=\"g404-btn\" type=\"button\">▶ START</button>\n `;\n this.startScreen.querySelector('button')!.addEventListener('click', (e) => {\n e.stopPropagation();\n opts.onStart();\n });\n\n // Game-over screen\n this.gameOverScreen = document.createElement('div');\n this.gameOverScreen.className = 'g404-screen g404-gameover';\n this.gameOverScreen.style.display = 'none';\n this.gameOverScreen.innerHTML = `\n <div class=\"g404-screen-title\">GAME OVER</div>\n <div class=\"g404-final-score\">Score: <span class=\"g404-final\">0</span></div>\n <button class=\"g404-btn\" type=\"button\">↻ PLAY AGAIN</button>\n `;\n this.finalScoreEl = this.gameOverScreen.querySelector('.g404-final')!;\n this.gameOverScreen.querySelector('button')!.addEventListener('click', (e) => {\n e.stopPropagation();\n opts.onRestart();\n });\n\n this.injectStyles(opts.theme);\n this.root.appendChild(this.banner);\n this.root.appendChild(this.hud);\n this.root.appendChild(this.startScreen);\n this.root.appendChild(this.gameOverScreen);\n }\n\n private injectStyles(theme: Theme): void {\n if (document.getElementById('g404-styles')) return;\n const style = document.createElement('style');\n style.id = 'g404-styles';\n style.textContent = `\n .g404-root { position: relative; display: inline-block; font-family: ${theme.font}; }\n .g404-banner { text-align: center; padding: 12px 8px 8px; color: ${theme.fg}; font-family: ${theme.font}; }\n .g404-banner-title { font-size: 22px; font-weight: bold; letter-spacing: 2px; color: ${theme.accent}; text-shadow: 0 0 12px ${theme.accent}55; }\n .g404-banner-sub { font-size: 13px; color: ${theme.muted}; margin-top: 4px; }\n .g404-hud { display: flex; justify-content: space-between; padding: 6px 12px; color: ${theme.fg}; font-family: ${theme.font}; font-size: 14px; }\n .g404-score { color: ${theme.fg}; }\n .g404-high { color: ${theme.muted}; }\n .g404-canvas-wrap { position: relative; line-height: 0; }\n .g404-canvas { display: block; border-radius: 6px; touch-action: none; cursor: pointer; }\n .g404-screen { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; background: ${theme.bg}cc; backdrop-filter: blur(4px); color: ${theme.fg}; font-family: ${theme.font}; border-radius: 6px; }\n .g404-screen-title { font-size: 32px; font-weight: bold; letter-spacing: 3px; color: ${theme.accent}; text-shadow: 0 0 16px ${theme.accent}88; }\n .g404-screen-sub { font-size: 14px; color: ${theme.muted}; }\n .g404-final-score { font-size: 18px; color: ${theme.fg}; }\n .g404-final { color: ${theme.accent}; font-weight: bold; }\n .g404-btn { background: ${theme.accent}; color: ${theme.bg}; border: none; padding: 12px 28px; font-size: 16px; font-weight: bold; letter-spacing: 1px; border-radius: 6px; cursor: pointer; font-family: ${theme.font}; transition: transform 0.1s, box-shadow 0.2s; }\n .g404-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 16px ${theme.accent}66; }\n .g404-btn:active { transform: translateY(0); }\n `;\n document.head.appendChild(style);\n }\n\n setScore(score: number): void {\n this.scoreEl.textContent = `Score: ${score}`;\n }\n\n setHighscore(hs: number): void {\n this.highEl.textContent = `Best: ${hs}`;\n }\n\n showStart(): void {\n this.startScreen.style.display = 'flex';\n this.gameOverScreen.style.display = 'none';\n }\n\n hideStart(): void {\n this.startScreen.style.display = 'none';\n }\n\n showGameOver(score: number): void {\n this.finalScoreEl.textContent = String(score);\n this.gameOverScreen.style.display = 'flex';\n this.startScreen.style.display = 'none';\n }\n\n hideGameOver(): void {\n this.gameOverScreen.style.display = 'none';\n }\n\n destroy(): void {\n this.banner.remove();\n this.hud.remove();\n this.startScreen.remove();\n this.gameOverScreen.remove();\n }\n}\n\nfunction escape(s: string): string {\n const div = document.createElement('div');\n div.textContent = s;\n return div.innerHTML;\n}\n","const PREFIX = '404game.hs.';\n\nexport const Storage = {\n getHighscore(gameName: string): number {\n try {\n const v = localStorage.getItem(PREFIX + gameName);\n return v ? parseInt(v, 10) || 0 : 0;\n } catch {\n return 0;\n }\n },\n\n setHighscore(gameName: string, score: number): boolean {\n try {\n const prev = Storage.getHighscore(gameName);\n if (score > prev) {\n localStorage.setItem(PREFIX + gameName, String(score));\n return true;\n }\n } catch {\n /* ignore */\n }\n return false;\n },\n};\n","import type { Theme, ThemeName } from '../types';\n\nexport const themes: Record<ThemeName, Theme> = {\n neon: {\n bg: '#0a0a18',\n fg: '#f0f0ff',\n accent: '#00ffd5',\n danger: '#ff3b6b',\n muted: '#5a5a8a',\n font: '\"Courier New\", monospace',\n },\n retro: {\n bg: '#2b1d0e',\n fg: '#ffe1a8',\n accent: '#ffaa00',\n danger: '#d62828',\n muted: '#8a6b3b',\n font: '\"Courier New\", monospace',\n },\n minimal: {\n bg: '#fafafa',\n fg: '#111111',\n accent: '#0066ff',\n danger: '#e63946',\n muted: '#999999',\n font: 'system-ui, -apple-system, sans-serif',\n },\n dark: {\n bg: '#1a1a1a',\n fg: '#e8e8e8',\n accent: '#7c5cff',\n danger: '#ff5252',\n muted: '#666666',\n font: 'system-ui, -apple-system, sans-serif',\n },\n};\n\nexport function getTheme(name: ThemeName | undefined): Theme {\n return themes[name ?? 'neon'] ?? themes.neon;\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Vec { x: number; y: number; }\ntype Dir = 'up' | 'down' | 'left' | 'right';\n\nconst CELL = 16;\nconst TICK = 0.12; // seconds per move\n\nexport function createSnake(): GameModule {\n let cols = 0;\n let rows = 0;\n let snake: Vec[] = [];\n let dir: Dir = 'right';\n let queuedDir: Dir = 'right';\n let food: Vec = { x: 0, y: 0 };\n let acc = 0;\n let score = 0;\n\n function spawnFood(): void {\n while (true) {\n const f = { x: Math.floor(Math.random() * cols), y: Math.floor(Math.random() * rows) };\n if (!snake.some((s) => s.x === f.x && s.y === f.y)) {\n food = f;\n return;\n }\n }\n }\n\n function turn(d: Dir): void {\n const opposites: Record<Dir, Dir> = { up: 'down', down: 'up', left: 'right', right: 'left' };\n if (opposites[d] !== dir) queuedDir = d;\n }\n\n return {\n name: 'snake',\n init(ctx: GameContext) {\n cols = Math.floor(ctx.width / CELL);\n rows = Math.floor(ctx.height / CELL);\n const cx = Math.floor(cols / 2);\n const cy = Math.floor(rows / 2);\n snake = [{ x: cx, y: cy }, { x: cx - 1, y: cy }, { x: cx - 2, y: cy }];\n dir = 'right';\n queuedDir = 'right';\n acc = 0;\n score = 0;\n spawnFood();\n ctx.setScore(0);\n },\n\n update(ctx: GameContext) {\n const { input } = ctx;\n if (input.swipe === 'up' || input.up) turn('up');\n else if (input.swipe === 'down' || input.down) turn('down');\n else if (input.swipe === 'left' || input.left) turn('left');\n else if (input.swipe === 'right' || input.right) turn('right');\n\n acc += ctx.dt;\n while (acc >= TICK) {\n acc -= TICK;\n dir = queuedDir;\n const head = snake[0]!;\n const nh: Vec = { x: head.x, y: head.y };\n if (dir === 'up') nh.y -= 1;\n else if (dir === 'down') nh.y += 1;\n else if (dir === 'left') nh.x -= 1;\n else if (dir === 'right') nh.x += 1;\n\n if (nh.x < 0 || nh.x >= cols || nh.y < 0 || nh.y >= rows) {\n ctx.gameOver();\n return;\n }\n if (snake.some((s) => s.x === nh.x && s.y === nh.y)) {\n ctx.gameOver();\n return;\n }\n snake.unshift(nh);\n if (nh.x === food.x && nh.y === food.y) {\n score += 10;\n ctx.setScore(score);\n spawnFood();\n } else {\n snake.pop();\n }\n }\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme } = ctx;\n\n // grid hint\n c.strokeStyle = theme.muted + '22';\n c.lineWidth = 1;\n for (let x = 0; x <= cols; x++) {\n c.beginPath(); c.moveTo(x * CELL, 0); c.lineTo(x * CELL, rows * CELL); c.stroke();\n }\n for (let y = 0; y <= rows; y++) {\n c.beginPath(); c.moveTo(0, y * CELL); c.lineTo(cols * CELL, y * CELL); c.stroke();\n }\n\n // food\n Renderer.glow(c, theme.danger, 12, () => {\n Renderer.circle(c, food.x * CELL + CELL / 2, food.y * CELL + CELL / 2, CELL / 2 - 2, theme.danger);\n });\n\n // snake\n Renderer.glow(c, theme.accent, 8, () => {\n for (let i = 0; i < snake.length; i++) {\n const s = snake[i]!;\n Renderer.rect(c, s.x * CELL + 1, s.y * CELL + 1, CELL - 2, CELL - 2, i === 0 ? theme.accent : theme.accent + 'cc');\n }\n });\n },\n };\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Pipe { x: number; gapY: number; passed: boolean; }\n\nconst GRAVITY = 1400;\nconst FLAP_VELOCITY = -380;\nconst PIPE_WIDTH = 50;\nconst PIPE_GAP = 130;\nconst PIPE_SPACING = 200;\nconst PIPE_SPEED = 160;\nconst BIRD_RADIUS = 12;\n\nexport function createFlappy(): GameModule {\n let birdY = 0;\n let birdX = 0;\n let velocity = 0;\n let pipes: Pipe[] = [];\n let score = 0;\n let spawnAcc = 0;\n\n return {\n name: 'flappy',\n init(ctx: GameContext) {\n birdX = ctx.width * 0.25;\n birdY = ctx.height / 2;\n velocity = 0;\n pipes = [];\n score = 0;\n spawnAcc = PIPE_SPACING; // spawn first immediately\n ctx.setScore(0);\n },\n\n update(ctx: GameContext) {\n const { input, dt, width, height } = ctx;\n\n if (input.actionPressed || input.tap || input.swipe === 'up') {\n velocity = FLAP_VELOCITY;\n }\n\n velocity += GRAVITY * dt;\n birdY += velocity * dt;\n\n if (birdY > height - BIRD_RADIUS || birdY < BIRD_RADIUS) {\n ctx.gameOver();\n return;\n }\n\n // spawn pipes by distance\n spawnAcc += PIPE_SPEED * dt;\n if (spawnAcc >= PIPE_SPACING) {\n spawnAcc -= PIPE_SPACING;\n const gapY = 60 + Math.random() * (height - 120 - PIPE_GAP);\n pipes.push({ x: width + PIPE_WIDTH, gapY, passed: false });\n }\n\n for (const p of pipes) {\n p.x -= PIPE_SPEED * dt;\n if (!p.passed && p.x + PIPE_WIDTH < birdX) {\n p.passed = true;\n score += 1;\n ctx.setScore(score);\n }\n // collision\n if (birdX + BIRD_RADIUS > p.x && birdX - BIRD_RADIUS < p.x + PIPE_WIDTH) {\n if (birdY - BIRD_RADIUS < p.gapY || birdY + BIRD_RADIUS > p.gapY + PIPE_GAP) {\n ctx.gameOver();\n return;\n }\n }\n }\n pipes = pipes.filter((p) => p.x + PIPE_WIDTH > -10);\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme, height } = ctx;\n\n // pipes\n Renderer.glow(c, theme.accent, 8, () => {\n for (const p of pipes) {\n Renderer.rect(c, p.x, 0, PIPE_WIDTH, p.gapY, theme.accent);\n Renderer.rect(c, p.x, p.gapY + PIPE_GAP, PIPE_WIDTH, height - (p.gapY + PIPE_GAP), theme.accent);\n }\n });\n\n // bird\n Renderer.glow(c, theme.danger, 12, () => {\n Renderer.circle(c, birdX, birdY, BIRD_RADIUS, theme.danger);\n });\n // eye\n Renderer.circle(c, birdX + 4, birdY - 3, 2, theme.bg);\n },\n };\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Brick { x: number; y: number; w: number; h: number; alive: boolean; color: string; }\n\nconst PADDLE_W = 80;\nconst PADDLE_H = 10;\nconst PADDLE_Y_OFFSET = 30;\nconst PADDLE_SPEED = 420;\nconst BALL_RADIUS = 6;\nconst BALL_SPEED = 280;\nconst BRICK_ROWS = 5;\nconst BRICK_COLS = 8;\n\nexport function createBreakout(): GameModule {\n let paddleX = 0;\n let ballX = 0, ballY = 0;\n let vx = 0, vy = 0;\n let bricks: Brick[] = [];\n let score = 0;\n let launched = false;\n\n return {\n name: 'breakout',\n init(ctx: GameContext) {\n paddleX = ctx.width / 2 - PADDLE_W / 2;\n ballX = ctx.width / 2;\n ballY = ctx.height - PADDLE_Y_OFFSET - PADDLE_H - BALL_RADIUS - 1;\n vx = 0;\n vy = 0;\n launched = false;\n score = 0;\n ctx.setScore(0);\n\n bricks = [];\n const pad = 6;\n const offsetY = 40;\n const brickW = (ctx.width - pad * (BRICK_COLS + 1)) / BRICK_COLS;\n const brickH = 16;\n const palette = [ctx.theme.danger, ctx.theme.accent, ctx.theme.fg, ctx.theme.accent, ctx.theme.danger];\n for (let r = 0; r < BRICK_ROWS; r++) {\n for (let col = 0; col < BRICK_COLS; col++) {\n bricks.push({\n x: pad + col * (brickW + pad),\n y: offsetY + r * (brickH + pad),\n w: brickW,\n h: brickH,\n alive: true,\n color: palette[r] ?? ctx.theme.accent,\n });\n }\n }\n },\n\n update(ctx: GameContext) {\n const { input, dt, width, height } = ctx;\n\n // Paddle controls: keyboard, swipe (treated as direction hold via input.left/right),\n // or follow tap position on touch (use last touch X via tap fallback - simple: arrows only).\n if (input.left) paddleX -= PADDLE_SPEED * dt;\n if (input.right) paddleX += PADDLE_SPEED * dt;\n if (input.swipe === 'left') paddleX -= 40;\n if (input.swipe === 'right') paddleX += 40;\n paddleX = Math.max(0, Math.min(width - PADDLE_W, paddleX));\n\n const paddleY = height - PADDLE_Y_OFFSET - PADDLE_H;\n\n if (!launched) {\n ballX = paddleX + PADDLE_W / 2;\n ballY = paddleY - BALL_RADIUS - 1;\n if (input.actionPressed || input.tap) {\n launched = true;\n const angle = -Math.PI / 2 + (Math.random() - 0.5) * 0.6;\n vx = Math.cos(angle) * BALL_SPEED;\n vy = Math.sin(angle) * BALL_SPEED;\n }\n return;\n }\n\n ballX += vx * dt;\n ballY += vy * dt;\n\n // walls\n if (ballX < BALL_RADIUS) { ballX = BALL_RADIUS; vx = -vx; }\n if (ballX > width - BALL_RADIUS) { ballX = width - BALL_RADIUS; vx = -vx; }\n if (ballY < BALL_RADIUS) { ballY = BALL_RADIUS; vy = -vy; }\n\n // paddle collision\n if (\n ballY + BALL_RADIUS >= paddleY &&\n ballY - BALL_RADIUS <= paddleY + PADDLE_H &&\n ballX >= paddleX && ballX <= paddleX + PADDLE_W && vy > 0\n ) {\n vy = -Math.abs(vy);\n const offset = (ballX - (paddleX + PADDLE_W / 2)) / (PADDLE_W / 2);\n vx = offset * BALL_SPEED * 0.9;\n // normalize\n const speed = Math.sqrt(vx * vx + vy * vy);\n const target = BALL_SPEED;\n vx = (vx / speed) * target;\n vy = (vy / speed) * target;\n }\n\n // brick collisions\n for (const b of bricks) {\n if (!b.alive) continue;\n if (ballX + BALL_RADIUS > b.x && ballX - BALL_RADIUS < b.x + b.w &&\n ballY + BALL_RADIUS > b.y && ballY - BALL_RADIUS < b.y + b.h) {\n b.alive = false;\n score += 10;\n ctx.setScore(score);\n // simple bounce: flip whichever side has more penetration\n const overlapX = Math.min(ballX + BALL_RADIUS - b.x, b.x + b.w - (ballX - BALL_RADIUS));\n const overlapY = Math.min(ballY + BALL_RADIUS - b.y, b.y + b.h - (ballY - BALL_RADIUS));\n if (overlapX < overlapY) vx = -vx;\n else vy = -vy;\n break;\n }\n }\n\n if (bricks.every((b) => !b.alive)) {\n ctx.win();\n return;\n }\n\n // bottom\n if (ballY > height + 20) {\n ctx.gameOver();\n return;\n }\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme, height } = ctx;\n\n // bricks\n for (const b of bricks) {\n if (!b.alive) continue;\n Renderer.glow(c, b.color, 6, () => {\n Renderer.rect(c, b.x, b.y, b.w, b.h, b.color);\n });\n }\n\n // paddle\n const paddleY = height - PADDLE_Y_OFFSET - PADDLE_H;\n Renderer.glow(c, theme.accent, 8, () => {\n Renderer.rect(c, paddleX, paddleY, PADDLE_W, PADDLE_H, theme.accent);\n });\n\n // ball\n Renderer.glow(c, theme.fg, 10, () => {\n Renderer.circle(c, ballX, ballY, BALL_RADIUS, theme.fg);\n });\n\n if (!launched) {\n Renderer.text(c, 'Press SPACE / TAP to launch', ctx.width / 2, ctx.height - 8, {\n color: theme.muted, size: 11, font: theme.font, align: 'center',\n });\n }\n },\n };\n}\n","import type { GameContext, GameModule } from '../types';\nimport { Renderer } from '../core/Renderer';\n\ninterface Obstacle { x: number; w: number; h: number; }\n\nconst GRAVITY = 2200;\nconst JUMP_VELOCITY = -780;\nconst DINO_W = 24;\nconst DINO_H = 28;\nconst GROUND_OFFSET = 30;\nconst BASE_SPEED = 240;\n\nexport function createDino(): GameModule {\n let dinoX = 0;\n let dinoY = 0;\n let velocity = 0;\n let groundY = 0;\n let obstacles: Obstacle[] = [];\n let spawnTimer = 0;\n let score = 0;\n let scoreAcc = 0;\n let speed = BASE_SPEED;\n\n return {\n name: 'dino',\n init(ctx: GameContext) {\n dinoX = 60;\n groundY = ctx.height - GROUND_OFFSET;\n dinoY = groundY - DINO_H;\n velocity = 0;\n obstacles = [];\n spawnTimer = 0.8;\n score = 0;\n scoreAcc = 0;\n speed = BASE_SPEED;\n ctx.setScore(0);\n },\n\n update(ctx: GameContext) {\n const { input, dt, width } = ctx;\n const onGround = dinoY >= groundY - DINO_H;\n\n if ((input.actionPressed || input.tap || input.swipe === 'up') && onGround) {\n velocity = JUMP_VELOCITY;\n }\n\n velocity += GRAVITY * dt;\n dinoY += velocity * dt;\n if (dinoY > groundY - DINO_H) {\n dinoY = groundY - DINO_H;\n velocity = 0;\n }\n\n // speed up over time\n speed += dt * 8;\n\n // score\n scoreAcc += dt * 10;\n if (scoreAcc >= 1) {\n const inc = Math.floor(scoreAcc);\n scoreAcc -= inc;\n score += inc;\n ctx.setScore(score);\n }\n\n // spawn obstacles\n spawnTimer -= dt;\n if (spawnTimer <= 0) {\n const h = 22 + Math.random() * 28;\n const w = 12 + Math.random() * 16;\n obstacles.push({ x: width + 20, w, h });\n spawnTimer = 0.7 + Math.random() * 0.9 - Math.min(0.4, (speed - BASE_SPEED) / 1000);\n if (spawnTimer < 0.4) spawnTimer = 0.4;\n }\n\n for (const o of obstacles) o.x -= speed * dt;\n obstacles = obstacles.filter((o) => o.x + o.w > -10);\n\n // collision (axis-aligned)\n for (const o of obstacles) {\n const ox = o.x;\n const oy = groundY - o.h;\n if (\n dinoX + DINO_W > ox && dinoX < ox + o.w &&\n dinoY + DINO_H > oy && dinoY < oy + o.h\n ) {\n ctx.gameOver();\n return;\n }\n }\n },\n\n draw(ctx: GameContext) {\n const { ctx: c, theme, width } = ctx;\n\n // ground line\n c.strokeStyle = theme.muted;\n c.lineWidth = 1;\n c.beginPath();\n c.moveTo(0, groundY + 0.5);\n c.lineTo(width, groundY + 0.5);\n c.stroke();\n\n // ground tick marks (parallax-ish)\n for (let i = 0; i < 20; i++) {\n const tx = ((i * 40) - (ctx.time * 80) % 40);\n Renderer.rect(c, tx, groundY + 4, 16, 2, theme.muted);\n }\n\n // obstacles (cacti)\n Renderer.glow(c, theme.danger, 6, () => {\n for (const o of obstacles) {\n Renderer.rect(c, o.x, groundY - o.h, o.w, o.h, theme.danger);\n }\n });\n\n // dino\n Renderer.glow(c, theme.accent, 10, () => {\n Renderer.rect(c, dinoX, dinoY, DINO_W, DINO_H, theme.accent);\n });\n // eye\n Renderer.rect(c, dinoX + DINO_W - 7, dinoY + 6, 3, 3, theme.bg);\n },\n };\n}\n","/**\n * 404game.js — Turn boring 404 pages into playable mini-games.\n *\n * Quick start:\n * <script src=\"404game.js\"></script>\n * <div id=\"game\"></div>\n * <script>Game404.mount('#game', { game: 'snake' });</script>\n */\n\nimport type {\n Game404Options,\n Game404Instance,\n GameModule,\n GameName,\n} from './types';\nimport { Engine } from './core/Engine';\nimport { Overlay } from './ui/Overlay';\nimport { Storage } from './core/Storage';\nimport { getTheme } from './ui/themes';\nimport { createSnake } from './games/Snake';\nimport { createFlappy } from './games/Flappy';\nimport { createBreakout } from './games/Breakout';\nimport { createDino } from './games/Dino';\n\nconst VERSION = '0.1.0';\n\nconst REGISTRY: Record<Exclude<GameName, 'random'>, () => GameModule> = {\n snake: createSnake,\n flappy: createFlappy,\n breakout: createBreakout,\n dino: createDino,\n};\n\nconst instances = new WeakMap<Element, Game404Instance>();\n\nfunction resolveTarget(selector: string | Element): HTMLElement {\n const el = typeof selector === 'string' ? document.querySelector(selector) : selector;\n if (!el) throw new Error(`404game: target not found: ${String(selector)}`);\n return el as HTMLElement;\n}\n\nfunction pickGame(name: GameName | undefined): { name: string; module: GameModule } {\n if (!name || name === 'random') {\n const keys = Object.keys(REGISTRY) as Array<Exclude<GameName, 'random'>>;\n const k = keys[Math.floor(Math.random() * keys.length)]!;\n return { name: k, module: REGISTRY[k]() };\n }\n const factory = REGISTRY[name as Exclude<GameName, 'random'>];\n if (!factory) throw new Error(`404game: unknown game \"${name}\"`);\n return { name, module: factory() };\n}\n\nexport function mount(\n selector: string | Element,\n options: Game404Options = {},\n): Game404Instance {\n const target = resolveTarget(selector);\n // If already mounted, destroy previous instance first.\n const existing = instances.get(target);\n if (existing) existing.destroy();\n\n const theme = getTheme(options.theme);\n const width = options.width ?? (Math.min(target.clientWidth || 480, 720) || 480);\n const height = options.height ?? 360;\n\n // Wrap container so we own the DOM inside.\n target.classList.add('g404-root');\n target.innerHTML = '';\n\n const canvasWrap = document.createElement('div');\n canvasWrap.className = 'g404-canvas-wrap';\n const canvas = document.createElement('canvas');\n canvas.className = 'g404-canvas';\n canvas.style.background = theme.bg;\n canvasWrap.appendChild(canvas);\n\n // Order: banner, hud, canvas (overlays appended into canvasWrap)\n // Overlay constructor appends banner+hud+screens to `target` directly.\n // We want layout: [banner][hud][canvasWrap (with absolute overlays)]\n target.appendChild(canvasWrap);\n\n let currentGameName = '';\n let currentScore = 0;\n\n // Build initial game module\n let picked = pickGame(options.game);\n currentGameName = picked.name;\n\n const overlay = new Overlay(target, {\n message: options.message ?? '404 — Page Not Found',\n subMessage: options.subMessage ?? 'But hey, want to play instead?',\n theme,\n showHighscore: options.showHighscore !== false,\n onStart: () => {\n overlay.hideStart();\n overlay.hideGameOver();\n engine.start();\n options.onStart?.();\n },\n onRestart: () => {\n overlay.hideGameOver();\n // re-create game module so internal state resets cleanly\n picked = pickGame(options.game);\n currentGameName = picked.name;\n engine.destroy();\n buildEngine();\n engine.start();\n options.onStart?.();\n },\n });\n\n // Move overlays into canvas wrap so they sit on top of the canvas.\n const startScreen = target.querySelector('.g404-start') as HTMLElement | null;\n const gameOverScreen = target.querySelector('.g404-gameover') as HTMLElement | null;\n if (startScreen) canvasWrap.appendChild(startScreen);\n if (gameOverScreen) canvasWrap.appendChild(gameOverScreen);\n canvasWrap.style.position = 'relative';\n\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n\n let engine!: Engine;\n\n function buildEngine(): void {\n engine = new Engine({\n canvas,\n theme,\n width,\n height,\n game: picked.module,\n onScore: (s: number) => {\n currentScore = s;\n overlay.setScore(s);\n options.onScore?.(s);\n },\n onGameOver: (s: number) => {\n Storage.setHighscore(currentGameName, s);\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n overlay.showGameOver(s);\n options.onGameOver?.(s);\n },\n onWin: (s: number) => {\n Storage.setHighscore(currentGameName, s);\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n overlay.showGameOver(s);\n options.onWin?.(s);\n },\n });\n }\n\n buildEngine();\n\n // Start flow\n if (options.autoStart) {\n overlay.hideStart();\n engine.start();\n } else {\n overlay.showStart();\n // Allow tap/space anywhere on canvas to start.\n const startOnAction = (e: Event) => {\n e.preventDefault();\n if (!engine.isRunning() && target.querySelector('.g404-start')?.checkVisibility?.() !== false) {\n const startVisible = (startScreen?.style.display !== 'none');\n if (startVisible) {\n overlay.hideStart();\n engine.start();\n options.onStart?.();\n }\n }\n };\n canvas.addEventListener('click', startOnAction);\n window.addEventListener('keydown', (e) => {\n if ((e.key === ' ' || e.key === 'Enter') && !engine.isRunning()) {\n const startVisible = (startScreen?.style.display !== 'none');\n if (startVisible) {\n e.preventDefault();\n overlay.hideStart();\n engine.start();\n options.onStart?.();\n }\n }\n });\n }\n\n const instance: Game404Instance = {\n destroy() {\n engine.destroy();\n overlay.destroy();\n target.innerHTML = '';\n target.classList.remove('g404-root');\n instances.delete(target);\n },\n restart() {\n engine.destroy();\n picked = pickGame(options.game);\n currentGameName = picked.name;\n buildEngine();\n overlay.setHighscore(Storage.getHighscore(currentGameName));\n overlay.hideGameOver();\n overlay.hideStart();\n engine.start();\n },\n getScore() {\n return currentScore;\n },\n };\n\n instances.set(target, instance);\n return instance;\n}\n\nexport function unmount(selector: string | Element): void {\n const target = resolveTarget(selector);\n const instance = instances.get(target);\n if (instance) instance.destroy();\n}\n\nexport const games: ReadonlyArray<Exclude<GameName, 'random'>> = ['snake', 'flappy', 'breakout', 'dino'];\n\nexport const version = VERSION;\n\n// Re-export public types\nexport type {\n Game404Options,\n Game404Instance,\n GameName,\n ThemeName,\n Theme,\n} from './types';\n\n// Default export so UMD build exposes `window.Game404` with all members.\nconst Game404 = { mount, unmount, games, version };\nexport default Game404;\n"],"names":["ACTION_KEYS","UP_KEYS","DOWN_KEYS","LEFT_KEYS","RIGHT_KEYS","SWIPE_THRESHOLD","Input","target","onKeyDown","e","onKeyUp","onTouchStart","t","onTouchEnd","dx","dy","elapsed","absDx","absDy","onMouseDown","type","fn","options","key","down","handled","wasDown","Renderer","ctx","w","h","theme","x","y","color","r","text","size","weight","blur","draw","Engine","opts","now","dt","dpr","s","_b","_a","Overlay","container","escape","style","score","hs","div","PREFIX","Storage","gameName","v","prev","themes","getTheme","name","CELL","TICK","createSnake","cols","rows","snake","dir","queuedDir","food","acc","spawnFood","f","turn","d","cx","cy","input","head","nh","c","i","GRAVITY","FLAP_VELOCITY","PIPE_WIDTH","PIPE_GAP","PIPE_SPACING","PIPE_SPEED","BIRD_RADIUS","createFlappy","birdY","birdX","velocity","pipes","spawnAcc","width","height","gapY","p","PADDLE_W","PADDLE_H","PADDLE_Y_OFFSET","PADDLE_SPEED","BALL_RADIUS","BALL_SPEED","BRICK_ROWS","BRICK_COLS","createBreakout","paddleX","ballX","ballY","vx","vy","bricks","launched","pad","offsetY","brickW","brickH","palette","col","paddleY","angle","speed","b","overlapX","overlapY","JUMP_VELOCITY","DINO_W","DINO_H","GROUND_OFFSET","BASE_SPEED","createDino","dinoX","dinoY","groundY","obstacles","spawnTimer","scoreAcc","onGround","inc","o","ox","oy","tx","VERSION","REGISTRY","instances","resolveTarget","selector","el","pickGame","keys","k","factory","mount","existing","canvasWrap","canvas","currentGameName","currentScore","picked","overlay","engine","buildEngine","startScreen","gameOverScreen","startOnAction","_c","instance","unmount","games","version","Game404"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { GameModule, Theme, InputState } from '../types';
|
|
2
|
+
export interface EngineOptions {
|
|
3
|
+
canvas: HTMLCanvasElement;
|
|
4
|
+
theme: Theme;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
game: GameModule;
|
|
8
|
+
onScore: (score: number) => void;
|
|
9
|
+
onGameOver: (score: number) => void;
|
|
10
|
+
onWin: (score: number) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare class Engine {
|
|
13
|
+
private opts;
|
|
14
|
+
private ctx2d;
|
|
15
|
+
private input;
|
|
16
|
+
private rafId;
|
|
17
|
+
private state;
|
|
18
|
+
private lastTime;
|
|
19
|
+
private startTime;
|
|
20
|
+
private score;
|
|
21
|
+
private gameCtx;
|
|
22
|
+
constructor(opts: EngineOptions);
|
|
23
|
+
start(): void;
|
|
24
|
+
stop(): void;
|
|
25
|
+
isRunning(): boolean;
|
|
26
|
+
private loop;
|
|
27
|
+
private handleGameOver;
|
|
28
|
+
private handleWin;
|
|
29
|
+
getInputState(): InputState;
|
|
30
|
+
destroy(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InputState } from '../types';
|
|
2
|
+
export declare class Input {
|
|
3
|
+
state: InputState;
|
|
4
|
+
private actionWasPressed;
|
|
5
|
+
private nextSwipe;
|
|
6
|
+
private nextTap;
|
|
7
|
+
private touchStartX;
|
|
8
|
+
private touchStartY;
|
|
9
|
+
private touchStartTime;
|
|
10
|
+
private target;
|
|
11
|
+
private boundHandlers;
|
|
12
|
+
constructor(target: HTMLElement);
|
|
13
|
+
private attach;
|
|
14
|
+
private addListener;
|
|
15
|
+
private handleKey;
|
|
16
|
+
/** Called by Engine once per frame BEFORE update. */
|
|
17
|
+
beginFrame(): void;
|
|
18
|
+
/** Called by Engine once per frame AFTER update. */
|
|
19
|
+
endFrame(): void;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Theme } from '../types';
|
|
2
|
+
/** Helpers for drawing on a 2D canvas with theme support. */
|
|
3
|
+
export declare const Renderer: {
|
|
4
|
+
clear(ctx: CanvasRenderingContext2D, w: number, h: number, theme: Theme): void;
|
|
5
|
+
rect(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, color: string): void;
|
|
6
|
+
circle(ctx: CanvasRenderingContext2D, x: number, y: number, r: number, color: string): void;
|
|
7
|
+
text(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, options?: {
|
|
8
|
+
color?: string;
|
|
9
|
+
size?: number;
|
|
10
|
+
font?: string;
|
|
11
|
+
align?: CanvasTextAlign;
|
|
12
|
+
baseline?: CanvasTextBaseline;
|
|
13
|
+
bold?: boolean;
|
|
14
|
+
}): void;
|
|
15
|
+
glow(ctx: CanvasRenderingContext2D, color: string, blur: number, draw: () => void): void;
|
|
16
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 404game.js — Turn boring 404 pages into playable mini-games.
|
|
3
|
+
*
|
|
4
|
+
* Quick start:
|
|
5
|
+
* <script src="404game.js"></script>
|
|
6
|
+
* <div id="game"></div>
|
|
7
|
+
* <script>Game404.mount('#game', { game: 'snake' });</script>
|
|
8
|
+
*/
|
|
9
|
+
import type { Game404Options, Game404Instance, GameName } from './types';
|
|
10
|
+
export declare function mount(selector: string | Element, options?: Game404Options): Game404Instance;
|
|
11
|
+
export declare function unmount(selector: string | Element): void;
|
|
12
|
+
export declare const games: ReadonlyArray<Exclude<GameName, 'random'>>;
|
|
13
|
+
export declare const version = "0.1.0";
|
|
14
|
+
export type { Game404Options, Game404Instance, GameName, ThemeName, Theme, } from './types';
|
|
15
|
+
declare const Game404: {
|
|
16
|
+
mount: typeof mount;
|
|
17
|
+
unmount: typeof unmount;
|
|
18
|
+
games: readonly ("snake" | "flappy" | "breakout" | "dino")[];
|
|
19
|
+
version: string;
|
|
20
|
+
};
|
|
21
|
+
export default Game404;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 404game.js — Public Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
export type GameName = 'snake' | 'flappy' | 'breakout' | 'dino' | 'random';
|
|
5
|
+
export type ThemeName = 'neon' | 'retro' | 'minimal' | 'dark';
|
|
6
|
+
export interface Game404Options {
|
|
7
|
+
/** Which game to load. Defaults to 'random'. */
|
|
8
|
+
game?: GameName;
|
|
9
|
+
/** Visual theme. Defaults to 'neon'. */
|
|
10
|
+
theme?: ThemeName;
|
|
11
|
+
/** Canvas width in CSS pixels. Defaults to container width or 480. */
|
|
12
|
+
width?: number;
|
|
13
|
+
/** Canvas height in CSS pixels. Defaults to container height or 360. */
|
|
14
|
+
height?: number;
|
|
15
|
+
/** Banner message above the game. Defaults to '404 — Page Not Found'. */
|
|
16
|
+
message?: string;
|
|
17
|
+
/** Sub-message text. Defaults to 'But hey, want to play instead?'. */
|
|
18
|
+
subMessage?: string;
|
|
19
|
+
/** Show high score panel. Defaults to true. */
|
|
20
|
+
showHighscore?: boolean;
|
|
21
|
+
/** Auto-start without showing the start overlay. Defaults to false. */
|
|
22
|
+
autoStart?: boolean;
|
|
23
|
+
/** Fired when the player presses start. */
|
|
24
|
+
onStart?: () => void;
|
|
25
|
+
/** Fired when the player loses; receives final score. */
|
|
26
|
+
onGameOver?: (score: number) => void;
|
|
27
|
+
/** Fired when the player wins (where applicable); receives final score. */
|
|
28
|
+
onWin?: (score: number) => void;
|
|
29
|
+
/** Fired on every score change. */
|
|
30
|
+
onScore?: (score: number) => void;
|
|
31
|
+
}
|
|
32
|
+
export interface Theme {
|
|
33
|
+
bg: string;
|
|
34
|
+
fg: string;
|
|
35
|
+
accent: string;
|
|
36
|
+
danger: string;
|
|
37
|
+
muted: string;
|
|
38
|
+
font: string;
|
|
39
|
+
}
|
|
40
|
+
export interface GameContext {
|
|
41
|
+
ctx: CanvasRenderingContext2D;
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
theme: Theme;
|
|
45
|
+
input: InputState;
|
|
46
|
+
dt: number;
|
|
47
|
+
time: number;
|
|
48
|
+
setScore: (score: number) => void;
|
|
49
|
+
gameOver: () => void;
|
|
50
|
+
win: () => void;
|
|
51
|
+
}
|
|
52
|
+
export interface InputState {
|
|
53
|
+
up: boolean;
|
|
54
|
+
down: boolean;
|
|
55
|
+
left: boolean;
|
|
56
|
+
right: boolean;
|
|
57
|
+
action: boolean;
|
|
58
|
+
/** True only on the frame the action was first pressed. */
|
|
59
|
+
actionPressed: boolean;
|
|
60
|
+
/** Most recent swipe direction this frame (or null). */
|
|
61
|
+
swipe: 'up' | 'down' | 'left' | 'right' | null;
|
|
62
|
+
/** Pointer tap occurred this frame. */
|
|
63
|
+
tap: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface GameModule {
|
|
66
|
+
name: string;
|
|
67
|
+
init(ctx: GameContext): void;
|
|
68
|
+
update(ctx: GameContext): void;
|
|
69
|
+
draw(ctx: GameContext): void;
|
|
70
|
+
destroy?(): void;
|
|
71
|
+
}
|
|
72
|
+
export interface Game404Instance {
|
|
73
|
+
destroy(): void;
|
|
74
|
+
restart(): void;
|
|
75
|
+
getScore(): number;
|
|
76
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Theme } from '../types';
|
|
2
|
+
export interface OverlayOptions {
|
|
3
|
+
message: string;
|
|
4
|
+
subMessage: string;
|
|
5
|
+
theme: Theme;
|
|
6
|
+
showHighscore: boolean;
|
|
7
|
+
onStart: () => void;
|
|
8
|
+
onRestart: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare class Overlay {
|
|
11
|
+
private root;
|
|
12
|
+
private banner;
|
|
13
|
+
private startScreen;
|
|
14
|
+
private gameOverScreen;
|
|
15
|
+
private hud;
|
|
16
|
+
private scoreEl;
|
|
17
|
+
private highEl;
|
|
18
|
+
private finalScoreEl;
|
|
19
|
+
private opts;
|
|
20
|
+
constructor(container: HTMLElement, opts: OverlayOptions);
|
|
21
|
+
private injectStyles;
|
|
22
|
+
setScore(score: number): void;
|
|
23
|
+
setHighscore(hs: number): void;
|
|
24
|
+
showStart(): void;
|
|
25
|
+
hideStart(): void;
|
|
26
|
+
showGameOver(score: number): void;
|
|
27
|
+
hideGameOver(): void;
|
|
28
|
+
destroy(): void;
|
|
29
|
+
}
|