diff --git a/app/nose/bin/game.tsx b/app/nose/bin/game.tsx
index 9ba74c2..de247af 100644
--- a/app/nose/bin/game.tsx
+++ b/app/nose/bin/game.tsx
@@ -1,7 +1,8 @@
///
export const game = true
-import type { GameContext, InputState } from "@/shared/types"
+import type { InputState } from "@/shared/types"
+import type { GameContext } from "@/shared/game"
import { rng } from "@/shared/utils.ts"
const WIDTH = 960
diff --git a/app/src/js/game.ts b/app/src/js/game.ts
index 5f323d7..45e263b 100644
--- a/app/src/js/game.ts
+++ b/app/src/js/game.ts
@@ -1,5 +1,5 @@
import type { Message, InputState } from "../shared/types.js"
-import { GameContext } from "../shared/types.js"
+import { GameContext } from "../shared/game.js"
import { focusInput } from "./focus.js"
import { $ } from "./dom.js"
import { randomId } from "../shared/utils.js"
@@ -8,7 +8,7 @@ import { browserCommands } from "./commands.js"
const FPS = 30
let oldMode = "cinema"
-let stopGame = false
+let running = false
let canvas: HTMLCanvasElement
type Game = { init?: () => void, update?: (delta: number, input: InputState) => void, draw?: (ctx: GameContext) => void }
@@ -56,7 +56,7 @@ function handleKeydown(e: KeyboardEvent) {
e.preventDefault()
if (e.key === "Escape" || (e.ctrlKey && e.key === "c")) {
- stopGame = true
+ running = false
if (oldMode === "tall") browserCommands.mode?.()
canvas.classList.remove("active")
canvas.style.height = canvas.height / 2 + "px"
@@ -81,12 +81,13 @@ function handleKeyup(e: KeyboardEvent) {
}
function gameLoop(ctx: GameContext, game: Game) {
+ running = true
let last = 0
if (game.init) game.init()
function loop(ts: number) {
- if (stopGame) return
+ if (!running) return
const delta = ts - last
if (delta >= 1000 / FPS) {
diff --git a/app/src/shared/game.ts b/app/src/shared/game.ts
new file mode 100644
index 0000000..aae325d
--- /dev/null
+++ b/app/src/shared/game.ts
@@ -0,0 +1,143 @@
+
+export class GameContext {
+ constructor(public ctx: CanvasRenderingContext2D) { }
+
+ get width() { return this.ctx.canvas.width }
+ get height() { return this.ctx.canvas.height }
+
+ clear() {
+ this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
+ }
+
+ 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)
+ }
+}
\ No newline at end of file
diff --git a/app/src/shared/types.ts b/app/src/shared/types.ts
index 7748471..acd04a7 100644
--- a/app/src/shared/types.ts
+++ b/app/src/shared/types.ts
@@ -17,146 +17,3 @@ export type CommandResult = {
}
export type InputState = { key: string, shift: boolean, ctrl: boolean, meta: boolean, pressed: Set }
-
-export class GameContext {
- constructor(public ctx: CanvasRenderingContext2D) { }
-
- get width() { return this.ctx.canvas.width }
- get height() { return this.ctx.canvas.height }
-
- clear() {
- this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
- }
-
- 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)
- }
-}
\ No newline at end of file