From 816f252ccd8748d965f1d6e472025d4fae8d40c5 Mon Sep 17 00:00:00 2001 From: lordlogo2002 Date: Sat, 2 Aug 2025 22:01:19 +0200 Subject: [PATCH] fix: update version to 1.3.8 in package.json and package-lock.json; refactor RickGamePanel for improved gameplay mechanics and asset handling --- source/package-lock.json | 4 +- source/package.json | 2 +- source/src/index.tsx | 372 +++++++++++++++++++++------------------ 3 files changed, 205 insertions(+), 173 deletions(-) diff --git a/source/package-lock.json b/source/package-lock.json index ee55089..dffd1dd 100644 --- a/source/package-lock.json +++ b/source/package-lock.json @@ -1,12 +1,12 @@ { "name": "@funky-flask-test/funky-flask-test", - "version": "1.3.7", + "version": "1.3.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@funky-flask-test/funky-flask-test", - "version": "1.3.7", + "version": "1.3.8", "devDependencies": { "@types/node": "^24.0.15", "typescript": "^5.8.3", diff --git a/source/package.json b/source/package.json index 1545edf..7d65bea 100644 --- a/source/package.json +++ b/source/package.json @@ -1,6 +1,6 @@ { "name": "@funky-flask-test/funky-flask-test", - "version": "1.3.7", + "version": "1.3.8", "type": "module", "main": "./dist/index.jsx", "exports": { diff --git a/source/src/index.tsx b/source/src/index.tsx index e0ab2b6..8cecf56 100644 --- a/source/src/index.tsx +++ b/source/src/index.tsx @@ -1,238 +1,270 @@ -const ASSETS = { - squirrel: 'https://i.imgur.com/PN3NfdR.png', - dogWalk: 'https://i.imgur.com/vdgnU7H.png', - dogFly: 'https://i.imgur.com/fByuVDo.png', - nut: 'https://i.imgur.com/Nzt9t6E.png' -} - export class RickGamePanel { - private canvas!: HTMLCanvasElement; - private ctx!: CanvasRenderingContext2D; - private isStarted = false; - private isGameOver = false; - private score = 0; - private player = { x: 20, y: 180, vy: 0, width: 32, height: 32, onGround: false }; - private enemies: any[] = []; - private powerups: any[] = []; - private spawnTimer = 0; - private assetImgs: any = {}; + private canvas!: HTMLCanvasElement + private ctx!: CanvasRenderingContext2D + private isStarted = false + private isGameOver = false + private score = 0 + private enemies: any[] = [] + private powerups: any[] = [] + private spawnTimer = 0 + private frameTick = 0 - constructor(private container: HTMLElement) { - this.loadAssets(ASSETS).then(imgs => { - this.assetImgs = imgs; - this.createUI(); - this.ctx = this.canvas.getContext('2d')!; - this.attachListeners(); - this.drawStartup(); - }); + private player = { + x: 20, + y: 180, + vy: 0, + width: 10, + height: 10, + onGround: false, + frame: 0 } - private async loadAssets(assets: Record) { - const entries = Object.entries(assets); - const result: any = {}; - await Promise.all(entries.map(([key, url]) => - new Promise(res => { - const img = new Image(); - img.src = url as string; - img.onload = () => { - result[key] = img; - res(); - }; - }) - )); - return result; + constructor(private container: HTMLElement) { + this.createUI() + this.ctx = this.canvas.getContext('2d')! + this.attachListeners() + this.drawStartup() } private createUI() { - const panel = document.createElement('div'); - panel.style.cssText = `position: relative; width: 100%; max-width: 400px;`; - this.canvas = document.createElement('canvas'); - this.canvas.width = 300; - this.canvas.height = 200; - this.canvas.style.cssText = `width: 100%; border: 2px solid #333; background: #000; cursor: pointer;`; - panel.appendChild(this.canvas); - this.container.appendChild(panel); + const panel = document.createElement('div') + panel.style.cssText = `width: 100%; max-width: 400px;` + this.canvas = document.createElement('canvas') + this.canvas.width = 300 + this.canvas.height = 200 + this.canvas.style.cssText = `width: 100%; border: 2px solid #333; background: #000; cursor: pointer;` + panel.appendChild(this.canvas) + this.container.appendChild(panel) } private attachListeners() { window.addEventListener('keydown', e => { if ((e.code === 'Space' || e.code === 'Enter') && this.player.onGround && this.isStarted && !this.isGameOver) { - this.player.vy = -12; - this.player.onGround = false; + this.player.vy = -12 + this.player.onGround = false } - }); + }) this.canvas.addEventListener('click', () => { if (!this.isStarted) { - this.isStarted = true; - this.frame(); + this.isStarted = true + this.resetGame() + this.frame() } else if (this.isGameOver) { - this.reset(); + this.resetGame() } else if (this.player.onGround) { - this.player.vy = -12; - this.player.onGround = false; + this.player.vy = -12 + this.player.onGround = false } - }); + }) } private frame = () => { - if (!this.isStarted) return; + this.frameTick++ if (this.isGameOver) { - this.draw(); - return; + this.draw() + return } - this.player.vy += 0.7; - this.player.y += this.player.vy; - if (this.player.y >= 180) { this.player.y = 180; this.player.vy = 0; this.player.onGround = true; } - this.spawnTimer--; - if (this.spawnTimer <= 0) { - this.spawnEnemy(); - if (Math.random() < 0.3) this.spawnPowerup(); - this.spawnTimer = 80; + + // Update player + this.player.vy += 0.7 + this.player.y += this.player.vy + if (this.player.y >= 180) { + this.player.y = 180 + this.player.vy = 0 + this.player.onGround = true } - this.enemies.forEach(e => e.x -= 1.5); - this.powerups.forEach(p => p.x -= 1.5); - this.enemies = this.enemies.filter(e => e.x + e.size > 0); - this.powerups = this.powerups.filter(p => p.x + p.size > 0); + + if (this.frameTick % 10 === 0) { + this.player.frame = (this.player.frame + 1) % 2 + } + + // Update enemies this.enemies.forEach(e => { - if (this.collide(this.player, e)) this.gameOver(); - }); - this.powerups.forEach((p, idx) => { - if (this.collide(this.player, p)) { - this.score += 5; - this.powerups.splice(idx, 1); + e.x -= e.speed + e.tick++ + if (e.type === 'fly') { + e.y += Math.sin(e.wobblePhase + e.tick * 0.1) * 0.5 } - }); - this.draw(); - requestAnimationFrame(this.frame); + if (e.tick % 10 === 0) { + e.frame = (e.frame + 1) % 2 + } + }) + + // Update powerups + this.powerups.forEach(p => p.x -= 1.5) + + // Remove offscreen + this.enemies = this.enemies.filter(e => e.x + e.size > 0) + this.powerups = this.powerups.filter(p => p.x + p.size > 0) + + // Collisions + for (const e of this.enemies) { + if (this.collide(this.player, e)) { + this.isGameOver = true + window.open('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '_blank') + return + } + } + for (const [i, p] of this.powerups.entries()) { + if (this.collide(this.player, p)) { + this.score += 5 + this.powerups.splice(i, 1) + } + } + + // Spawn + if (--this.spawnTimer <= 0) { + this.spawnEnemy() + if (Math.random() < 0.3) this.spawnPowerup() + this.spawnTimer = 80 + } + + // Score + for (const e of this.enemies) { + if (!e.passed && e.x + e.size < this.player.x) { + e.passed = true + this.score++ + } + } + + this.draw() + requestAnimationFrame(this.frame) } private spawnEnemy() { - const variant = Math.random() < 0.5 ? 'walk' : 'fly'; - const y = variant === 'fly' ? 120 : 180 - 32; - this.enemies.push({ x: 300, y, size: 32, variant }); + const type = Math.random() < 0.3 ? 'fly' : 'walk' + const size = 10 + const y = type === 'fly' ? 120 + Math.random() * 20 : 180 - size + this.enemies.push({ + x: 300, + y, + size, + type, + speed: 1.5, + frame: 0, + tick: 0, + wobblePhase: Math.random() * Math.PI * 2, + passed: false + }) } private spawnPowerup() { - this.powerups.push({ x: 300, y: 160, size: 24 }); + this.powerups.push({ x: 300, y: 150 + Math.random() * 20, size: 8 }) } private collide(a: any, b: any) { - return a.x < b.x + b.size && a.x + a.width > b.x && - a.y < b.y + b.size && a.y + a.height > b.y; + return a.x < b.x + b.size && + a.x + a.width > b.x && + a.y < b.y + b.size && + a.y + a.height > b.y } - private gameOver() { - this.isGameOver = true; - window.open('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '_blank'); - } - - private reset() { - this.score = 0; - this.enemies = []; - this.powerups = []; - this.player.y = 180; - this.player.vy = 0; - this.isGameOver = false; - this.frame(); + private resetGame() { + this.enemies = [] + this.powerups = [] + this.score = 0 + this.spawnTimer = 0 + this.isGameOver = false + this.player.y = 180 + this.player.vy = 0 + this.player.onGround = true + this.player.frame = 0 + this.frameTick = 0 + this.frame() } private drawStartup() { - this.ctx = this.canvas.getContext('2d')!; - this.ctx.font = 'bold 16px monospace'; - this.ctx.fillStyle = '#fff'; - this.ctx.fillText('Click To Start', 80, 90); + const ctx = this.ctx + ctx.fillStyle = '#000' + ctx.fillRect(0, 0, 300, 200) + ctx.fillStyle = '#fff' + ctx.font = 'bold 16px monospace' + ctx.fillText('Click to Start', 90, 100) } private draw() { - const ctx = this.ctx; - ctx.clearRect(0, 0, 300, 200); - ctx.fillStyle = '#222'; - ctx.fillRect(0, 0, 300, 200); // background - ctx.fillStyle = '#444'; - ctx.fillRect(0, 190, 300, 10); // ground + const ctx = this.ctx + ctx.clearRect(0, 0, 300, 200) + ctx.fillStyle = '#222' + ctx.fillRect(0, 0, 300, 200) + ctx.fillStyle = '#444' + ctx.fillRect(0, 190, 300, 10) - // draw enemies + // Draw enemies this.enemies.forEach(e => { - if (e.variant === 'fly') { - this.drawDogFly(e.x, e.y); - } else { - this.drawDogWalk(e.x, e.y); - } - }); + if (e.type === 'walk') this.drawDogWalk(e.x, e.y, e.frame) + else this.drawDogFly(e.x, e.y, e.frame) + }) - // draw powerups - this.powerups.forEach(p => this.drawNut(p.x, p.y)); + // Powerups + this.powerups.forEach(p => this.drawNut(p.x, p.y)) - // draw player - this.drawSquirrel(this.player.x, this.player.y); + // Player + this.drawSquirrel(this.player.x, this.player.y, this.player.frame) - // score - ctx.fillStyle = '#fff'; - ctx.font = '12px monospace'; - ctx.fillText(`Score: ${this.score}`, 200, 20); + // Score + ctx.fillStyle = '#fff' + ctx.font = '12px monospace' + ctx.fillText(`Score: ${this.score}`, 200, 20) - // game over + // Game over if (this.isGameOver) { - ctx.font = 'bold 16px monospace'; - ctx.fillText('Game Over! Click to restart', 50, 100); + ctx.font = 'bold 16px monospace' + ctx.fillText('Game Over! Click to restart', 50, 100) } } - - private drawSquirrel(x: number, y: number) { - const ctx = this.ctx; - ctx.fillStyle = '#33f'; - ctx.fillRect(x, y, 10, 10); // body - ctx.fillStyle = '#66f'; - ctx.fillRect(x - 4, y + 2, 6, 6); // tail + private drawSquirrel(x: number, y: number, frame: number) { + const ctx = this.ctx + ctx.fillStyle = frame % 2 === 0 ? '#3cf' : '#6df' + ctx.fillRect(x, y, 10, 10) // body + ctx.fillStyle = '#9cf' + ctx.fillRect(x - 4, y + 2, 5, 6) // tail } - private drawDogWalk(x: number, y: number) { - const ctx = this.ctx; - ctx.fillStyle = '#f33'; - ctx.fillRect(x, y, 10, 10); // body - ctx.fillStyle = '#000'; - ctx.fillRect(x + 2, y - 2, 2, 2); // ear left - ctx.fillRect(x + 6, y - 2, 2, 2); // ear right + private drawDogWalk(x: number, y: number, frame: number) { + const ctx = this.ctx + ctx.fillStyle = '#f55' + ctx.fillRect(x, y, 10, 10) + ctx.fillStyle = '#000' + const legY = frame % 2 === 0 ? 10 : 9 + ctx.fillRect(x + 1, y + legY, 2, 2) + ctx.fillRect(x + 7, y + legY, 2, 2) } - private drawDogFly(x: number, y: number) { - const ctx = this.ctx; - ctx.fillStyle = '#ff3'; - ctx.fillRect(x, y, 10, 10); // body + private drawDogFly(x: number, y: number, frame: number) { + const ctx = this.ctx + ctx.fillStyle = '#ff3' + ctx.fillRect(x, y, 10, 10) + ctx.fillStyle = '#ccc' + const wiggle = frame % 2 === 0 ? -3 : 3 + ctx.beginPath() + ctx.moveTo(x, y) + ctx.lineTo(x - 5, y + 5 + wiggle) + ctx.lineTo(x, y + 10) + ctx.fill() - ctx.fillStyle = '#ccc'; - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x - 5, y + 5); - ctx.lineTo(x, y + 10); - ctx.fill(); // wing left - - ctx.beginPath(); - ctx.moveTo(x + 10, y); - ctx.lineTo(x + 15, y + 5); - ctx.lineTo(x + 10, y + 10); - ctx.fill(); // wing right + ctx.beginPath() + ctx.moveTo(x + 10, y) + ctx.lineTo(x + 15, y + 5 - wiggle) + ctx.lineTo(x + 10, y + 10) + ctx.fill() } private drawNut(x: number, y: number) { - const ctx = this.ctx; - ctx.fillStyle = '#a52a2a'; - ctx.beginPath(); - ctx.arc(x + 5, y + 5, 5, 0, Math.PI * 2); - ctx.fill(); - - ctx.fillStyle = '#228B22'; - ctx.fillRect(x + 3, y - 2, 4, 2); // little leaf on top + const ctx = this.ctx + ctx.fillStyle = '#a52a2a' + ctx.beginPath() + ctx.arc(x + 5, y + 5, 4, 0, Math.PI * 2) + ctx.fill() + ctx.fillStyle = '#228B22' + ctx.fillRect(x + 3, y - 2, 4, 2) } } export function insertRickPanel(target: HTMLElement | string = document.body): void { - const container = typeof target === 'string' - ? document.querySelector(target) - : target - + const container = typeof target === 'string' ? document.querySelector(target) : target if (container instanceof HTMLElement) { new RickGamePanel(container) } -} \ No newline at end of file +}