@al8b/screen 0.1.12 → 0.1.14

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.
@@ -24,7 +24,6 @@ __export(base_screen_exports, {
24
24
  BaseScreen: () => BaseScreen
25
25
  });
26
26
  module.exports = __toCommonJS(base_screen_exports);
27
- var import_diagnostics = require("@al8b/diagnostics");
28
27
 
29
28
  // src/tri/ttri.ts
30
29
  var ZBuffer = class {
@@ -167,10 +166,13 @@ var BaseScreen = class {
167
166
  alpha: false
168
167
  });
169
168
  if (!ctx) {
170
- const diagnostic = (0, import_diagnostics.createDiagnostic)(import_diagnostics.APIErrorCode.E7001);
171
- const formatted = (0, import_diagnostics.formatForBrowser)(diagnostic);
172
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7001, {});
173
- throw new Error(formatted);
169
+ const message = "Failed to get 2D rendering context";
170
+ this.runtime?.listener?.reportError?.({
171
+ code: "E7001",
172
+ message,
173
+ data: {}
174
+ });
175
+ throw new Error(message);
174
176
  }
175
177
  if (ctx !== this.context) {
176
178
  this.context = ctx;
@@ -223,8 +225,12 @@ var BaseScreen = class {
223
225
  } else if (typeof color === "string") {
224
226
  const isValidColor = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) || /^rgb\(|^rgba\(|^hsl\(|^hsla\(/.test(color) || /^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);
225
227
  if (!isValidColor) {
226
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7003, {
227
- color
228
+ this.runtime?.listener?.reportError?.({
229
+ code: "E7003",
230
+ message: "Invalid color",
231
+ data: {
232
+ color
233
+ }
228
234
  });
229
235
  return;
230
236
  }
@@ -241,8 +247,12 @@ var BaseScreen = class {
241
247
  setBlending(blending) {
242
248
  const blend = this.blending[blending || "normal"];
243
249
  if (!blend) {
244
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7007, {
245
- blendMode: blending
250
+ this.runtime?.listener?.reportError?.({
251
+ code: "E7007",
252
+ message: "Invalid blend mode",
253
+ data: {
254
+ blendMode: blending
255
+ }
246
256
  });
247
257
  this.context.globalCompositeOperation = "source-over";
248
258
  return;
@@ -284,13 +294,21 @@ var BaseScreen = class {
284
294
  this.font_load_requested[font] = true;
285
295
  try {
286
296
  document.fonts?.load?.(`16pt ${font}`).catch(() => {
287
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7006, {
288
- font
297
+ this.runtime?.listener?.reportError?.({
298
+ code: "E7006",
299
+ message: "Font loading failed",
300
+ data: {
301
+ font
302
+ }
289
303
  });
290
304
  });
291
305
  } catch {
292
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7006, {
293
- font
306
+ this.runtime?.listener?.reportError?.({
307
+ code: "E7006",
308
+ message: "Font loading failed",
309
+ data: {
310
+ font
311
+ }
294
312
  });
295
313
  }
296
314
  }
@@ -398,9 +416,13 @@ var BaseScreen = class {
398
416
  resize(width, height) {
399
417
  if (width && height) {
400
418
  if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
401
- (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7002, {
402
- width,
403
- height
419
+ this.runtime?.listener?.reportError?.({
420
+ code: "E7002",
421
+ message: "Invalid resize dimensions",
422
+ data: {
423
+ width,
424
+ height
425
+ }
404
426
  });
405
427
  return;
406
428
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/base-screen.ts","../../src/tri/ttri.ts"],"sourcesContent":["import { APIErrorCode, createDiagnostic, formatForBrowser, reportRuntimeError } from \"@al8b/diagnostics\";\nimport { ZBuffer } from \"../tri\";\nimport type { ScreenInterface, ScreenOptions } from \"../types\";\n\n/**\n * BaseScreen encapsulates canvas/context management plus shared drawing state.\n * Feature-specific behaviors (primitives, sprites, text, triangles) extend this class.\n */\nexport class BaseScreen {\n\tprotected canvas: HTMLCanvasElement;\n\tprotected context!: CanvasRenderingContext2D;\n\tprotected runtime: any;\n\n\tpublic width!: number;\n\tpublic height!: number;\n\n\t// Drawing state\n\tprotected alpha = 1;\n\tprotected pixelated = 1;\n\tprotected line_width = 1;\n\tprotected font = \"BitCell\";\n\n\t// Transformations\n\tprotected translation_x = 0;\n\tprotected translation_y = 0;\n\tprotected rotation = 0;\n\tprotected scale_x = 1;\n\tprotected scale_y = 1;\n\tprotected screen_transform = false;\n\n\t// Object transformations\n\tprotected object_rotation = 0;\n\tprotected object_scale_x = 1;\n\tprotected object_scale_y = 1;\n\tprotected anchor_x = 0;\n\tprotected anchor_y = 0;\n\n\t// Blending + font caches\n\tprotected blending: Record<string, string> = {};\n\tprotected font_load_requested: Record<string, boolean> = {};\n\tprotected font_loaded: Record<string, boolean> = {};\n\n\t// Interface cache\n\tprotected interfaceCache: ScreenInterface | null = null;\n\n\t// Cursor management\n\tprotected cursor: string = \"default\";\n\tprotected cursor_visibility: string = \"auto\";\n\tprotected last_mouse_move: number = Date.now();\n\n\t// 3D helper\n\tprotected zBuffer: ZBuffer;\n\n\tconstructor(options: ScreenOptions = {}) {\n\t\tthis.runtime = options.runtime;\n\n\t\tif (options.canvas) {\n\t\t\tthis.canvas = options.canvas;\n\t\t\tif (this.canvas.width === 0 || this.canvas.height === 0) {\n\t\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\t\tthis.canvas.height = options.height || 1920;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.canvas = document.createElement(\"canvas\");\n\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\tthis.canvas.height = options.height || 1920;\n\t\t}\n\n\t\tthis.initContext();\n\n\t\tthis.blending = {\n\t\t\tnormal: \"source-over\",\n\t\t\tadditive: \"lighter\",\n\t\t};\n\n\t\tconst blendModes = [\n\t\t\t\"source-over\",\n\t\t\t\"source-in\",\n\t\t\t\"source-out\",\n\t\t\t\"source-atop\",\n\t\t\t\"destination-over\",\n\t\t\t\"destination-in\",\n\t\t\t\"destination-out\",\n\t\t\t\"destination-atop\",\n\t\t\t\"lighter\",\n\t\t\t\"copy\",\n\t\t\t\"xor\",\n\t\t\t\"multiply\",\n\t\t\t\"screen\",\n\t\t\t\"overlay\",\n\t\t\t\"darken\",\n\t\t\t\"lighten\",\n\t\t\t\"color-dodge\",\n\t\t\t\"color-burn\",\n\t\t\t\"hard-light\",\n\t\t\t\"soft-light\",\n\t\t\t\"difference\",\n\t\t\t\"exclusion\",\n\t\t\t\"hue\",\n\t\t\t\"saturation\",\n\t\t\t\"color\",\n\t\t\t\"luminosity\",\n\t\t];\n\n\t\tfor (const mode of blendModes) {\n\t\t\tthis.blending[mode] = mode;\n\t\t}\n\n\t\tthis.loadFont(this.font);\n\t\tthis.zBuffer = new ZBuffer(this.canvas.width, this.canvas.height);\n\n\t\tthis.cursor = \"default\";\n\n\t\tthis.canvas.addEventListener(\"mousemove\", () => {\n\t\t\tthis.last_mouse_move = Date.now();\n\t\t\tif (this.cursor !== \"default\" && this.cursor_visibility === \"auto\") {\n\t\t\t\tthis.cursor = \"default\";\n\t\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t\t}\n\t\t});\n\n\t\t// When the context is lost and then restored, base transform needs to be reinstated\n\t\tthis.canvas.addEventListener(\"contextrestored\", () => {\n\t\t\tthis.initContext();\n\t\t});\n\n\t\tsetInterval(() => this.checkMouseCursor(), 1000);\n\t\tthis.cursor_visibility = \"auto\";\n\t}\n\n\tprotected initContext(): void {\n\t\tconst ctx = this.canvas.getContext(\"2d\", {\n\t\t\talpha: false,\n\t\t});\n\t\tif (!ctx) {\n\t\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7001);\n\t\t\tconst formatted = formatForBrowser(diagnostic);\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7001, {});\n\n\t\t\tthrow new Error(formatted);\n\t\t}\n\n\t\tif (ctx !== this.context) {\n\t\t\tthis.context = ctx;\n\t\t} else {\n\t\t\tthis.context.restore();\n\t\t}\n\n\t\tthis.context.save();\n\t\tthis.context.translate(this.canvas.width / 2, this.canvas.height / 2);\n\n\t\t// Calculate ratio: Math.min(canvas.width/200, canvas.height/200)\n\t\tconst ratio = Math.min(this.canvas.width / 200, this.canvas.height / 200);\n\t\tthis.context.scale(ratio, ratio);\n\n\t\t// Set logical width/height\n\t\tthis.width = this.canvas.width / ratio;\n\t\tthis.height = this.canvas.height / ratio;\n\t\tthis.context.lineCap = \"round\";\n\t}\n\n\t/**\n\t * Initialize draw state (called before each draw frame)\n\t */\n\tinitDraw(): void {\n\t\tthis.alpha = 1;\n\t\tthis.line_width = 1;\n\t\t// Note: Supersampling not implemented in l8b\n\t\t// If needed, add: if (this.supersampling != this.previous_supersampling) { this.resize(); this.previous_supersampling = this.supersampling; }\n\t}\n\n\t/**\n\t * Update interface dimensions (called before each draw frame)\n\t */\n\tupdateInterface(): void {\n\t\t// Update interface cache if it exists\n\t\tif (this.interfaceCache) {\n\t\t\tthis.interfaceCache.width = this.width;\n\t\t\tthis.interfaceCache.height = this.height;\n\t\t}\n\t}\n\n\tclear(color?: string): void {\n\t\tthis.context.globalAlpha = 1;\n\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\tthis.context.fillStyle = color || \"#000\";\n\t\tthis.context.strokeStyle = color || \"#000\";\n\t\tthis.context.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);\n\t\tthis.zBuffer.clear();\n\t}\n\n\tsetColor(color: string | number): void {\n\t\tif (!color) return;\n\n\t\tif (!Number.isNaN(Number.parseInt(String(color)))) {\n\t\t\tconst num = Number.parseInt(String(color));\n\t\t\tconst r = ((Math.floor(num / 100) % 10) / 9) * 255;\n\t\t\tconst g = ((Math.floor(num / 10) % 10) / 9) * 255;\n\t\t\tconst b = ((num % 10) / 9) * 255;\n\t\t\tconst c = 0xff000000 + (r << 16) + (g << 8) + b;\n\t\t\tconst hex = \"#\" + c.toString(16).substring(2, 8);\n\t\t\tthis.context.fillStyle = hex;\n\t\t\tthis.context.strokeStyle = hex;\n\t\t} else if (typeof color === \"string\") {\n\t\t\t// Validate color format\n\t\t\tconst isValidColor =\n\t\t\t\t/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) ||\n\t\t\t\t/^rgb\\(|^rgba\\(|^hsl\\(|^hsla\\(/.test(color) ||\n\t\t\t\t/^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);\n\n\t\t\tif (!isValidColor) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7003, { color });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.context.fillStyle = color;\n\t\t\tthis.context.strokeStyle = color;\n\t\t}\n\t}\n\n\tsetAlpha(alpha: number): void {\n\t\tthis.alpha = alpha;\n\t}\n\n\tsetPixelated(pixelated: number): void {\n\t\tthis.pixelated = pixelated;\n\t}\n\n\tsetBlending(blending: string): void {\n\t\tconst blend = this.blending[blending || \"normal\"];\n\n\t\tif (!blend) {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7007, { blendMode: blending });\n\t\t\t// Fallback to normal blend mode\n\t\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\t\treturn;\n\t\t}\n\n\t\tthis.context.globalCompositeOperation = blend as GlobalCompositeOperation;\n\t}\n\n\tsetLineWidth(width: number): void {\n\t\tthis.line_width = width;\n\t}\n\n\tsetLineDash(dash: number[] | null): void {\n\t\tif (!Array.isArray(dash)) {\n\t\t\tthis.context.setLineDash([]);\n\t\t} else {\n\t\t\tthis.context.setLineDash(dash);\n\t\t}\n\t}\n\n\tsetLinearGradient(x1: number, y1: number, x2: number, y2: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createLinearGradient(x1, -y1, x2, -y2);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetRadialGradient(x: number, y: number, radius: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createRadialGradient(x, -y, 0, x, -y, radius);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetFont(font: string): void {\n\t\tthis.font = font || \"Verdana\";\n\t\tthis.loadFont(this.font);\n\t}\n\n\tloadFont(font: string = \"BitCell\"): void {\n\t\tif (this.font_load_requested[font]) {\n\t\t\treturn;\n\t\t}\n\t\tthis.font_load_requested[font] = true;\n\t\ttry {\n\t\t\tdocument.fonts?.load?.(`16pt ${font}`).catch(() => {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7006, { font });\n\t\t\t});\n\t\t} catch {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7006, { font });\n\t\t}\n\t}\n\n\tisFontReady(font: string = this.font): number {\n\t\tif (this.font_loaded[font]) {\n\t\t\treturn 1;\n\t\t}\n\n\t\ttry {\n\t\t\tconst ready = document.fonts?.check?.(`16pt ${font}`) ?? true;\n\t\t\tif (ready) {\n\t\t\t\tthis.font_loaded[font] = true;\n\t\t\t}\n\t\t\treturn ready ? 1 : 0;\n\t\t} catch {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tsetTranslation(tx: number, ty: number): void {\n\t\tthis.translation_x = isFinite(tx) ? tx : 0;\n\t\tthis.translation_y = isFinite(ty) ? ty : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetScale(x: number, y: number): void {\n\t\tthis.scale_x = isFinite(x) && x !== 0 ? x : 1;\n\t\tthis.scale_y = isFinite(y) && y !== 0 ? y : 1;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetRotation(rotation: number): void {\n\t\tthis.rotation = isFinite(rotation) ? rotation : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tprotected updateScreenTransform(): void {\n\t\tthis.screen_transform =\n\t\t\tthis.translation_x !== 0 ||\n\t\t\tthis.translation_y !== 0 ||\n\t\t\tthis.scale_x !== 1 ||\n\t\t\tthis.scale_y !== 1 ||\n\t\t\tthis.rotation !== 0;\n\t}\n\n\tsetDrawAnchor(ax: number, ay: number): void {\n\t\tthis.anchor_x = typeof ax === \"number\" ? ax : 0;\n\t\tthis.anchor_y = typeof ay === \"number\" ? ay : 0;\n\t}\n\n\tsetDrawRotation(rotation: number): void {\n\t\tthis.object_rotation = rotation;\n\t}\n\n\tsetDrawScale(x: number, y: number = x): void {\n\t\tthis.object_scale_x = x;\n\t\tthis.object_scale_y = y;\n\t}\n\n\tprotected initDrawOp(x: number, y: number, object_transform: boolean = true): boolean {\n\t\tlet res = false;\n\n\t\tif (this.screen_transform) {\n\t\t\tthis.context.save();\n\t\t\tres = true;\n\t\t\tthis.context.translate(this.translation_x, -this.translation_y);\n\t\t\tthis.context.scale(this.scale_x, this.scale_y);\n\t\t\tthis.context.rotate((-this.rotation / 180) * Math.PI);\n\t\t\tthis.context.translate(x, y);\n\t\t}\n\n\t\tif (object_transform && (this.object_rotation !== 0 || this.object_scale_x !== 1 || this.object_scale_y !== 1)) {\n\t\t\tif (!res) {\n\t\t\t\tthis.context.save();\n\t\t\t\tres = true;\n\t\t\t\tthis.context.translate(x, y);\n\t\t\t}\n\n\t\t\tif (this.object_rotation !== 0) {\n\t\t\t\tthis.context.rotate((-this.object_rotation / 180) * Math.PI);\n\t\t\t}\n\n\t\t\tif (this.object_scale_x !== 1 || this.object_scale_y !== 1) {\n\t\t\t\tthis.context.scale(this.object_scale_x, this.object_scale_y);\n\t\t\t}\n\t\t}\n\n\t\treturn res;\n\t}\n\n\tprotected closeDrawOp(): void {\n\t\tthis.context.restore();\n\t}\n\n\t/**\n\t * Check mouse cursor visibility\n\t * Auto-hides cursor after 4 seconds of inactivity\n\t */\n\tprotected checkMouseCursor(): void {\n\t\tif (Date.now() > this.last_mouse_move + 4000 && this.cursor_visibility === \"auto\") {\n\t\t\tif (this.cursor !== \"none\") {\n\t\t\t\tthis.cursor = \"none\";\n\t\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Set cursor visibility\n\t */\n\tsetCursorVisible(visible: boolean): void {\n\t\tthis.cursor_visibility = visible ? \"default\" : \"none\";\n\t\tif (visible) {\n\t\t\tthis.cursor = \"default\";\n\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t} else {\n\t\t\tthis.cursor = \"none\";\n\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t}\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\treturn this.canvas;\n\t}\n\n\tgetContext(): CanvasRenderingContext2D {\n\t\treturn this.context;\n\t}\n\n\tresize(width?: number, height?: number): void {\n\t\tif (width && height) {\n\t\t\t// Validate dimensions\n\t\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7002, { width, height });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.canvas.width = width;\n\t\t\tthis.canvas.height = height;\n\t\t\tthis.initContext();\n\t\t\tthis.zBuffer.resize(width, height);\n\t\t\t// Update interface cache immediately after resize\n\t\t\tthis.updateInterface();\n\t\t}\n\t}\n}\n","/**\n * TTRI - Textured Triangle Rendering (Software Rasterization)\n *\n * Based on TIC-80's ttri implementation for 3D-style graphics.\n * This is NOT a 3D engine - it's pure 2D pixel manipulation using Canvas 2D API.\n *\n * How it works:\n * 1. Use getImageData() to get pixel buffer\n * 2. Rasterize triangle pixel-by-pixel (barycentric coordinates)\n * 3. Interpolate UV texture coordinates (perspective-correct with 1/z)\n * 4. Sample texture and write to pixel buffer\n * 5. Use putImageData() to update canvas\n *\n * This is software rendering like Doom, Quake software mode, and PlayStation 1.\n * No WebGL, no GPU - just CPU pixel manipulation.\n */\n\nimport type { TileMap as Map } from \"@al8b/map\";\nimport type { Sprite } from \"@al8b/sprites\";\n\nexport interface Vec2 {\n\tx: number;\n\ty: number;\n}\n\nexport interface Vec3 {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\nexport interface TexVert {\n\tx: number;\n\ty: number;\n\tu: number;\n\tv: number;\n\tz: number;\n}\n\nexport type TextureSource = \"tiles\" | \"map\" | \"screen\";\n\nexport interface TriangleData {\n\tcontext: CanvasRenderingContext2D;\n\twidth: number;\n\theight: number;\n\truntime?: any;\n\tpixelated: boolean;\n}\n\n/**\n * Z-Buffer for depth testing\n */\nexport class ZBuffer {\n\tprivate buffer: Float32Array;\n\tprivate width: number;\n\tprivate height: number;\n\n\tconstructor(width: number, height: number) {\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.buffer = new Float32Array(width * height);\n\t}\n\n\tclear(): void {\n\t\tthis.buffer.fill(0);\n\t}\n\n\tget(x: number, y: number): number {\n\t\treturn this.buffer[y * this.width + x] || 0;\n\t}\n\n\tset(x: number, y: number, z: number): void {\n\t\tthis.buffer[y * this.width + x] = z;\n\t}\n\n\tresize(width: number, height: number): void {\n\t\tif (this.width !== width || this.height !== height) {\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.buffer = new Float32Array(width * height);\n\t\t}\n\t}\n}\n\n/**\n * Edge function for triangle rasterization\n */\nfunction edgeFn(a: Vec2, b: Vec2, c: Vec2): number {\n\treturn (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\n/**\n * Get pixel from sprite/image\n */\nfunction getSpritePixel(\n\tsprite: Sprite | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Resolve sprite canvas from object or runtime registry\n\tlet canvas: HTMLCanvasElement | null = null;\n\n\tif (sprite && typeof sprite === \"object\" && sprite.canvas) {\n\t\tcanvas = sprite.canvas;\n\t} else if (typeof sprite === \"string\" && runtime?.sprites) {\n\t\tconst spriteObj = runtime.sprites[sprite];\n\t\tif (spriteObj?.frames?.[0]?.canvas) {\n\t\t\tcanvas = spriteObj.frames[0].canvas;\n\t\t}\n\t}\n\n\tif (!canvas) return null;\n\n\t// Apply texture coordinate wrapping (repeat mode)\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Sample pixel color from sprite canvas\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Get pixel from map\n */\nfunction getMapPixel(\n\tmap: Map | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Get map object\n\tlet mapObj: any = null;\n\n\tif (map && typeof map === \"object\" && map.getCanvas) {\n\t\tmapObj = map;\n\t} else if (typeof map === \"string\" && runtime?.maps) {\n\t\tmapObj = runtime.maps[map];\n\t}\n\n\tif (!mapObj) return null;\n\n\t// Get canvas from map\n\tconst canvas = mapObj.getCanvas ? mapObj.getCanvas() : mapObj.canvas;\n\tif (!canvas) return null;\n\n\t// Wrap texture coordinates\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Get pixel data\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Draw textured triangle with perspective correction\n */\nexport function drawTexturedTriangle(\n\tdata: TriangleData,\n\tv0: TexVert,\n\tv1: TexVert,\n\tv2: TexVert,\n\ttexture: Sprite | Map | string | any,\n\ttextureSource: TextureSource = \"tiles\",\n\tzBuffer?: ZBuffer,\n\tuseDepth: boolean = false,\n): void {\n\tconst { context, width, height, runtime, pixelated } = data;\n\n\t// Get bounding box\n\tconst minX = Math.max(0, Math.floor(Math.min(v0.x, v1.x, v2.x)));\n\tconst minY = Math.max(0, Math.floor(Math.min(v0.y, v1.y, v2.y)));\n\tconst maxX = Math.min(width, Math.ceil(Math.max(v0.x, v1.x, v2.x)));\n\tconst maxY = Math.min(height, Math.ceil(Math.max(v0.y, v1.y, v2.y)));\n\n\tif (minX >= maxX || minY >= maxY) return;\n\n\t// Calculate triangle area\n\tconst area = edgeFn(v0, v1, v2);\n\tif (Math.abs(area) < 0.001) return;\n\n\t// Backface culling\n\tif (area < 0) return;\n\n\t// Prepare perspective-correct interpolation\n\tconst useZ = useDepth && v0.z > 0 && v1.z > 0 && v2.z > 0;\n\n\tlet w0 = 1,\n\t\tw1 = 1,\n\t\tw2 = 1;\n\tlet u0 = v0.u,\n\t\tu1 = v1.u,\n\t\tu2 = v2.u;\n\tlet v0v = v0.v,\n\t\tv1v = v1.v,\n\t\tv2v = v2.v;\n\n\tif (useZ) {\n\t\tw0 = 1 / v0.z;\n\t\tw1 = 1 / v1.z;\n\t\tw2 = 1 / v2.z;\n\t\tu0 *= w0;\n\t\tu1 *= w1;\n\t\tu2 *= w2;\n\t\tv0v *= w0;\n\t\tv1v *= w1;\n\t\tv2v *= w2;\n\t}\n\n\t// Get image data for fast pixel manipulation\n\tconst imageData = context.getImageData(minX, minY, maxX - minX, maxY - minY);\n\tconst pixels = imageData.data;\n\n\t// Rasterize\n\tfor (let y = minY; y < maxY; y++) {\n\t\tfor (let x = minX; x < maxX; x++) {\n\t\t\tconst p = {\n\t\t\t\tx: x + 0.5,\n\t\t\t\ty: y + 0.5,\n\t\t\t};\n\n\t\t\t// Calculate barycentric coordinates\n\t\t\tconst w0b = edgeFn(v1, v2, p);\n\t\t\tconst w1b = edgeFn(v2, v0, p);\n\t\t\tconst w2b = edgeFn(v0, v1, p);\n\n\t\t\t// Check if point is inside triangle\n\t\t\tif (w0b >= 0 && w1b >= 0 && w2b >= 0) {\n\t\t\t\t// Normalize barycentric coordinates\n\t\t\t\tconst bary0 = w0b / area;\n\t\t\t\tconst bary1 = w1b / area;\n\t\t\t\tconst bary2 = w2b / area;\n\n\t\t\t\t// Depth test\n\t\t\t\tif (useZ && zBuffer) {\n\t\t\t\t\tconst z = bary0 * v0.z + bary1 * v1.z + bary2 * v2.z;\n\t\t\t\t\tconst currentZ = zBuffer.get(x, y);\n\t\t\t\t\tif (currentZ > 0 && currentZ >= z) continue;\n\t\t\t\t\tzBuffer.set(x, y, z);\n\t\t\t\t}\n\n\t\t\t\t// Interpolate texture coordinates\n\t\t\t\tlet u: number, v: number;\n\n\t\t\t\tif (useZ) {\n\t\t\t\t\tconst w = bary0 * w0 + bary1 * w1 + bary2 * w2;\n\t\t\t\t\tu = (bary0 * u0 + bary1 * u1 + bary2 * u2) / w;\n\t\t\t\t\tv = (bary0 * v0v + bary1 * v1v + bary2 * v2v) / w;\n\t\t\t\t} else {\n\t\t\t\t\tu = bary0 * v0.u + bary1 * v1.u + bary2 * v2.u;\n\t\t\t\t\tv = bary0 * v0.v + bary1 * v1.v + bary2 * v2.v;\n\t\t\t\t}\n\n\t\t\t\t// Sample texture\n\t\t\t\tlet pixel: {\n\t\t\t\t\tr: number;\n\t\t\t\t\tg: number;\n\t\t\t\t\tb: number;\n\t\t\t\t\ta: number;\n\t\t\t\t} | null = null;\n\n\t\t\t\tif (textureSource === \"map\") {\n\t\t\t\t\tpixel = getMapPixel(texture, u, v, runtime);\n\t\t\t\t} else {\n\t\t\t\t\tpixel = getSpritePixel(texture, u, v, runtime);\n\t\t\t\t}\n\n\t\t\t\t// Draw pixel\n\t\t\t\tif (pixel && pixel.a > 0) {\n\t\t\t\t\tconst idx = ((y - minY) * (maxX - minX) + (x - minX)) * 4;\n\t\t\t\t\tpixels[idx] = pixel.r;\n\t\t\t\t\tpixels[idx + 1] = pixel.g;\n\t\t\t\t\tpixels[idx + 2] = pixel.b;\n\t\t\t\t\tpixels[idx + 3] = pixel.a;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Put image data back\n\tcontext.imageSmoothingEnabled = !pixelated;\n\tcontext.putImageData(imageData, minX, minY);\n}\n\n/**\n * Draw solid color triangle\n */\nexport function drawTriangle(context: CanvasRenderingContext2D, v0: Vec2, v1: Vec2, v2: Vec2, color: string): void {\n\tcontext.fillStyle = color;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\n/**\n * Draw triangle outline\n */\nexport function drawTriangleOutline(\n\tcontext: CanvasRenderingContext2D,\n\tv0: Vec2,\n\tv1: Vec2,\n\tv2: Vec2,\n\tcolor: string,\n\tlineWidth: number = 1,\n): void {\n\tcontext.strokeStyle = color;\n\tcontext.lineWidth = lineWidth;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.stroke();\n}\n"],"mappings":"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","names":["ZBuffer","buffer","width","height","Float32Array","clear","fill","get","x","y","set","z","resize","BaseScreen","canvas","context","runtime","width","height","alpha","pixelated","line_width","font","translation_x","translation_y","rotation","scale_x","scale_y","screen_transform","object_rotation","object_scale_x","object_scale_y","anchor_x","anchor_y","blending","font_load_requested","font_loaded","interfaceCache","cursor","cursor_visibility","last_mouse_move","Date","now","zBuffer","options","document","createElement","initContext","normal","additive","blendModes","mode","loadFont","ZBuffer","addEventListener","style","setInterval","checkMouseCursor","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7001","formatted","formatForBrowser","reportRuntimeError","listener","Error","restore","save","translate","ratio","Math","min","scale","lineCap","initDraw","updateInterface","clear","color","globalAlpha","globalCompositeOperation","fillStyle","strokeStyle","fillRect","setColor","Number","isNaN","parseInt","String","num","r","floor","g","b","c","hex","toString","substring","isValidColor","test","E7003","setAlpha","setPixelated","setBlending","blend","E7007","blendMode","setLineWidth","setLineDash","dash","Array","isArray","setLinearGradient","x1","y1","x2","y2","c1","c2","grd","createLinearGradient","addColorStop","setRadialGradient","x","y","radius","createRadialGradient","setFont","fonts","load","catch","E7006","isFontReady","ready","check","setTranslation","tx","ty","isFinite","updateScreenTransform","setScale","setRotation","setDrawAnchor","ax","ay","setDrawRotation","setDrawScale","initDrawOp","object_transform","res","rotate","PI","closeDrawOp","setCursorVisible","visible","getCanvas","resize","E7002"]}
1
+ {"version":3,"sources":["../../src/core/base-screen.ts","../../src/tri/ttri.ts"],"sourcesContent":["import { ZBuffer } from \"../tri\";\nimport type { ScreenInterface, ScreenOptions } from \"../types\";\n\n/**\n * BaseScreen encapsulates canvas/context management plus shared drawing state.\n * Feature-specific behaviors (primitives, sprites, text, triangles) extend this class.\n */\nexport class BaseScreen {\n\tprotected canvas: HTMLCanvasElement;\n\tprotected context!: CanvasRenderingContext2D;\n\tprotected runtime: any;\n\n\tpublic width!: number;\n\tpublic height!: number;\n\n\t// Drawing state\n\tprotected alpha = 1;\n\tprotected pixelated = 1;\n\tprotected line_width = 1;\n\tprotected font = \"BitCell\";\n\n\t// Transformations\n\tprotected translation_x = 0;\n\tprotected translation_y = 0;\n\tprotected rotation = 0;\n\tprotected scale_x = 1;\n\tprotected scale_y = 1;\n\tprotected screen_transform = false;\n\n\t// Object transformations\n\tprotected object_rotation = 0;\n\tprotected object_scale_x = 1;\n\tprotected object_scale_y = 1;\n\tprotected anchor_x = 0;\n\tprotected anchor_y = 0;\n\n\t// Blending + font caches\n\tprotected blending: Record<string, string> = {};\n\tprotected font_load_requested: Record<string, boolean> = {};\n\tprotected font_loaded: Record<string, boolean> = {};\n\n\t// Interface cache\n\tprotected interfaceCache: ScreenInterface | null = null;\n\n\t// Cursor management\n\tprotected cursor: string = \"default\";\n\tprotected cursor_visibility: string = \"auto\";\n\tprotected last_mouse_move: number = Date.now();\n\n\t// 3D helper\n\tprotected zBuffer: ZBuffer;\n\n\tconstructor(options: ScreenOptions = {}) {\n\t\tthis.runtime = options.runtime;\n\n\t\tif (options.canvas) {\n\t\t\tthis.canvas = options.canvas;\n\t\t\tif (this.canvas.width === 0 || this.canvas.height === 0) {\n\t\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\t\tthis.canvas.height = options.height || 1920;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.canvas = document.createElement(\"canvas\");\n\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\tthis.canvas.height = options.height || 1920;\n\t\t}\n\n\t\tthis.initContext();\n\n\t\tthis.blending = {\n\t\t\tnormal: \"source-over\",\n\t\t\tadditive: \"lighter\",\n\t\t};\n\n\t\tconst blendModes = [\n\t\t\t\"source-over\",\n\t\t\t\"source-in\",\n\t\t\t\"source-out\",\n\t\t\t\"source-atop\",\n\t\t\t\"destination-over\",\n\t\t\t\"destination-in\",\n\t\t\t\"destination-out\",\n\t\t\t\"destination-atop\",\n\t\t\t\"lighter\",\n\t\t\t\"copy\",\n\t\t\t\"xor\",\n\t\t\t\"multiply\",\n\t\t\t\"screen\",\n\t\t\t\"overlay\",\n\t\t\t\"darken\",\n\t\t\t\"lighten\",\n\t\t\t\"color-dodge\",\n\t\t\t\"color-burn\",\n\t\t\t\"hard-light\",\n\t\t\t\"soft-light\",\n\t\t\t\"difference\",\n\t\t\t\"exclusion\",\n\t\t\t\"hue\",\n\t\t\t\"saturation\",\n\t\t\t\"color\",\n\t\t\t\"luminosity\",\n\t\t];\n\n\t\tfor (const mode of blendModes) {\n\t\t\tthis.blending[mode] = mode;\n\t\t}\n\n\t\tthis.loadFont(this.font);\n\t\tthis.zBuffer = new ZBuffer(this.canvas.width, this.canvas.height);\n\n\t\tthis.cursor = \"default\";\n\n\t\tthis.canvas.addEventListener(\"mousemove\", () => {\n\t\t\tthis.last_mouse_move = Date.now();\n\t\t\tif (this.cursor !== \"default\" && this.cursor_visibility === \"auto\") {\n\t\t\t\tthis.cursor = \"default\";\n\t\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t\t}\n\t\t});\n\n\t\t// When the context is lost and then restored, base transform needs to be reinstated\n\t\tthis.canvas.addEventListener(\"contextrestored\", () => {\n\t\t\tthis.initContext();\n\t\t});\n\n\t\tsetInterval(() => this.checkMouseCursor(), 1000);\n\t\tthis.cursor_visibility = \"auto\";\n\t}\n\n\tprotected initContext(): void {\n\t\tconst ctx = this.canvas.getContext(\"2d\", {\n\t\t\talpha: false,\n\t\t});\n\t\tif (!ctx) {\n\t\t\tconst message = \"Failed to get 2D rendering context\";\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7001\", message, data: {} });\n\n\t\t\tthrow new Error(message);\n\t\t}\n\n\t\tif (ctx !== this.context) {\n\t\t\tthis.context = ctx;\n\t\t} else {\n\t\t\tthis.context.restore();\n\t\t}\n\n\t\tthis.context.save();\n\t\tthis.context.translate(this.canvas.width / 2, this.canvas.height / 2);\n\n\t\t// Calculate ratio: Math.min(canvas.width/200, canvas.height/200)\n\t\tconst ratio = Math.min(this.canvas.width / 200, this.canvas.height / 200);\n\t\tthis.context.scale(ratio, ratio);\n\n\t\t// Set logical width/height\n\t\tthis.width = this.canvas.width / ratio;\n\t\tthis.height = this.canvas.height / ratio;\n\t\tthis.context.lineCap = \"round\";\n\t}\n\n\t/**\n\t * Initialize draw state (called before each draw frame)\n\t */\n\tinitDraw(): void {\n\t\tthis.alpha = 1;\n\t\tthis.line_width = 1;\n\t\t// Note: Supersampling not implemented in l8b\n\t\t// If needed, add: if (this.supersampling != this.previous_supersampling) { this.resize(); this.previous_supersampling = this.supersampling; }\n\t}\n\n\t/**\n\t * Update interface dimensions (called before each draw frame)\n\t */\n\tupdateInterface(): void {\n\t\t// Update interface cache if it exists\n\t\tif (this.interfaceCache) {\n\t\t\tthis.interfaceCache.width = this.width;\n\t\t\tthis.interfaceCache.height = this.height;\n\t\t}\n\t}\n\n\tclear(color?: string): void {\n\t\tthis.context.globalAlpha = 1;\n\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\tthis.context.fillStyle = color || \"#000\";\n\t\tthis.context.strokeStyle = color || \"#000\";\n\t\tthis.context.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);\n\t\tthis.zBuffer.clear();\n\t}\n\n\tsetColor(color: string | number): void {\n\t\tif (!color) return;\n\n\t\tif (!Number.isNaN(Number.parseInt(String(color)))) {\n\t\t\tconst num = Number.parseInt(String(color));\n\t\t\tconst r = ((Math.floor(num / 100) % 10) / 9) * 255;\n\t\t\tconst g = ((Math.floor(num / 10) % 10) / 9) * 255;\n\t\t\tconst b = ((num % 10) / 9) * 255;\n\t\t\tconst c = 0xff000000 + (r << 16) + (g << 8) + b;\n\t\t\tconst hex = \"#\" + c.toString(16).substring(2, 8);\n\t\t\tthis.context.fillStyle = hex;\n\t\t\tthis.context.strokeStyle = hex;\n\t\t} else if (typeof color === \"string\") {\n\t\t\t// Validate color format\n\t\t\tconst isValidColor =\n\t\t\t\t/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) ||\n\t\t\t\t/^rgb\\(|^rgba\\(|^hsl\\(|^hsla\\(/.test(color) ||\n\t\t\t\t/^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);\n\n\t\t\tif (!isValidColor) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7003\", message: \"Invalid color\", data: { color } });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.context.fillStyle = color;\n\t\t\tthis.context.strokeStyle = color;\n\t\t}\n\t}\n\n\tsetAlpha(alpha: number): void {\n\t\tthis.alpha = alpha;\n\t}\n\n\tsetPixelated(pixelated: number): void {\n\t\tthis.pixelated = pixelated;\n\t}\n\n\tsetBlending(blending: string): void {\n\t\tconst blend = this.blending[blending || \"normal\"];\n\n\t\tif (!blend) {\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7007\", message: \"Invalid blend mode\", data: { blendMode: blending } });\n\t\t\t// Fallback to normal blend mode\n\t\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\t\treturn;\n\t\t}\n\n\t\tthis.context.globalCompositeOperation = blend as GlobalCompositeOperation;\n\t}\n\n\tsetLineWidth(width: number): void {\n\t\tthis.line_width = width;\n\t}\n\n\tsetLineDash(dash: number[] | null): void {\n\t\tif (!Array.isArray(dash)) {\n\t\t\tthis.context.setLineDash([]);\n\t\t} else {\n\t\t\tthis.context.setLineDash(dash);\n\t\t}\n\t}\n\n\tsetLinearGradient(x1: number, y1: number, x2: number, y2: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createLinearGradient(x1, -y1, x2, -y2);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetRadialGradient(x: number, y: number, radius: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createRadialGradient(x, -y, 0, x, -y, radius);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetFont(font: string): void {\n\t\tthis.font = font || \"Verdana\";\n\t\tthis.loadFont(this.font);\n\t}\n\n\tloadFont(font: string = \"BitCell\"): void {\n\t\tif (this.font_load_requested[font]) {\n\t\t\treturn;\n\t\t}\n\t\tthis.font_load_requested[font] = true;\n\t\ttry {\n\t\t\tdocument.fonts?.load?.(`16pt ${font}`).catch(() => {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7006\", message: \"Font loading failed\", data: { font } });\n\t\t\t});\n\t\t} catch {\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7006\", message: \"Font loading failed\", data: { font } });\n\t\t}\n\t}\n\n\tisFontReady(font: string = this.font): number {\n\t\tif (this.font_loaded[font]) {\n\t\t\treturn 1;\n\t\t}\n\n\t\ttry {\n\t\t\tconst ready = document.fonts?.check?.(`16pt ${font}`) ?? true;\n\t\t\tif (ready) {\n\t\t\t\tthis.font_loaded[font] = true;\n\t\t\t}\n\t\t\treturn ready ? 1 : 0;\n\t\t} catch {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tsetTranslation(tx: number, ty: number): void {\n\t\tthis.translation_x = isFinite(tx) ? tx : 0;\n\t\tthis.translation_y = isFinite(ty) ? ty : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetScale(x: number, y: number): void {\n\t\tthis.scale_x = isFinite(x) && x !== 0 ? x : 1;\n\t\tthis.scale_y = isFinite(y) && y !== 0 ? y : 1;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetRotation(rotation: number): void {\n\t\tthis.rotation = isFinite(rotation) ? rotation : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tprotected updateScreenTransform(): void {\n\t\tthis.screen_transform =\n\t\t\tthis.translation_x !== 0 ||\n\t\t\tthis.translation_y !== 0 ||\n\t\t\tthis.scale_x !== 1 ||\n\t\t\tthis.scale_y !== 1 ||\n\t\t\tthis.rotation !== 0;\n\t}\n\n\tsetDrawAnchor(ax: number, ay: number): void {\n\t\tthis.anchor_x = typeof ax === \"number\" ? ax : 0;\n\t\tthis.anchor_y = typeof ay === \"number\" ? ay : 0;\n\t}\n\n\tsetDrawRotation(rotation: number): void {\n\t\tthis.object_rotation = rotation;\n\t}\n\n\tsetDrawScale(x: number, y: number = x): void {\n\t\tthis.object_scale_x = x;\n\t\tthis.object_scale_y = y;\n\t}\n\n\tprotected initDrawOp(x: number, y: number, object_transform: boolean = true): boolean {\n\t\tlet res = false;\n\n\t\tif (this.screen_transform) {\n\t\t\tthis.context.save();\n\t\t\tres = true;\n\t\t\tthis.context.translate(this.translation_x, -this.translation_y);\n\t\t\tthis.context.scale(this.scale_x, this.scale_y);\n\t\t\tthis.context.rotate((-this.rotation / 180) * Math.PI);\n\t\t\tthis.context.translate(x, y);\n\t\t}\n\n\t\tif (object_transform && (this.object_rotation !== 0 || this.object_scale_x !== 1 || this.object_scale_y !== 1)) {\n\t\t\tif (!res) {\n\t\t\t\tthis.context.save();\n\t\t\t\tres = true;\n\t\t\t\tthis.context.translate(x, y);\n\t\t\t}\n\n\t\t\tif (this.object_rotation !== 0) {\n\t\t\t\tthis.context.rotate((-this.object_rotation / 180) * Math.PI);\n\t\t\t}\n\n\t\t\tif (this.object_scale_x !== 1 || this.object_scale_y !== 1) {\n\t\t\t\tthis.context.scale(this.object_scale_x, this.object_scale_y);\n\t\t\t}\n\t\t}\n\n\t\treturn res;\n\t}\n\n\tprotected closeDrawOp(): void {\n\t\tthis.context.restore();\n\t}\n\n\t/**\n\t * Check mouse cursor visibility\n\t * Auto-hides cursor after 4 seconds of inactivity\n\t */\n\tprotected checkMouseCursor(): void {\n\t\tif (Date.now() > this.last_mouse_move + 4000 && this.cursor_visibility === \"auto\") {\n\t\t\tif (this.cursor !== \"none\") {\n\t\t\t\tthis.cursor = \"none\";\n\t\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Set cursor visibility\n\t */\n\tsetCursorVisible(visible: boolean): void {\n\t\tthis.cursor_visibility = visible ? \"default\" : \"none\";\n\t\tif (visible) {\n\t\t\tthis.cursor = \"default\";\n\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t} else {\n\t\t\tthis.cursor = \"none\";\n\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t}\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\treturn this.canvas;\n\t}\n\n\tgetContext(): CanvasRenderingContext2D {\n\t\treturn this.context;\n\t}\n\n\tresize(width?: number, height?: number): void {\n\t\tif (width && height) {\n\t\t\t// Validate dimensions\n\t\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7002\", message: \"Invalid resize dimensions\", data: { width, height } });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.canvas.width = width;\n\t\t\tthis.canvas.height = height;\n\t\t\tthis.initContext();\n\t\t\tthis.zBuffer.resize(width, height);\n\t\t\t// Update interface cache immediately after resize\n\t\t\tthis.updateInterface();\n\t\t}\n\t}\n}\n","/**\n * TTRI - Textured Triangle Rendering (Software Rasterization)\n *\n * Based on TIC-80's ttri implementation for 3D-style graphics.\n * This is NOT a 3D engine - it's pure 2D pixel manipulation using Canvas 2D API.\n *\n * How it works:\n * 1. Use getImageData() to get pixel buffer\n * 2. Rasterize triangle pixel-by-pixel (barycentric coordinates)\n * 3. Interpolate UV texture coordinates (perspective-correct with 1/z)\n * 4. Sample texture and write to pixel buffer\n * 5. Use putImageData() to update canvas\n *\n * This is software rendering like Doom, Quake software mode, and PlayStation 1.\n * No WebGL, no GPU - just CPU pixel manipulation.\n */\n\nimport type { TileMap as Map } from \"@al8b/map\";\nimport type { Sprite } from \"@al8b/sprites\";\n\nexport interface Vec2 {\n\tx: number;\n\ty: number;\n}\n\nexport interface Vec3 {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\nexport interface TexVert {\n\tx: number;\n\ty: number;\n\tu: number;\n\tv: number;\n\tz: number;\n}\n\nexport type TextureSource = \"tiles\" | \"map\" | \"screen\";\n\nexport interface TriangleData {\n\tcontext: CanvasRenderingContext2D;\n\twidth: number;\n\theight: number;\n\truntime?: any;\n\tpixelated: boolean;\n}\n\n/**\n * Z-Buffer for depth testing\n */\nexport class ZBuffer {\n\tprivate buffer: Float32Array;\n\tprivate width: number;\n\tprivate height: number;\n\n\tconstructor(width: number, height: number) {\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.buffer = new Float32Array(width * height);\n\t}\n\n\tclear(): void {\n\t\tthis.buffer.fill(0);\n\t}\n\n\tget(x: number, y: number): number {\n\t\treturn this.buffer[y * this.width + x] || 0;\n\t}\n\n\tset(x: number, y: number, z: number): void {\n\t\tthis.buffer[y * this.width + x] = z;\n\t}\n\n\tresize(width: number, height: number): void {\n\t\tif (this.width !== width || this.height !== height) {\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.buffer = new Float32Array(width * height);\n\t\t}\n\t}\n}\n\n/**\n * Edge function for triangle rasterization\n */\nfunction edgeFn(a: Vec2, b: Vec2, c: Vec2): number {\n\treturn (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\n/**\n * Get pixel from sprite/image\n */\nfunction getSpritePixel(\n\tsprite: Sprite | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Resolve sprite canvas from object or runtime registry\n\tlet canvas: HTMLCanvasElement | null = null;\n\n\tif (sprite && typeof sprite === \"object\" && sprite.canvas) {\n\t\tcanvas = sprite.canvas;\n\t} else if (typeof sprite === \"string\" && runtime?.sprites) {\n\t\tconst spriteObj = runtime.sprites[sprite];\n\t\tif (spriteObj?.frames?.[0]?.canvas) {\n\t\t\tcanvas = spriteObj.frames[0].canvas;\n\t\t}\n\t}\n\n\tif (!canvas) return null;\n\n\t// Apply texture coordinate wrapping (repeat mode)\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Sample pixel color from sprite canvas\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Get pixel from map\n */\nfunction getMapPixel(\n\tmap: Map | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Get map object\n\tlet mapObj: any = null;\n\n\tif (map && typeof map === \"object\" && map.getCanvas) {\n\t\tmapObj = map;\n\t} else if (typeof map === \"string\" && runtime?.maps) {\n\t\tmapObj = runtime.maps[map];\n\t}\n\n\tif (!mapObj) return null;\n\n\t// Get canvas from map\n\tconst canvas = mapObj.getCanvas ? mapObj.getCanvas() : mapObj.canvas;\n\tif (!canvas) return null;\n\n\t// Wrap texture coordinates\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Get pixel data\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Draw textured triangle with perspective correction\n */\nexport function drawTexturedTriangle(\n\tdata: TriangleData,\n\tv0: TexVert,\n\tv1: TexVert,\n\tv2: TexVert,\n\ttexture: Sprite | Map | string | any,\n\ttextureSource: TextureSource = \"tiles\",\n\tzBuffer?: ZBuffer,\n\tuseDepth: boolean = false,\n): void {\n\tconst { context, width, height, runtime, pixelated } = data;\n\n\t// Get bounding box\n\tconst minX = Math.max(0, Math.floor(Math.min(v0.x, v1.x, v2.x)));\n\tconst minY = Math.max(0, Math.floor(Math.min(v0.y, v1.y, v2.y)));\n\tconst maxX = Math.min(width, Math.ceil(Math.max(v0.x, v1.x, v2.x)));\n\tconst maxY = Math.min(height, Math.ceil(Math.max(v0.y, v1.y, v2.y)));\n\n\tif (minX >= maxX || minY >= maxY) return;\n\n\t// Calculate triangle area\n\tconst area = edgeFn(v0, v1, v2);\n\tif (Math.abs(area) < 0.001) return;\n\n\t// Backface culling\n\tif (area < 0) return;\n\n\t// Prepare perspective-correct interpolation\n\tconst useZ = useDepth && v0.z > 0 && v1.z > 0 && v2.z > 0;\n\n\tlet w0 = 1,\n\t\tw1 = 1,\n\t\tw2 = 1;\n\tlet u0 = v0.u,\n\t\tu1 = v1.u,\n\t\tu2 = v2.u;\n\tlet v0v = v0.v,\n\t\tv1v = v1.v,\n\t\tv2v = v2.v;\n\n\tif (useZ) {\n\t\tw0 = 1 / v0.z;\n\t\tw1 = 1 / v1.z;\n\t\tw2 = 1 / v2.z;\n\t\tu0 *= w0;\n\t\tu1 *= w1;\n\t\tu2 *= w2;\n\t\tv0v *= w0;\n\t\tv1v *= w1;\n\t\tv2v *= w2;\n\t}\n\n\t// Get image data for fast pixel manipulation\n\tconst imageData = context.getImageData(minX, minY, maxX - minX, maxY - minY);\n\tconst pixels = imageData.data;\n\n\t// Rasterize\n\tfor (let y = minY; y < maxY; y++) {\n\t\tfor (let x = minX; x < maxX; x++) {\n\t\t\tconst p = {\n\t\t\t\tx: x + 0.5,\n\t\t\t\ty: y + 0.5,\n\t\t\t};\n\n\t\t\t// Calculate barycentric coordinates\n\t\t\tconst w0b = edgeFn(v1, v2, p);\n\t\t\tconst w1b = edgeFn(v2, v0, p);\n\t\t\tconst w2b = edgeFn(v0, v1, p);\n\n\t\t\t// Check if point is inside triangle\n\t\t\tif (w0b >= 0 && w1b >= 0 && w2b >= 0) {\n\t\t\t\t// Normalize barycentric coordinates\n\t\t\t\tconst bary0 = w0b / area;\n\t\t\t\tconst bary1 = w1b / area;\n\t\t\t\tconst bary2 = w2b / area;\n\n\t\t\t\t// Depth test\n\t\t\t\tif (useZ && zBuffer) {\n\t\t\t\t\tconst z = bary0 * v0.z + bary1 * v1.z + bary2 * v2.z;\n\t\t\t\t\tconst currentZ = zBuffer.get(x, y);\n\t\t\t\t\tif (currentZ > 0 && currentZ >= z) continue;\n\t\t\t\t\tzBuffer.set(x, y, z);\n\t\t\t\t}\n\n\t\t\t\t// Interpolate texture coordinates\n\t\t\t\tlet u: number, v: number;\n\n\t\t\t\tif (useZ) {\n\t\t\t\t\tconst w = bary0 * w0 + bary1 * w1 + bary2 * w2;\n\t\t\t\t\tu = (bary0 * u0 + bary1 * u1 + bary2 * u2) / w;\n\t\t\t\t\tv = (bary0 * v0v + bary1 * v1v + bary2 * v2v) / w;\n\t\t\t\t} else {\n\t\t\t\t\tu = bary0 * v0.u + bary1 * v1.u + bary2 * v2.u;\n\t\t\t\t\tv = bary0 * v0.v + bary1 * v1.v + bary2 * v2.v;\n\t\t\t\t}\n\n\t\t\t\t// Sample texture\n\t\t\t\tlet pixel: {\n\t\t\t\t\tr: number;\n\t\t\t\t\tg: number;\n\t\t\t\t\tb: number;\n\t\t\t\t\ta: number;\n\t\t\t\t} | null = null;\n\n\t\t\t\tif (textureSource === \"map\") {\n\t\t\t\t\tpixel = getMapPixel(texture, u, v, runtime);\n\t\t\t\t} else {\n\t\t\t\t\tpixel = getSpritePixel(texture, u, v, runtime);\n\t\t\t\t}\n\n\t\t\t\t// Draw pixel\n\t\t\t\tif (pixel && pixel.a > 0) {\n\t\t\t\t\tconst idx = ((y - minY) * (maxX - minX) + (x - minX)) * 4;\n\t\t\t\t\tpixels[idx] = pixel.r;\n\t\t\t\t\tpixels[idx + 1] = pixel.g;\n\t\t\t\t\tpixels[idx + 2] = pixel.b;\n\t\t\t\t\tpixels[idx + 3] = pixel.a;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Put image data back\n\tcontext.imageSmoothingEnabled = !pixelated;\n\tcontext.putImageData(imageData, minX, minY);\n}\n\n/**\n * Draw solid color triangle\n */\nexport function drawTriangle(context: CanvasRenderingContext2D, v0: Vec2, v1: Vec2, v2: Vec2, color: string): void {\n\tcontext.fillStyle = color;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\n/**\n * Draw triangle outline\n */\nexport function drawTriangleOutline(\n\tcontext: CanvasRenderingContext2D,\n\tv0: Vec2,\n\tv1: Vec2,\n\tv2: Vec2,\n\tcolor: string,\n\tlineWidth: number = 1,\n): void {\n\tcontext.strokeStyle = color;\n\tcontext.lineWidth = lineWidth;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.stroke();\n}\n"],"mappings":"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","names":["ZBuffer","buffer","width","height","Float32Array","clear","fill","get","x","y","set","z","resize","BaseScreen","canvas","context","runtime","width","height","alpha","pixelated","line_width","font","translation_x","translation_y","rotation","scale_x","scale_y","screen_transform","object_rotation","object_scale_x","object_scale_y","anchor_x","anchor_y","blending","font_load_requested","font_loaded","interfaceCache","cursor","cursor_visibility","last_mouse_move","Date","now","zBuffer","options","document","createElement","initContext","normal","additive","blendModes","mode","loadFont","ZBuffer","addEventListener","style","setInterval","checkMouseCursor","ctx","getContext","message","listener","reportError","code","data","Error","restore","save","translate","ratio","Math","min","scale","lineCap","initDraw","updateInterface","clear","color","globalAlpha","globalCompositeOperation","fillStyle","strokeStyle","fillRect","setColor","Number","isNaN","parseInt","String","num","r","floor","g","b","c","hex","toString","substring","isValidColor","test","setAlpha","setPixelated","setBlending","blend","blendMode","setLineWidth","setLineDash","dash","Array","isArray","setLinearGradient","x1","y1","x2","y2","c1","c2","grd","createLinearGradient","addColorStop","setRadialGradient","x","y","radius","createRadialGradient","setFont","fonts","load","catch","isFontReady","ready","check","setTranslation","tx","ty","isFinite","updateScreenTransform","setScale","setRotation","setDrawAnchor","ax","ay","setDrawRotation","setDrawScale","initDrawOp","object_transform","res","rotate","PI","closeDrawOp","setCursorVisible","visible","getCanvas","resize"]}
@@ -1,9 +1,6 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
- // src/core/base-screen.ts
5
- import { APIErrorCode, createDiagnostic, formatForBrowser, reportRuntimeError } from "@al8b/diagnostics";
6
-
7
4
  // src/tri/ttri.ts
8
5
  var ZBuffer = class {
9
6
  static {
@@ -145,10 +142,13 @@ var BaseScreen = class {
145
142
  alpha: false
146
143
  });
147
144
  if (!ctx) {
148
- const diagnostic = createDiagnostic(APIErrorCode.E7001);
149
- const formatted = formatForBrowser(diagnostic);
150
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7001, {});
151
- throw new Error(formatted);
145
+ const message = "Failed to get 2D rendering context";
146
+ this.runtime?.listener?.reportError?.({
147
+ code: "E7001",
148
+ message,
149
+ data: {}
150
+ });
151
+ throw new Error(message);
152
152
  }
153
153
  if (ctx !== this.context) {
154
154
  this.context = ctx;
@@ -201,8 +201,12 @@ var BaseScreen = class {
201
201
  } else if (typeof color === "string") {
202
202
  const isValidColor = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) || /^rgb\(|^rgba\(|^hsl\(|^hsla\(/.test(color) || /^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);
203
203
  if (!isValidColor) {
204
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7003, {
205
- color
204
+ this.runtime?.listener?.reportError?.({
205
+ code: "E7003",
206
+ message: "Invalid color",
207
+ data: {
208
+ color
209
+ }
206
210
  });
207
211
  return;
208
212
  }
@@ -219,8 +223,12 @@ var BaseScreen = class {
219
223
  setBlending(blending) {
220
224
  const blend = this.blending[blending || "normal"];
221
225
  if (!blend) {
222
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7007, {
223
- blendMode: blending
226
+ this.runtime?.listener?.reportError?.({
227
+ code: "E7007",
228
+ message: "Invalid blend mode",
229
+ data: {
230
+ blendMode: blending
231
+ }
224
232
  });
225
233
  this.context.globalCompositeOperation = "source-over";
226
234
  return;
@@ -262,13 +270,21 @@ var BaseScreen = class {
262
270
  this.font_load_requested[font] = true;
263
271
  try {
264
272
  document.fonts?.load?.(`16pt ${font}`).catch(() => {
265
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7006, {
266
- font
273
+ this.runtime?.listener?.reportError?.({
274
+ code: "E7006",
275
+ message: "Font loading failed",
276
+ data: {
277
+ font
278
+ }
267
279
  });
268
280
  });
269
281
  } catch {
270
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7006, {
271
- font
282
+ this.runtime?.listener?.reportError?.({
283
+ code: "E7006",
284
+ message: "Font loading failed",
285
+ data: {
286
+ font
287
+ }
272
288
  });
273
289
  }
274
290
  }
@@ -376,9 +392,13 @@ var BaseScreen = class {
376
392
  resize(width, height) {
377
393
  if (width && height) {
378
394
  if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
379
- reportRuntimeError(this.runtime?.listener, APIErrorCode.E7002, {
380
- width,
381
- height
395
+ this.runtime?.listener?.reportError?.({
396
+ code: "E7002",
397
+ message: "Invalid resize dimensions",
398
+ data: {
399
+ width,
400
+ height
401
+ }
382
402
  });
383
403
  return;
384
404
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/base-screen.ts","../../src/tri/ttri.ts"],"sourcesContent":["import { APIErrorCode, createDiagnostic, formatForBrowser, reportRuntimeError } from \"@al8b/diagnostics\";\nimport { ZBuffer } from \"../tri\";\nimport type { ScreenInterface, ScreenOptions } from \"../types\";\n\n/**\n * BaseScreen encapsulates canvas/context management plus shared drawing state.\n * Feature-specific behaviors (primitives, sprites, text, triangles) extend this class.\n */\nexport class BaseScreen {\n\tprotected canvas: HTMLCanvasElement;\n\tprotected context!: CanvasRenderingContext2D;\n\tprotected runtime: any;\n\n\tpublic width!: number;\n\tpublic height!: number;\n\n\t// Drawing state\n\tprotected alpha = 1;\n\tprotected pixelated = 1;\n\tprotected line_width = 1;\n\tprotected font = \"BitCell\";\n\n\t// Transformations\n\tprotected translation_x = 0;\n\tprotected translation_y = 0;\n\tprotected rotation = 0;\n\tprotected scale_x = 1;\n\tprotected scale_y = 1;\n\tprotected screen_transform = false;\n\n\t// Object transformations\n\tprotected object_rotation = 0;\n\tprotected object_scale_x = 1;\n\tprotected object_scale_y = 1;\n\tprotected anchor_x = 0;\n\tprotected anchor_y = 0;\n\n\t// Blending + font caches\n\tprotected blending: Record<string, string> = {};\n\tprotected font_load_requested: Record<string, boolean> = {};\n\tprotected font_loaded: Record<string, boolean> = {};\n\n\t// Interface cache\n\tprotected interfaceCache: ScreenInterface | null = null;\n\n\t// Cursor management\n\tprotected cursor: string = \"default\";\n\tprotected cursor_visibility: string = \"auto\";\n\tprotected last_mouse_move: number = Date.now();\n\n\t// 3D helper\n\tprotected zBuffer: ZBuffer;\n\n\tconstructor(options: ScreenOptions = {}) {\n\t\tthis.runtime = options.runtime;\n\n\t\tif (options.canvas) {\n\t\t\tthis.canvas = options.canvas;\n\t\t\tif (this.canvas.width === 0 || this.canvas.height === 0) {\n\t\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\t\tthis.canvas.height = options.height || 1920;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.canvas = document.createElement(\"canvas\");\n\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\tthis.canvas.height = options.height || 1920;\n\t\t}\n\n\t\tthis.initContext();\n\n\t\tthis.blending = {\n\t\t\tnormal: \"source-over\",\n\t\t\tadditive: \"lighter\",\n\t\t};\n\n\t\tconst blendModes = [\n\t\t\t\"source-over\",\n\t\t\t\"source-in\",\n\t\t\t\"source-out\",\n\t\t\t\"source-atop\",\n\t\t\t\"destination-over\",\n\t\t\t\"destination-in\",\n\t\t\t\"destination-out\",\n\t\t\t\"destination-atop\",\n\t\t\t\"lighter\",\n\t\t\t\"copy\",\n\t\t\t\"xor\",\n\t\t\t\"multiply\",\n\t\t\t\"screen\",\n\t\t\t\"overlay\",\n\t\t\t\"darken\",\n\t\t\t\"lighten\",\n\t\t\t\"color-dodge\",\n\t\t\t\"color-burn\",\n\t\t\t\"hard-light\",\n\t\t\t\"soft-light\",\n\t\t\t\"difference\",\n\t\t\t\"exclusion\",\n\t\t\t\"hue\",\n\t\t\t\"saturation\",\n\t\t\t\"color\",\n\t\t\t\"luminosity\",\n\t\t];\n\n\t\tfor (const mode of blendModes) {\n\t\t\tthis.blending[mode] = mode;\n\t\t}\n\n\t\tthis.loadFont(this.font);\n\t\tthis.zBuffer = new ZBuffer(this.canvas.width, this.canvas.height);\n\n\t\tthis.cursor = \"default\";\n\n\t\tthis.canvas.addEventListener(\"mousemove\", () => {\n\t\t\tthis.last_mouse_move = Date.now();\n\t\t\tif (this.cursor !== \"default\" && this.cursor_visibility === \"auto\") {\n\t\t\t\tthis.cursor = \"default\";\n\t\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t\t}\n\t\t});\n\n\t\t// When the context is lost and then restored, base transform needs to be reinstated\n\t\tthis.canvas.addEventListener(\"contextrestored\", () => {\n\t\t\tthis.initContext();\n\t\t});\n\n\t\tsetInterval(() => this.checkMouseCursor(), 1000);\n\t\tthis.cursor_visibility = \"auto\";\n\t}\n\n\tprotected initContext(): void {\n\t\tconst ctx = this.canvas.getContext(\"2d\", {\n\t\t\talpha: false,\n\t\t});\n\t\tif (!ctx) {\n\t\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7001);\n\t\t\tconst formatted = formatForBrowser(diagnostic);\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7001, {});\n\n\t\t\tthrow new Error(formatted);\n\t\t}\n\n\t\tif (ctx !== this.context) {\n\t\t\tthis.context = ctx;\n\t\t} else {\n\t\t\tthis.context.restore();\n\t\t}\n\n\t\tthis.context.save();\n\t\tthis.context.translate(this.canvas.width / 2, this.canvas.height / 2);\n\n\t\t// Calculate ratio: Math.min(canvas.width/200, canvas.height/200)\n\t\tconst ratio = Math.min(this.canvas.width / 200, this.canvas.height / 200);\n\t\tthis.context.scale(ratio, ratio);\n\n\t\t// Set logical width/height\n\t\tthis.width = this.canvas.width / ratio;\n\t\tthis.height = this.canvas.height / ratio;\n\t\tthis.context.lineCap = \"round\";\n\t}\n\n\t/**\n\t * Initialize draw state (called before each draw frame)\n\t */\n\tinitDraw(): void {\n\t\tthis.alpha = 1;\n\t\tthis.line_width = 1;\n\t\t// Note: Supersampling not implemented in l8b\n\t\t// If needed, add: if (this.supersampling != this.previous_supersampling) { this.resize(); this.previous_supersampling = this.supersampling; }\n\t}\n\n\t/**\n\t * Update interface dimensions (called before each draw frame)\n\t */\n\tupdateInterface(): void {\n\t\t// Update interface cache if it exists\n\t\tif (this.interfaceCache) {\n\t\t\tthis.interfaceCache.width = this.width;\n\t\t\tthis.interfaceCache.height = this.height;\n\t\t}\n\t}\n\n\tclear(color?: string): void {\n\t\tthis.context.globalAlpha = 1;\n\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\tthis.context.fillStyle = color || \"#000\";\n\t\tthis.context.strokeStyle = color || \"#000\";\n\t\tthis.context.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);\n\t\tthis.zBuffer.clear();\n\t}\n\n\tsetColor(color: string | number): void {\n\t\tif (!color) return;\n\n\t\tif (!Number.isNaN(Number.parseInt(String(color)))) {\n\t\t\tconst num = Number.parseInt(String(color));\n\t\t\tconst r = ((Math.floor(num / 100) % 10) / 9) * 255;\n\t\t\tconst g = ((Math.floor(num / 10) % 10) / 9) * 255;\n\t\t\tconst b = ((num % 10) / 9) * 255;\n\t\t\tconst c = 0xff000000 + (r << 16) + (g << 8) + b;\n\t\t\tconst hex = \"#\" + c.toString(16).substring(2, 8);\n\t\t\tthis.context.fillStyle = hex;\n\t\t\tthis.context.strokeStyle = hex;\n\t\t} else if (typeof color === \"string\") {\n\t\t\t// Validate color format\n\t\t\tconst isValidColor =\n\t\t\t\t/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) ||\n\t\t\t\t/^rgb\\(|^rgba\\(|^hsl\\(|^hsla\\(/.test(color) ||\n\t\t\t\t/^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);\n\n\t\t\tif (!isValidColor) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7003, { color });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.context.fillStyle = color;\n\t\t\tthis.context.strokeStyle = color;\n\t\t}\n\t}\n\n\tsetAlpha(alpha: number): void {\n\t\tthis.alpha = alpha;\n\t}\n\n\tsetPixelated(pixelated: number): void {\n\t\tthis.pixelated = pixelated;\n\t}\n\n\tsetBlending(blending: string): void {\n\t\tconst blend = this.blending[blending || \"normal\"];\n\n\t\tif (!blend) {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7007, { blendMode: blending });\n\t\t\t// Fallback to normal blend mode\n\t\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\t\treturn;\n\t\t}\n\n\t\tthis.context.globalCompositeOperation = blend as GlobalCompositeOperation;\n\t}\n\n\tsetLineWidth(width: number): void {\n\t\tthis.line_width = width;\n\t}\n\n\tsetLineDash(dash: number[] | null): void {\n\t\tif (!Array.isArray(dash)) {\n\t\t\tthis.context.setLineDash([]);\n\t\t} else {\n\t\t\tthis.context.setLineDash(dash);\n\t\t}\n\t}\n\n\tsetLinearGradient(x1: number, y1: number, x2: number, y2: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createLinearGradient(x1, -y1, x2, -y2);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetRadialGradient(x: number, y: number, radius: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createRadialGradient(x, -y, 0, x, -y, radius);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetFont(font: string): void {\n\t\tthis.font = font || \"Verdana\";\n\t\tthis.loadFont(this.font);\n\t}\n\n\tloadFont(font: string = \"BitCell\"): void {\n\t\tif (this.font_load_requested[font]) {\n\t\t\treturn;\n\t\t}\n\t\tthis.font_load_requested[font] = true;\n\t\ttry {\n\t\t\tdocument.fonts?.load?.(`16pt ${font}`).catch(() => {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7006, { font });\n\t\t\t});\n\t\t} catch {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7006, { font });\n\t\t}\n\t}\n\n\tisFontReady(font: string = this.font): number {\n\t\tif (this.font_loaded[font]) {\n\t\t\treturn 1;\n\t\t}\n\n\t\ttry {\n\t\t\tconst ready = document.fonts?.check?.(`16pt ${font}`) ?? true;\n\t\t\tif (ready) {\n\t\t\t\tthis.font_loaded[font] = true;\n\t\t\t}\n\t\t\treturn ready ? 1 : 0;\n\t\t} catch {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tsetTranslation(tx: number, ty: number): void {\n\t\tthis.translation_x = isFinite(tx) ? tx : 0;\n\t\tthis.translation_y = isFinite(ty) ? ty : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetScale(x: number, y: number): void {\n\t\tthis.scale_x = isFinite(x) && x !== 0 ? x : 1;\n\t\tthis.scale_y = isFinite(y) && y !== 0 ? y : 1;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetRotation(rotation: number): void {\n\t\tthis.rotation = isFinite(rotation) ? rotation : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tprotected updateScreenTransform(): void {\n\t\tthis.screen_transform =\n\t\t\tthis.translation_x !== 0 ||\n\t\t\tthis.translation_y !== 0 ||\n\t\t\tthis.scale_x !== 1 ||\n\t\t\tthis.scale_y !== 1 ||\n\t\t\tthis.rotation !== 0;\n\t}\n\n\tsetDrawAnchor(ax: number, ay: number): void {\n\t\tthis.anchor_x = typeof ax === \"number\" ? ax : 0;\n\t\tthis.anchor_y = typeof ay === \"number\" ? ay : 0;\n\t}\n\n\tsetDrawRotation(rotation: number): void {\n\t\tthis.object_rotation = rotation;\n\t}\n\n\tsetDrawScale(x: number, y: number = x): void {\n\t\tthis.object_scale_x = x;\n\t\tthis.object_scale_y = y;\n\t}\n\n\tprotected initDrawOp(x: number, y: number, object_transform: boolean = true): boolean {\n\t\tlet res = false;\n\n\t\tif (this.screen_transform) {\n\t\t\tthis.context.save();\n\t\t\tres = true;\n\t\t\tthis.context.translate(this.translation_x, -this.translation_y);\n\t\t\tthis.context.scale(this.scale_x, this.scale_y);\n\t\t\tthis.context.rotate((-this.rotation / 180) * Math.PI);\n\t\t\tthis.context.translate(x, y);\n\t\t}\n\n\t\tif (object_transform && (this.object_rotation !== 0 || this.object_scale_x !== 1 || this.object_scale_y !== 1)) {\n\t\t\tif (!res) {\n\t\t\t\tthis.context.save();\n\t\t\t\tres = true;\n\t\t\t\tthis.context.translate(x, y);\n\t\t\t}\n\n\t\t\tif (this.object_rotation !== 0) {\n\t\t\t\tthis.context.rotate((-this.object_rotation / 180) * Math.PI);\n\t\t\t}\n\n\t\t\tif (this.object_scale_x !== 1 || this.object_scale_y !== 1) {\n\t\t\t\tthis.context.scale(this.object_scale_x, this.object_scale_y);\n\t\t\t}\n\t\t}\n\n\t\treturn res;\n\t}\n\n\tprotected closeDrawOp(): void {\n\t\tthis.context.restore();\n\t}\n\n\t/**\n\t * Check mouse cursor visibility\n\t * Auto-hides cursor after 4 seconds of inactivity\n\t */\n\tprotected checkMouseCursor(): void {\n\t\tif (Date.now() > this.last_mouse_move + 4000 && this.cursor_visibility === \"auto\") {\n\t\t\tif (this.cursor !== \"none\") {\n\t\t\t\tthis.cursor = \"none\";\n\t\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Set cursor visibility\n\t */\n\tsetCursorVisible(visible: boolean): void {\n\t\tthis.cursor_visibility = visible ? \"default\" : \"none\";\n\t\tif (visible) {\n\t\t\tthis.cursor = \"default\";\n\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t} else {\n\t\t\tthis.cursor = \"none\";\n\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t}\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\treturn this.canvas;\n\t}\n\n\tgetContext(): CanvasRenderingContext2D {\n\t\treturn this.context;\n\t}\n\n\tresize(width?: number, height?: number): void {\n\t\tif (width && height) {\n\t\t\t// Validate dimensions\n\t\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7002, { width, height });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.canvas.width = width;\n\t\t\tthis.canvas.height = height;\n\t\t\tthis.initContext();\n\t\t\tthis.zBuffer.resize(width, height);\n\t\t\t// Update interface cache immediately after resize\n\t\t\tthis.updateInterface();\n\t\t}\n\t}\n}\n","/**\n * TTRI - Textured Triangle Rendering (Software Rasterization)\n *\n * Based on TIC-80's ttri implementation for 3D-style graphics.\n * This is NOT a 3D engine - it's pure 2D pixel manipulation using Canvas 2D API.\n *\n * How it works:\n * 1. Use getImageData() to get pixel buffer\n * 2. Rasterize triangle pixel-by-pixel (barycentric coordinates)\n * 3. Interpolate UV texture coordinates (perspective-correct with 1/z)\n * 4. Sample texture and write to pixel buffer\n * 5. Use putImageData() to update canvas\n *\n * This is software rendering like Doom, Quake software mode, and PlayStation 1.\n * No WebGL, no GPU - just CPU pixel manipulation.\n */\n\nimport type { TileMap as Map } from \"@al8b/map\";\nimport type { Sprite } from \"@al8b/sprites\";\n\nexport interface Vec2 {\n\tx: number;\n\ty: number;\n}\n\nexport interface Vec3 {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\nexport interface TexVert {\n\tx: number;\n\ty: number;\n\tu: number;\n\tv: number;\n\tz: number;\n}\n\nexport type TextureSource = \"tiles\" | \"map\" | \"screen\";\n\nexport interface TriangleData {\n\tcontext: CanvasRenderingContext2D;\n\twidth: number;\n\theight: number;\n\truntime?: any;\n\tpixelated: boolean;\n}\n\n/**\n * Z-Buffer for depth testing\n */\nexport class ZBuffer {\n\tprivate buffer: Float32Array;\n\tprivate width: number;\n\tprivate height: number;\n\n\tconstructor(width: number, height: number) {\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.buffer = new Float32Array(width * height);\n\t}\n\n\tclear(): void {\n\t\tthis.buffer.fill(0);\n\t}\n\n\tget(x: number, y: number): number {\n\t\treturn this.buffer[y * this.width + x] || 0;\n\t}\n\n\tset(x: number, y: number, z: number): void {\n\t\tthis.buffer[y * this.width + x] = z;\n\t}\n\n\tresize(width: number, height: number): void {\n\t\tif (this.width !== width || this.height !== height) {\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.buffer = new Float32Array(width * height);\n\t\t}\n\t}\n}\n\n/**\n * Edge function for triangle rasterization\n */\nfunction edgeFn(a: Vec2, b: Vec2, c: Vec2): number {\n\treturn (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\n/**\n * Get pixel from sprite/image\n */\nfunction getSpritePixel(\n\tsprite: Sprite | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Resolve sprite canvas from object or runtime registry\n\tlet canvas: HTMLCanvasElement | null = null;\n\n\tif (sprite && typeof sprite === \"object\" && sprite.canvas) {\n\t\tcanvas = sprite.canvas;\n\t} else if (typeof sprite === \"string\" && runtime?.sprites) {\n\t\tconst spriteObj = runtime.sprites[sprite];\n\t\tif (spriteObj?.frames?.[0]?.canvas) {\n\t\t\tcanvas = spriteObj.frames[0].canvas;\n\t\t}\n\t}\n\n\tif (!canvas) return null;\n\n\t// Apply texture coordinate wrapping (repeat mode)\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Sample pixel color from sprite canvas\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Get pixel from map\n */\nfunction getMapPixel(\n\tmap: Map | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Get map object\n\tlet mapObj: any = null;\n\n\tif (map && typeof map === \"object\" && map.getCanvas) {\n\t\tmapObj = map;\n\t} else if (typeof map === \"string\" && runtime?.maps) {\n\t\tmapObj = runtime.maps[map];\n\t}\n\n\tif (!mapObj) return null;\n\n\t// Get canvas from map\n\tconst canvas = mapObj.getCanvas ? mapObj.getCanvas() : mapObj.canvas;\n\tif (!canvas) return null;\n\n\t// Wrap texture coordinates\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Get pixel data\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Draw textured triangle with perspective correction\n */\nexport function drawTexturedTriangle(\n\tdata: TriangleData,\n\tv0: TexVert,\n\tv1: TexVert,\n\tv2: TexVert,\n\ttexture: Sprite | Map | string | any,\n\ttextureSource: TextureSource = \"tiles\",\n\tzBuffer?: ZBuffer,\n\tuseDepth: boolean = false,\n): void {\n\tconst { context, width, height, runtime, pixelated } = data;\n\n\t// Get bounding box\n\tconst minX = Math.max(0, Math.floor(Math.min(v0.x, v1.x, v2.x)));\n\tconst minY = Math.max(0, Math.floor(Math.min(v0.y, v1.y, v2.y)));\n\tconst maxX = Math.min(width, Math.ceil(Math.max(v0.x, v1.x, v2.x)));\n\tconst maxY = Math.min(height, Math.ceil(Math.max(v0.y, v1.y, v2.y)));\n\n\tif (minX >= maxX || minY >= maxY) return;\n\n\t// Calculate triangle area\n\tconst area = edgeFn(v0, v1, v2);\n\tif (Math.abs(area) < 0.001) return;\n\n\t// Backface culling\n\tif (area < 0) return;\n\n\t// Prepare perspective-correct interpolation\n\tconst useZ = useDepth && v0.z > 0 && v1.z > 0 && v2.z > 0;\n\n\tlet w0 = 1,\n\t\tw1 = 1,\n\t\tw2 = 1;\n\tlet u0 = v0.u,\n\t\tu1 = v1.u,\n\t\tu2 = v2.u;\n\tlet v0v = v0.v,\n\t\tv1v = v1.v,\n\t\tv2v = v2.v;\n\n\tif (useZ) {\n\t\tw0 = 1 / v0.z;\n\t\tw1 = 1 / v1.z;\n\t\tw2 = 1 / v2.z;\n\t\tu0 *= w0;\n\t\tu1 *= w1;\n\t\tu2 *= w2;\n\t\tv0v *= w0;\n\t\tv1v *= w1;\n\t\tv2v *= w2;\n\t}\n\n\t// Get image data for fast pixel manipulation\n\tconst imageData = context.getImageData(minX, minY, maxX - minX, maxY - minY);\n\tconst pixels = imageData.data;\n\n\t// Rasterize\n\tfor (let y = minY; y < maxY; y++) {\n\t\tfor (let x = minX; x < maxX; x++) {\n\t\t\tconst p = {\n\t\t\t\tx: x + 0.5,\n\t\t\t\ty: y + 0.5,\n\t\t\t};\n\n\t\t\t// Calculate barycentric coordinates\n\t\t\tconst w0b = edgeFn(v1, v2, p);\n\t\t\tconst w1b = edgeFn(v2, v0, p);\n\t\t\tconst w2b = edgeFn(v0, v1, p);\n\n\t\t\t// Check if point is inside triangle\n\t\t\tif (w0b >= 0 && w1b >= 0 && w2b >= 0) {\n\t\t\t\t// Normalize barycentric coordinates\n\t\t\t\tconst bary0 = w0b / area;\n\t\t\t\tconst bary1 = w1b / area;\n\t\t\t\tconst bary2 = w2b / area;\n\n\t\t\t\t// Depth test\n\t\t\t\tif (useZ && zBuffer) {\n\t\t\t\t\tconst z = bary0 * v0.z + bary1 * v1.z + bary2 * v2.z;\n\t\t\t\t\tconst currentZ = zBuffer.get(x, y);\n\t\t\t\t\tif (currentZ > 0 && currentZ >= z) continue;\n\t\t\t\t\tzBuffer.set(x, y, z);\n\t\t\t\t}\n\n\t\t\t\t// Interpolate texture coordinates\n\t\t\t\tlet u: number, v: number;\n\n\t\t\t\tif (useZ) {\n\t\t\t\t\tconst w = bary0 * w0 + bary1 * w1 + bary2 * w2;\n\t\t\t\t\tu = (bary0 * u0 + bary1 * u1 + bary2 * u2) / w;\n\t\t\t\t\tv = (bary0 * v0v + bary1 * v1v + bary2 * v2v) / w;\n\t\t\t\t} else {\n\t\t\t\t\tu = bary0 * v0.u + bary1 * v1.u + bary2 * v2.u;\n\t\t\t\t\tv = bary0 * v0.v + bary1 * v1.v + bary2 * v2.v;\n\t\t\t\t}\n\n\t\t\t\t// Sample texture\n\t\t\t\tlet pixel: {\n\t\t\t\t\tr: number;\n\t\t\t\t\tg: number;\n\t\t\t\t\tb: number;\n\t\t\t\t\ta: number;\n\t\t\t\t} | null = null;\n\n\t\t\t\tif (textureSource === \"map\") {\n\t\t\t\t\tpixel = getMapPixel(texture, u, v, runtime);\n\t\t\t\t} else {\n\t\t\t\t\tpixel = getSpritePixel(texture, u, v, runtime);\n\t\t\t\t}\n\n\t\t\t\t// Draw pixel\n\t\t\t\tif (pixel && pixel.a > 0) {\n\t\t\t\t\tconst idx = ((y - minY) * (maxX - minX) + (x - minX)) * 4;\n\t\t\t\t\tpixels[idx] = pixel.r;\n\t\t\t\t\tpixels[idx + 1] = pixel.g;\n\t\t\t\t\tpixels[idx + 2] = pixel.b;\n\t\t\t\t\tpixels[idx + 3] = pixel.a;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Put image data back\n\tcontext.imageSmoothingEnabled = !pixelated;\n\tcontext.putImageData(imageData, minX, minY);\n}\n\n/**\n * Draw solid color triangle\n */\nexport function drawTriangle(context: CanvasRenderingContext2D, v0: Vec2, v1: Vec2, v2: Vec2, color: string): void {\n\tcontext.fillStyle = color;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\n/**\n * Draw triangle outline\n */\nexport function drawTriangleOutline(\n\tcontext: CanvasRenderingContext2D,\n\tv0: Vec2,\n\tv1: Vec2,\n\tv2: Vec2,\n\tcolor: string,\n\tlineWidth: number = 1,\n): void {\n\tcontext.strokeStyle = color;\n\tcontext.lineWidth = lineWidth;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.stroke();\n}\n"],"mappings":"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","names":["APIErrorCode","createDiagnostic","formatForBrowser","reportRuntimeError","ZBuffer","buffer","width","height","Float32Array","clear","fill","get","x","y","set","z","resize","BaseScreen","canvas","context","runtime","width","height","alpha","pixelated","line_width","font","translation_x","translation_y","rotation","scale_x","scale_y","screen_transform","object_rotation","object_scale_x","object_scale_y","anchor_x","anchor_y","blending","font_load_requested","font_loaded","interfaceCache","cursor","cursor_visibility","last_mouse_move","Date","now","zBuffer","options","document","createElement","initContext","normal","additive","blendModes","mode","loadFont","ZBuffer","addEventListener","style","setInterval","checkMouseCursor","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7001","formatted","formatForBrowser","reportRuntimeError","listener","Error","restore","save","translate","ratio","Math","min","scale","lineCap","initDraw","updateInterface","clear","color","globalAlpha","globalCompositeOperation","fillStyle","strokeStyle","fillRect","setColor","Number","isNaN","parseInt","String","num","r","floor","g","b","c","hex","toString","substring","isValidColor","test","E7003","setAlpha","setPixelated","setBlending","blend","E7007","blendMode","setLineWidth","setLineDash","dash","Array","isArray","setLinearGradient","x1","y1","x2","y2","c1","c2","grd","createLinearGradient","addColorStop","setRadialGradient","x","y","radius","createRadialGradient","setFont","fonts","load","catch","E7006","isFontReady","ready","check","setTranslation","tx","ty","isFinite","updateScreenTransform","setScale","setRotation","setDrawAnchor","ax","ay","setDrawRotation","setDrawScale","initDrawOp","object_transform","res","rotate","PI","closeDrawOp","setCursorVisible","visible","getCanvas","resize","E7002"]}
1
+ {"version":3,"sources":["../../src/tri/ttri.ts","../../src/core/base-screen.ts"],"sourcesContent":["/**\n * TTRI - Textured Triangle Rendering (Software Rasterization)\n *\n * Based on TIC-80's ttri implementation for 3D-style graphics.\n * This is NOT a 3D engine - it's pure 2D pixel manipulation using Canvas 2D API.\n *\n * How it works:\n * 1. Use getImageData() to get pixel buffer\n * 2. Rasterize triangle pixel-by-pixel (barycentric coordinates)\n * 3. Interpolate UV texture coordinates (perspective-correct with 1/z)\n * 4. Sample texture and write to pixel buffer\n * 5. Use putImageData() to update canvas\n *\n * This is software rendering like Doom, Quake software mode, and PlayStation 1.\n * No WebGL, no GPU - just CPU pixel manipulation.\n */\n\nimport type { TileMap as Map } from \"@al8b/map\";\nimport type { Sprite } from \"@al8b/sprites\";\n\nexport interface Vec2 {\n\tx: number;\n\ty: number;\n}\n\nexport interface Vec3 {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\nexport interface TexVert {\n\tx: number;\n\ty: number;\n\tu: number;\n\tv: number;\n\tz: number;\n}\n\nexport type TextureSource = \"tiles\" | \"map\" | \"screen\";\n\nexport interface TriangleData {\n\tcontext: CanvasRenderingContext2D;\n\twidth: number;\n\theight: number;\n\truntime?: any;\n\tpixelated: boolean;\n}\n\n/**\n * Z-Buffer for depth testing\n */\nexport class ZBuffer {\n\tprivate buffer: Float32Array;\n\tprivate width: number;\n\tprivate height: number;\n\n\tconstructor(width: number, height: number) {\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.buffer = new Float32Array(width * height);\n\t}\n\n\tclear(): void {\n\t\tthis.buffer.fill(0);\n\t}\n\n\tget(x: number, y: number): number {\n\t\treturn this.buffer[y * this.width + x] || 0;\n\t}\n\n\tset(x: number, y: number, z: number): void {\n\t\tthis.buffer[y * this.width + x] = z;\n\t}\n\n\tresize(width: number, height: number): void {\n\t\tif (this.width !== width || this.height !== height) {\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.buffer = new Float32Array(width * height);\n\t\t}\n\t}\n}\n\n/**\n * Edge function for triangle rasterization\n */\nfunction edgeFn(a: Vec2, b: Vec2, c: Vec2): number {\n\treturn (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\n/**\n * Get pixel from sprite/image\n */\nfunction getSpritePixel(\n\tsprite: Sprite | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Resolve sprite canvas from object or runtime registry\n\tlet canvas: HTMLCanvasElement | null = null;\n\n\tif (sprite && typeof sprite === \"object\" && sprite.canvas) {\n\t\tcanvas = sprite.canvas;\n\t} else if (typeof sprite === \"string\" && runtime?.sprites) {\n\t\tconst spriteObj = runtime.sprites[sprite];\n\t\tif (spriteObj?.frames?.[0]?.canvas) {\n\t\t\tcanvas = spriteObj.frames[0].canvas;\n\t\t}\n\t}\n\n\tif (!canvas) return null;\n\n\t// Apply texture coordinate wrapping (repeat mode)\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Sample pixel color from sprite canvas\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Get pixel from map\n */\nfunction getMapPixel(\n\tmap: Map | any,\n\tu: number,\n\tv: number,\n\truntime?: any,\n): {\n\tr: number;\n\tg: number;\n\tb: number;\n\ta: number;\n} | null {\n\t// Get map object\n\tlet mapObj: any = null;\n\n\tif (map && typeof map === \"object\" && map.getCanvas) {\n\t\tmapObj = map;\n\t} else if (typeof map === \"string\" && runtime?.maps) {\n\t\tmapObj = runtime.maps[map];\n\t}\n\n\tif (!mapObj) return null;\n\n\t// Get canvas from map\n\tconst canvas = mapObj.getCanvas ? mapObj.getCanvas() : mapObj.canvas;\n\tif (!canvas) return null;\n\n\t// Wrap texture coordinates\n\tconst width = canvas.width;\n\tconst height = canvas.height;\n\tconst x = Math.floor(u) % width;\n\tconst y = Math.floor(v) % height;\n\tconst px = x < 0 ? x + width : x;\n\tconst py = y < 0 ? y + height : y;\n\n\t// Get pixel data\n\tconst ctx = canvas.getContext(\"2d\");\n\tif (!ctx) return null;\n\n\ttry {\n\t\tconst imageData = ctx.getImageData(px, py, 1, 1);\n\t\treturn {\n\t\t\tr: imageData.data[0],\n\t\t\tg: imageData.data[1],\n\t\t\tb: imageData.data[2],\n\t\t\ta: imageData.data[3],\n\t\t};\n\t} catch (e) {\n\t\treturn null;\n\t}\n}\n\n/**\n * Draw textured triangle with perspective correction\n */\nexport function drawTexturedTriangle(\n\tdata: TriangleData,\n\tv0: TexVert,\n\tv1: TexVert,\n\tv2: TexVert,\n\ttexture: Sprite | Map | string | any,\n\ttextureSource: TextureSource = \"tiles\",\n\tzBuffer?: ZBuffer,\n\tuseDepth: boolean = false,\n): void {\n\tconst { context, width, height, runtime, pixelated } = data;\n\n\t// Get bounding box\n\tconst minX = Math.max(0, Math.floor(Math.min(v0.x, v1.x, v2.x)));\n\tconst minY = Math.max(0, Math.floor(Math.min(v0.y, v1.y, v2.y)));\n\tconst maxX = Math.min(width, Math.ceil(Math.max(v0.x, v1.x, v2.x)));\n\tconst maxY = Math.min(height, Math.ceil(Math.max(v0.y, v1.y, v2.y)));\n\n\tif (minX >= maxX || minY >= maxY) return;\n\n\t// Calculate triangle area\n\tconst area = edgeFn(v0, v1, v2);\n\tif (Math.abs(area) < 0.001) return;\n\n\t// Backface culling\n\tif (area < 0) return;\n\n\t// Prepare perspective-correct interpolation\n\tconst useZ = useDepth && v0.z > 0 && v1.z > 0 && v2.z > 0;\n\n\tlet w0 = 1,\n\t\tw1 = 1,\n\t\tw2 = 1;\n\tlet u0 = v0.u,\n\t\tu1 = v1.u,\n\t\tu2 = v2.u;\n\tlet v0v = v0.v,\n\t\tv1v = v1.v,\n\t\tv2v = v2.v;\n\n\tif (useZ) {\n\t\tw0 = 1 / v0.z;\n\t\tw1 = 1 / v1.z;\n\t\tw2 = 1 / v2.z;\n\t\tu0 *= w0;\n\t\tu1 *= w1;\n\t\tu2 *= w2;\n\t\tv0v *= w0;\n\t\tv1v *= w1;\n\t\tv2v *= w2;\n\t}\n\n\t// Get image data for fast pixel manipulation\n\tconst imageData = context.getImageData(minX, minY, maxX - minX, maxY - minY);\n\tconst pixels = imageData.data;\n\n\t// Rasterize\n\tfor (let y = minY; y < maxY; y++) {\n\t\tfor (let x = minX; x < maxX; x++) {\n\t\t\tconst p = {\n\t\t\t\tx: x + 0.5,\n\t\t\t\ty: y + 0.5,\n\t\t\t};\n\n\t\t\t// Calculate barycentric coordinates\n\t\t\tconst w0b = edgeFn(v1, v2, p);\n\t\t\tconst w1b = edgeFn(v2, v0, p);\n\t\t\tconst w2b = edgeFn(v0, v1, p);\n\n\t\t\t// Check if point is inside triangle\n\t\t\tif (w0b >= 0 && w1b >= 0 && w2b >= 0) {\n\t\t\t\t// Normalize barycentric coordinates\n\t\t\t\tconst bary0 = w0b / area;\n\t\t\t\tconst bary1 = w1b / area;\n\t\t\t\tconst bary2 = w2b / area;\n\n\t\t\t\t// Depth test\n\t\t\t\tif (useZ && zBuffer) {\n\t\t\t\t\tconst z = bary0 * v0.z + bary1 * v1.z + bary2 * v2.z;\n\t\t\t\t\tconst currentZ = zBuffer.get(x, y);\n\t\t\t\t\tif (currentZ > 0 && currentZ >= z) continue;\n\t\t\t\t\tzBuffer.set(x, y, z);\n\t\t\t\t}\n\n\t\t\t\t// Interpolate texture coordinates\n\t\t\t\tlet u: number, v: number;\n\n\t\t\t\tif (useZ) {\n\t\t\t\t\tconst w = bary0 * w0 + bary1 * w1 + bary2 * w2;\n\t\t\t\t\tu = (bary0 * u0 + bary1 * u1 + bary2 * u2) / w;\n\t\t\t\t\tv = (bary0 * v0v + bary1 * v1v + bary2 * v2v) / w;\n\t\t\t\t} else {\n\t\t\t\t\tu = bary0 * v0.u + bary1 * v1.u + bary2 * v2.u;\n\t\t\t\t\tv = bary0 * v0.v + bary1 * v1.v + bary2 * v2.v;\n\t\t\t\t}\n\n\t\t\t\t// Sample texture\n\t\t\t\tlet pixel: {\n\t\t\t\t\tr: number;\n\t\t\t\t\tg: number;\n\t\t\t\t\tb: number;\n\t\t\t\t\ta: number;\n\t\t\t\t} | null = null;\n\n\t\t\t\tif (textureSource === \"map\") {\n\t\t\t\t\tpixel = getMapPixel(texture, u, v, runtime);\n\t\t\t\t} else {\n\t\t\t\t\tpixel = getSpritePixel(texture, u, v, runtime);\n\t\t\t\t}\n\n\t\t\t\t// Draw pixel\n\t\t\t\tif (pixel && pixel.a > 0) {\n\t\t\t\t\tconst idx = ((y - minY) * (maxX - minX) + (x - minX)) * 4;\n\t\t\t\t\tpixels[idx] = pixel.r;\n\t\t\t\t\tpixels[idx + 1] = pixel.g;\n\t\t\t\t\tpixels[idx + 2] = pixel.b;\n\t\t\t\t\tpixels[idx + 3] = pixel.a;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Put image data back\n\tcontext.imageSmoothingEnabled = !pixelated;\n\tcontext.putImageData(imageData, minX, minY);\n}\n\n/**\n * Draw solid color triangle\n */\nexport function drawTriangle(context: CanvasRenderingContext2D, v0: Vec2, v1: Vec2, v2: Vec2, color: string): void {\n\tcontext.fillStyle = color;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.fill();\n}\n\n/**\n * Draw triangle outline\n */\nexport function drawTriangleOutline(\n\tcontext: CanvasRenderingContext2D,\n\tv0: Vec2,\n\tv1: Vec2,\n\tv2: Vec2,\n\tcolor: string,\n\tlineWidth: number = 1,\n): void {\n\tcontext.strokeStyle = color;\n\tcontext.lineWidth = lineWidth;\n\tcontext.beginPath();\n\tcontext.moveTo(v0.x, v0.y);\n\tcontext.lineTo(v1.x, v1.y);\n\tcontext.lineTo(v2.x, v2.y);\n\tcontext.closePath();\n\tcontext.stroke();\n}\n","import { ZBuffer } from \"../tri\";\nimport type { ScreenInterface, ScreenOptions } from \"../types\";\n\n/**\n * BaseScreen encapsulates canvas/context management plus shared drawing state.\n * Feature-specific behaviors (primitives, sprites, text, triangles) extend this class.\n */\nexport class BaseScreen {\n\tprotected canvas: HTMLCanvasElement;\n\tprotected context!: CanvasRenderingContext2D;\n\tprotected runtime: any;\n\n\tpublic width!: number;\n\tpublic height!: number;\n\n\t// Drawing state\n\tprotected alpha = 1;\n\tprotected pixelated = 1;\n\tprotected line_width = 1;\n\tprotected font = \"BitCell\";\n\n\t// Transformations\n\tprotected translation_x = 0;\n\tprotected translation_y = 0;\n\tprotected rotation = 0;\n\tprotected scale_x = 1;\n\tprotected scale_y = 1;\n\tprotected screen_transform = false;\n\n\t// Object transformations\n\tprotected object_rotation = 0;\n\tprotected object_scale_x = 1;\n\tprotected object_scale_y = 1;\n\tprotected anchor_x = 0;\n\tprotected anchor_y = 0;\n\n\t// Blending + font caches\n\tprotected blending: Record<string, string> = {};\n\tprotected font_load_requested: Record<string, boolean> = {};\n\tprotected font_loaded: Record<string, boolean> = {};\n\n\t// Interface cache\n\tprotected interfaceCache: ScreenInterface | null = null;\n\n\t// Cursor management\n\tprotected cursor: string = \"default\";\n\tprotected cursor_visibility: string = \"auto\";\n\tprotected last_mouse_move: number = Date.now();\n\n\t// 3D helper\n\tprotected zBuffer: ZBuffer;\n\n\tconstructor(options: ScreenOptions = {}) {\n\t\tthis.runtime = options.runtime;\n\n\t\tif (options.canvas) {\n\t\t\tthis.canvas = options.canvas;\n\t\t\tif (this.canvas.width === 0 || this.canvas.height === 0) {\n\t\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\t\tthis.canvas.height = options.height || 1920;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.canvas = document.createElement(\"canvas\");\n\t\t\tthis.canvas.width = options.width || 1080;\n\t\t\tthis.canvas.height = options.height || 1920;\n\t\t}\n\n\t\tthis.initContext();\n\n\t\tthis.blending = {\n\t\t\tnormal: \"source-over\",\n\t\t\tadditive: \"lighter\",\n\t\t};\n\n\t\tconst blendModes = [\n\t\t\t\"source-over\",\n\t\t\t\"source-in\",\n\t\t\t\"source-out\",\n\t\t\t\"source-atop\",\n\t\t\t\"destination-over\",\n\t\t\t\"destination-in\",\n\t\t\t\"destination-out\",\n\t\t\t\"destination-atop\",\n\t\t\t\"lighter\",\n\t\t\t\"copy\",\n\t\t\t\"xor\",\n\t\t\t\"multiply\",\n\t\t\t\"screen\",\n\t\t\t\"overlay\",\n\t\t\t\"darken\",\n\t\t\t\"lighten\",\n\t\t\t\"color-dodge\",\n\t\t\t\"color-burn\",\n\t\t\t\"hard-light\",\n\t\t\t\"soft-light\",\n\t\t\t\"difference\",\n\t\t\t\"exclusion\",\n\t\t\t\"hue\",\n\t\t\t\"saturation\",\n\t\t\t\"color\",\n\t\t\t\"luminosity\",\n\t\t];\n\n\t\tfor (const mode of blendModes) {\n\t\t\tthis.blending[mode] = mode;\n\t\t}\n\n\t\tthis.loadFont(this.font);\n\t\tthis.zBuffer = new ZBuffer(this.canvas.width, this.canvas.height);\n\n\t\tthis.cursor = \"default\";\n\n\t\tthis.canvas.addEventListener(\"mousemove\", () => {\n\t\t\tthis.last_mouse_move = Date.now();\n\t\t\tif (this.cursor !== \"default\" && this.cursor_visibility === \"auto\") {\n\t\t\t\tthis.cursor = \"default\";\n\t\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t\t}\n\t\t});\n\n\t\t// When the context is lost and then restored, base transform needs to be reinstated\n\t\tthis.canvas.addEventListener(\"contextrestored\", () => {\n\t\t\tthis.initContext();\n\t\t});\n\n\t\tsetInterval(() => this.checkMouseCursor(), 1000);\n\t\tthis.cursor_visibility = \"auto\";\n\t}\n\n\tprotected initContext(): void {\n\t\tconst ctx = this.canvas.getContext(\"2d\", {\n\t\t\talpha: false,\n\t\t});\n\t\tif (!ctx) {\n\t\t\tconst message = \"Failed to get 2D rendering context\";\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7001\", message, data: {} });\n\n\t\t\tthrow new Error(message);\n\t\t}\n\n\t\tif (ctx !== this.context) {\n\t\t\tthis.context = ctx;\n\t\t} else {\n\t\t\tthis.context.restore();\n\t\t}\n\n\t\tthis.context.save();\n\t\tthis.context.translate(this.canvas.width / 2, this.canvas.height / 2);\n\n\t\t// Calculate ratio: Math.min(canvas.width/200, canvas.height/200)\n\t\tconst ratio = Math.min(this.canvas.width / 200, this.canvas.height / 200);\n\t\tthis.context.scale(ratio, ratio);\n\n\t\t// Set logical width/height\n\t\tthis.width = this.canvas.width / ratio;\n\t\tthis.height = this.canvas.height / ratio;\n\t\tthis.context.lineCap = \"round\";\n\t}\n\n\t/**\n\t * Initialize draw state (called before each draw frame)\n\t */\n\tinitDraw(): void {\n\t\tthis.alpha = 1;\n\t\tthis.line_width = 1;\n\t\t// Note: Supersampling not implemented in l8b\n\t\t// If needed, add: if (this.supersampling != this.previous_supersampling) { this.resize(); this.previous_supersampling = this.supersampling; }\n\t}\n\n\t/**\n\t * Update interface dimensions (called before each draw frame)\n\t */\n\tupdateInterface(): void {\n\t\t// Update interface cache if it exists\n\t\tif (this.interfaceCache) {\n\t\t\tthis.interfaceCache.width = this.width;\n\t\t\tthis.interfaceCache.height = this.height;\n\t\t}\n\t}\n\n\tclear(color?: string): void {\n\t\tthis.context.globalAlpha = 1;\n\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\tthis.context.fillStyle = color || \"#000\";\n\t\tthis.context.strokeStyle = color || \"#000\";\n\t\tthis.context.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);\n\t\tthis.zBuffer.clear();\n\t}\n\n\tsetColor(color: string | number): void {\n\t\tif (!color) return;\n\n\t\tif (!Number.isNaN(Number.parseInt(String(color)))) {\n\t\t\tconst num = Number.parseInt(String(color));\n\t\t\tconst r = ((Math.floor(num / 100) % 10) / 9) * 255;\n\t\t\tconst g = ((Math.floor(num / 10) % 10) / 9) * 255;\n\t\t\tconst b = ((num % 10) / 9) * 255;\n\t\t\tconst c = 0xff000000 + (r << 16) + (g << 8) + b;\n\t\t\tconst hex = \"#\" + c.toString(16).substring(2, 8);\n\t\t\tthis.context.fillStyle = hex;\n\t\t\tthis.context.strokeStyle = hex;\n\t\t} else if (typeof color === \"string\") {\n\t\t\t// Validate color format\n\t\t\tconst isValidColor =\n\t\t\t\t/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) ||\n\t\t\t\t/^rgb\\(|^rgba\\(|^hsl\\(|^hsla\\(/.test(color) ||\n\t\t\t\t/^(red|green|blue|yellow|cyan|magenta|black|white|gray|grey|orange|pink|purple|brown|transparent)$/i.test(color);\n\n\t\t\tif (!isValidColor) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7003\", message: \"Invalid color\", data: { color } });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.context.fillStyle = color;\n\t\t\tthis.context.strokeStyle = color;\n\t\t}\n\t}\n\n\tsetAlpha(alpha: number): void {\n\t\tthis.alpha = alpha;\n\t}\n\n\tsetPixelated(pixelated: number): void {\n\t\tthis.pixelated = pixelated;\n\t}\n\n\tsetBlending(blending: string): void {\n\t\tconst blend = this.blending[blending || \"normal\"];\n\n\t\tif (!blend) {\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7007\", message: \"Invalid blend mode\", data: { blendMode: blending } });\n\t\t\t// Fallback to normal blend mode\n\t\t\tthis.context.globalCompositeOperation = \"source-over\";\n\t\t\treturn;\n\t\t}\n\n\t\tthis.context.globalCompositeOperation = blend as GlobalCompositeOperation;\n\t}\n\n\tsetLineWidth(width: number): void {\n\t\tthis.line_width = width;\n\t}\n\n\tsetLineDash(dash: number[] | null): void {\n\t\tif (!Array.isArray(dash)) {\n\t\t\tthis.context.setLineDash([]);\n\t\t} else {\n\t\t\tthis.context.setLineDash(dash);\n\t\t}\n\t}\n\n\tsetLinearGradient(x1: number, y1: number, x2: number, y2: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createLinearGradient(x1, -y1, x2, -y2);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetRadialGradient(x: number, y: number, radius: number, c1: string, c2: string): void {\n\t\tconst grd = this.context.createRadialGradient(x, -y, 0, x, -y, radius);\n\t\tgrd.addColorStop(0, c1);\n\t\tgrd.addColorStop(1, c2);\n\t\tthis.context.fillStyle = grd;\n\t\tthis.context.strokeStyle = grd;\n\t}\n\n\tsetFont(font: string): void {\n\t\tthis.font = font || \"Verdana\";\n\t\tthis.loadFont(this.font);\n\t}\n\n\tloadFont(font: string = \"BitCell\"): void {\n\t\tif (this.font_load_requested[font]) {\n\t\t\treturn;\n\t\t}\n\t\tthis.font_load_requested[font] = true;\n\t\ttry {\n\t\t\tdocument.fonts?.load?.(`16pt ${font}`).catch(() => {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7006\", message: \"Font loading failed\", data: { font } });\n\t\t\t});\n\t\t} catch {\n\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7006\", message: \"Font loading failed\", data: { font } });\n\t\t}\n\t}\n\n\tisFontReady(font: string = this.font): number {\n\t\tif (this.font_loaded[font]) {\n\t\t\treturn 1;\n\t\t}\n\n\t\ttry {\n\t\t\tconst ready = document.fonts?.check?.(`16pt ${font}`) ?? true;\n\t\t\tif (ready) {\n\t\t\t\tthis.font_loaded[font] = true;\n\t\t\t}\n\t\t\treturn ready ? 1 : 0;\n\t\t} catch {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tsetTranslation(tx: number, ty: number): void {\n\t\tthis.translation_x = isFinite(tx) ? tx : 0;\n\t\tthis.translation_y = isFinite(ty) ? ty : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetScale(x: number, y: number): void {\n\t\tthis.scale_x = isFinite(x) && x !== 0 ? x : 1;\n\t\tthis.scale_y = isFinite(y) && y !== 0 ? y : 1;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tsetRotation(rotation: number): void {\n\t\tthis.rotation = isFinite(rotation) ? rotation : 0;\n\t\tthis.updateScreenTransform();\n\t}\n\n\tprotected updateScreenTransform(): void {\n\t\tthis.screen_transform =\n\t\t\tthis.translation_x !== 0 ||\n\t\t\tthis.translation_y !== 0 ||\n\t\t\tthis.scale_x !== 1 ||\n\t\t\tthis.scale_y !== 1 ||\n\t\t\tthis.rotation !== 0;\n\t}\n\n\tsetDrawAnchor(ax: number, ay: number): void {\n\t\tthis.anchor_x = typeof ax === \"number\" ? ax : 0;\n\t\tthis.anchor_y = typeof ay === \"number\" ? ay : 0;\n\t}\n\n\tsetDrawRotation(rotation: number): void {\n\t\tthis.object_rotation = rotation;\n\t}\n\n\tsetDrawScale(x: number, y: number = x): void {\n\t\tthis.object_scale_x = x;\n\t\tthis.object_scale_y = y;\n\t}\n\n\tprotected initDrawOp(x: number, y: number, object_transform: boolean = true): boolean {\n\t\tlet res = false;\n\n\t\tif (this.screen_transform) {\n\t\t\tthis.context.save();\n\t\t\tres = true;\n\t\t\tthis.context.translate(this.translation_x, -this.translation_y);\n\t\t\tthis.context.scale(this.scale_x, this.scale_y);\n\t\t\tthis.context.rotate((-this.rotation / 180) * Math.PI);\n\t\t\tthis.context.translate(x, y);\n\t\t}\n\n\t\tif (object_transform && (this.object_rotation !== 0 || this.object_scale_x !== 1 || this.object_scale_y !== 1)) {\n\t\t\tif (!res) {\n\t\t\t\tthis.context.save();\n\t\t\t\tres = true;\n\t\t\t\tthis.context.translate(x, y);\n\t\t\t}\n\n\t\t\tif (this.object_rotation !== 0) {\n\t\t\t\tthis.context.rotate((-this.object_rotation / 180) * Math.PI);\n\t\t\t}\n\n\t\t\tif (this.object_scale_x !== 1 || this.object_scale_y !== 1) {\n\t\t\t\tthis.context.scale(this.object_scale_x, this.object_scale_y);\n\t\t\t}\n\t\t}\n\n\t\treturn res;\n\t}\n\n\tprotected closeDrawOp(): void {\n\t\tthis.context.restore();\n\t}\n\n\t/**\n\t * Check mouse cursor visibility\n\t * Auto-hides cursor after 4 seconds of inactivity\n\t */\n\tprotected checkMouseCursor(): void {\n\t\tif (Date.now() > this.last_mouse_move + 4000 && this.cursor_visibility === \"auto\") {\n\t\t\tif (this.cursor !== \"none\") {\n\t\t\t\tthis.cursor = \"none\";\n\t\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Set cursor visibility\n\t */\n\tsetCursorVisible(visible: boolean): void {\n\t\tthis.cursor_visibility = visible ? \"default\" : \"none\";\n\t\tif (visible) {\n\t\t\tthis.cursor = \"default\";\n\t\t\tthis.canvas.style.cursor = \"default\";\n\t\t} else {\n\t\t\tthis.cursor = \"none\";\n\t\t\tthis.canvas.style.cursor = \"none\";\n\t\t}\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\treturn this.canvas;\n\t}\n\n\tgetContext(): CanvasRenderingContext2D {\n\t\treturn this.context;\n\t}\n\n\tresize(width?: number, height?: number): void {\n\t\tif (width && height) {\n\t\t\t// Validate dimensions\n\t\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\t\tthis.runtime?.listener?.reportError?.({ code: \"E7002\", message: \"Invalid resize dimensions\", data: { width, height } });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.canvas.width = width;\n\t\t\tthis.canvas.height = height;\n\t\t\tthis.initContext();\n\t\t\tthis.zBuffer.resize(width, height);\n\t\t\t// Update interface cache immediately after resize\n\t\t\tthis.updateInterface();\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["ZBuffer","buffer","width","height","Float32Array","clear","fill","get","x","y","set","z","resize","BaseScreen","canvas","context","runtime","width","height","alpha","pixelated","line_width","font","translation_x","translation_y","rotation","scale_x","scale_y","screen_transform","object_rotation","object_scale_x","object_scale_y","anchor_x","anchor_y","blending","font_load_requested","font_loaded","interfaceCache","cursor","cursor_visibility","last_mouse_move","Date","now","zBuffer","options","document","createElement","initContext","normal","additive","blendModes","mode","loadFont","ZBuffer","addEventListener","style","setInterval","checkMouseCursor","ctx","getContext","message","listener","reportError","code","data","Error","restore","save","translate","ratio","Math","min","scale","lineCap","initDraw","updateInterface","clear","color","globalAlpha","globalCompositeOperation","fillStyle","strokeStyle","fillRect","setColor","Number","isNaN","parseInt","String","num","r","floor","g","b","c","hex","toString","substring","isValidColor","test","setAlpha","setPixelated","setBlending","blend","blendMode","setLineWidth","setLineDash","dash","Array","isArray","setLinearGradient","x1","y1","x2","y2","c1","c2","grd","createLinearGradient","addColorStop","setRadialGradient","x","y","radius","createRadialGradient","setFont","fonts","load","catch","isFontReady","ready","check","setTranslation","tx","ty","isFinite","updateScreenTransform","setScale","setRotation","setDrawAnchor","ax","ay","setDrawRotation","setDrawScale","initDrawOp","object_transform","res","rotate","PI","closeDrawOp","setCursorVisible","visible","getCanvas","resize"]}