export type InputState = { key: string, shift: boolean, ctrl: boolean, meta: boolean, pressed: Set } 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) } }