shooterland 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,11 +16,11 @@ React 18 or 19 is an optional peer dependency: the `shooterland/engine` and `sho
16
16
  import { Shooterland } from 'shooterland';
17
17
 
18
18
  export function Page() {
19
- return <Shooterland style={{ maxWidth: 800 }} />;
19
+ return <Shooterland style={{ height: '100vh' }} />;
20
20
  }
21
21
  ```
22
22
 
23
- The component renders a 4:3 canvas that fills its container. By default the game's assets (sprites, fonts, levels, sounds, an 8 MB General MIDI sound bank) load from jsDelivr for the installed version. To self-host, copy `node_modules/shooterland/assets` somewhere static and point `assetBaseUrl` at it:
23
+ The component fills its container and draws the game as the largest 4:3 box that fits, centred on black. Give the container (or the `style` prop) a height to let the game grow and shrink with the available space; with no height it becomes a 4:3 box as wide as its container. By default the game's assets (sprites, fonts, levels, sounds, an 8 MB General MIDI sound bank) load from jsDelivr for the installed version. To self-host, copy `node_modules/shooterland/assets` somewhere static and point `assetBaseUrl` at it:
24
24
 
25
25
  ```tsx
26
26
  <Shooterland assetBaseUrl="/shooterland-assets/" />
@@ -831,9 +831,8 @@ var Renderer = class {
831
831
  //#region src/render/Viewport.ts
832
832
  /**
833
833
  * Maps the fixed 800x600 virtual space onto a canvas of any size: the largest
834
- * 4:3 rectangle that fits, centred (letterboxed). Scale is a whole number of
835
- * device pixels per virtual pixel whenever the canvas is large enough, so
836
- * sprites stay crisp; below 1x it falls back to fractional downscaling.
834
+ * 4:3 rectangle that fits, centred (letterboxed). The scale is fractional in
835
+ * both directions so the game always fills the canvas it is given.
837
836
  */
838
837
  var Viewport = class {
839
838
  canvasWidth = 800;
@@ -848,8 +847,7 @@ var Viewport = class {
848
847
  resize(canvasWidth, canvasHeight) {
849
848
  this.canvasWidth = Math.max(1, Math.floor(canvasWidth));
850
849
  this.canvasHeight = Math.max(1, Math.floor(canvasHeight));
851
- const raw = Math.min(this.canvasWidth / 800, this.canvasHeight / 600);
852
- this.scale = raw >= 1 ? Math.floor(raw) : raw;
850
+ this.scale = Math.min(this.canvasWidth / 800, this.canvasHeight / 600);
853
851
  const w = Math.round(800 * this.scale);
854
852
  const h = Math.round(600 * this.scale);
855
853
  this.rect.x = Math.floor((this.canvasWidth - w) / 2);
@@ -2955,7 +2953,7 @@ var GameFlow = class {
2955
2953
  //#endregion
2956
2954
  //#region src/engine/createGame.ts
2957
2955
  const MAX_CATCHUP_STEPS = 5;
2958
- const PACKAGE_VERSION = "0.1.0";
2956
+ const PACKAGE_VERSION = "1.0.1";
2959
2957
  /** Where the published package's assets live when no `assetBaseUrl` is given. */
2960
2958
  function defaultAssetBaseUrl() {
2961
2959
  return `https://cdn.jsdelivr.net/npm/shooterland@${PACKAGE_VERSION}/assets/`;
