157 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export class RickGamePanel {
private canvas!: HTMLCanvasElement
private ctx!: CanvasRenderingContext2D
private player = { x: 20, y: 180, vy: 0, width: 10, height: 10, onGround: false }
private gravity = 0.7
private jumpPower = -12
private isGameOver = false
private enemies: { x: number, y: number, width: number, height: number, speed: number }[] = []
private spawnTimer = 0
constructor(private container: HTMLElement) {
this.createUI()
this.ctx = this.canvas.getContext('2d')!
this.attachListeners()
this.frame()
}
private createUI() {
const panel = document.createElement('div')
panel.style.cssText = `
background: #1a1a1a;
color: #eee;
font-family: monospace;
padding: 16px;
border-radius: 12px;
width: fit-content;
box-shadow: 0 0 20px rgba(255,255,255,0.1);
`
const title = document.createElement('div')
title.innerHTML = `<strong>🌀 Rickjump 2: Invasion</strong>`
title.style.marginBottom = '10px'
panel.appendChild(title)
this.canvas = document.createElement('canvas')
this.canvas.width = 300
this.canvas.height = 200
this.canvas.style.cssText = `
border: 2px solid #444;
background: #000;
image-rendering: pixelated;
`
panel.appendChild(this.canvas)
const footer = document.createElement('div')
footer.textContent = '↳ try not to get rickd'
footer.style.cssText = `
margin-top: 10px;
font-size: 0.7em;
color: #aaa;
`
panel.appendChild(footer)
this.container.appendChild(panel)
}
private attachListeners() {
this.canvas.addEventListener('click', () => {
if (this.player.onGround) {
this.player.vy = this.jumpPower
this.player.onGround = false
}
})
}
private frame = () => {
if (this.isGameOver) return
// physics
this.player.vy += this.gravity
this.player.y += this.player.vy
if (this.player.y >= 180) {
this.player.y = 180
this.player.vy = 0
this.player.onGround = true
}
// enemies
for (const enemy of this.enemies) {
enemy.x -= enemy.speed
}
// cleanup
this.enemies = this.enemies.filter(e => e.x + e.width > 0)
// collisions
for (const enemy of this.enemies) {
const p = this.player
const collide =
p.x < enemy.x + enemy.width &&
p.x + p.width > enemy.x &&
p.y < enemy.y + enemy.height &&
p.y + p.height > enemy.y
if (collide) {
this.isGameOver = true
setTimeout(() => {
window.open('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '_blank')
}, 200)
return
}
}
// spawn enemy
this.spawnTimer--
if (this.spawnTimer <= 0) {
this.spawnEnemy()
this.spawnTimer = 30 + Math.floor(Math.random() * 60)
}
this.draw()
requestAnimationFrame(this.frame)
}
private spawnEnemy() {
const h = 10 + Math.floor(Math.random() * 20)
const y = 190 - h
const speed = 1.5 + Math.random() * 1.5
this.enemies.push({ x: 300, y, width: 10, height: h, speed })
}
private draw() {
const ctx = this.ctx
ctx.clearRect(0, 0, 300, 200)
// background
ctx.fillStyle = '#111'
ctx.fillRect(0, 0, 300, 200)
// ground
ctx.fillStyle = '#444'
ctx.fillRect(0, 190, 300, 10)
// enemies
for (const e of this.enemies) {
ctx.fillStyle = '#f00'
ctx.fillRect(e.x, e.y, e.width, e.height)
}
// player
ctx.fillStyle = '#0af'
ctx.fillRect(this.player.x, this.player.y, this.player.width, this.player.height)
}
}
export function insertRickPanel(target: HTMLElement | string = document.body): void {
const container = typeof target === 'string'
? document.querySelector(target)
: target
if (container instanceof HTMLElement) {
new RickGamePanel(container)
}
}