nose-pluto/app/src/shared/game.ts
2025-09-28 18:34:18 -07:00

166 lines
4.1 KiB
TypeScript

export type InputState = { key: string, shift: boolean, ctrl: boolean, meta: boolean, pressed: Set<string> }
export class GameContext {
constructor(public ctx: CanvasRenderingContext2D) { }
width = 960
height = 540
clear() {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
}
text(msg: string, x: number, y: number, color = "black", size = 16, font = "C64ProMono") {
const c = this.ctx
c.save()
c.fillStyle = color
c.font = `${size}px ${font}`
c.textBaseline = "top"
c.fillText(msg, x, y)
c.restore()
}
centerText(msg: string, y: number, color = "black", size = 16, font = "C64ProMono") {
const c = this.ctx
c.save()
c.fillStyle = color
c.font = `${size}px ${font}`
c.textBaseline = "middle"
const metrics = c.measureText(msg)
const x = (this.width - metrics.width) / 2
c.fillText(msg, x, y)
c.restore()
}
circ(x: number, y: number, r: number, color = "black") {
const c = this.ctx
c.save()
c.beginPath()
c.strokeStyle = color
c.arc(x, y, r, 0, Math.PI * 2)
c.stroke()
c.restore()
}
circfill(x: number, y: number, r: number, color = "black") {
const c = this.ctx
c.save()
c.beginPath()
c.fillStyle = color
c.arc(x, y, r, 0, Math.PI * 2)
c.fill()
c.restore()
}
line(x0: number, y0: number, x1: number, y1: number, color = "black") {
const c = this.ctx
c.save()
c.beginPath()
c.strokeStyle = color
c.moveTo(x0, y0)
c.lineTo(x1, y1)
c.stroke()
c.restore()
}
oval(x0: number, y0: number, x1: number, y1: number, color = "black") {
const c = this.ctx
const w = x1 - x0
const h = y1 - y0
c.save()
c.beginPath()
c.strokeStyle = color
c.ellipse(x0 + w / 2, y0 + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2)
c.stroke()
c.restore()
}
ovalfill(x0: number, y0: number, x1: number, y1: number, color = "black") {
const c = this.ctx
const w = x1 - x0
const h = y1 - y0
c.save()
c.beginPath()
c.fillStyle = color
c.ellipse(x0 + w / 2, y0 + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2)
c.fill()
c.restore()
}
rect(x0: number, y0: number, x1: number, y1: number, color = "black") {
const c = this.ctx
c.save()
c.beginPath()
c.strokeStyle = color
c.rect(x0, y0, x1 - x0, y1 - y0)
c.stroke()
c.restore()
}
rectfill(x0: number, y0: number, x1: number, y1: number, color = "black") {
const c = this.ctx
c.save()
this.ctx.fillStyle = color
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0)
c.restore()
}
rrect(x: number, y: number, w: number, h: number, r: number, color = "black") {
const c = this.ctx
c.save()
c.beginPath()
c.strokeStyle = color
this.roundRectPath(x, y, w, h, r)
c.stroke()
c.restore()
}
rrectfill(x: number, y: number, w: number, h: number, r: number, color = "black") {
const c = this.ctx
c.save()
c.beginPath()
c.fillStyle = color
this.roundRectPath(x, y, w, h, r)
c.fill()
c.restore()
}
trianglefill(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, color = "black") {
return this.polygonfill(
[
[x0, y0],
[x1, y1],
[x2, y2]
],
color
)
}
polygonfill(points: [number, number][], color = "black") {
if (points.length < 3) return // need at least a triangle
const c = this.ctx
c.save()
c.beginPath()
c.fillStyle = color
c.moveTo(points[0]![0], points[0]![1])
for (let i = 1; i < points.length; i++) {
c.lineTo(points[i]![0], points[i]![1])
}
c.closePath()
c.fill()
c.restore()
}
private roundRectPath(x: number, y: number, w: number, h: number, r: number) {
const c = this.ctx
c.moveTo(x + r, y)
c.lineTo(x + w - r, y)
c.quadraticCurveTo(x + w, y, x + w, y + r)
c.lineTo(x + w, y + h - r)
c.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
c.lineTo(x + r, y + h)
c.quadraticCurveTo(x, y + h, x, y + h - r)
c.lineTo(x, y + r)
c.quadraticCurveTo(x, y, x + r, y)
}
}