@@ -3142,4 +3140,4 @@ function createGame(canvas, options = {}) {
3142
3140
  //#endregion
3143
3141
  export { createGame as n, defaultAssetBaseUrl as r, PACKAGE_VERSION as t };
3144
3142
 
3145
- //# sourceMappingURL=engine-ByOVwLal.js.map
3143
+ //# sourceMappingURL=engine-BydvrFg-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-BydvrFg-.js","names":["rect","PLAY","BETA"],"sources":["../src/render/BitmapFont.ts","../src/render/Texture.ts","../src/assets/AssetLoader.ts","../src/audio/MidiMusic.ts","../src/audio/AudioEngine.ts","../src/input/PointerInput.ts","../src/render/gl.ts","../src/render/SpriteBatch.ts","../src/render/Renderer.ts","../src/render/Viewport.ts","../src/scenes/AssetViewerScene.ts","../src/scenes/BootScene.ts","../src/scenes/LoadingScene.ts","../src/scenes/Scene.ts","../src/render/sprites.ts","../src/scenes/fonts.ts","../src/scenes/layout.ts","../src/scenes/ui.ts","../src/scenes/GameplayScene.ts","../src/render/text.ts","../src/scenes/IntroScene.ts","../src/scenes/LogoScene.ts","../src/scenes/ResultScene.ts","../src/scenes/SaveSelectScene.ts","../src/scenes/StatsScene.ts","../src/scenes/TitleScene.ts","../src/scenes/WorldMapScene.ts","../src/engine/GameFlow.ts","../src/engine/createGame.ts"],"sourcesContent":["import type { FontMetrics } from '../assets/manifest';\nimport type { SpriteBatch } from './SpriteBatch';\nimport type { Texture } from './Texture';\n\nexport interface TextStyle {\n /** 0..1 colour components; text textures are white so this is the text colour. */\n r?: number;\n g?: number;\n b?: number;\n a?: number;\n align?: 'left' | 'center' | 'right';\n scale?: number;\n}\n\n/**\n * LMNOpc Bitmap Font Builder font: a 16x16 grid of equal cells indexed by\n * character code, glyphs centred in their cells, and a per-character advance.\n */\nexport class BitmapFont {\n readonly cell: number;\n\n constructor(\n readonly texture: Texture,\n readonly metrics: FontMetrics,\n ) {\n this.cell = metrics.cell;\n }\n\n /** Line height in virtual pixels at scale 1. */\n get lineHeight(): number {\n return this.cell;\n }\n\n advance(code: number): number {\n return this.metrics.advances[code & 255] ?? 0;\n }\n\n measure(text: string, scale = 1): number {\n let w = 0;\n for (let i = 0; i < text.length; i++) w += this.advance(text.charCodeAt(i));\n return w * scale;\n }\n\n /** Draws one line of text with its top-left (or top-centre/right) at x,y. */\n draw(batch: SpriteBatch, text: string, x: number, y: number, style: TextStyle = {}): void {\n const scale = style.scale ?? 1;\n const align = style.align ?? 'left';\n let pen = x;\n if (align !== 'left') {\n const w = this.measure(text, scale);\n pen = align === 'center' ? x - w / 2 : x - w;\n }\n const { cell } = this;\n const r = style.r ?? 1;\n const g = style.g ?? 1;\n const b = style.b ?? 1;\n const a = style.a ?? 1;\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i) & 255;\n const adv = this.advance(code);\n if (code !== 32) {\n const sx = (code & 15) * cell;\n const sy = (code >> 4) * cell;\n // Glyphs are centred in their cell; shift left so the ink starts near the pen.\n const dx = pen - Math.floor((cell - adv) / 2) * scale;\n batch.draw(this.texture, sx, sy, cell, cell, dx, y, cell * scale, cell * scale, r, g, b, a);\n }\n pen += adv * scale;\n }\n }\n}\n","export type TextureFilter = 'nearest' | 'linear';\n\n/** A GL texture plus its pixel size. Sprite rects are given in these pixels. */\nexport class Texture {\n constructor(\n readonly gl: WebGL2RenderingContext,\n readonly handle: WebGLTexture,\n readonly width: number,\n readonly height: number,\n ) {}\n\n static fromImage(\n gl: WebGL2RenderingContext,\n source: TexImageSource,\n width: number,\n height: number,\n filter: TextureFilter = 'nearest',\n ): Texture {\n const handle = gl.createTexture();\n if (!handle) throw new Error('could not create texture');\n gl.bindTexture(gl.TEXTURE_2D, handle);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\n gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n const f = filter === 'nearest' ? gl.NEAREST : gl.LINEAR;\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, f);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, f);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n return new Texture(gl, handle, width, height);\n }\n\n /** A 1x1 opaque white texture used for solid rectangles. */\n static white(gl: WebGL2RenderingContext): Texture {\n const handle = gl.createTexture();\n if (!handle) throw new Error('could not create texture');\n gl.bindTexture(gl.TEXTURE_2D, handle);\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n gl.RGBA,\n 1,\n 1,\n 0,\n gl.RGBA,\n gl.UNSIGNED_BYTE,\n new Uint8Array([255, 255, 255, 255]),\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.bindTexture(gl.TEXTURE_2D, null);\n return new Texture(gl, handle, 1, 1);\n }\n\n destroy(): void {\n this.gl.deleteTexture(this.handle);\n }\n}\n","import type { LevelData } from '../core/level/LevelData';\nimport { BitmapFont } from '../render/BitmapFont';\nimport { Texture } from '../render/Texture';\nimport type { AssetManifest, FontMetrics } from './manifest';\n\nexport interface WorldAssets {\n readonly tiles: Texture;\n readonly supplement: Texture;\n readonly background: Texture;\n readonly backgroundDark: Texture;\n readonly map: Texture;\n}\n\nexport interface MapNode {\n readonly x: number;\n readonly y: number;\n}\n\n/** Everything loaded at boot. World art is fetched on demand via `world(n)`. */\nexport class Assets {\n private readonly worlds = new Map<number, Promise<WorldAssets>>();\n\n constructor(\n readonly manifest: AssetManifest,\n readonly images: ReadonlyMap<string, Texture>,\n readonly fonts: ReadonlyMap<string, BitmapFont>,\n readonly levels: readonly LevelData[],\n readonly times: readonly number[],\n readonly mapNodes: readonly MapNode[],\n private readonly loader: AssetLoader,\n ) {}\n\n image(key: string): Texture {\n const t = this.images.get(key);\n if (!t) throw new Error(`unknown image \"${key}\"`);\n return t;\n }\n\n font(key: string): BitmapFont {\n const f = this.fonts.get(key);\n if (!f) throw new Error(`unknown font \"${key}\"`);\n return f;\n }\n\n level(world: number, level: number): LevelData | undefined {\n return this.levels.find((l) => l.world === world && l.level === level);\n }\n\n world(n: number): Promise<WorldAssets> {\n let p = this.worlds.get(n);\n if (!p) {\n p = this.loader.loadWorld(this.manifest, n);\n this.worlds.set(n, p);\n }\n return p;\n }\n\n destroy(): void {\n for (const t of this.images.values()) t.destroy();\n for (const f of this.fonts.values()) f.texture.destroy();\n for (const p of this.worlds.values()) {\n p.then((w) => {\n for (const t of [w.tiles, w.supplement, w.background, w.backgroundDark, w.map]) t.destroy();\n }).catch(() => {});\n }\n }\n}\n\nexport interface LoadProgress {\n loaded: number;\n total: number;\n}\n\n/** Fetches and decodes assets relative to a base URL into GL textures. */\nexport class AssetLoader {\n constructor(\n private readonly gl: WebGL2RenderingContext,\n private readonly baseUrl: string,\n private readonly signal?: AbortSignal,\n ) {}\n\n /** Resolves a manifest path against the base URL. */\n resolve(path: string): string {\n return this.url(path);\n }\n\n private url(path: string): string {\n // Relative bases (\"/assets/\", \"./assets/\") resolve against the page; absolute ones stand alone.\n const pageHref = typeof location !== 'undefined' ? location.href : undefined;\n return new URL(path, new URL(this.baseUrl, pageHref)).toString();\n }\n\n private async fetchJson<T>(path: string): Promise<T> {\n const res = await fetch(this.url(path), this.signal ? { signal: this.signal } : undefined);\n if (!res.ok) throw new Error(`failed to load ${path}: HTTP ${res.status}`);\n return (await res.json()) as T;\n }\n\n private async fetchTexture(path: string): Promise<Texture> {\n const res = await fetch(this.url(path), this.signal ? { signal: this.signal } : undefined);\n if (!res.ok) throw new Error(`failed to load ${path}: HTTP ${res.status}`);\n const bitmap = await createImageBitmap(await res.blob(), {\n premultiplyAlpha: 'none',\n colorSpaceConversion: 'none',\n });\n try {\n return Texture.fromImage(this.gl, bitmap, bitmap.width, bitmap.height);\n } finally {\n bitmap.close();\n }\n }\n\n async loadAll(onProgress?: (p: LoadProgress) => void): Promise<Assets> {\n const manifest = await this.fetchJson<AssetManifest>('manifest.json');\n const imageKeys = Object.keys(manifest.images);\n const fontKeys = Object.keys(manifest.fonts);\n const progress: LoadProgress = { loaded: 0, total: imageKeys.length + fontKeys.length * 2 + 3 };\n const tick = <T>(p: Promise<T>): Promise<T> =>\n p.then((v) => {\n progress.loaded++;\n onProgress?.(progress);\n return v;\n });\n\n const [images, fonts, levels, times, mapNodes] = await Promise.all([\n Promise.all(\n imageKeys.map(async (k) => {\n const entry = manifest.images[k];\n if (!entry) throw new Error(`manifest has no image \"${k}\"`);\n return [k, await tick(this.fetchTexture(entry.path))] as const;\n }),\n ),\n Promise.all(\n fontKeys.map(async (k) => {\n const entry = manifest.fonts[k];\n if (!entry) throw new Error(`manifest has no font \"${k}\"`);\n const [tex, metrics] = await Promise.all([\n tick(this.fetchTexture(entry.image)),\n tick(this.fetchJson<FontMetrics>(entry.metrics)),\n ]);\n return [k, new BitmapFont(tex, metrics)] as const;\n }),\n ),\n tick(this.fetchJson<LevelData[]>(manifest.levels)),\n tick(this.fetchJson<number[]>(manifest.times)),\n tick(this.fetchJson<MapNode[]>(manifest.mapNodes)),\n ]);\n return new Assets(manifest, new Map(images), new Map(fonts), levels, times, mapNodes, this);\n }\n\n async loadWorld(manifest: AssetManifest, n: number): Promise<WorldAssets> {\n const entry = manifest.worlds[String(n)];\n if (!entry) throw new Error(`manifest has no world ${n}`);\n const [tiles, supplement, background, backgroundDark, map] = await Promise.all([\n this.fetchTexture(entry.tiles),\n this.fetchTexture(entry.supplement),\n this.fetchTexture(entry.background),\n this.fetchTexture(entry.backgroundDark),\n this.fetchTexture(entry.map),\n ]);\n return { tiles, supplement, background, backgroundDark, map };\n }\n}\n","import { Sequencer, WorkerSynthesizer } from 'spessasynth_lib';\n\nexport interface MidiMusicSources {\n /** Resolves a manifest path to a fetchable URL. */\n url(path: string): string;\n soundfont: string;\n synthWorker: string;\n tracks: Readonly<Record<string, string>>;\n}\n\n/**\n * Plays the game's MIDI tracks with a General MIDI sound bank, synthesised in\n * a Web Worker (spessasynth_lib). Everything heavy (worker script, ~8 MB\n * sound bank, MIDI files) is fetched on first use.\n */\nexport class MidiMusic {\n private synth: WorkerSynthesizer | null = null;\n private sequencer: Sequencer | null = null;\n private worker: Worker | null = null;\n private workerUrl: string | null = null;\n private setup: Promise<void> | null = null;\n private readonly midi = new Map<string, Promise<ArrayBuffer>>();\n private current: string | null = null;\n private wanted: string | null = null;\n private failed = false;\n\n constructor(\n private readonly context: AudioContext,\n private readonly output: AudioNode,\n private readonly sources: MidiMusicSources,\n private readonly signal: AbortSignal,\n ) {}\n\n private async fetchBuffer(path: string): Promise<ArrayBuffer> {\n const res = await fetch(this.sources.url(path), { signal: this.signal });\n if (!res.ok) throw new Error(`failed to load ${path}: HTTP ${res.status}`);\n return res.arrayBuffer();\n }\n\n private ensure(): Promise<void> {\n if (!this.setup) {\n this.setup = this.boot().catch((error: unknown) => {\n this.failed = true;\n if (!this.signal.aborted) console.warn('[shooterland] music disabled:', error);\n });\n }\n return this.setup;\n }\n\n private async boot(): Promise<void> {\n const [workerSource, soundfont] = await Promise.all([\n fetch(this.sources.url(this.sources.synthWorker), { signal: this.signal }).then((r) => {\n if (!r.ok) throw new Error(`failed to load synth worker: HTTP ${r.status}`);\n return r.text();\n }),\n this.fetchBuffer(this.sources.soundfont),\n ]);\n if (this.signal.aborted) return;\n this.workerUrl = URL.createObjectURL(new Blob([workerSource], { type: 'text/javascript' }));\n this.worker = new Worker(this.workerUrl);\n await WorkerSynthesizer.registerPlaybackWorklet(this.context);\n const synth = new WorkerSynthesizer(this.context, this.worker.postMessage.bind(this.worker));\n this.worker.onmessage = (e) => synth.handleWorkerMessage(e.data);\n // Route through our gain node instead of the context destination.\n synth.disconnect();\n synth.connect(this.output);\n await synth.soundBankManager.addSoundBank(soundfont, 'main');\n await synth.isReady;\n this.synth = synth;\n const seq = new Sequencer(synth, { skipToFirstNoteOn: true });\n seq.loopCount = -1; // -1 loops forever\n this.sequencer = seq;\n }\n\n /** Switches to `key` (a manifest music entry), or stops with null. Safe to call repeatedly. */\n play(key: string | null): void {\n this.wanted = key;\n if (this.failed) return;\n void this.ensure().then(() => this.apply());\n }\n\n private async apply(): Promise<void> {\n const seq = this.sequencer;\n if (!seq || this.failed) return;\n const key = this.wanted;\n if (key === this.current) return;\n if (key === null) {\n seq.pause();\n this.current = null;\n return;\n }\n const path = this.sources.tracks[key];\n if (!path) return;\n let p = this.midi.get(key);\n if (!p) {\n p = this.fetchBuffer(path);\n this.midi.set(key, p);\n }\n try {\n const binary = await p;\n if (this.wanted !== key || this.signal.aborted) return;\n seq.loadNewSongList([{ binary: binary.slice(0), fileName: key }]);\n seq.loopCount = -1;\n seq.play();\n this.current = key;\n } catch (error) {\n this.midi.delete(key);\n if (!this.signal.aborted) console.warn('[shooterland] could not play music:', error);\n }\n }\n\n destroy(): void {\n this.sequencer?.pause();\n this.synth?.disconnect();\n this.worker?.terminate();\n if (this.workerUrl) URL.revokeObjectURL(this.workerUrl);\n this.sequencer = null;\n this.synth = null;\n this.worker = null;\n }\n}\n","import type { AssetManifest } from '../assets/manifest';\nimport { MidiMusic } from './MidiMusic';\n\n/** What scenes talk to. `NullAudio` is used when audio is disabled. */\nexport interface GameAudio {\n /** Must be called from inside a user gesture to satisfy autoplay policies. */\n unlock(): void;\n /** Plays a sound effect by manifest key ('shoot', 'thingyhit', 'combo', 'bomb', 'pop'). */\n sfx(name: string): void;\n /** Switches the looping music track by manifest key, or stops it with null. */\n music(key: string | null): void;\n setMuted(muted: boolean): void;\n setVolume(volume: number): void;\n suspend(): void;\n resume(): void;\n destroy(): void;\n}\n\nexport class NullAudio implements GameAudio {\n unlock(): void {}\n sfx(_name: string): void {}\n music(_key: string | null): void {}\n setMuted(_muted: boolean): void {}\n setVolume(_volume: number): void {}\n suspend(): void {}\n resume(): void {}\n destroy(): void {}\n}\n\n/**\n * WebAudio implementation: sound effects from the legacy WAVs, music from the\n * legacy MIDIs through the worker synthesizer. The AudioContext is created on\n * the first user gesture; sounds requested before that are dropped.\n */\nexport class WebAudioEngine implements GameAudio {\n private context: AudioContext | null = null;\n private master: GainNode | null = null;\n private sfxGain: GainNode | null = null;\n private musicPlayer: MidiMusic | null = null;\n private readonly wavBytes = new Map<string, Promise<ArrayBuffer>>();\n private readonly buffers = new Map<string, Promise<AudioBuffer | null>>();\n private muted: boolean;\n private volume = 1;\n private wantedMusic: string | null = null;\n private suspended = false;\n\n constructor(\n private readonly manifest: AssetManifest,\n private readonly url: (path: string) => string,\n private readonly signal: AbortSignal,\n options: { muted?: boolean | undefined } = {},\n ) {\n this.muted = options.muted ?? false;\n // Fetch the WAV bytes right away (they are small); decoding waits for the context.\n for (const [name, path] of Object.entries(manifest.sounds)) {\n this.wavBytes.set(\n name,\n fetch(url(path), { signal }).then((r) => {\n if (!r.ok) throw new Error(`failed to load ${path}: HTTP ${r.status}`);\n return r.arrayBuffer();\n }),\n );\n }\n for (const p of this.wavBytes.values()) p.catch(() => {});\n }\n\n unlock(): void {\n if (this.signal.aborted) return;\n if (!this.context) {\n try {\n const Ctx = globalThis.AudioContext;\n if (!Ctx) return;\n const context = new Ctx({ sampleRate: 44100 });\n const master = context.createGain();\n const sfxGain = context.createGain();\n const musicGain = context.createGain();\n sfxGain.connect(master);\n musicGain.connect(master);\n master.connect(context.destination);\n musicGain.gain.value = 0.6;\n this.context = context;\n this.master = master;\n this.sfxGain = sfxGain;\n this.applyGain();\n this.musicPlayer = new MidiMusic(\n context,\n musicGain,\n {\n url: this.url,\n soundfont: this.manifest.soundfont,\n synthWorker: this.manifest.synthWorker,\n tracks: this.manifest.music,\n },\n this.signal,\n );\n if (this.wantedMusic) this.musicPlayer.play(this.wantedMusic);\n } catch (error) {\n console.warn('[shooterland] audio unavailable:', error);\n return;\n }\n }\n if (this.context.state === 'suspended' && !this.suspended) void this.context.resume();\n }\n\n private applyGain(): void {\n if (this.master) this.master.gain.value = this.muted ? 0 : this.volume;\n }\n\n private decode(name: string): Promise<AudioBuffer | null> {\n let p = this.buffers.get(name);\n if (!p) {\n const bytes = this.wavBytes.get(name);\n const context = this.context;\n p =\n bytes && context\n ? bytes.then((b) => context.decodeAudioData(b.slice(0))).catch(() => null)\n : Promise.resolve(null);\n this.buffers.set(name, p);\n }\n return p;\n }\n\n sfx(name: string): void {\n const context = this.context;\n const out = this.sfxGain;\n if (!context || !out || this.muted || this.suspended) return;\n void this.decode(name).then((buffer) => {\n if (!buffer || this.signal.aborted) return;\n const source = context.createBufferSource();\n source.buffer = buffer;\n source.connect(out);\n source.start();\n });\n }\n\n music(key: string | null): void {\n this.wantedMusic = key;\n this.musicPlayer?.play(key);\n }\n\n setMuted(muted: boolean): void {\n this.muted = muted;\n this.applyGain();\n }\n\n setVolume(volume: number): void {\n this.volume = Math.max(0, Math.min(1, volume));\n this.applyGain();\n }\n\n suspend(): void {\n this.suspended = true;\n void this.context?.suspend();\n }\n\n resume(): void {\n this.suspended = false;\n if (this.context?.state === 'suspended') void this.context.resume();\n }\n\n destroy(): void {\n this.musicPlayer?.destroy();\n this.musicPlayer = null;\n void this.context?.close();\n this.context = null;\n }\n}\n","import type { Viewport } from '../render/Viewport';\n\nexport type InputEvent =\n | { readonly type: 'down'; readonly x: number; readonly y: number; readonly button: number }\n | { readonly type: 'up'; readonly x: number; readonly y: number; readonly button: number }\n | { readonly type: 'move'; readonly x: number; readonly y: number }\n /** A key press while the canvas has focus; `key` is the DOM KeyboardEvent key. */\n | { readonly type: 'key'; readonly key: string };\n\n/**\n * Listens to pointer and keyboard events on the canvas and queues them in\n * virtual (800x600) coordinates. Scenes drain the queue once per tick.\n */\nexport class PointerInput {\n private readonly queue: InputEvent[] = [];\n private readonly abort = new AbortController();\n /** Latest pointer position in virtual coordinates. */\n x = -1;\n y = -1;\n isDown = false;\n\n constructor(\n private readonly canvas: HTMLCanvasElement,\n private readonly viewport: Viewport,\n ) {\n const { signal } = this.abort;\n const opts = { signal, passive: false } as const;\n if (canvas.tabIndex < 0) canvas.tabIndex = 0;\n canvas.addEventListener(\n 'pointerdown',\n (e) => {\n canvas.setPointerCapture(e.pointerId);\n canvas.focus({ preventScroll: true });\n const p = this.project(e);\n this.isDown = true;\n this.queue.push({ type: 'down', x: p.x, y: p.y, button: e.button });\n e.preventDefault();\n },\n opts,\n );\n const up = (e: PointerEvent): void => {\n const p = this.project(e);\n this.isDown = false;\n this.queue.push({ type: 'up', x: p.x, y: p.y, button: e.button });\n };\n canvas.addEventListener('pointerup', up, opts);\n canvas.addEventListener('pointercancel', up, opts);\n canvas.addEventListener(\n 'pointermove',\n (e) => {\n const p = this.project(e);\n this.queue.push({ type: 'move', x: p.x, y: p.y });\n },\n opts,\n );\n canvas.addEventListener('contextmenu', (e) => e.preventDefault(), opts);\n canvas.addEventListener(\n 'keydown',\n (e) => {\n if (e.repeat) return;\n this.queue.push({ type: 'key', key: e.key });\n if (e.key === ' ' || e.key.startsWith('Arrow')) e.preventDefault();\n },\n opts,\n );\n }\n\n private project(e: PointerEvent): { x: number; y: number } {\n const r = this.canvas.getBoundingClientRect();\n const p = this.viewport.toVirtual(e.clientX - r.left, e.clientY - r.top, r.width, r.height);\n this.x = p.x;\n this.y = p.y;\n return p;\n }\n\n /** Returns and clears the events received since the last drain. */\n drain(): InputEvent[] {\n if (this.queue.length === 0) return [];\n return this.queue.splice(0, this.queue.length);\n }\n\n destroy(): void {\n this.abort.abort();\n }\n}\n","export class WebGLUnavailableError extends Error {\n constructor() {\n super('WebGL2 is not available in this browser or context.');\n this.name = 'WebGLUnavailableError';\n }\n}\n\n/** Acquires a WebGL2 context tuned for 2D sprite rendering. Throws if unavailable. */\nexport function createGLContext(canvas: HTMLCanvasElement): WebGL2RenderingContext {\n const gl = canvas.getContext('webgl2', {\n alpha: false,\n antialias: false,\n depth: false,\n stencil: false,\n premultipliedAlpha: false,\n preserveDrawingBuffer: false,\n powerPreference: 'low-power',\n });\n if (!gl) throw new WebGLUnavailableError();\n return gl;\n}\n","import { VIRTUAL_HEIGHT, VIRTUAL_WIDTH } from '../core/constants';\nimport type { Texture } from './Texture';\n\nconst VERTEX_SRC = `#version 300 es\nprecision highp float;\nlayout(location = 0) in vec2 a_pos;\nlayout(location = 1) in vec2 a_uv;\nlayout(location = 2) in vec4 a_color;\nuniform vec2 u_virtualSize;\nout vec2 v_uv;\nout vec4 v_color;\nvoid main() {\n vec2 clip = vec2(a_pos.x / u_virtualSize.x * 2.0 - 1.0, 1.0 - a_pos.y / u_virtualSize.y * 2.0);\n gl_Position = vec4(clip, 0.0, 1.0);\n v_uv = a_uv;\n v_color = a_color;\n}`;\n\nconst FRAGMENT_SRC = `#version 300 es\nprecision mediump float;\nuniform sampler2D u_tex;\nin vec2 v_uv;\nin vec4 v_color;\nout vec4 fragColor;\nvoid main() {\n fragColor = texture(u_tex, v_uv) * v_color;\n}`;\n\nconst FLOATS_PER_VERTEX = 8; // x y u v r g b a\nconst MAX_QUADS = 4096;\n\nfunction compile(gl: WebGL2RenderingContext, type: number, src: string): WebGLShader {\n const shader = gl.createShader(type);\n if (!shader) throw new Error('could not create shader');\n gl.shaderSource(shader, src);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(shader);\n gl.deleteShader(shader);\n throw new Error(`shader compile failed: ${log}`);\n }\n return shader;\n}\n\n/**\n * Batches textured quads in virtual (800x600) coordinates into as few draw\n * calls as possible: one per texture change or buffer overflow.\n */\nexport class SpriteBatch {\n private readonly program: WebGLProgram;\n private readonly vao: WebGLVertexArrayObject;\n private readonly vbo: WebGLBuffer;\n private readonly vertices = new Float32Array(MAX_QUADS * 4 * FLOATS_PER_VERTEX);\n private quadCount = 0;\n private current: Texture | null = null;\n drawCalls = 0;\n\n constructor(private readonly gl: WebGL2RenderingContext) {\n const vs = compile(gl, gl.VERTEX_SHADER, VERTEX_SRC);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAGMENT_SRC);\n const program = gl.createProgram();\n if (!program) throw new Error('could not create program');\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n throw new Error(`program link failed: ${gl.getProgramInfoLog(program)}`);\n }\n this.program = program;\n\n const vao = gl.createVertexArray();\n const vbo = gl.createBuffer();\n const ibo = gl.createBuffer();\n if (!vao || !vbo || !ibo) throw new Error('could not create buffers');\n this.vao = vao;\n this.vbo = vbo;\n\n gl.bindVertexArray(vao);\n gl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n gl.bufferData(gl.ARRAY_BUFFER, this.vertices.byteLength, gl.DYNAMIC_DRAW);\n const stride = FLOATS_PER_VERTEX * 4;\n gl.enableVertexAttribArray(0);\n gl.vertexAttribPointer(0, 2, gl.FLOAT, false, stride, 0);\n gl.enableVertexAttribArray(1);\n gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, 8);\n gl.enableVertexAttribArray(2);\n gl.vertexAttribPointer(2, 4, gl.FLOAT, false, stride, 16);\n\n const indices = new Uint16Array(MAX_QUADS * 6);\n for (let q = 0, v = 0; q < MAX_QUADS; q++, v += 4) {\n indices.set([v, v + 1, v + 2, v, v + 2, v + 3], q * 6);\n }\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);\n gl.bindVertexArray(null);\n }\n\n begin(): void {\n const { gl } = this;\n gl.useProgram(this.program);\n gl.uniform2f(\n gl.getUniformLocation(this.program, 'u_virtualSize'),\n VIRTUAL_WIDTH,\n VIRTUAL_HEIGHT,\n );\n gl.uniform1i(gl.getUniformLocation(this.program, 'u_tex'), 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n gl.activeTexture(gl.TEXTURE0);\n gl.bindVertexArray(this.vao);\n this.drawCalls = 0;\n this.quadCount = 0;\n this.current = null;\n }\n\n /**\n * Queues one quad. Source rect in texture pixels, destination in virtual pixels.\n * Colour components are 0..1 and multiply the texture (alpha included).\n */\n draw(\n tex: Texture,\n sx: number,\n sy: number,\n sw: number,\n sh: number,\n dx: number,\n dy: number,\n dw: number,\n dh: number,\n r = 1,\n g = 1,\n b = 1,\n a = 1,\n ): void {\n if (this.current !== tex || this.quadCount === MAX_QUADS) {\n this.flush();\n this.current = tex;\n }\n const u0 = sx / tex.width;\n const v0 = sy / tex.height;\n const u1 = (sx + sw) / tex.width;\n const v1 = (sy + sh) / tex.height;\n const o = this.quadCount * 4 * FLOATS_PER_VERTEX;\n const v = this.vertices;\n // top-left, top-right, bottom-right, bottom-left\n v[o] = dx;\n v[o + 1] = dy;\n v[o + 2] = u0;\n v[o + 3] = v0;\n v[o + 4] = r;\n v[o + 5] = g;\n v[o + 6] = b;\n v[o + 7] = a;\n v[o + 8] = dx + dw;\n v[o + 9] = dy;\n v[o + 10] = u1;\n v[o + 11] = v0;\n v[o + 12] = r;\n v[o + 13] = g;\n v[o + 14] = b;\n v[o + 15] = a;\n v[o + 16] = dx + dw;\n v[o + 17] = dy + dh;\n v[o + 18] = u1;\n v[o + 19] = v1;\n v[o + 20] = r;\n v[o + 21] = g;\n v[o + 22] = b;\n v[o + 23] = a;\n v[o + 24] = dx;\n v[o + 25] = dy + dh;\n v[o + 26] = u0;\n v[o + 27] = v1;\n v[o + 28] = r;\n v[o + 29] = g;\n v[o + 30] = b;\n v[o + 31] = a;\n this.quadCount++;\n }\n\n flush(): void {\n if (this.quadCount === 0 || !this.current) return;\n const { gl } = this;\n gl.bindTexture(gl.TEXTURE_2D, this.current.handle);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);\n gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices, 0, this.quadCount * 4 * FLOATS_PER_VERTEX);\n gl.drawElements(gl.TRIANGLES, this.quadCount * 6, gl.UNSIGNED_SHORT, 0);\n this.drawCalls++;\n this.quadCount = 0;\n }\n\n end(): void {\n this.flush();\n this.gl.bindVertexArray(null);\n }\n\n destroy(): void {\n const { gl } = this;\n gl.deleteVertexArray(this.vao);\n gl.deleteBuffer(this.vbo);\n gl.deleteProgram(this.program);\n }\n}\n","import type { BitmapFont, TextStyle } from './BitmapFont';\nimport { SpriteBatch } from './SpriteBatch';\nimport { Texture } from './Texture';\nimport type { Viewport } from './Viewport';\n\n/**\n * Frame-level drawing API in virtual 800x600 coordinates. Everything goes\n * through one sprite batch; solid rectangles use a 1x1 white texture.\n */\nexport class Renderer {\n readonly batch: SpriteBatch;\n private readonly white: Texture;\n\n constructor(\n readonly gl: WebGL2RenderingContext,\n readonly viewport: Viewport,\n ) {\n this.batch = new SpriteBatch(gl);\n this.white = Texture.white(gl);\n }\n\n /** Clears the canvas to black and the letterboxed virtual area to `rgb` (0..1), then starts batching. */\n begin(r = 0, g = 0, b = 0): void {\n const { gl, viewport } = this;\n const { rect } = viewport;\n gl.disable(gl.SCISSOR_TEST);\n gl.viewport(0, 0, viewport.canvasWidth, viewport.canvasHeight);\n gl.clearColor(0, 0, 0, 1);\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n // GL's origin is bottom-left; flip the letterbox rect's y.\n const y = viewport.canvasHeight - rect.y - rect.h;\n gl.enable(gl.SCISSOR_TEST);\n gl.scissor(rect.x, y, rect.w, rect.h);\n gl.viewport(rect.x, y, rect.w, rect.h);\n gl.clearColor(r, g, b, 1);\n gl.clear(gl.COLOR_BUFFER_BIT);\n this.batch.begin();\n }\n\n /** Draws a sub-rectangle of a texture. Destination size defaults to the source size. */\n drawSprite(\n tex: Texture,\n sx: number,\n sy: number,\n sw: number,\n sh: number,\n dx: number,\n dy: number,\n dw = sw,\n dh = sh,\n alpha = 1,\n ): void {\n this.batch.draw(tex, sx, sy, sw, sh, dx, dy, dw, dh, 1, 1, 1, alpha);\n }\n\n /** Draws a whole texture at dx,dy. */\n drawImage(tex: Texture, dx: number, dy: number, alpha = 1): void {\n this.batch.draw(\n tex,\n 0,\n 0,\n tex.width,\n tex.height,\n dx,\n dy,\n tex.width,\n tex.height,\n 1,\n 1,\n 1,\n alpha,\n );\n }\n\n drawRect(\n x: number,\n y: number,\n w: number,\n h: number,\n r: number,\n g: number,\n b: number,\n a = 1,\n ): void {\n this.batch.draw(this.white, 0, 0, 1, 1, x, y, w, h, r, g, b, a);\n }\n\n drawText(font: BitmapFont, text: string, x: number, y: number, style?: TextStyle): void {\n font.draw(this.batch, text, x, y, style);\n }\n\n end(): void {\n this.batch.end();\n this.gl.disable(this.gl.SCISSOR_TEST);\n }\n\n /** Draws on top of a finished frame (cursor, debug), clipped to the virtual area. */\n drawOverlay(draw: (r: Renderer) => void): void {\n const { gl, viewport } = this;\n const { rect } = viewport;\n const y = viewport.canvasHeight - rect.y - rect.h;\n gl.enable(gl.SCISSOR_TEST);\n gl.scissor(rect.x, y, rect.w, rect.h);\n gl.viewport(rect.x, y, rect.w, rect.h);\n this.batch.begin();\n draw(this);\n this.batch.end();\n gl.disable(gl.SCISSOR_TEST);\n }\n\n destroy(): void {\n this.batch.destroy();\n this.white.destroy();\n }\n}\n","import { VIRTUAL_HEIGHT, VIRTUAL_WIDTH } from '../core/constants';\n\n/** A rectangle in device pixels on the canvas. */\nexport interface DeviceRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/**\n * Maps the fixed 800x600 virtual space onto a canvas of any size: the largest\n * 4:3 rectangle that fits, centred (letterboxed). The scale is fractional in\n * both directions so the game always fills the canvas it is given.\n */\nexport class Viewport {\n canvasWidth = VIRTUAL_WIDTH;\n canvasHeight = VIRTUAL_HEIGHT;\n scale = 1;\n readonly rect: DeviceRect = { x: 0, y: 0, w: VIRTUAL_WIDTH, h: VIRTUAL_HEIGHT };\n\n resize(canvasWidth: number, canvasHeight: number): void {\n this.canvasWidth = Math.max(1, Math.floor(canvasWidth));\n this.canvasHeight = Math.max(1, Math.floor(canvasHeight));\n this.scale = Math.min(this.canvasWidth / VIRTUAL_WIDTH, this.canvasHeight / VIRTUAL_HEIGHT);\n const w = Math.round(VIRTUAL_WIDTH * this.scale);\n const h = Math.round(VIRTUAL_HEIGHT * this.scale);\n this.rect.x = Math.floor((this.canvasWidth - w) / 2);\n this.rect.y = Math.floor((this.canvasHeight - h) / 2);\n this.rect.w = w;\n this.rect.h = h;\n }\n\n /**\n * Converts a point in canvas-relative CSS pixels to virtual coordinates.\n * The result may fall outside 0..800 / 0..600 when the pointer is in the letterbox.\n */\n toVirtual(\n cssX: number,\n cssY: number,\n cssWidth: number,\n cssHeight: number,\n ): { x: number; y: number } {\n const dx = (cssX / cssWidth) * this.canvasWidth;\n const dy = (cssY / cssHeight) * this.canvasHeight;\n return {\n x: (dx - this.rect.x) / this.scale,\n y: (dy - this.rect.y) / this.scale,\n };\n }\n}\n","import type { Assets, WorldAssets } from '../assets/AssetLoader';\nimport { VIRTUAL_HEIGHT, VIRTUAL_WIDTH } from '../core/constants';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport type { Scene, SceneContext } from './Scene';\n\nconst SAMPLE = 'Shooterland $123 Time: 45s TTB: 60s (Best 30s)';\n\n/**\n * Development scene that exercises the renderer: the three bitmap fonts,\n * a colour-keyed sprite sheet, and one world's tile sheet. Clicking cycles\n * through worlds; the left/right halves of the screen go back/forward.\n */\nexport class AssetViewerScene implements Scene {\n private world = 1;\n private worldAssets: WorldAssets | null = null;\n private loading = false;\n private t = 0;\n\n constructor(private readonly assets: Assets) {}\n\n enter(_ctx: SceneContext): void {\n this.requestWorld(this.world);\n }\n\n private requestWorld(n: number): void {\n this.loading = true;\n this.worldAssets = null;\n this.assets\n .world(n)\n .then((w) => {\n if (this.world === n) {\n this.worldAssets = w;\n this.loading = false;\n }\n })\n .catch(() => {\n this.loading = false;\n });\n }\n\n update(dt: number, input: readonly InputEvent[]): void {\n this.t += dt;\n for (const e of input) {\n if (e.type !== 'down') continue;\n const next = e.x < VIRTUAL_WIDTH / 2 ? this.world - 1 : this.world + 1;\n this.world = ((next - 1 + 7) % 7) + 1;\n this.requestWorld(this.world);\n }\n }\n\n draw(renderer: Renderer): void {\n const { assets } = this;\n renderer.begin(0.12, 0.1, 0.18);\n\n // Fonts: each line in its own font, plus alignment and tint checks.\n renderer.drawText(assets.font('font1'), SAMPLE, 10, 8);\n renderer.drawText(assets.font('font2'), SAMPLE, 10, 44);\n renderer.drawText(assets.font('font3'), SAMPLE, 10, 64, { r: 1, g: 0.9, b: 0.3 });\n renderer.drawText(assets.font('font1'), 'centred', VIRTUAL_WIDTH / 2, 100, { align: 'center' });\n renderer.drawText(assets.font('font2'), 'right-aligned', VIRTUAL_WIDTH - 10, 110, {\n align: 'right',\n r: 0.5,\n g: 0.9,\n b: 1,\n });\n\n // Keyed sprite sheets on a light panel so holes show.\n renderer.drawRect(0, 130, VIRTUAL_WIDTH, VIRTUAL_HEIGHT - 130, 0.55, 0.55, 0.6);\n renderer.drawImage(assets.image('items'), 10, 140);\n renderer.drawImage(assets.image('shooters'), 60, 140);\n renderer.drawImage(assets.image('raccoon'), 190, 140);\n renderer.drawImage(assets.image('stumpandmoney'), 190, 190);\n renderer.drawImage(assets.image('bubbles'), 240, 190);\n renderer.drawImage(assets.image('mouse'), 10, 520);\n renderer.drawImage(assets.image('plusminus'), 30, 520);\n renderer.drawImage(assets.image('buttonz'), 60, 520);\n\n const w = this.worldAssets;\n if (w) {\n renderer.drawImage(w.tiles, 350, 140);\n renderer.drawSprite(w.supplement, 0, 0, 200, 234, 60, 320, 160, 187);\n // One animated thingie frame to prove sub-rect sampling: row 1, 11 frames.\n const frame = Math.floor(this.t * 10) % 11;\n renderer.drawSprite(w.tiles, frame * 40, 40, 40, 40, 240, 360, 80, 80);\n }\n const label = this.loading ? `world ${this.world} (loading)` : `world ${this.world}`;\n renderer.drawText(assets.font('font1'), label, VIRTUAL_WIDTH - 10, VIRTUAL_HEIGHT - 40, {\n align: 'right',\n });\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport type { Scene, SceneContext } from './Scene';\n\n/**\n * Placeholder first scene: proves the loop, letterbox and input plumbing work\n * by clearing to a colour that shifts while the pointer is held down.\n * Replaced by the studio-logo / title scenes in phase 6.\n */\nexport class BootScene implements Scene {\n private t = 0;\n private held = false;\n\n enter(_ctx: SceneContext): void {\n this.t = 0;\n }\n\n update(dt: number, input: readonly InputEvent[]): void {\n this.t += dt;\n for (const e of input) {\n if (e.type === 'down') this.held = true;\n else if (e.type === 'up') this.held = false;\n }\n }\n\n draw(renderer: Renderer): void {\n const pulse = 0.5 + 0.5 * Math.sin(this.t * 2);\n if (this.held) renderer.begin(0.85, 0.3 + 0.2 * pulse, 0.1);\n else renderer.begin(0.1, 0.35 + 0.15 * pulse, 0.6);\n renderer.end();\n }\n\n leave(): void {}\n}\n","import { VIRTUAL_HEIGHT, VIRTUAL_WIDTH } from '../core/constants';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport type { Scene, SceneContext } from './Scene';\n\n/**\n * Shown while assets download. Draws a bare progress bar (fonts are not\n * available yet) and turns red if loading fails.\n */\nexport class LoadingScene implements Scene {\n private fraction = 0;\n private failed = false;\n\n setProgress(loaded: number, total: number): void {\n this.fraction = total > 0 ? loaded / total : 0;\n }\n\n setFailed(): void {\n this.failed = true;\n }\n\n enter(_ctx: SceneContext): void {}\n\n update(_dt: number, _input: readonly InputEvent[]): void {}\n\n draw(renderer: Renderer): void {\n renderer.begin(0.04, 0.05, 0.08);\n const w = VIRTUAL_WIDTH * 0.5;\n const h = 14;\n const x = (VIRTUAL_WIDTH - w) / 2;\n const y = VIRTUAL_HEIGHT * 0.6;\n renderer.drawRect(x - 2, y - 2, w + 4, h + 4, 0.25, 0.25, 0.3);\n if (this.failed) renderer.drawRect(x, y, w, h, 0.8, 0.15, 0.1);\n else renderer.drawRect(x, y, w * this.fraction, h, 0.95, 0.75, 0.2);\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\n\nexport interface SceneContext {\n /** Requests a scene change; applied at the start of the next tick. */\n go(next: Scene): void;\n}\n\n/**\n * One screen of the game (title, world map, gameplay, ...). Mirrors the\n * original's state-machine shape: enter / update / draw / leave.\n */\nexport interface Scene {\n enter(ctx: SceneContext): void;\n /** One fixed simulation step. `input` holds the pointer events since the last step. */\n update(dt: number, input: readonly InputEvent[]): void;\n draw(renderer: Renderer): void;\n leave(): void;\n}\n\nexport class SceneManager implements SceneContext {\n private current: Scene | null = null;\n private next: Scene | null = null;\n\n go(next: Scene): void {\n this.next = next;\n }\n\n /** Class name of the active scene, for tests and debugging. */\n get currentName(): string {\n return this.current?.constructor.name ?? 'none';\n }\n\n /** Applies any pending transition, then updates the active scene. */\n update(dt: number, input: readonly InputEvent[]): void {\n if (this.next) {\n this.current?.leave();\n this.current = this.next;\n this.next = null;\n this.current.enter(this);\n }\n this.current?.update(dt, input);\n }\n\n draw(renderer: Renderer): void {\n this.current?.draw(renderer);\n }\n\n destroy(): void {\n this.current?.leave();\n this.current = null;\n this.next = null;\n }\n}\n","import { TILE_PX } from '../core/constants';\nimport { enemyBlockKind, enemyKind, isRamp, isThingie, isWeight } from '../core/tiles';\nimport { type EnemyKind, TileId } from '../core/types';\n\n/** A source rectangle in a sheet, in pixels. */\nexport interface SpriteRect {\n readonly x: number;\n readonly y: number;\n readonly w: number;\n readonly h: number;\n}\n\nconst T = TILE_PX;\nconst rect = (x: number, y: number, w = T, h = T): SpriteRect => ({ x, y, w, h });\n\n/**\n * Per-world tile sheet (`worlds/N/tiles.png`, 440x480 = 11 columns x 12 rows):\n * row 0 holds the bomb (frames 0-3) and its explosion (4-10); rows 1-6 the six\n * thingies with 11 animation frames each; rows 7-11 the five enemies with 9\n * frames each, and in columns 9-10 the ring, mine, plain blocks and enemy blocks.\n */\nexport const TILESHEET = {\n columns: 11,\n thingieFrames: 11,\n enemyFrames: 9,\n bombFrames: 4,\n explosionFrames: 7,\n} as const;\n\nconst ENEMY_ROW: Record<EnemyKind, number> = {\n crony: 7,\n crazy: 8,\n fatty: 9,\n fangie: 10,\n skully: 11,\n};\nconst ENEMY_BLOCK_CELL: Record<EnemyKind, SpriteRect> = {\n crony: rect(9 * T, 9 * T),\n crazy: rect(10 * T, 9 * T),\n fatty: rect(9 * T, 10 * T),\n fangie: rect(10 * T, 10 * T),\n skully: rect(9 * T, 11 * T),\n};\n\nexport const RING = rect(9 * T, 7 * T);\nexport const MINE = rect(10 * T, 7 * T);\nexport const GREY_BLOCK = rect(9 * T, 8 * T);\nexport const BROWN_BLOCK = rect(10 * T, 8 * T);\n\nexport function bombFrame(frame: number): SpriteRect {\n return rect((frame % TILESHEET.bombFrames) * T, 0);\n}\n\nexport function explosionFrame(frame: number): SpriteRect {\n return rect((TILESHEET.bombFrames + Math.min(frame, TILESHEET.explosionFrames - 1)) * T, 0);\n}\n\nexport function thingieFrame(id: number, frame: number): SpriteRect {\n const row = id - TileId.Thingie1 + 1;\n return rect((frame % TILESHEET.thingieFrames) * T, row * T);\n}\n\nexport function enemyFrame(kind: EnemyKind, frame: number): SpriteRect {\n return rect((frame % TILESHEET.enemyFrames) * T, ENEMY_ROW[kind] * T);\n}\n\n/** Where a board tile is drawn from. `frame` animates thingies and enemies. */\nexport function tileSheetRect(id: number, frame: number): SpriteRect | null {\n if (isThingie(id)) return thingieFrame(id, frame);\n const enemy = enemyKind(id);\n if (enemy) return enemyFrame(enemy, frame);\n const block = enemyBlockKind(id);\n if (block) return ENEMY_BLOCK_CELL[block];\n switch (id) {\n case TileId.GreyBlock:\n return GREY_BLOCK;\n case TileId.BrownBlock:\n return BROWN_BLOCK;\n case TileId.Mine:\n return MINE;\n case TileId.Bomb:\n return bombFrame(0);\n default:\n return null;\n }\n}\n\n/**\n * `items.png` (40x360): weights 1-3, the unsold heavy weight, the four ramps in\n * id order 16-19, and the green ring (the exe draws ids 12-19 from row id-12).\n */\nexport function itemRect(id: number): SpriteRect | null {\n if (isWeight(id)) return rect(0, (id - TileId.Weight1) * T);\n if (isRamp(id)) return rect(0, (4 + id - TileId.RampTopLeft) * T);\n return null;\n}\n\nexport const ITEM_RING = rect(0, 8 * T);\n\n/** Which sheet a tile id lives in. */\nexport function spriteFor(\n id: number,\n frame: number,\n): { sheet: 'tiles' | 'items'; rect: SpriteRect } | null {\n const item = itemRect(id);\n if (item) return { sheet: 'items', rect: item };\n const tile = tileSheetRect(id, frame);\n return tile ? { sheet: 'tiles', rect: tile } : null;\n}\n\n/**\n * `shooters.png` (120x160): Mary's red horizontal bars and Poindexter's blue\n * vertical bars in sizes 1-3, nested so each size is a whole number of cells.\n */\nexport function bottomShooterRect(size: number): SpriteRect {\n return rect(120 - T * size, T * (3 - size), T * size, T);\n}\n\nexport function rightShooterRect(size: number): SpriteRect {\n return rect(T * (3 - size), 160 - T * size, T, T * size);\n}\n\n/** `raccoon.png` (320x40): 8 frames. */\nexport const ROSCO = {\n walkLeft: [0, 1],\n walkAway: [2, 3],\n walkTowardWithBag: [4, 5],\n walkRightWithBag: [6, 7],\n} as const;\n\nexport function roscoFrame(frame: number): SpriteRect {\n return rect(frame * T, 0);\n}\n\n/** `stumpandmoney.png` (40x80): the stump above the piggy bank. */\nexport const STUMP = rect(0, 0);\nexport const PIGGY = rect(0, T);\n\n/** `bubbles.png` (124x124): 62px glossy squares, blue/red and their highlighted variants. */\nexport const BUBBLE = {\n blue: rect(0, 0, 62, 62),\n red: rect(62, 0, 62, 62),\n redLit: rect(0, 62, 62, 62),\n blueLit: rect(62, 62, 62, 62),\n} as const;\n\n/** `explosion.png` (440x80): fire explosion on row 0, blue swirl on row 1, 11 frames each. */\nexport function fireFrame(frame: number): SpriteRect {\n return rect(Math.min(frame, 10) * T, 0);\n}\n\nexport function swirlFrame(frame: number): SpriteRect {\n return rect(Math.min(frame, 10) * T, T);\n}\n\n/**\n * `mini.png` (120x560): 6 columns x 28 rows of 20px preview tiles whose row\n * index is the legacy tile id. Thingie rows carry one column per world.\n */\nexport const MINI_PX = 20;\nexport function miniRect(id: number, world: number): SpriteRect | null {\n if (id < 0 || id > 27) return null;\n const col = isThingie(id) ? Math.max(0, Math.min(5, world - 1)) : 0;\n return rect(col * MINI_PX, id * MINI_PX, MINI_PX, MINI_PX);\n}\n\n/** `checkandstar.png` (2304x80): 36 frames of 64x40, a check mark on row 0 and a spinning star on row 1. */\nexport const CHECK_FRAMES = 36;\nexport function checkFrame(frame: number): SpriteRect {\n return rect((frame % CHECK_FRAMES) * 64, 0, 64, 40);\n}\nexport function starFrame(frame: number): SpriteRect {\n return rect((frame % CHECK_FRAMES) * 64, 40, 64, 40);\n}\n\n/** Letter strips: `complete.png` 8x60x60, `defeat.png` 6x60x60, `pause.png` 5x80x80. */\nexport function letterRect(index: number, size: number): SpriteRect {\n return rect(0, index * size, size, size);\n}\n","import type { Assets } from '../assets/AssetLoader';\nimport type { BitmapFont } from '../render/BitmapFont';\nimport type { Renderer } from '../render/Renderer';\nimport type { Rect } from './ui';\n\n/**\n * The exe loads its three bitmap fonts eleven times, each with a colour\n * (`FUN_0040d4b7`; docs/DISASSEMBLY.md \"Font slots\"). Slot numbers below are\n * the exe's, so coordinates in the disassembly notes can be used as they are.\n */\nexport const FONT_SLOTS: readonly { font: string; r: number; g: number; b: number }[] = [\n { font: 'font1', r: 1, g: 0, b: 0 },\n { font: 'font1', r: 1, g: 1, b: 0 },\n { font: 'font1', r: 0, g: 1, b: 0 },\n { font: 'font1', r: 0, g: 0, b: 1 },\n { font: 'font1', r: 1, g: 1, b: 1 },\n { font: 'font1', r: 0, g: 0, b: 0 },\n { font: 'font2', r: 0, g: 0, b: 0 },\n { font: 'font2', r: 1, g: 1, b: 1 },\n { font: 'font2', r: 0, g: 1, b: 0 },\n { font: 'font3', r: 1, g: 1, b: 1 },\n { font: 'font3', r: 0, g: 0, b: 0 },\n];\n\nexport const Slot = {\n red: 0,\n yellow: 1,\n green: 2,\n blue: 3,\n white: 4,\n black: 5,\n smallBlack: 6,\n smallWhite: 7,\n smallGreen: 8,\n bigWhite: 9,\n bigBlack: 10,\n} as const;\n\nexport function slotFont(assets: Assets, slot: number): BitmapFont {\n return assets.font(FONT_SLOTS[slot]?.font ?? 'font1');\n}\n\n/** Draws text with its top-left at (x,y) in the slot's font and colour (`FUN_0040ee5f`). */\nexport function drawSlot(\n renderer: Renderer,\n assets: Assets,\n slot: number,\n text: string,\n x: number,\n y: number,\n align: 'left' | 'center' | 'right' = 'left',\n): void {\n const s = FONT_SLOTS[slot] ?? FONT_SLOTS[4];\n if (!s) return;\n renderer.drawText(assets.font(s.font), text, x, y, { r: s.r, g: s.g, b: s.b, align });\n}\n\n/** Draws text centred in a rectangle (`FUN_0040f0b0`). */\nexport function drawSlotCentred(\n renderer: Renderer,\n assets: Assets,\n slot: number,\n text: string,\n rect: Rect,\n): void {\n const s = FONT_SLOTS[slot] ?? FONT_SLOTS[4];\n if (!s) return;\n const font = assets.font(s.font);\n const y = rect.y + Math.floor(rect.h / 2) - Math.floor(font.lineHeight / 2);\n renderer.drawText(font, text, rect.x + Math.floor(rect.w / 2), y, {\n r: s.r,\n g: s.g,\n b: s.b,\n align: 'center',\n });\n}\n","import { BOARD_PX, TILE_PX } from '../core/constants';\nimport type { Rect } from './ui';\n\nconst r = (x: number, y: number, w: number, h: number): Rect => ({ x, y, w, h });\n\n/**\n * Screen positions of every gameplay element, as the exe draws them\n * (docs/DISASSEMBLY.md \"Layout\"). Virtual 800x600 pixels.\n */\nexport const LAYOUT = {\n board: { x: 40, y: 40, size: BOARD_PX },\n /** Mary's bar sits just under the board, Poindexter's just right of it. */\n bottomBarY: 559,\n rightBarX: 559,\n /** Rosco's stump (top-right corner) and the piggy bank (bottom-left corner). */\n stump: { x: 560, y: 0 },\n piggy: { x: 0, y: 560 },\n /** Rosco's countdown to the next speed-up. */\n countdown: { x: 550, y: 17 },\n hud: {\n x: 600,\n width: 200,\n /** \"Level %d-%d\", centred, in the black box at the top of the panel. */\n title: r(632, 30, 138, 28),\n /** Money, centred, in the \"$\" box of the store panel. */\n money: r(641, 374, 63, 23),\n /** Enemy counts: Cronies, Crazies, Fatties in the left column, Fangies and Skullies in the right. */\n counters: [\n { x: 675, y: 80 },\n { x: 675, y: 115 },\n { x: 675, y: 149 },\n { x: 742, y: 80 },\n { x: 742, y: 115 },\n ],\n time: { x: 700, y: 150 },\n ttb: { x: 700, y: 165 },\n /** Next item per lane: Mary's along the bottom (right-aligned), Poindexter's down the side (bottom-aligned). */\n nextBottom: { xs: [639, 679, 719], y: 526 },\n nextRight: { x: 722, ys: [379, 419, 459] },\n musicButton: r(672, 480, 27, 31),\n storeButton: r(630, 397, 66, 19),\n /** The store panel art (supplement.png) is blitted here while the store is open. */\n supplement: { x: 600, y: 366 },\n /** Retry, Pause, Quit. */\n menuButtons: [r(626, 210, 150, 39), r(626, 261, 150, 39), r(626, 311, 150, 39)],\n },\n store: {\n weights: [r(655, 420, 40, 40), r(655, 460, 40, 40), r(655, 500, 40, 40)],\n bomb: r(655, 540, 40, 35),\n bombQuantity: { x: 665, y: 569 },\n minus: r(655, 578, 9, 9),\n plus: r(689, 578, 9, 9),\n ramps: [r(705, 420, 40, 40), r(705, 460, 40, 40), r(705, 500, 40, 40), r(705, 540, 40, 40)],\n priceLeftX: 625,\n priceRightX: 750,\n /** Bars park here (first lane) while the store is open: 11 - size. */\n park: 11,\n },\n /** The retry / quit prompt: wood panel, two text rows, two buttons. */\n prompt: {\n panel: { x: 100, y: 100 },\n line1: r(100, 220, 400, 50),\n line2: r(100, 260, 400, 50),\n buttons: [r(210, 335, 80, 40), r(310, 335, 80, 40)],\n },\n pause: {\n /** Column x of the four kill-method headers (bomb, combo, weight, mine) and their row. */\n headerXs: [280, 320, 360, 400],\n headerY: 280,\n enemyX: 240,\n nameX: 120,\n firstRowY: 320,\n rowStep: 40,\n letters: { x: 60, step: 100, y: 40, size: 80 },\n },\n} as const;\n\nexport const BUBBLE_PX = 62;\n\nexport function cellX(col: number): number {\n return LAYOUT.board.x + col * TILE_PX;\n}\n\nexport function cellY(row: number): number {\n return LAYOUT.board.y + row * TILE_PX;\n}\n\n/** Board cell under a virtual-space point, or null outside the board. */\nexport function cellAt(x: number, y: number): { col: number; row: number } | null {\n const col = Math.floor((x - LAYOUT.board.x) / TILE_PX);\n const row = Math.floor((y - LAYOUT.board.y) / TILE_PX);\n if (col < 0 || row < 0 || col >= 13 || row >= 13) return null;\n return { col, row };\n}\n\n/**\n * The bar position the exe derives from the pointer (`FUN_004084ac`): the\n * bar is centred under the pointer, with C integer division (toward zero).\n */\nexport function aimedLane(pointer: number, origin: number, size: number): number {\n return Math.trunc((pointer - origin - (size - 1) * 20) / TILE_PX);\n}\n\n/**\n * Top-left corners of the 62 px drop bubbles for each lane of a bar while\n * the store is open (`FUN_00408561`).\n */\nexport function bubbleOrigins(side: 'bottom' | 'right', pos: number, size: number): number[] {\n const p = pos * TILE_PX;\n const origin = side === 'bottom' ? LAYOUT.board.x : LAYOUT.board.y;\n switch (size) {\n case 1:\n return [p - 10 + origin];\n case 2:\n return [p - 25 + origin, p + TILE_PX + 2 + origin];\n default:\n return [p - 34 + origin, p + TILE_PX - 10 + origin, p + 94 + origin];\n }\n}\n\nexport function inRect(x: number, y: number, rect: Rect, ox = 0, oy = 0): boolean {\n return (\n x >= rect.x + ox && y >= rect.y + oy && x < rect.x + ox + rect.w && y < rect.y + oy + rect.h\n );\n}\n","import type { BitmapFont } from '../render/BitmapFont';\nimport type { Renderer } from '../render/Renderer';\n\nexport interface Rect {\n readonly x: number;\n readonly y: number;\n readonly w: number;\n readonly h: number;\n}\n\nexport const rect = (x: number, y: number, w: number, h: number): Rect => ({ x, y, w, h });\n\nexport function contains(r: Rect, x: number, y: number): boolean {\n return x >= r.x && y >= r.y && x < r.x + r.w && y < r.y + r.h;\n}\n\n/** A plain labelled button drawn with rectangles, for screens whose art has none. */\nexport function drawButton(\n renderer: Renderer,\n font: BitmapFont,\n r: Rect,\n label: string,\n hovered: boolean,\n): void {\n renderer.drawRect(r.x, r.y, r.w, r.h, 0.32, 0.18, 0.08, 0.95);\n renderer.drawRect(\n r.x + 2,\n r.y + 2,\n r.w - 4,\n r.h - 4,\n hovered ? 0.72 : 0.55,\n hovered ? 0.4 : 0.28,\n 0.12,\n );\n renderer.drawText(font, label, r.x + r.w / 2, r.y + (r.h - font.lineHeight) / 2, {\n align: 'center',\n });\n}\n\n/** Dims the whole screen. */\nexport function drawDim(renderer: Renderer, alpha = 0.55): void {\n renderer.drawRect(0, 0, 800, 600, 0, 0, 0, alpha);\n}\n\n/** A framed panel with a wood-coloured fill. */\nexport function drawPanel(renderer: Renderer, r: Rect): void {\n renderer.drawRect(r.x - 4, r.y - 4, r.w + 8, r.h + 8, 0.25, 0.14, 0.05);\n renderer.drawRect(r.x, r.y, r.w, r.h, 0.55, 0.42, 0.22);\n}\n","import type { Assets, WorldAssets } from '../assets/AssetLoader';\nimport type { GameAudio } from '../audio/AudioEngine';\nimport { GRID_SIZE, TICK_RATE, TILE_PX } from '../core/constants';\nimport type { LevelData } from '../core/level/LevelData';\nimport type { Rules } from '../core/rules';\nimport { type Command, GameSession, type SessionEvent } from '../core/sim/GameSession';\nimport { enemyKind, isEnemy, isThingie } from '../core/tiles';\nimport {\n ENEMY_KINDS,\n type EnemyKind,\n type ItemKind,\n type ShooterSide,\n TileId,\n} from '../core/types';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport {\n bottomShooterRect,\n enemyFrame,\n fireFrame,\n itemRect,\n letterRect,\n MINE,\n PIGGY,\n RING,\n rightShooterRect,\n roscoFrame,\n STUMP,\n swirlFrame,\n thingieFrame,\n tileSheetRect,\n} from '../render/sprites';\nimport { drawSlot, drawSlotCentred, Slot } from './fonts';\nimport { aimedLane, BUBBLE_PX, bubbleOrigins, cellAt, cellX, cellY, LAYOUT } from './layout';\nimport type { Scene, SceneContext } from './Scene';\nimport { contains, type Rect } from './ui';\n\nexport interface GameplayResult {\n readonly outcome: 'won' | 'lost';\n readonly session: GameSession;\n}\n\nexport interface GameplayOptions {\n readonly assets: Assets;\n readonly world: WorldAssets;\n readonly level: LevelData;\n readonly seed: number;\n readonly rules?: Rules | undefined;\n readonly audio?: GameAudio | undefined;\n readonly onFinish: (result: GameplayResult) => void;\n readonly onEvents?: ((events: readonly SessionEvent[]) => void) | undefined;\n /** The player confirmed Quit (or pressed Escape). */\n readonly onQuit?: (() => void) | undefined;\n /** The player confirmed Retry. */\n readonly onRetry?: (() => void) | undefined;\n}\n\nconst ENEMY_LABEL: Record<EnemyKind, string> = {\n crony: 'Cronies:',\n crazy: 'Crazies:',\n fatty: 'Fatties:',\n fangie: 'Fangies:',\n skully: 'Skullies:',\n};\n\n/** Store slots in the exe's order: weights 1-3, the bomb, the four ramps. */\nconst STORE_SLOTS: readonly { item: ItemKind; rect: Rect }[] = [\n { item: TileId.Weight1, rect: LAYOUT.store.weights[0] },\n { item: TileId.Weight2, rect: LAYOUT.store.weights[1] },\n { item: TileId.Weight3, rect: LAYOUT.store.weights[2] },\n { item: TileId.Bomb, rect: LAYOUT.store.bomb },\n { item: TileId.RampTopLeft, rect: LAYOUT.store.ramps[0] },\n { item: TileId.RampTopRight, rect: LAYOUT.store.ramps[1] },\n { item: TileId.RampBottomLeft, rect: LAYOUT.store.ramps[2] },\n { item: TileId.RampBottomRight, rect: LAYOUT.store.ramps[3] },\n];\n\n/** Ticks per animation frame: 100 ms for idle frames, 56 ms for destroy frames (as in the exe). */\nconst IDLE_FRAME_TICKS = 6;\nconst DESTROY_FRAMES = 11;\n\ninterface Wobble {\n readonly frames: readonly number[];\n readonly start: number;\n readonly until: number;\n}\n\n/**\n * One level of play, drawn and controlled the way the exe does it\n * (docs/RULES.md, docs/DISASSEMBLY.md \"Layout\"): the pointer aims both bars,\n * a left click on the board fires, the right button or the Store button\n * opens the store, the three panel buttons retry, pause and quit.\n */\nexport class GameplayScene implements Scene {\n readonly session: GameSession;\n private readonly assets: Assets;\n private readonly world: WorldAssets;\n private pending: Command[] = [];\n private pointerX = -1;\n private pointerY = -1;\n private frameTicks = 0;\n private finished = false;\n paused = false;\n /** The confirmation asked by the Retry or Quit button. */\n prompt: 'retry' | 'quit' | null = null;\n private musicOn = true;\n /** Item being dragged from the store toward a lane bubble. */\n private drag: ItemKind | null = null;\n private bombQuantity = 1;\n /** Landed-thingy wobbles by cell index. */\n private readonly wobbles = new Map<number, Wobble>();\n\n constructor(private readonly opts: GameplayOptions) {\n this.assets = opts.assets;\n this.world = opts.world;\n this.session = new GameSession(opts.level, opts.seed, opts.rules);\n }\n\n enter(_ctx: SceneContext): void {}\n\n update(_dt: number, input: readonly InputEvent[]): void {\n this.handleInput(input);\n if (this.paused || this.prompt) {\n this.pending = [];\n return;\n }\n const commands = this.pending;\n this.pending = [];\n const events = this.session.tick(commands);\n this.frameTicks++;\n for (const e of events) this.onEvent(e);\n if (events.length > 0) this.opts.onEvents?.(events);\n if (!this.finished && this.session.status !== 'playing') {\n this.finished = true;\n this.opts.onFinish({\n outcome: this.session.status === 'won' ? 'won' : 'lost',\n session: this.session,\n });\n }\n }\n\n // ---- input ---------------------------------------------------------------\n\n private handleInput(input: readonly InputEvent[]): void {\n for (const e of input) {\n if (e.type === 'key') {\n this.keyInput(e.key);\n continue;\n }\n this.pointerX = e.x;\n this.pointerY = e.y;\n if (e.type === 'move') {\n if (!this.session.storeOpen && !this.paused && !this.prompt) this.aim(e.x, e.y);\n continue;\n }\n if (e.type === 'up') {\n if (e.button === 0 && this.drag !== null) this.drop(e.x, e.y);\n continue;\n }\n // down\n if (this.prompt) {\n this.promptInput(e.x, e.y);\n continue;\n }\n if (e.button === 2) {\n if (!this.paused) this.toggleStore();\n continue;\n }\n if (e.button !== 0) continue;\n if (contains(LAYOUT.hud.menuButtons[1], e.x, e.y)) {\n this.paused = !this.paused;\n continue;\n }\n if (this.paused) continue;\n if (contains(LAYOUT.hud.menuButtons[0], e.x, e.y)) {\n this.prompt = 'retry';\n continue;\n }\n if (contains(LAYOUT.hud.menuButtons[2], e.x, e.y)) {\n this.prompt = 'quit';\n continue;\n }\n if (contains(LAYOUT.hud.musicButton, e.x, e.y) && !this.session.storeOpen) {\n this.toggleMusic();\n continue;\n }\n if (contains(LAYOUT.hud.storeButton, e.x, e.y)) {\n this.toggleStore();\n continue;\n }\n if (this.session.storeOpen) {\n this.storeInput(e.x, e.y);\n continue;\n }\n if (e.x < LAYOUT.hud.x) {\n this.aim(e.x, e.y);\n this.pending.push({ t: 'fire' });\n }\n }\n }\n\n private keyInput(key: string): void {\n if (key === 'Escape') {\n if (this.prompt) this.prompt = null;\n else this.opts.onQuit?.();\n } else if (key === 'p' || key === 'P') {\n if (!this.prompt) this.paused = !this.paused;\n }\n }\n\n private aim(x: number, y: number): void {\n const b = this.session.shooters.bottom;\n const r = this.session.shooters.right;\n this.pending.push({\n t: 'aim',\n bottom: aimedLane(x, LAYOUT.board.x, b.size),\n right: aimedLane(y, LAYOUT.board.y, r.size),\n });\n }\n\n private toggleStore(): void {\n if (this.session.storeOpen) {\n this.pending.push({ t: 'closeStore' });\n this.drag = null;\n } else {\n this.pending.push({ t: 'openStore' });\n this.bombQuantity = 1;\n }\n }\n\n private toggleMusic(): void {\n this.musicOn = !this.musicOn;\n const world = Math.min(7, Math.max(1, this.opts.level.world));\n this.opts.audio?.music(this.musicOn ? `world${world}` : null);\n }\n\n private promptInput(x: number, y: number): void {\n const [confirm, back] = LAYOUT.prompt.buttons;\n if (confirm && contains(confirm, x, y)) {\n const which = this.prompt;\n this.prompt = null;\n if (which === 'retry') this.opts.onRetry?.();\n else this.opts.onQuit?.();\n } else if (back && contains(back, x, y)) {\n this.prompt = null;\n }\n }\n\n /** Press on a store slot, the +/- buttons, or a thingy on the board (`FUN_00408561`). */\n private storeInput(x: number, y: number): void {\n if (contains(LAYOUT.store.minus, x, y)) {\n this.bombQuantity = Math.max(1, this.bombQuantity - 1);\n return;\n }\n if (contains(LAYOUT.store.plus, x, y)) {\n this.bombQuantity++;\n return;\n }\n for (const slot of STORE_SLOTS) {\n if (contains(slot.rect, x, y)) {\n this.drag = slot.item;\n return;\n }\n }\n if (x < LAYOUT.hud.x) {\n const cell = cellAt(x, y);\n const id = cell ? this.session.grid.get(cell.col, cell.row) : TileId.Empty;\n if (isThingie(id)) this.drag = id as ItemKind;\n }\n }\n\n private drop(x: number, y: number): void {\n const item = this.drag;\n this.drag = null;\n if (item === null) return;\n const target = this.bubbleAt(x, y);\n if (!target) return;\n this.pending.push({\n t: 'buy',\n item,\n shooter: target.side,\n lane: target.lane,\n quantity: item === TileId.Bomb ? this.bombQuantity : 1,\n });\n }\n\n private bubbleRects(side: ShooterSide): Rect[] {\n const s = this.session.shooters[side];\n return bubbleOrigins(side, s.lane, s.size).map((o) =>\n side === 'bottom'\n ? { x: o, y: 540, w: BUBBLE_PX, h: BUBBLE_PX }\n : { x: 540, y: o, w: BUBBLE_PX, h: BUBBLE_PX },\n );\n }\n\n private bubbleAt(x: number, y: number): { side: ShooterSide; lane: number } | null {\n for (const side of ['bottom', 'right'] as const) {\n const rects = this.bubbleRects(side);\n for (let lane = 0; lane < rects.length; lane++) {\n const r = rects[lane];\n if (r && contains(r, x, y)) return { side, lane };\n }\n }\n return null;\n }\n\n // ---- events --------------------------------------------------------------\n\n private onEvent(e: SessionEvent): void {\n const audio = this.opts.audio;\n switch (e.t) {\n case 'fired':\n audio?.sfx('shoot');\n break;\n case 'landed':\n if (isThingie(e.ammo) && e.at.col < GRID_SIZE && e.at.row < GRID_SIZE) {\n audio?.sfx('thingyhit');\n // The exe wobbles a landed thingy for 0.8 to 8.8 s (frames 3-6 or 7-10 of its row).\n const vertical = e.dir === 'up' || e.dir === 'down';\n const frames = vertical ? [7, 8, 9, 10, 9, 8] : [3, 4, 5, 6, 5, 4];\n const seed = (e.at.col * 31 + e.at.row * 17 + this.frameTicks) % 480;\n this.wobbles.set(e.at.row * GRID_SIZE + e.at.col, {\n frames,\n start: this.frameTicks,\n until: this.frameTicks + 48 + seed,\n });\n }\n break;\n case 'bought':\n audio?.sfx('pop');\n break;\n case 'bomb':\n case 'mine':\n audio?.sfx('bomb');\n break;\n case 'combo':\n audio?.sfx('combo');\n break;\n case 'enemyKilled':\n if (e.by === 'weight' || e.by === 'mine') audio?.sfx('pop');\n break;\n case 'cleared':\n this.wobbles.delete(e.at.row * GRID_SIZE + e.at.col);\n break;\n default:\n break;\n }\n }\n\n // ---- drawing -------------------------------------------------------------\n\n draw(renderer: Renderer): void {\n const { session } = this;\n const hidden = this.paused || this.prompt !== null;\n renderer.begin();\n // The exe draws the \"dark\" variant during play: it carries the finished HUD art.\n renderer.drawImage(this.world.backgroundDark, 0, 0);\n if (!hidden) {\n this.drawBoard(renderer);\n this.drawGridLines(renderer, false);\n this.drawAuras(renderer);\n this.drawBars(renderer);\n this.drawProjectiles(renderer);\n } else {\n this.drawGridLines(renderer, true);\n }\n this.drawHud(renderer, hidden);\n if (!hidden) this.drawRosco(renderer);\n if (session.storeOpen && !hidden) this.drawStore(renderer);\n if (this.paused) this.drawPause(renderer);\n if (this.prompt) this.drawPrompt(renderer);\n renderer.end();\n }\n\n private drawTile(renderer: Renderer, id: number, frame: number, x: number, y: number): void {\n const item = itemRect(id);\n if (item) {\n renderer.drawSprite(this.assets.image('items'), item.x, item.y, item.w, item.h, x, y);\n return;\n }\n const r = tileSheetRect(id, frame);\n if (r) renderer.drawSprite(this.world.tiles, r.x, r.y, r.w, r.h, x, y);\n }\n\n /** Idle frame of a tile: landed thingies wobble, enemies breathe, everything else is still. */\n private idleFrame(id: number, col: number, row: number): number {\n if (isThingie(id)) {\n const w = this.wobbles.get(row * GRID_SIZE + col);\n if (!w) return 0;\n if (this.frameTicks >= w.until) {\n this.wobbles.delete(row * GRID_SIZE + col);\n return 0;\n }\n const step = Math.floor((this.frameTicks - w.start) / IDLE_FRAME_TICKS);\n return w.frames[step % w.frames.length] ?? 0;\n }\n if (isEnemy(id)) {\n const cycle = [0, 1, 2, 3, 4, 3, 2, 1, 0, 5, 6, 7, 8, 7, 6, 5];\n const step = Math.floor(this.frameTicks / IDLE_FRAME_TICKS) + col * 3 + row * 5;\n return cycle[step % cycle.length] ?? 0;\n }\n return 0;\n }\n\n private drawBoard(renderer: Renderer): void {\n const { session } = this;\n for (let row = 0; row < GRID_SIZE; row++) {\n for (let col = 0; col < GRID_SIZE; col++) {\n const id = session.grid.get(col, row);\n if (id === TileId.Empty) continue;\n const x = cellX(col);\n const y = cellY(row);\n const dying = session.dying(col, row);\n if (!dying) {\n this.drawTile(renderer, id, this.idleFrame(id, col, row), x, y);\n continue;\n }\n const frame = Math.min(\n DESTROY_FRAMES - 1,\n Math.floor((dying.elapsed * DESTROY_FRAMES) / dying.total),\n );\n if (dying.kind === 'blast') {\n // A landed bomb plays the tile sheet's bomb row.\n const r = thingieFrame(TileId.Thingie1, frame);\n renderer.drawSprite(this.world.tiles, r.x, r.y - TILE_PX, r.w, r.h, x, y);\n continue;\n }\n if (frame < 7) this.drawTile(renderer, id, 0, x, y);\n const fx = dying.kind === 'destroyed' ? fireFrame(frame) : swirlFrame(frame);\n renderer.drawSprite(this.assets.image('explosion'), fx.x, fx.y, fx.w, fx.h, x, y);\n }\n }\n }\n\n /** The exe's 14 grid lines each way; the lines around the bars cycle colour (`FUN_004016db`). */\n private drawGridLines(renderer: Renderer, plain: boolean): void {\n const { board } = LAYOUT;\n const t = (this.frameTicks * 1000) / TICK_RATE;\n const pulse = (period: number): number => (Math.abs(Math.sin(t / period)) * 128 + 128) / 255;\n const hi = { r: pulse(8000), g: pulse(6400), b: pulse(4800) };\n const lo = { r: 1 / 255, g: 0, b: 11 / 255 };\n const b = this.session.shooters.bottom;\n const r = this.session.shooters.right;\n for (let i = 0; i <= GRID_SIZE; i++) {\n const hLit = !plain && i >= r.lane && i <= r.lane + r.size;\n const vLit = !plain && i >= b.lane && i <= b.lane + b.size;\n const hc = hLit ? hi : lo;\n const vc = vLit ? hi : lo;\n renderer.drawRect(board.x, board.y + i * TILE_PX, board.size, 1, hc.r, hc.g, hc.b);\n renderer.drawRect(board.x + i * TILE_PX, board.y, 1, board.size, vc.r, vc.g, vc.b);\n }\n }\n\n /** Pulsing red 3 px outline around every enemy (`FUN_004015cc`). */\n private drawAuras(renderer: Renderer): void {\n const t = (this.frameTicks * 1000) / TICK_RATE;\n const red = (Math.abs(Math.sin(t / 1000)) * 128) / 255;\n for (const p of this.session.grid.positions(isEnemy)) {\n const x = cellX(p.col) - 1;\n const y = cellY(p.row) - 1;\n renderer.drawRect(x, y, TILE_PX, 3, red, 0, 0);\n renderer.drawRect(x, y + TILE_PX - 1, TILE_PX + 2, 3, red, 0, 0);\n renderer.drawRect(x, y, 3, TILE_PX, red, 0, 0);\n renderer.drawRect(x + TILE_PX - 1, y, 3, TILE_PX + 3, red, 0, 0);\n }\n }\n\n /** Bars with their loaded items showing through (`FUN_0040e9d1`). */\n private drawBars(renderer: Renderer): void {\n const { session } = this;\n const shooters = this.assets.image('shooters');\n const b = session.shooters.bottom;\n const bx = cellX(b.lane);\n b.lanes.forEach((lane, i) => {\n if (lane.loaded !== TileId.Empty) {\n this.drawTile(renderer, lane.loaded, 0, bx + i * TILE_PX, LAYOUT.bottomBarY);\n }\n });\n const br = bottomShooterRect(b.size);\n renderer.drawSprite(shooters, br.x, br.y, br.w, br.h, bx, LAYOUT.bottomBarY);\n\n const r = session.shooters.right;\n const ry = cellY(r.lane);\n r.lanes.forEach((lane, i) => {\n if (lane.loaded !== TileId.Empty) {\n this.drawTile(renderer, lane.loaded, 0, LAYOUT.rightBarX, ry + i * TILE_PX);\n }\n });\n const rr = rightShooterRect(r.size);\n renderer.drawSprite(shooters, rr.x, rr.y, rr.w, rr.h, LAYOUT.rightBarX, ry);\n }\n\n private drawProjectiles(renderer: Renderer): void {\n for (const p of this.session.inFlight) {\n this.drawTile(renderer, p.ammo, 0, cellX(p.col), cellY(p.row));\n }\n }\n\n private drawHud(renderer: Renderer, hidden: boolean): void {\n const { session, assets } = this;\n const { hud } = LAYOUT;\n drawSlotCentred(\n renderer,\n assets,\n Slot.bigWhite,\n `Level ${session.level.world}-${session.level.level}`,\n hud.title,\n );\n drawSlotCentred(renderer, assets, Slot.yellow, `${session.money}`, hud.money);\n\n // Enemy counts next to the icons painted into the panel art.\n const counts = session.enemies;\n ENEMY_KINDS.forEach((kind, i) => {\n const pos = hud.counters[i];\n if (pos) drawSlot(renderer, assets, Slot.bigBlack, `${counts[kind]}`, pos.x, pos.y);\n });\n\n if (!hidden) {\n drawSlot(\n renderer,\n assets,\n Slot.smallBlack,\n `Time: ${session.elapsedSeconds}s`,\n hud.time.x,\n hud.time.y,\n );\n drawSlot(\n renderer,\n assets,\n Slot.smallBlack,\n `TTB: ${session.level.timeToBeat}s`,\n hud.ttb.x,\n hud.ttb.y,\n );\n }\n\n // Next item per lane, in black boxes (`FUN_0040e3a8`).\n const b = session.shooters.bottom;\n b.lanes.forEach((lane, i) => {\n const x = hud.nextBottom.xs[3 - b.size + i];\n if (x === undefined) return;\n renderer.drawRect(x, hud.nextBottom.y, TILE_PX, TILE_PX, 0, 0, 0);\n if (lane.next !== TileId.Empty) this.drawTile(renderer, lane.next, 0, x, hud.nextBottom.y);\n });\n const r = session.shooters.right;\n r.lanes.forEach((lane, i) => {\n const y = hud.nextRight.ys[3 - r.size + i];\n if (y === undefined) return;\n renderer.drawRect(hud.nextRight.x, y, TILE_PX, TILE_PX, 0, 0, 0);\n if (lane.next !== TileId.Empty) this.drawTile(renderer, lane.next, 0, hud.nextRight.x, y);\n });\n\n // Music toggle (buttonz.png: 0/1 off, 2/3 on; odd frames are hovered).\n const hovered = contains(hud.musicButton, this.pointerX, this.pointerY);\n const frame = (this.musicOn ? 2 : 0) + (hovered ? 1 : 0);\n const mb = hud.musicButton;\n renderer.drawSprite(assets.image('buttonz'), frame * mb.w, 0, mb.w, mb.h, mb.x, mb.y);\n\n // Retry / Pause / Quit (painted into the panel) get an outline under the pointer, as the exe's button class draws.\n hud.menuButtons.forEach((rect, i) => {\n const lit = contains(rect, this.pointerX, this.pointerY) || (i === 1 && this.paused);\n this.drawOutline(renderer, rect, lit);\n });\n }\n\n private drawOutline(renderer: Renderer, rect: Rect, lit: boolean): void {\n const c = lit ? { r: 0.67, g: 0.67, b: 1 } : { r: 0.25, g: 0.25, b: 0.25 };\n renderer.drawRect(rect.x - 1, rect.y - 1, rect.w + 2, 1, c.r, c.g, c.b);\n renderer.drawRect(rect.x - 1, rect.y + rect.h, rect.w + 2, 1, c.r, c.g, c.b);\n renderer.drawRect(rect.x - 1, rect.y - 1, 1, rect.h + 2, c.r, c.g, c.b);\n renderer.drawRect(rect.x + rect.w, rect.y - 1, 1, rect.h + 2, c.r, c.g, c.b);\n }\n\n /** Stump, piggy bank, Rosco on his rounds and the speed-up countdown (`FUN_00409e61`). */\n private drawRosco(renderer: Renderer): void {\n const { assets, session } = this;\n const sheet = assets.image('stumpandmoney');\n renderer.drawSprite(sheet, STUMP.x, STUMP.y, STUMP.w, STUMP.h, LAYOUT.stump.x, LAYOUT.stump.y);\n renderer.drawSprite(sheet, PIGGY.x, PIGGY.y, PIGGY.w, PIGGY.h, LAYOUT.piggy.x, LAYOUT.piggy.y);\n const { rosco } = session;\n if (!rosco.emerged) return;\n const base = { left: 0, down: 2, up: 4, right: 6 }[rosco.facing];\n const f = roscoFrame(base + rosco.walkFrame);\n renderer.drawSprite(\n assets.image('raccoon'),\n f.x,\n f.y,\n f.w,\n f.h,\n Math.round(rosco.x),\n Math.round(rosco.y),\n );\n drawSlot(\n renderer,\n assets,\n Slot.smallWhite,\n `${rosco.countdownSeconds}`,\n LAYOUT.countdown.x,\n LAYOUT.countdown.y,\n );\n }\n\n private drawStore(renderer: Renderer): void {\n const { session, assets } = this;\n const { store, hud } = LAYOUT;\n // The panel art covers the next-item boxes while the store is open, as in the exe.\n renderer.drawImage(this.world.supplement, hud.supplement.x, hud.supplement.y);\n drawSlotCentred(renderer, assets, Slot.yellow, `${session.money}`, hud.money);\n const prices = session.rules.prices;\n for (const slot of STORE_SLOTS) {\n this.drawTile(renderer, slot.item, 0, slot.rect.x, slot.rect.y);\n const priceX = slot.rect.x < 700 ? store.priceLeftX : store.priceRightX;\n drawSlot(renderer, assets, Slot.black, `$${prices[slot.item] ?? 0}`, priceX, slot.rect.y + 5);\n }\n const pm = assets.image('plusminus');\n renderer.drawSprite(pm, 0, 0, 9, 9, store.minus.x, store.minus.y);\n renderer.drawSprite(pm, 9, 0, 9, 9, store.plus.x, store.plus.y);\n drawSlot(\n renderer,\n assets,\n Slot.smallWhite,\n `X${this.bombQuantity}`,\n store.bombQuantity.x,\n store.bombQuantity.y,\n );\n\n // Drop bubbles over every lane; the one under a dragged item turns red.\n const bubbles = assets.image('bubbles');\n const hover = this.drag !== null ? this.bubbleAt(this.pointerX, this.pointerY) : null;\n for (const side of ['bottom', 'right'] as const) {\n this.bubbleRects(side).forEach((rect, lane) => {\n const lit = hover !== null && hover.side === side && hover.lane === lane;\n renderer.drawSprite(\n bubbles,\n lit ? BUBBLE_PX : 0,\n 0,\n BUBBLE_PX,\n BUBBLE_PX,\n rect.x,\n rect.y,\n BUBBLE_PX,\n BUBBLE_PX,\n 0.5,\n );\n });\n }\n\n if (this.drag !== null) {\n const x = this.pointerX - TILE_PX / 2;\n const y = this.pointerY - TILE_PX / 2;\n this.drawTile(renderer, this.drag, 0, x, y);\n if (this.drag === TileId.Bomb) {\n drawSlot(renderer, assets, Slot.smallWhite, `X${this.bombQuantity}`, x + 10, y + 10);\n } else if (isThingie(this.drag)) {\n drawSlot(renderer, assets, Slot.smallWhite, `$${prices[this.drag] ?? 0}`, x, y);\n }\n }\n }\n\n /** Pause screen (`FUN_00407371`): bobbing letters and the kill-method chart. */\n private drawPause(renderer: Renderer): void {\n const { assets, session } = this;\n const { pause } = LAYOUT;\n const t = (this.frameTicks * 1000) / TICK_RATE;\n const letters = assets.image('pause');\n for (let i = 0; i < 5; i++) {\n const r = letterRect(i, pause.letters.size);\n const y = pause.letters.y + Math.sin((t + i * 800) / 500) * 15 + 15;\n renderer.drawSprite(letters, r.x, r.y, r.w, r.h, pause.letters.x + i * pause.letters.step, y);\n }\n const [bombX, comboX, weightX, mineX] = pause.headerXs;\n const bomb = tileSheetRect(TileId.Bomb, 0);\n if (bomb)\n renderer.drawSprite(\n this.world.tiles,\n bomb.x,\n bomb.y,\n bomb.w,\n bomb.h,\n bombX ?? 0,\n pause.headerY,\n );\n const pop = swirlFrame(7);\n renderer.drawSprite(\n assets.image('explosion'),\n pop.x,\n pop.y,\n pop.w,\n pop.h,\n comboX ?? 0,\n pause.headerY,\n );\n const weight = itemRect(TileId.HeavyWeight);\n if (weight)\n renderer.drawSprite(\n assets.image('items'),\n weight.x,\n weight.y,\n weight.w,\n weight.h,\n weightX ?? 0,\n pause.headerY,\n );\n renderer.drawSprite(\n this.world.tiles,\n MINE.x,\n MINE.y,\n MINE.w,\n MINE.h,\n mineX ?? 0,\n pause.headerY,\n );\n\n ENEMY_KINDS.forEach((kind, i) => {\n const y = pause.firstRowY + i * pause.rowStep;\n const e = enemyFrame(kind, 0);\n renderer.drawSprite(this.world.tiles, e.x, e.y, e.w, e.h, pause.enemyX, y);\n drawSlot(renderer, assets, Slot.white, ENEMY_LABEL[kind], pause.nameX, y + 3);\n const rule = session.rules.enemies[kind];\n const marks: (number | undefined)[] = [\n rule.bomb ? bombX : undefined,\n rule.comboMin !== null ? comboX : undefined,\n rule.weight ? weightX : undefined,\n rule.mine ? mineX : undefined,\n ];\n marks.forEach((x, c) => {\n if (x === undefined) return;\n renderer.drawSprite(this.world.tiles, RING.x, RING.y, RING.w, RING.h, x, y);\n if (c === 1 && rule.comboMin !== null) {\n drawSlot(renderer, assets, Slot.white, `${rule.comboMin}`, x + 12, y + 3);\n }\n });\n });\n }\n\n /** \"Do you wish to retry the level?\" / \"Are you sure you wish to quit?\" on the wood panel. */\n private drawPrompt(renderer: Renderer): void {\n const { assets } = this;\n const { prompt } = LAYOUT;\n renderer.drawImage(assets.image('wood'), prompt.panel.x, prompt.panel.y);\n const retry = this.prompt === 'retry';\n drawSlotCentred(\n renderer,\n assets,\n Slot.white,\n retry ? 'Do you wish to' : 'Are you sure you',\n prompt.line1,\n );\n drawSlotCentred(\n renderer,\n assets,\n Slot.white,\n retry ? 'retry the level?' : 'wish to quit?',\n prompt.line2,\n );\n const labels = retry ? ['Retry', 'Back'] : ['Quit', 'Back'];\n prompt.buttons.forEach((rect, i) => {\n this.drawOutline(renderer, rect, contains(rect, this.pointerX, this.pointerY));\n drawSlotCentred(renderer, assets, Slot.smallWhite, labels[i] ?? '', rect);\n });\n }\n\n leave(): void {}\n}\n\nexport { enemyKind };\n","import type { BitmapFont } from './BitmapFont';\n\n/** Greedy word wrap honouring explicit line breaks. */\nexport function wrapText(font: BitmapFont, text: string, maxWidth: number, scale = 1): string[] {\n const lines: string[] = [];\n for (const paragraph of text.split('\\n')) {\n const words = paragraph.split(' ').filter((w) => w.length > 0);\n if (words.length === 0) {\n lines.push('');\n continue;\n }\n let line = '';\n for (const word of words) {\n const candidate = line ? `${line} ${word}` : word;\n if (line && font.measure(candidate, scale) > maxWidth) {\n lines.push(line);\n line = word;\n } else {\n line = candidate;\n }\n }\n lines.push(line);\n }\n return lines;\n}\n","import type { Assets, WorldAssets } from '../assets/AssetLoader';\nimport { GRID_SIZE, TILE_PX } from '../core/constants';\nimport { isThingie } from '../core/tiles';\nimport { TileId } from '../core/types';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport { bottomShooterRect, itemRect, rightShooterRect, tileSheetRect } from '../render/sprites';\nimport { wrapText } from '../render/text';\nimport { drawSlot, drawSlotCentred, Slot, slotFont } from './fonts';\nimport type { GameplayScene } from './GameplayScene';\nimport { cellX, cellY, LAYOUT } from './layout';\nimport type { Scene, SceneContext } from './Scene';\nimport { contains, rect } from './ui';\n\nconst PLAY = rect(626, 530, 150, 39);\n/** Message lines are centred in 200x20 boxes down the side panel from y = 80 (`FUN_00406cc2`). */\nconst MESSAGE_X = 600;\nconst MESSAGE_Y = 80;\nconst MESSAGE_W = 200;\nconst MESSAGE_LINE = 20;\n/** The exe wraps the message at 170 px of font slot 6. */\nconst WRAP_WIDTH = 170;\nconst MAX_LINES = 30;\n\n/**\n * The level intro (the exe's screen 3): the loaded board and bars, the\n * level's message down the side panel, and a Play button that starts the\n * clock. The gameplay scene already exists; this just shows it standing still.\n */\nexport class IntroScene implements Scene {\n private hoverX = -1;\n private hoverY = -1;\n\n constructor(\n private readonly assets: Assets,\n private readonly world: WorldAssets,\n private readonly gameplay: GameplayScene,\n private readonly actions: { onPlay: () => void; onBack: () => void },\n ) {}\n\n enter(_ctx: SceneContext): void {}\n\n update(_dt: number, input: readonly InputEvent[]): void {\n for (const e of input) {\n if (e.type === 'move') {\n this.hoverX = e.x;\n this.hoverY = e.y;\n } else if (e.type === 'key') {\n if (e.key === 'Escape') this.actions.onBack();\n else if (e.key === 'Enter' || e.key === ' ') this.actions.onPlay();\n } else if (e.type === 'down' && e.button === 0 && contains(PLAY, e.x, e.y)) {\n this.actions.onPlay();\n }\n }\n }\n\n draw(renderer: Renderer): void {\n const { assets, world } = this;\n const { session } = this.gameplay;\n const level = session.level;\n renderer.begin();\n renderer.drawImage(world.background, 0, 0);\n\n const items = assets.image('items');\n const drawTile = (id: number, x: number, y: number): void => {\n const item = itemRect(id);\n if (item) {\n renderer.drawSprite(items, item.x, item.y, item.w, item.h, x, y);\n return;\n }\n const r = tileSheetRect(id, 0);\n if (r) renderer.drawSprite(world.tiles, r.x, r.y, r.w, r.h, x, y);\n };\n for (let row = 0; row < GRID_SIZE; row++) {\n for (let col = 0; col < GRID_SIZE; col++) {\n const id = session.grid.get(col, row);\n if (id !== TileId.Empty) drawTile(id, cellX(col), cellY(row));\n }\n }\n const { board } = LAYOUT;\n for (let i = 0; i <= GRID_SIZE; i++) {\n renderer.drawRect(board.x, board.y + i * TILE_PX, board.size, 1, 1 / 255, 0, 11 / 255);\n renderer.drawRect(board.x + i * TILE_PX, board.y, 1, board.size, 1 / 255, 0, 11 / 255);\n }\n\n const shooters = assets.image('shooters');\n const b = session.shooters.bottom;\n b.lanes.forEach((lane, i) => {\n if (isThingie(lane.loaded) || lane.loaded === TileId.Bomb) {\n drawTile(lane.loaded, cellX(b.lane) + i * TILE_PX, LAYOUT.bottomBarY);\n }\n });\n const br = bottomShooterRect(b.size);\n renderer.drawSprite(shooters, br.x, br.y, br.w, br.h, cellX(b.lane), LAYOUT.bottomBarY);\n const r = session.shooters.right;\n r.lanes.forEach((lane, i) => {\n if (isThingie(lane.loaded) || lane.loaded === TileId.Bomb) {\n drawTile(lane.loaded, LAYOUT.rightBarX, cellY(r.lane) + i * TILE_PX);\n }\n });\n const rr = rightShooterRect(r.size);\n renderer.drawSprite(shooters, rr.x, rr.y, rr.w, rr.h, LAYOUT.rightBarX, cellY(r.lane));\n\n drawSlotCentred(\n renderer,\n assets,\n Slot.bigWhite,\n `Level ${level.world}-${level.level}`,\n LAYOUT.hud.title,\n );\n const text = level.message || (level.placeholder ? 'This level was never finished.' : '');\n const font = slotFont(assets, Slot.smallBlack);\n const lines = wrapText(font, text, WRAP_WIDTH).slice(0, MAX_LINES);\n lines.forEach((line, i) => {\n drawSlotCentred(renderer, assets, Slot.smallBlack, line, {\n x: MESSAGE_X,\n y: MESSAGE_Y + i * MESSAGE_LINE,\n w: MESSAGE_W,\n h: MESSAGE_LINE,\n });\n });\n\n const lit = contains(PLAY, this.hoverX, this.hoverY);\n const c = lit ? 0.9 : 0.35;\n renderer.drawRect(PLAY.x - 1, PLAY.y - 1, PLAY.w + 2, PLAY.h + 2, c, c, c);\n renderer.drawRect(PLAY.x, PLAY.y, PLAY.w, PLAY.h, 0.08, 0.05, 0.02);\n drawSlotCentred(renderer, assets, Slot.white, 'Play', PLAY);\n drawSlot(renderer, assets, Slot.smallWhite, 'Esc: back to the map', 606, 580);\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { Assets } from '../assets/AssetLoader';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport type { Scene, SceneContext } from './Scene';\n\n/** The exe fades the logo in over 3.3 s, holds it until 6 s, then fades it out over 3.3 s (`FUN_004023dd`). */\nconst FADE = 3.314;\nconst HOLD_UNTIL = 6;\nconst LOGO = { x: 200, y: 150 };\n\n/** \"9 Minute Productions\" studio card. A click after the fade-in skips it. */\nexport class LogoScene implements Scene {\n private t = 0;\n private done = false;\n\n constructor(\n private readonly assets: Assets,\n private readonly onDone: () => void,\n ) {}\n\n enter(_ctx: SceneContext): void {\n this.t = 0;\n }\n\n update(dt: number, input: readonly InputEvent[]): void {\n this.t += dt;\n const skip = this.t >= FADE && input.some((e) => e.type === 'down' || e.type === 'key');\n if (!this.done && (skip || this.t >= HOLD_UNTIL + FADE)) {\n this.done = true;\n this.onDone();\n }\n }\n\n draw(renderer: Renderer): void {\n const t = this.t;\n const alpha =\n t < FADE ? t / FADE : t < HOLD_UNTIL ? 1 : Math.max(0, 1 - (t - HOLD_UNTIL) / FADE);\n renderer.begin();\n renderer.drawImage(this.assets.image('logo'), LOGO.x, LOGO.y, alpha);\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { Assets, WorldAssets } from '../assets/AssetLoader';\nimport { GRID_SIZE, TICK_RATE, TILE_PX } from '../core/constants';\nimport type { GameSession } from '../core/sim/GameSession';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport { letterRect } from '../render/sprites';\nimport { drawSlot, drawSlotCentred, Slot } from './fonts';\nimport { LAYOUT } from './layout';\nimport type { Scene, SceneContext } from './Scene';\nimport { contains, type Rect, rect } from './ui';\n\nexport type ResultAction = 'next' | 'retry' | 'map';\n\nexport interface ResultOptions {\n readonly assets: Assets;\n readonly world: WorldAssets;\n readonly session: GameSession;\n readonly outcome: 'won' | 'lost';\n /** Best time before this run, 0 if never cleared. */\n readonly previousBest: number;\n /** Wins and losses on the file after this result, for \"Current Record\". */\n readonly record: { wins: number; losses: number; slot: number };\n /** True when this was the last playable level of the game. */\n readonly finalLevel: boolean;\n readonly onContinue: (action: ResultAction) => void;\n}\n\n/** OK and Retry (`FUN_004025a3`). */\nconst OK = rect(210, 530, 80, 60);\nconst RETRY = rect(310, 530, 80, 60);\nconst TEXT_X = 100;\n\n/**\n * Level complete / defeat screen (`FUN_00406ea9`): the darkened background\n * with the grid, the bobbing letter strip, the run's numbers, then OK / Retry.\n */\nexport class ResultScene implements Scene {\n private ticks = 0;\n private hoverX = -1;\n private hoverY = -1;\n\n constructor(private readonly opts: ResultOptions) {}\n\n enter(_ctx: SceneContext): void {\n this.ticks = 0;\n }\n\n update(_dt: number, input: readonly InputEvent[]): void {\n this.ticks++;\n for (const e of input) {\n if (e.type === 'move') {\n this.hoverX = e.x;\n this.hoverY = e.y;\n continue;\n }\n if (this.ticks < 24) continue;\n if (e.type === 'key') {\n if (e.key === 'Enter' || e.key === ' ')\n this.opts.onContinue(this.opts.outcome === 'won' ? 'next' : 'retry');\n else if (e.key === 'Escape') this.opts.onContinue('map');\n continue;\n }\n if (e.type !== 'down' || e.button !== 0) continue;\n if (contains(OK, e.x, e.y))\n this.opts.onContinue(this.opts.outcome === 'won' ? 'next' : 'map');\n else if (contains(RETRY, e.x, e.y)) this.opts.onContinue('retry');\n }\n }\n\n private drawButton(renderer: Renderer, r: Rect, label: string): void {\n const lit = contains(r, this.hoverX, this.hoverY);\n const c = lit ? 0.9 : 0.4;\n renderer.drawRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, c, c, c);\n renderer.drawRect(r.x, r.y, r.w, r.h, 0.08, 0.05, 0.02);\n drawSlotCentred(renderer, this.opts.assets, Slot.white, label, r);\n }\n\n draw(renderer: Renderer): void {\n const { assets, world, session, outcome } = this.opts;\n renderer.begin();\n renderer.drawImage(world.backgroundDark, 0, 0);\n const { board } = LAYOUT;\n for (let i = 0; i <= GRID_SIZE; i++) {\n renderer.drawRect(board.x, board.y + i * TILE_PX, board.size, 1, 1 / 255, 0, 11 / 255);\n renderer.drawRect(board.x + i * TILE_PX, board.y, 1, board.size, 1 / 255, 0, 11 / 255);\n }\n\n const t = (this.ticks * 1000) / TICK_RATE;\n const won = outcome === 'won';\n const sheet = assets.image(won ? 'complete' : 'defeat');\n const count = won ? 8 : 6;\n const startX = won ? 51 : 70;\n const step = won ? 60 : 80;\n for (let i = 0; i < count; i++) {\n const r = letterRect(i, 60);\n const y = 40 + Math.sin((t + i * 800) / 500) * 20 + 20;\n renderer.drawSprite(sheet, r.x, r.y, r.w, r.h, startX + i * step, y);\n }\n\n const secs = session.elapsedSeconds;\n const ttb = session.level.timeToBeat;\n const best = this.opts.previousBest > 0 ? Math.min(this.opts.previousBest, secs) : secs;\n drawSlot(renderer, assets, Slot.white, `Time: ${secs} s (Best Time: ${best} s)`, TEXT_X, 180);\n drawSlot(renderer, assets, Slot.white, `Time to beat: ${ttb} s`, TEXT_X, 213);\n let starLine: string;\n if (session.level.world === 7) {\n starLine = won\n ? \"Congratulations! You've won the game!\"\n : `Live at least ${ttb} seconds to beat this final challenge of the game!`;\n } else if (won && secs <= ttb) starLine = \"You have this level's star!\";\n else starLine = `Beat ${ttb} seconds to acquire this level's star.`;\n drawSlot(renderer, assets, Slot.smallWhite, starLine, TEXT_X, 246);\n drawSlot(renderer, assets, Slot.white, `Number of shots: ${session.stats.shots}`, TEXT_X, 300);\n drawSlot(\n renderer,\n assets,\n Slot.white,\n `Number of combos: ${session.stats.combos}`,\n TEXT_X,\n 333,\n );\n const avg =\n session.stats.combos > 0\n ? `Average combo size: ${(session.stats.comboPieces / session.stats.combos).toFixed(2)}`\n : 'Average combo size: N/A';\n drawSlot(renderer, assets, Slot.white, avg, TEXT_X, 366);\n const { record } = this.opts;\n drawSlot(\n renderer,\n assets,\n Slot.white,\n record.slot === 3\n ? 'Current Record: I HATE CHICKEN!'\n : `Current Record: ${record.wins}-${record.losses}`,\n TEXT_X,\n 430,\n );\n if (this.ticks >= 24) {\n this.drawButton(renderer, OK, 'OK');\n this.drawButton(renderer, RETRY, 'Retry');\n }\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { Assets } from '../assets/AssetLoader';\nimport { computeStats, isNewGame, type SlotNumber } from '../core/save/SaveData';\nimport type { SaveService } from '../core/save/SaveService';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport { drawSlot, drawSlotCentred, Slot } from './fonts';\nimport type { Scene, SceneContext } from './Scene';\nimport { contains, type Rect, rect } from './ui';\n\n/** menusupplement.png sits over the title screen's buttons; hot-spots from `FUN_004025a3`. */\nconst PANEL = { x: 0, y: 435 };\nconst FILE_BUTTONS: Rect[] = [rect(25, 442, 47, 45), rect(25, 493, 47, 45), rect(25, 544, 47, 45)];\nconst BACK = rect(79, 563, 77, 28);\nconst LOAD = rect(161, 563, 77, 28);\nconst DELETE = rect(244, 563, 77, 28);\nconst HEADING = rect(85, 450, 228, 20);\nconst LINE_X = 90;\nconst LINE_YS = [477, 492, 507, 522];\nconst CONFIRM_YES = rect(120, 520, 60, 35);\nconst CONFIRM_NO = rect(220, 520, 60, 35);\nconst BETA = { x: 450, y: 110 };\n\n/**\n * File 1/2/3 menu over the title screen: pick a slot, read its summary in\n * the black panel, then Load, Delete (with confirmation) or go Back.\n */\nexport class SaveSelectScene implements Scene {\n private selected: SlotNumber | null = null;\n private confirmingDelete = false;\n private hoverX = -1;\n private hoverY = -1;\n private loading = true;\n\n constructor(\n private readonly assets: Assets,\n private readonly saves: SaveService,\n private readonly actions: { onLoad: (slot: SlotNumber) => void; onBack: () => void },\n ) {}\n\n enter(_ctx: SceneContext): void {\n this.loading = true;\n this.selected = this.saves.slot;\n void this.saves.peekAll().then(() => {\n this.loading = false;\n });\n }\n\n update(_dt: number, input: readonly InputEvent[]): void {\n for (const e of input) {\n if (e.type === 'move') {\n this.hoverX = e.x;\n this.hoverY = e.y;\n continue;\n }\n if (e.type === 'key' && e.key === 'Escape') {\n if (this.confirmingDelete) this.confirmingDelete = false;\n else this.actions.onBack();\n continue;\n }\n if (e.type !== 'down' || e.button !== 0 || this.loading) continue;\n if (this.confirmingDelete) {\n if (contains(CONFIRM_YES, e.x, e.y) && this.selected) {\n const slot = this.selected;\n this.confirmingDelete = false;\n this.loading = true;\n void this.saves.remove(slot).then(() => {\n this.loading = false;\n });\n } else if (contains(CONFIRM_NO, e.x, e.y)) {\n this.confirmingDelete = false;\n }\n continue;\n }\n FILE_BUTTONS.forEach((r, i) => {\n if (contains(r, e.x, e.y)) this.selected = (i + 1) as SlotNumber;\n });\n if (contains(BACK, e.x, e.y)) this.actions.onBack();\n else if (contains(LOAD, e.x, e.y) && this.selected) this.actions.onLoad(this.selected);\n else if (contains(DELETE, e.x, e.y) && this.selected && this.saves.peek(this.selected)) {\n this.confirmingDelete = true;\n }\n }\n }\n\n draw(renderer: Renderer): void {\n const { assets } = this;\n renderer.begin();\n renderer.drawImage(assets.image('shooterland'), 0, 0);\n renderer.drawImage(assets.image('beta'), BETA.x, BETA.y);\n renderer.drawImage(assets.image('menusupplement'), PANEL.x, PANEL.y);\n\n if (this.selected) {\n const r = FILE_BUTTONS[this.selected - 1] ?? rect(0, 0, 0, 0);\n renderer.drawRect(r.x - 3, r.y - 3, r.w + 6, r.h + 6, 1, 1, 0.6, 0.35);\n }\n for (const r of [...FILE_BUTTONS, BACK, LOAD, DELETE]) {\n if (contains(r, this.hoverX, this.hoverY))\n renderer.drawRect(r.x, r.y, r.w, r.h, 1, 1, 1, 0.15);\n }\n\n if (this.loading || !this.selected) {\n drawSlotCentred(renderer, assets, Slot.bigWhite, 'Loading...', HEADING);\n renderer.end();\n return;\n }\n const slot = this.selected;\n const data = this.saves.peek(slot);\n if (!data || isNewGame(data)) {\n drawSlotCentred(renderer, assets, Slot.bigWhite, `File ${slot}: New Game`, HEADING);\n } else {\n const stats = computeStats(data, this.saves.levels);\n drawSlotCentred(\n renderer,\n assets,\n Slot.bigWhite,\n `File ${slot}: Level ${data.world}-${data.level}`,\n HEADING,\n );\n if (!this.confirmingDelete) {\n const h = Math.floor(data.secondsPlayed / 3600);\n const m = Math.floor((data.secondsPlayed % 3600) / 60);\n const s = data.secondsPlayed % 60;\n const lines = [\n `Stars: ${stats.stars}`,\n slot === 3 ? 'Record: I HATE CHICKEN' : `Record: ${data.wins}-${data.losses}`,\n `Complete: ${stats.completePercent}%`,\n `Time Played: ${h}h ${m}m ${s}s`,\n ];\n lines.forEach((line, i) => {\n drawSlot(renderer, assets, Slot.smallWhite, line, LINE_X, LINE_YS[i] ?? 0);\n });\n }\n }\n if (this.confirmingDelete) {\n drawSlot(renderer, assets, Slot.smallGreen, 'Are you sure you want', 120, 478);\n drawSlot(renderer, assets, Slot.smallGreen, `to delete File ${slot}?`, 130, 498);\n drawSlot(renderer, assets, Slot.smallWhite, 'Yes', 137, 528);\n drawSlot(renderer, assets, Slot.smallWhite, 'No', 241, 528);\n for (const r of [CONFIRM_YES, CONFIRM_NO]) {\n if (contains(r, this.hoverX, this.hoverY))\n renderer.drawRect(r.x, r.y, r.w, r.h, 1, 1, 1, 0.15);\n }\n }\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { Assets } from '../assets/AssetLoader';\nimport { computeStats } from '../core/save/SaveData';\nimport type { SaveService } from '../core/save/SaveService';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport { drawSlot, drawSlotCentred, Slot } from './fonts';\nimport type { Scene, SceneContext } from './Scene';\nimport { rect } from './ui';\n\nconst LINE = (y: number) => rect(200, y, 400, 50);\nconst SUFFLE = { x: 25, y: 50, w: 150, h: 500 };\n\n/** \"Stats: File N\" (`FUN_0040ad69`): the original's figures on a black screen; any click returns. */\nexport class StatsScene implements Scene {\n constructor(\n private readonly assets: Assets,\n private readonly saves: SaveService,\n private readonly onBack: () => void,\n ) {}\n\n enter(_ctx: SceneContext): void {}\n\n update(_dt: number, input: readonly InputEvent[]): void {\n for (const e of input) {\n if (e.type === 'key' && (e.key === 'Escape' || e.key === 'Enter')) this.onBack();\n else if (e.type === 'down') this.onBack();\n }\n }\n\n draw(renderer: Renderer): void {\n const { assets, saves } = this;\n const s = computeStats(saves.data, assets.levels);\n const d = saves.data;\n renderer.begin(0, 0, 0);\n // The tower of green thingies on the left, and a stretched mirror of it on the right (`FUN_0040df86`).\n const suffle = assets.image('suffle');\n renderer.drawImage(suffle, SUFFLE.x, SUFFLE.y);\n for (let i = 0; i < SUFFLE.w; i++) {\n renderer.drawSprite(suffle, SUFFLE.w - i, 0, 1, SUFFLE.h, 625 + i, SUFFLE.y, 1, SUFFLE.h);\n }\n\n const fmt = (v: number | null, digits: number): string =>\n v === null ? 'N/A' : v.toFixed(digits);\n const h = Math.floor(d.secondsPlayed / 3600);\n const m = Math.floor((d.secondsPlayed % 3600) / 60);\n const sec = d.secondsPlayed % 60;\n drawSlotCentred(renderer, assets, Slot.bigWhite, `Stats: File ${saves.slot}`, LINE(100));\n const lines = [\n saves.slot === 3 ? 'Record: I HATE CHICKEN' : `Record: ${d.wins}-${d.losses}`,\n s.winPercent === null ? 'Win Percentage: N/A' : `Win Percentage: ${s.winPercent}%`,\n `Stars: ${s.stars}`,\n `Time Played: ${h}h ${m}m ${sec}s`,\n s.averageComboSize === null\n ? 'Average Combo Size: N/A'\n : `Average Combo Size: ${fmt(s.averageComboSize, 3)}`,\n s.shotsPerCombo === null\n ? 'Shots per Combo: N/A'\n : `Shots per Combo: ${fmt(s.shotsPerCombo, 3)}`,\n `Number of Shots: ${d.shots}`,\n s.slRating === null ? 'SL Rating: N/A' : `SL Rating: ${fmt(s.slRating, 1)}`,\n ];\n lines.forEach((line, i) => {\n drawSlotCentred(renderer, assets, Slot.smallWhite, line, LINE(150 + 40 * i));\n });\n drawSlot(renderer, assets, Slot.smallWhite, 'Click to return...', 5, 580);\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { Assets } from '../assets/AssetLoader';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport type { Scene, SceneContext } from './Scene';\nimport { contains, rect } from './ui';\n\n/** Button hot-spots painted into shooterland.png (`FUN_004025a3`). */\nconst START = rect(43, 459, 97, 111);\nconst QUIT = rect(204, 459, 97, 111);\nconst BETA = { x: 450, y: 110 };\n\nexport class TitleScene implements Scene {\n private hoverX = -1;\n private hoverY = -1;\n\n constructor(\n private readonly assets: Assets,\n private readonly actions: { onStart: () => void; onQuit: () => void },\n ) {}\n\n enter(_ctx: SceneContext): void {}\n\n update(_dt: number, input: readonly InputEvent[]): void {\n for (const e of input) {\n if (e.type === 'move') {\n this.hoverX = e.x;\n this.hoverY = e.y;\n continue;\n }\n if (e.type === 'key' && (e.key === 'Enter' || e.key === ' ')) {\n this.actions.onStart();\n return;\n }\n if (e.type !== 'down' || e.button !== 0) continue;\n if (contains(START, e.x, e.y)) {\n this.actions.onStart();\n return;\n }\n if (contains(QUIT, e.x, e.y)) {\n this.actions.onQuit();\n return;\n }\n }\n }\n\n draw(renderer: Renderer): void {\n renderer.begin();\n renderer.drawImage(this.assets.image('shooterland'), 0, 0);\n renderer.drawImage(this.assets.image('beta'), BETA.x, BETA.y);\n for (const r of [START, QUIT]) {\n if (contains(r, this.hoverX, this.hoverY))\n renderer.drawRect(r.x, r.y, r.w, r.h, 1, 1, 1, 0.12);\n }\n renderer.end();\n }\n\n leave(): void {}\n}\n","import type { Assets, WorldAssets } from '../assets/AssetLoader';\nimport { GRID_SIZE } from '../core/constants';\nimport type { LevelData } from '../core/level/LevelData';\nimport { bestTime, hasStar, isCleared, starsForNextWorld } from '../core/save/SaveData';\nimport type { SaveService } from '../core/save/SaveService';\nimport { TileId } from '../core/types';\nimport type { InputEvent } from '../input/PointerInput';\nimport type { Renderer } from '../render/Renderer';\nimport {\n bottomShooterRect,\n CHECK_FRAMES,\n checkFrame,\n MINI_PX,\n miniRect,\n starFrame,\n} from '../render/sprites';\nimport { drawSlot, Slot } from './fonts';\nimport type { Scene, SceneContext } from './Scene';\nimport { contains, type Rect, rect } from './ui';\n\n/** Hot-spots painted into worldN/map.png, as the exe registers them (`FUN_004025a3`). */\nconst TAB = (i: number): Rect => rect(116 + 82 * i, 465, 80, 15);\nconst MENU = rect(89, 488, 44, 44);\nconst STATS = rect(141, 488, 44, 44);\nconst PREVIEW = rect(617, 488, 44, 44);\nconst PLAY = rect(669, 488, 44, 44);\nconst NODE_W = 60;\nconst NODE_H = 40;\n/** The preview overlay: wood panel and mini tiles (`FUN_00404660`). */\nconst PREVIEW_PANEL = { x: 200, y: 100 };\nconst PREVIEW_GRID = { x: 265, y: 165, step: 21 };\n/** The check mark and star cycle through their 36 frames every 36 ms. */\nconst CHECK_TICKS_PER_FRAME = 2;\n\nexport interface WorldMapActions {\n onPlay: (level: LevelData) => void;\n onMenu: () => void;\n onStats: () => void;\n}\n\n/**\n * The spiral of 25 gems per world with tabs for worlds 1-7. Cleared levels\n * get a check mark, starred ones a spinning star; hovering Preview shows the\n * level's layout in mini tiles; Play opens the level intro.\n */\nexport class WorldMapScene implements Scene {\n private world: number;\n private selected: number;\n private worldAssets: WorldAssets | null = null;\n private ticks = 0;\n private toast: { text: string; until: number } | null = null;\n private hoverX = -1;\n private hoverY = -1;\n\n constructor(\n private readonly assets: Assets,\n private readonly saves: SaveService,\n private readonly starThresholds: readonly number[],\n private readonly actions: WorldMapActions,\n start?: { world: number; level: number },\n ) {\n this.world = start?.world ?? saves.data.world;\n this.selected = start?.level ?? saves.data.level;\n if (!saves.worldUnlocked(this.world)) {\n this.world = 1;\n this.selected = 1;\n }\n }\n\n enter(_ctx: SceneContext): void {\n this.loadWorld();\n }\n\n private loadWorld(): void {\n const w = this.world;\n this.worldAssets = null;\n void this.assets.world(w).then((wa) => {\n if (this.world === w) this.worldAssets = wa;\n });\n }\n\n private levelAt(index: number): LevelData | undefined {\n return this.assets.level(this.world, index + 1);\n }\n\n private say(text: string): void {\n this.toast = { text, until: this.ticks + 120 };\n }\n\n update(_dt: number, input: readonly InputEvent[]): void {\n this.ticks++;\n for (const e of input) {\n if (e.type === 'move') {\n this.hoverX = e.x;\n this.hoverY = e.y;\n continue;\n }\n if (e.type === 'key') {\n if (e.key === 'Enter' || e.key === ' ') this.play();\n else if (e.key === 'Escape') this.actions.onMenu();\n continue;\n }\n if (e.type !== 'down' || e.button !== 0) continue;\n for (let i = 0; i < 7; i++) {\n if (contains(TAB(i), e.x, e.y)) this.selectWorld(i + 1);\n }\n this.assets.mapNodes.forEach((n, i) => {\n if (contains(rect(n.x, n.y, NODE_W, NODE_H), e.x, e.y) && this.levelAt(i)) {\n this.selected = i + 1;\n void this.saves.setPosition(this.world, this.selected);\n }\n });\n if (contains(MENU, e.x, e.y)) this.actions.onMenu();\n else if (contains(STATS, e.x, e.y)) this.actions.onStats();\n else if (contains(PLAY, e.x, e.y)) this.play();\n }\n }\n\n private selectWorld(w: number): void {\n if (w === this.world) return;\n if (!this.saves.worldUnlocked(w)) {\n const next = starsForNextWorld(this.saves.data, this.assets.levels, this.starThresholds);\n this.say(\n next && next.world === w\n ? `World ${w} needs ${next.needed} stars`\n : `World ${w} is not available yet`,\n );\n return;\n }\n this.world = w;\n this.selected = 1;\n void this.saves.setPosition(w, 1);\n this.loadWorld();\n }\n\n private play(): void {\n const level = this.levelAt(this.selected - 1);\n if (!level) return;\n if (!this.saves.levelUnlocked(level.world, level.level)) {\n this.say('Level not unlocked yet.');\n return;\n }\n this.actions.onPlay(level);\n }\n\n draw(renderer: Renderer): void {\n const { assets, saves } = this;\n renderer.begin(0.2, 0.17, 0.12);\n const wa = this.worldAssets;\n if (!wa) {\n drawSlot(renderer, assets, Slot.white, 'Loading...', 400, 280, 'center');\n renderer.end();\n return;\n }\n renderer.drawImage(wa.map, 0, 0);\n\n const checks = assets.image('checkandstar');\n const frame = Math.floor(this.ticks / CHECK_TICKS_PER_FRAME) % CHECK_FRAMES;\n const nodes = assets.mapNodes;\n nodes.forEach((n, i) => {\n const level = this.levelAt(i);\n if (!level) {\n renderer.drawRect(n.x, n.y, NODE_W, NODE_H, 0, 0, 0, 0.6);\n return;\n }\n if (!saves.levelUnlocked(level.world, level.level)) {\n renderer.drawRect(n.x, n.y, NODE_W, NODE_H, 0, 0, 0, 0.5);\n }\n if (isCleared(saves.data, level.world, level.level)) {\n const c = checkFrame(frame);\n renderer.drawSprite(checks, c.x, c.y, c.w, c.h, n.x + 2, n.y - 4);\n if (hasStar(saves.data, level)) {\n const s = starFrame(frame);\n renderer.drawSprite(checks, s.x, s.y, s.w, s.h, n.x - 8, n.y - 10);\n }\n }\n drawSlot(renderer, assets, Slot.smallWhite, `${i + 1}`, n.x + 36, n.y + 19);\n });\n\n // The player's marker is Mary's bar, parked on the selected gem.\n const marker = nodes[this.selected - 1];\n if (marker) {\n const m = bottomShooterRect(1);\n renderer.drawSprite(assets.image('shooters'), m.x, m.y, m.w, m.h, marker.x + 10, marker.y);\n }\n\n for (let i = 0; i < 7; i++) {\n if (!saves.worldUnlocked(i + 1)) {\n const r = TAB(i);\n renderer.drawRect(r.x, r.y, r.w, r.h, 0, 0, 0, 0.45);\n }\n }\n\n const star = starFrame(frame);\n renderer.drawSprite(checks, star.x, star.y, star.w, star.h, -12, 5);\n drawSlot(renderer, assets, Slot.white, `X ${saves.stars()}`, 35, 10);\n const next = starsForNextWorld(saves.data, assets.levels, this.starThresholds);\n if (next) drawSlot(renderer, assets, Slot.smallWhite, `Next: ${next.needed} stars`, 0, 40);\n\n const level = this.levelAt(this.selected - 1);\n if (level) {\n drawSlot(renderer, assets, Slot.bigWhite, `Level ${level.world}-${level.level}`, 210, 491);\n const best = bestTime(saves.data, level.world, level.level);\n drawSlot(renderer, assets, Slot.smallBlack, `Best time: ${best}`, 360, 500);\n drawSlot(renderer, assets, Slot.smallWhite, `Time to beat: ${level.timeToBeat}`, 488, 500);\n }\n\n if (this.toast && this.ticks < this.toast.until) {\n renderer.drawRect(200, 250, 400, 40, 0, 0, 0, 0.75);\n drawSlot(renderer, assets, Slot.smallWhite, this.toast.text, 400, 262, 'center');\n }\n for (const r of [MENU, STATS, PREVIEW, PLAY]) {\n if (contains(r, this.hoverX, this.hoverY))\n renderer.drawRect(r.x, r.y, r.w, r.h, 1, 1, 1, 0.15);\n }\n if (level && contains(PREVIEW, this.hoverX, this.hoverY)) this.drawPreview(renderer, level);\n renderer.end();\n }\n\n /** Hovering Preview shows the board in mini tiles on the wood panel. */\n private drawPreview(renderer: Renderer, level: LevelData): void {\n const { assets } = this;\n renderer.drawImage(assets.image('wood'), PREVIEW_PANEL.x, PREVIEW_PANEL.y);\n const mini = assets.image('mini');\n for (let row = 0; row < GRID_SIZE; row++) {\n for (let col = 0; col < GRID_SIZE; col++) {\n const x = PREVIEW_GRID.x + col * PREVIEW_GRID.step;\n const y = PREVIEW_GRID.y + row * PREVIEW_GRID.step;\n renderer.drawRect(x, y, MINI_PX, MINI_PX, 0, 0, 0);\n const id = level.cells[row * GRID_SIZE + col] ?? TileId.Empty;\n const r = miniRect(id, level.world);\n if (r) renderer.drawSprite(mini, r.x, r.y, r.w, r.h, x, y);\n }\n }\n }\n\n leave(): void {}\n}\n","import type { Assets } from '../assets/AssetLoader';\nimport type { GameAudio } from '../audio/AudioEngine';\nimport type { LevelData } from '../core/level/LevelData';\nimport type { Rules } from '../core/rules';\nimport { bestTime, lastLevelOfWorld, type SlotNumber, worldExists } from '../core/save/SaveData';\nimport type { SaveService } from '../core/save/SaveService';\nimport type { SessionEvent } from '../core/sim/GameSession';\nimport { GameplayScene } from '../scenes/GameplayScene';\nimport { IntroScene } from '../scenes/IntroScene';\nimport { LogoScene } from '../scenes/LogoScene';\nimport { ResultScene } from '../scenes/ResultScene';\nimport { SaveSelectScene } from '../scenes/SaveSelectScene';\nimport type { SceneManager } from '../scenes/Scene';\nimport { StatsScene } from '../scenes/StatsScene';\nimport { TitleScene } from '../scenes/TitleScene';\nimport { WorldMapScene } from '../scenes/WorldMapScene';\n\nexport type FlowEvent =\n | { readonly type: 'levelStart'; readonly world: number; readonly level: number }\n | {\n readonly type: 'levelComplete';\n readonly world: number;\n readonly level: number;\n readonly seconds: number;\n readonly star: boolean;\n }\n | { readonly type: 'defeat'; readonly world: number; readonly level: number }\n | { readonly type: 'session'; readonly events: readonly SessionEvent[] }\n | { readonly type: 'save'; readonly slot: SlotNumber }\n | { readonly type: 'quit' };\n\nexport interface FlowOptions {\n readonly rules: Rules;\n /** Seed for the next level; called per level start so replays can pin it. */\n readonly nextSeed: () => number;\n readonly emit: (event: FlowEvent) => void;\n readonly isDestroyed: () => boolean;\n}\n\n/** Leaving a level after this many seconds counts as a loss (`FUN_00402dc6`). */\nconst QUIT_LOSS_SECONDS = 5;\n\n/**\n * The screen graph, as the exe's state machine has it: logo -> title -> files\n * -> world map -> level intro -> gameplay -> result -> world map, plus stats\n * and the retry / quit prompts.\n */\nexport class GameFlow {\n gameplay: GameplayScene | null = null;\n\n constructor(\n private readonly assets: Assets,\n private readonly scenes: SceneManager,\n private readonly saves: SaveService,\n private readonly audio: GameAudio,\n private readonly opts: FlowOptions,\n ) {}\n\n logo(): void {\n this.audio.music('titlescreen');\n this.scenes.go(new LogoScene(this.assets, () => this.title()));\n }\n\n title(): void {\n this.gameplay = null;\n this.audio.music('titlescreen');\n this.scenes.go(\n new TitleScene(this.assets, {\n onStart: () => this.saveSelect(),\n onQuit: () => {\n this.opts.emit({ type: 'quit' });\n this.logo();\n },\n }),\n );\n }\n\n saveSelect(): void {\n this.scenes.go(\n new SaveSelectScene(this.assets, this.saves, {\n onLoad: (slot) => {\n void this.saves.select(slot).then(() => {\n if (!this.opts.isDestroyed()) this.worldMap();\n });\n },\n onBack: () => this.title(),\n }),\n );\n }\n\n worldMap(at?: { world: number; level: number }): void {\n this.gameplay = null;\n this.audio.music('maptheme');\n this.scenes.go(\n new WorldMapScene(\n this.assets,\n this.saves,\n this.opts.rules.starThresholds,\n {\n onPlay: (level) => void this.play(level),\n onMenu: () => this.title(),\n onStats: () => this.stats(),\n },\n at,\n ),\n );\n }\n\n stats(): void {\n this.scenes.go(new StatsScene(this.assets, this.saves, () => this.worldMap()));\n }\n\n /**\n * Loads a level: the intro screen shows the board standing still, and Play\n * starts the clock. `skipIntro` jumps straight in (deep links, retries).\n */\n async play(level: LevelData, seed = this.opts.nextSeed(), skipIntro = false): Promise<void> {\n const world = await this.assets.world(level.world);\n if (this.opts.isDestroyed()) return;\n const previousBest = bestTime(this.saves.data, level.world, level.level);\n this.audio.music(`world${Math.min(7, Math.max(1, level.world))}`);\n const leave = (scene: GameplayScene): void => {\n // Leaving a level after a few seconds of play counts as a loss, as in the exe.\n if (scene.session.seconds > QUIT_LOSS_SECONDS) {\n void this.saves.finishLevel(level, 'lost', {\n seconds: scene.session.seconds,\n shots: 0,\n combos: 0,\n comboPieces: 0,\n });\n this.opts.emit({ type: 'save', slot: this.saves.slot });\n }\n };\n const scene = new GameplayScene({\n assets: this.assets,\n world,\n level,\n seed,\n rules: this.opts.rules,\n audio: this.audio,\n onEvents: (events) => this.opts.emit({ type: 'session', events }),\n onQuit: () => {\n leave(scene);\n this.worldMap({ world: level.world, level: level.level });\n },\n onRetry: () => {\n leave(scene);\n void this.play(level, this.opts.nextSeed(), true);\n },\n onFinish: ({ outcome, session }) => {\n this.gameplay = null;\n void this.saves.finishLevel(level, outcome, {\n seconds: session.seconds,\n shots: session.stats.shots,\n combos: session.stats.combos,\n comboPieces: session.stats.comboPieces,\n });\n this.opts.emit({ type: 'save', slot: this.saves.slot });\n if (outcome === 'won') {\n this.opts.emit({\n type: 'levelComplete',\n world: level.world,\n level: level.level,\n seconds: session.seconds,\n star: session.elapsedSeconds <= level.timeToBeat,\n });\n } else {\n this.opts.emit({ type: 'defeat', world: level.world, level: level.level });\n }\n this.scenes.go(\n new ResultScene({\n assets: this.assets,\n world,\n session,\n outcome,\n previousBest,\n record: {\n wins: this.saves.data.wins,\n losses: this.saves.data.losses,\n slot: this.saves.slot,\n },\n finalLevel: this.isFinalLevel(level),\n onContinue: (action) => {\n if (action === 'retry') void this.play(level, this.opts.nextSeed(), true);\n else if (action === 'next') this.worldMap(this.nextPosition(level));\n else this.worldMap({ world: level.world, level: level.level });\n },\n }),\n );\n },\n });\n this.gameplay = scene;\n if (skipIntro) {\n this.scenes.go(scene);\n } else {\n this.scenes.go(\n new IntroScene(this.assets, world, scene, {\n onPlay: () => this.scenes.go(scene),\n onBack: () => this.worldMap({ world: level.world, level: level.level }),\n }),\n );\n }\n void this.saves.setPosition(level.world, level.level);\n this.opts.emit({ type: 'levelStart', world: level.world, level: level.level });\n }\n\n private isFinalLevel(level: LevelData): boolean {\n const levels = this.assets.levels;\n return (\n level.level === lastLevelOfWorld(levels, level.world) && !worldExists(levels, level.world + 1)\n );\n }\n\n /** After a win the map selects the following level, rolling into the next world. */\n private nextPosition(level: LevelData): { world: number; level: number } {\n const levels = this.assets.levels;\n if (level.level < lastLevelOfWorld(levels, level.world)) {\n return { world: level.world, level: level.level + 1 };\n }\n if (worldExists(levels, level.world + 1) && this.saves.worldUnlocked(level.world + 1)) {\n return { world: level.world + 1, level: 1 };\n }\n return { world: level.world, level: level.level };\n }\n}\n","import { AssetLoader, type Assets } from '../assets/AssetLoader';\nimport { type GameAudio, NullAudio, WebAudioEngine } from '../audio/AudioEngine';\nimport { TICK_SECONDS, VIRTUAL_HEIGHT, VIRTUAL_WIDTH } from '../core/constants';\nimport { DEFAULT_RULES, type Rules } from '../core/rules';\nimport { SaveService } from '../core/save/SaveService';\nimport { localStorageSaveStorage, type SaveStorage } from '../core/save/SaveStorage';\nimport type { SessionState } from '../core/sim/GameSession';\nimport { PointerInput } from '../input/PointerInput';\nimport { createGLContext } from '../render/gl';\nimport { Renderer } from '../render/Renderer';\nimport { Viewport } from '../render/Viewport';\nimport { AssetViewerScene } from '../scenes/AssetViewerScene';\nimport { BootScene } from '../scenes/BootScene';\nimport { LoadingScene } from '../scenes/LoadingScene';\nimport { SceneManager } from '../scenes/Scene';\nimport { type FlowEvent, GameFlow } from './GameFlow';\n\nexport type GameEvent =\n | { readonly type: 'ready' }\n | { readonly type: 'error'; readonly error: Error }\n | { readonly type: 'paused' }\n | { readonly type: 'resumed' }\n | FlowEvent;\n\n/**\n * Which screen to show once assets are loaded. The game normally starts at\n * the studio logo; the others exist for development and tests. 'boot' skips\n * asset loading entirely and shows the plumbing-test scene.\n */\nexport type StartScene =\n | 'logo'\n | 'title'\n | 'worldMap'\n | 'assetViewer'\n | 'boot'\n | {\n readonly kind: 'gameplay';\n readonly world: number;\n readonly level: number;\n readonly seed?: number | undefined;\n };\n\nexport interface GameOptions {\n /** Base URL (with trailing slash) where the package's `assets/` directory is served. */\n assetBaseUrl?: string | undefined;\n startScene?: StartScene | undefined;\n /** Where save files live. Defaults to browser localStorage. */\n storage?: SaveStorage | undefined;\n /** Gameplay constants; defaults to the reconstructed rules. */\n rules?: Rules | undefined;\n /** 'webaudio' (default) plays sound effects and synthesised MIDI music; 'none' is silent. */\n audio?: 'webaudio' | 'none' | undefined;\n /** Master volume 0..1. Default 1. */\n volume?: number | undefined;\n /** 'sprite' (default) draws the original arrow and hides the system cursor. */\n cursor?: 'sprite' | 'system' | undefined;\n /** Resize the canvas backing store to its CSS box automatically. Default true. */\n autoResize?: boolean | undefined;\n /** Upper bound on devicePixelRatio used for the backing store. Default 2. */\n maxPixelRatio?: number | undefined;\n muted?: boolean | undefined;\n /** Seed for the simulation RNG; each level advances it. Default: time-based. */\n seed?: number | undefined;\n onEvent?: ((event: GameEvent) => void) | undefined;\n /** Aborting cancels asset loading and destroys the game. */\n signal?: AbortSignal | undefined;\n}\n\nexport interface GameDebug {\n /** The live simulation state of the current level, if a level is being played. */\n session(): SessionState | null;\n /** Class name of the active scene. */\n scene(): string;\n}\n\nexport interface GameHandle {\n readonly version: string;\n /** Resolves once assets are loaded and the first scene is running; rejects if loading fails. */\n readonly ready: Promise<void>;\n pause(): void;\n resume(): void;\n readonly paused: boolean;\n setMuted(muted: boolean): void;\n setVolume(volume: number): void;\n /** Sets the canvas backing-store size in device pixels. Only needed when `autoResize` is false. */\n resize(width: number, height: number): void;\n destroy(): void;\n readonly debug: GameDebug;\n}\n\nconst MAX_CATCHUP_STEPS = 5;\n\nexport const PACKAGE_VERSION: string =\n typeof __SHOOTERLAND_VERSION__ === 'string' ? __SHOOTERLAND_VERSION__ : 'dev';\n\n/** Where the published package's assets live when no `assetBaseUrl` is given. */\nexport function defaultAssetBaseUrl(): string {\n return `https://cdn.jsdelivr.net/npm/shooterland@${PACKAGE_VERSION}/assets/`;\n}\n\n/**\n * Boots the game on an existing canvas. Owns the GL context, the fixed-step\n * loop, input listeners, assets and the scene stack until `destroy()` is called.\n */\nexport function createGame(canvas: HTMLCanvasElement, options: GameOptions = {}): GameHandle {\n const emit = (event: GameEvent): void => options.onEvent?.(event);\n const rules = options.rules ?? DEFAULT_RULES;\n\n const viewport = new Viewport();\n const gl = createGLContext(canvas);\n const renderer = new Renderer(gl, viewport);\n const input = new PointerInput(canvas, viewport);\n const scenes = new SceneManager();\n const abort = new AbortController();\n let assets: Assets | null = null;\n let flow: GameFlow | null = null;\n let audio: GameAudio = new NullAudio();\n // Autoplay policies require the AudioContext to be created inside a gesture.\n const unlockAudio = (): void => audio.unlock();\n canvas.addEventListener('pointerdown', unlockAudio, { signal: abort.signal });\n canvas.addEventListener('keydown', unlockAudio, { signal: abort.signal });\n\n let destroyed = false;\n let paused = false;\n let rafId = 0;\n let last = 0;\n let accumulator = 0;\n let seed = (options.seed ?? Date.now() & 0x7fffffff) >>> 0;\n\n const resize = (width: number, height: number): void => {\n const w = Math.max(1, Math.round(width));\n const h = Math.max(1, Math.round(height));\n if (canvas.width !== w) canvas.width = w;\n if (canvas.height !== h) canvas.height = h;\n viewport.resize(w, h);\n };\n\n let observer: ResizeObserver | null = null;\n if (options.autoResize !== false && typeof ResizeObserver !== 'undefined') {\n const maxDpr = options.maxPixelRatio ?? 2;\n const fitCanvas = (): void => {\n const dpr = Math.min(maxDpr, globalThis.devicePixelRatio || 1);\n const r = canvas.getBoundingClientRect();\n resize(r.width * dpr, r.height * dpr);\n };\n observer = new ResizeObserver(fitCanvas);\n observer.observe(canvas);\n fitCanvas();\n } else {\n resize(canvas.width, canvas.height);\n }\n\n const frame = (now: number): void => {\n if (destroyed) return;\n rafId = requestAnimationFrame(frame);\n if (last === 0) last = now;\n let elapsed = (now - last) / 1000;\n last = now;\n if (elapsed > MAX_CATCHUP_STEPS * TICK_SECONDS) elapsed = MAX_CATCHUP_STEPS * TICK_SECONDS;\n if (paused) {\n input.drain();\n scenes.draw(renderer);\n drawCursor();\n return;\n }\n accumulator += elapsed;\n while (accumulator >= TICK_SECONDS) {\n scenes.update(TICK_SECONDS, input.drain());\n accumulator -= TICK_SECONDS;\n }\n scenes.draw(renderer);\n drawCursor();\n };\n\n // The original drew its own arrow (mouse.png) and hid the system cursor.\n const drawCursor = (): void => {\n const a = assets;\n if (!a || options.cursor === 'system') return;\n const { x, y } = input;\n if (x < 0 || y < 0 || x >= VIRTUAL_WIDTH || y >= VIRTUAL_HEIGHT) return;\n renderer.drawOverlay((r) => r.drawImage(a.image('mouse'), Math.round(x), Math.round(y)));\n };\n if (options.cursor !== 'system') canvas.style.cursor = 'none';\n\n const startScene = options.startScene ?? 'logo';\n let ready: Promise<void>;\n if (startScene === 'boot') {\n scenes.go(new BootScene());\n ready = Promise.resolve();\n } else {\n const loading = new LoadingScene();\n scenes.go(loading);\n const baseUrl = options.assetBaseUrl ?? defaultAssetBaseUrl();\n const loader = new AssetLoader(gl, baseUrl, abort.signal);\n ready = loader\n .loadAll((p) => loading.setProgress(p.loaded, p.total))\n .then(async (loaded) => {\n if (destroyed) {\n loaded.destroy();\n return;\n }\n assets = loaded;\n if (options.audio !== 'none') {\n audio = new WebAudioEngine(loaded.manifest, (p) => loader.resolve(p), abort.signal, {\n muted: options.muted,\n });\n if (options.volume !== undefined) audio.setVolume(options.volume);\n }\n if (startScene === 'assetViewer') {\n scenes.go(new AssetViewerScene(loaded));\n return;\n }\n const saves = new SaveService(\n options.storage ?? localStorageSaveStorage(),\n loaded.levels,\n rules.starThresholds,\n );\n flow = new GameFlow(loaded, scenes, saves, audio, {\n rules,\n nextSeed: () => {\n seed = (seed + 0x9e3779b9) >>> 0;\n return seed;\n },\n emit,\n isDestroyed: () => destroyed,\n });\n if (startScene === 'logo') flow.logo();\n else if (startScene === 'title') flow.title();\n else if (startScene === 'worldMap') {\n await saves.select(1);\n flow.worldMap();\n } else {\n const level = loaded.level(startScene.world, startScene.level);\n if (!level) throw new Error(`no level ${startScene.world}-${startScene.level}`);\n await saves.select(1);\n await flow.play(level, startScene.seed ?? seed, true);\n }\n })\n .catch((error: unknown) => {\n if (destroyed) return;\n loading.setFailed();\n const err = error instanceof Error ? error : new Error(String(error));\n emit({ type: 'error', error: err });\n throw err;\n });\n }\n ready.then(\n () => {\n if (!destroyed) emit({ type: 'ready' });\n },\n () => {},\n );\n rafId = requestAnimationFrame(frame);\n\n const destroy = (): void => {\n if (destroyed) return;\n destroyed = true;\n abort.abort();\n cancelAnimationFrame(rafId);\n observer?.disconnect();\n input.destroy();\n scenes.destroy();\n audio.destroy();\n assets?.destroy();\n renderer.destroy();\n // The GL context is intentionally left alive: a host (React StrictMode, prop changes)\n // may create a new game on the same canvas, and a lost context cannot be re-acquired.\n };\n\n options.signal?.addEventListener('abort', destroy, { once: true });\n\n return {\n version: PACKAGE_VERSION,\n ready,\n get paused() {\n return paused;\n },\n pause() {\n if (paused || destroyed) return;\n paused = true;\n audio.suspend();\n emit({ type: 'paused' });\n },\n resume() {\n if (!paused || destroyed) return;\n paused = false;\n last = 0;\n accumulator = 0;\n audio.resume();\n emit({ type: 'resumed' });\n },\n setMuted(muted: boolean) {\n audio.setMuted(muted);\n },\n setVolume(volume: number) {\n audio.setVolume(volume);\n },\n resize,\n destroy,\n debug: {\n session: () => flow?.gameplay?.session ?? null,\n scene: () => scenes.currentName,\n },\n };\n}\n"],"mappings":"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"}
@@ -831,9 +831,8 @@ var Renderer = class {
831
831
  //#region src/render/Viewport.ts
832
832
  /**
833
833
  * Maps the fixed 800x600 virtual space onto a canvas of any size: the largest
834
- * 4:3 rectangle that fits, centred (letterboxed). Scale is a whole number of
835
- * device pixels per virtual pixel whenever the canvas is large enough, so
836
- * sprites stay crisp; below 1x it falls back to fractional downscaling.
834
+ * 4:3 rectangle that fits, centred (letterboxed). The scale is fractional in
835
+ * both directions so the game always fills the canvas it is given.
837
836
  */
838
837
  var Viewport = class {
839
838
  canvasWidth = 800;
@@ -848,8 +847,7 @@ var Viewport = class {
848
847
  resize(canvasWidth, canvasHeight) {
849
848
  this.canvasWidth = Math.max(1, Math.floor(canvasWidth));
850
849
  this.canvasHeight = Math.max(1, Math.floor(canvasHeight));
851
- const raw = Math.min(this.canvasWidth / 800, this.canvasHeight / 600);
852
- this.scale = raw >= 1 ? Math.floor(raw) : raw;
850
+ this.scale = Math.min(this.canvasWidth / 800, this.canvasHeight / 600);
853
851
  const w = Math.round(800 * this.scale);
854
852
  const h = Math.round(600 * this.scale);
855
853
  this.rect.x = Math.floor((this.canvasWidth - w) / 2);
@@ -2955,7 +2953,7 @@ var GameFlow = class {
2955
2953
  //#endregion
2956
2954
  //#region src/engine/createGame.ts
2957
2955
  const MAX_CATCHUP_STEPS = 5;
2958
- const PACKAGE_VERSION = "0.1.0";
2956
+ const PACKAGE_VERSION = "1.0.1";
2959
2957
  /** Where the published package's assets live when no `assetBaseUrl` is given. */
2960
2958
  function defaultAssetBaseUrl() {
2961
2959
  return `https://cdn.jsdelivr.net/npm/shooterland@${PACKAGE_VERSION}/assets/`;
@@ -3159,4 +3157,4 @@ Object.defineProperty(exports, "defaultAssetBaseUrl", {
3159
3157
  }
3160
3158
  });
3161
3159
 
3162
- //# sourceMappingURL=engine-BJys97Wn.cjs.map
3160
+ //# sourceMappingURL=engine-_WZUQwmB.cjs.map