@al8b/map 0.1.13 → 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.
@@ -27,10 +27,8 @@ __export(tile_map_exports, {
27
27
  UpdateMap: () => UpdateMap
28
28
  });
29
29
  module.exports = __toCommonJS(tile_map_exports);
30
- var import_diagnostics2 = require("@al8b/diagnostics");
31
30
 
32
31
  // src/drawing/renderer.ts
33
- var import_diagnostics = require("@al8b/diagnostics");
34
32
  var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blockHeight) => {
35
33
  if (!state.canvas) {
36
34
  state.canvas = document.createElement("canvas");
@@ -43,9 +41,8 @@ var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blo
43
41
  }
44
42
  const ctx = state.canvas.getContext("2d");
45
43
  if (!ctx) {
46
- const diagnostic = (0, import_diagnostics.createDiagnostic)(import_diagnostics.APIErrorCode.E7031);
47
- const formatted = (0, import_diagnostics.formatForBrowser)(diagnostic);
48
- throw new Error(formatted);
44
+ const message = "Failed to get 2D rendering context for tile canvas";
45
+ throw new Error(message);
49
46
  }
50
47
  ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
51
48
  state.animated = [];
@@ -151,14 +148,7 @@ var TileMap = class _TileMap {
151
148
  };
152
149
  constructor(width, height, block_width, block_height, sprites) {
153
150
  if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
154
- const diagnostic = (0, import_diagnostics2.createDiagnostic)(import_diagnostics2.APIErrorCode.E7034, {
155
- data: {
156
- width,
157
- height
158
- }
159
- });
160
- const formatted = (0, import_diagnostics2.formatForBrowser)(diagnostic);
161
- throw new Error(formatted);
151
+ throw new Error(`Invalid map dimensions: width=${width}, height=${height}`);
162
152
  }
163
153
  this.width = width;
164
154
  this.height = height;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/tile-map.ts","../../src/drawing/renderer.ts","../../src/shared/references.ts"],"sourcesContent":["/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7034, {\n\t\t\t\tdata: {\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst formatted = formatForBrowser(diagnostic);\n\t\t\tthrow new Error(formatted);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n","import { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7031);\n\t\tconst formatted = formatForBrowser(diagnostic);\n\t\tthrow new Error(formatted);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n"],"mappings":"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","names":["import_diagnostics","ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7031","formatted","formatForBrowser","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","diagnostic","createDiagnostic","APIErrorCode","E7034","data","formatted","formatForBrowser","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
1
+ {"version":3,"sources":["../../src/core/tile-map.ts","../../src/drawing/renderer.ts","../../src/shared/references.ts"],"sourcesContent":["/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tthrow new Error(`Invalid map dimensions: width=${width}, height=${height}`);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n","import type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst message = \"Failed to get 2D rendering context for tile canvas\";\n\t\tthrow new Error(message);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","message","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","data","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
@@ -1,11 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
- // src/core/tile-map.ts
5
- import { APIErrorCode as APIErrorCode2, createDiagnostic as createDiagnostic2, formatForBrowser as formatForBrowser2 } from "@al8b/diagnostics";
6
-
7
4
  // src/drawing/renderer.ts
8
- import { APIErrorCode, createDiagnostic, formatForBrowser } from "@al8b/diagnostics";
9
5
  var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blockHeight) => {
10
6
  if (!state.canvas) {
11
7
  state.canvas = document.createElement("canvas");
@@ -18,9 +14,8 @@ var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blo
18
14
  }
19
15
  const ctx = state.canvas.getContext("2d");
20
16
  if (!ctx) {
21
- const diagnostic = createDiagnostic(APIErrorCode.E7031);
22
- const formatted = formatForBrowser(diagnostic);
23
- throw new Error(formatted);
17
+ const message = "Failed to get 2D rendering context for tile canvas";
18
+ throw new Error(message);
24
19
  }
25
20
  ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
26
21
  state.animated = [];
@@ -126,14 +121,7 @@ var TileMap = class _TileMap {
126
121
  };
127
122
  constructor(width, height, block_width, block_height, sprites) {
128
123
  if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
129
- const diagnostic = createDiagnostic2(APIErrorCode2.E7034, {
130
- data: {
131
- width,
132
- height
133
- }
134
- });
135
- const formatted = formatForBrowser2(diagnostic);
136
- throw new Error(formatted);
124
+ throw new Error(`Invalid map dimensions: width=${width}, height=${height}`);
137
125
  }
138
126
  this.width = width;
139
127
  this.height = height;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/tile-map.ts","../../src/drawing/renderer.ts","../../src/shared/references.ts"],"sourcesContent":["/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7034, {\n\t\t\t\tdata: {\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst formatted = formatForBrowser(diagnostic);\n\t\t\tthrow new Error(formatted);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n","import { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7031);\n\t\tconst formatted = formatForBrowser(diagnostic);\n\t\tthrow new Error(formatted);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n"],"mappings":"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","names":["APIErrorCode","createDiagnostic","formatForBrowser","APIErrorCode","createDiagnostic","formatForBrowser","ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7031","formatted","formatForBrowser","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","diagnostic","createDiagnostic","APIErrorCode","E7034","data","formatted","formatForBrowser","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
1
+ {"version":3,"sources":["../../src/drawing/renderer.ts","../../src/shared/references.ts","../../src/core/tile-map.ts"],"sourcesContent":["import type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst message = \"Failed to get 2D rendering context for tile canvas\";\n\t\tthrow new Error(message);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n","/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tthrow new Error(`Invalid map dimensions: width=${width}, height=${height}`);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","message","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","data","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
@@ -27,7 +27,6 @@ __export(renderer_exports, {
27
27
  renderAnimatedTiles: () => renderAnimatedTiles
28
28
  });
29
29
  module.exports = __toCommonJS(renderer_exports);
30
- var import_diagnostics = require("@al8b/diagnostics");
31
30
  var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blockHeight) => {
32
31
  if (!state.canvas) {
33
32
  state.canvas = document.createElement("canvas");
@@ -40,9 +39,8 @@ var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blo
40
39
  }
41
40
  const ctx = state.canvas.getContext("2d");
42
41
  if (!ctx) {
43
- const diagnostic = (0, import_diagnostics.createDiagnostic)(import_diagnostics.APIErrorCode.E7031);
44
- const formatted = (0, import_diagnostics.formatForBrowser)(diagnostic);
45
- throw new Error(formatted);
42
+ const message = "Failed to get 2D rendering context for tile canvas";
43
+ throw new Error(message);
46
44
  }
47
45
  ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
48
46
  state.animated = [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/drawing/renderer.ts"],"sourcesContent":["import { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7031);\n\t\tconst formatted = formatForBrowser(diagnostic);\n\t\tthrow new Error(formatted);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7031","formatted","formatForBrowser","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps"]}
1
+ {"version":3,"sources":["../../src/drawing/renderer.ts"],"sourcesContent":["import type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst message = \"Failed to get 2D rendering context for tile canvas\";\n\t\tthrow new Error(message);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","message","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps"]}
@@ -2,7 +2,6 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/drawing/renderer.ts
5
- import { APIErrorCode, createDiagnostic, formatForBrowser } from "@al8b/diagnostics";
6
5
  var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blockHeight) => {
7
6
  if (!state.canvas) {
8
7
  state.canvas = document.createElement("canvas");
@@ -15,9 +14,8 @@ var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blo
15
14
  }
16
15
  const ctx = state.canvas.getContext("2d");
17
16
  if (!ctx) {
18
- const diagnostic = createDiagnostic(APIErrorCode.E7031);
19
- const formatted = formatForBrowser(diagnostic);
20
- throw new Error(formatted);
17
+ const message = "Failed to get 2D rendering context for tile canvas";
18
+ throw new Error(message);
21
19
  }
22
20
  ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
23
21
  state.animated = [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/drawing/renderer.ts"],"sourcesContent":["import { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7031);\n\t\tconst formatted = formatForBrowser(diagnostic);\n\t\tthrow new Error(formatted);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n"],"mappings":"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","names":["APIErrorCode","createDiagnostic","formatForBrowser","ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7031","formatted","formatForBrowser","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps"]}
1
+ {"version":3,"sources":["../../src/drawing/renderer.ts"],"sourcesContent":["import type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst message = \"Failed to get 2D rendering context for tile canvas\";\n\t\tthrow new Error(message);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","message","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps"]}
package/dist/index.js CHANGED
@@ -28,11 +28,7 @@ __export(index_exports, {
28
28
  });
29
29
  module.exports = __toCommonJS(index_exports);
30
30
 
31
- // src/core/tile-map.ts
32
- var import_diagnostics2 = require("@al8b/diagnostics");
33
-
34
31
  // src/drawing/renderer.ts
35
- var import_diagnostics = require("@al8b/diagnostics");
36
32
  var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blockHeight) => {
37
33
  if (!state.canvas) {
38
34
  state.canvas = document.createElement("canvas");
@@ -45,9 +41,8 @@ var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blo
45
41
  }
46
42
  const ctx = state.canvas.getContext("2d");
47
43
  if (!ctx) {
48
- const diagnostic = (0, import_diagnostics.createDiagnostic)(import_diagnostics.APIErrorCode.E7031);
49
- const formatted = (0, import_diagnostics.formatForBrowser)(diagnostic);
50
- throw new Error(formatted);
44
+ const message = "Failed to get 2D rendering context for tile canvas";
45
+ throw new Error(message);
51
46
  }
52
47
  ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
53
48
  state.animated = [];
@@ -153,14 +148,7 @@ var TileMap = class _TileMap {
153
148
  };
154
149
  constructor(width, height, block_width, block_height, sprites) {
155
150
  if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
156
- const diagnostic = (0, import_diagnostics2.createDiagnostic)(import_diagnostics2.APIErrorCode.E7034, {
157
- data: {
158
- width,
159
- height
160
- }
161
- });
162
- const formatted = (0, import_diagnostics2.formatForBrowser)(diagnostic);
163
- throw new Error(formatted);
151
+ throw new Error(`Invalid map dimensions: width=${width}, height=${height}`);
164
152
  }
165
153
  this.width = width;
166
154
  this.height = height;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/tile-map.ts","../src/drawing/renderer.ts","../src/shared/references.ts"],"sourcesContent":["/**\n * @al8b/map - Tile map management system\n *\n * Architecture:\n * - core/: TileMap runtime with load/update/save helpers\n * - drawing/: Canvas rendering utilities for animated tiles\n * - shared/: Reference normalization utilities\n * - data/: Raw map data types and loaders\n */\n\nexport { LoadMap, SaveMap, TileMap, UpdateMap } from \"./core/tile-map\";\nexport type { MapData } from \"./data/types\";\n","/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7034, {\n\t\t\t\tdata: {\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst formatted = formatForBrowser(diagnostic);\n\t\t\tthrow new Error(formatted);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n","import { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7031);\n\t\tconst formatted = formatForBrowser(diagnostic);\n\t\tthrow new Error(formatted);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n"],"mappings":"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","names":["import_diagnostics","ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7031","formatted","formatForBrowser","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","diagnostic","createDiagnostic","APIErrorCode","E7034","data","formatted","formatForBrowser","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/drawing/renderer.ts","../src/shared/references.ts","../src/core/tile-map.ts"],"sourcesContent":["/**\n * @al8b/map - Tile map management system\n *\n * Architecture:\n * - core/: TileMap runtime with load/update/save helpers\n * - drawing/: Canvas rendering utilities for animated tiles\n * - shared/: Reference normalization utilities\n * - data/: Raw map data types and loaders\n */\n\nexport { LoadMap, SaveMap, TileMap, UpdateMap } from \"./core/tile-map\";\nexport type { MapData } from \"./data/types\";\n","import type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst message = \"Failed to get 2D rendering context for tile canvas\";\n\t\tthrow new Error(message);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n","/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tthrow new Error(`Invalid map dimensions: width=${width}, height=${height}`);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","message","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","data","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
package/dist/index.mjs CHANGED
@@ -1,11 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
- // src/core/tile-map.ts
5
- import { APIErrorCode as APIErrorCode2, createDiagnostic as createDiagnostic2, formatForBrowser as formatForBrowser2 } from "@al8b/diagnostics";
6
-
7
4
  // src/drawing/renderer.ts
8
- import { APIErrorCode, createDiagnostic, formatForBrowser } from "@al8b/diagnostics";
9
5
  var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blockHeight) => {
10
6
  if (!state.canvas) {
11
7
  state.canvas = document.createElement("canvas");
@@ -18,9 +14,8 @@ var ensureCanvas = /* @__PURE__ */ __name((state, width, height, blockWidth, blo
18
14
  }
19
15
  const ctx = state.canvas.getContext("2d");
20
16
  if (!ctx) {
21
- const diagnostic = createDiagnostic(APIErrorCode.E7031);
22
- const formatted = formatForBrowser(diagnostic);
23
- throw new Error(formatted);
17
+ const message = "Failed to get 2D rendering context for tile canvas";
18
+ throw new Error(message);
24
19
  }
25
20
  ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
26
21
  state.animated = [];
@@ -126,14 +121,7 @@ var TileMap = class _TileMap {
126
121
  };
127
122
  constructor(width, height, block_width, block_height, sprites) {
128
123
  if (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {
129
- const diagnostic = createDiagnostic2(APIErrorCode2.E7034, {
130
- data: {
131
- width,
132
- height
133
- }
134
- });
135
- const formatted = formatForBrowser2(diagnostic);
136
- throw new Error(formatted);
124
+ throw new Error(`Invalid map dimensions: width=${width}, height=${height}`);
137
125
  }
138
126
  this.width = width;
139
127
  this.height = height;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/tile-map.ts","../src/drawing/renderer.ts","../src/shared/references.ts"],"sourcesContent":["/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7034, {\n\t\t\t\tdata: {\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst formatted = formatForBrowser(diagnostic);\n\t\t\tthrow new Error(formatted);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n","import { APIErrorCode, createDiagnostic, formatForBrowser } from \"@al8b/diagnostics\";\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst diagnostic = createDiagnostic(APIErrorCode.E7031);\n\t\tconst formatted = formatForBrowser(diagnostic);\n\t\tthrow new Error(formatted);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n"],"mappings":"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","names":["APIErrorCode","createDiagnostic","formatForBrowser","APIErrorCode","createDiagnostic","formatForBrowser","ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","diagnostic","createDiagnostic","APIErrorCode","E7031","formatted","formatForBrowser","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","diagnostic","createDiagnostic","APIErrorCode","E7034","data","formatted","formatForBrowser","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
1
+ {"version":3,"sources":["../src/drawing/renderer.ts","../src/shared/references.ts","../src/core/tile-map.ts"],"sourcesContent":["import type { Sprite } from \"@al8b/sprites\";\nimport type { AnimatedTile, ParsedTile } from \"../data/types\";\n\nexport interface RenderState {\n\tcanvas: HTMLCanvasElement | null;\n\tbuffer: HTMLCanvasElement | null;\n\tanimated: AnimatedTile[];\n}\n\nexport const ensureCanvas = (\n\tstate: RenderState,\n\twidth: number,\n\theight: number,\n\tblockWidth: number,\n\tblockHeight: number,\n): CanvasRenderingContext2D => {\n\tif (!state.canvas) {\n\t\tstate.canvas = document.createElement(\"canvas\");\n\t}\n\tconst expectedWidth = width * blockWidth;\n\tconst expectedHeight = height * blockHeight;\n\tif (state.canvas.width !== expectedWidth || state.canvas.height !== expectedHeight) {\n\t\tstate.canvas.width = expectedWidth;\n\t\tstate.canvas.height = expectedHeight;\n\t}\n\tconst ctx = state.canvas.getContext(\"2d\");\n\tif (!ctx) {\n\t\tconst message = \"Failed to get 2D rendering context for tile canvas\";\n\t\tthrow new Error(message);\n\t}\n\tctx.clearRect(0, 0, state.canvas.width, state.canvas.height);\n\tstate.animated = [];\n\treturn ctx;\n};\n\nexport const drawTile = (\n\tctx: CanvasRenderingContext2D,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): boolean => {\n\tconst canvas = sprite.frames[0].canvas;\n\tif (!canvas || canvas.width === 0 || canvas.height === 0) {\n\t\treturn false;\n\t}\n\tif (parsed.subX != null) {\n\t\tconst tx = parsed.subX * blockWidth;\n\t\tconst ty = (parsed.subY ?? 0) * blockHeight;\n\t\tctx.drawImage(\n\t\t\tcanvas,\n\t\t\ttx,\n\t\t\tty,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t\tblockWidth * gridX,\n\t\t\tblockHeight * gridY,\n\t\t\tblockWidth,\n\t\t\tblockHeight,\n\t\t);\n\t} else {\n\t\tctx.drawImage(canvas, blockWidth * gridX, blockHeight * gridY);\n\t}\n\treturn true;\n};\n\nexport const queueAnimatedTile = (\n\tstate: RenderState,\n\tsprite: Sprite,\n\tblockWidth: number,\n\tblockHeight: number,\n\tgridX: number,\n\tgridY: number,\n\tparsed: ParsedTile,\n): void => {\n\tconst tile: AnimatedTile = {\n\t\tx: blockWidth * gridX,\n\t\ty: blockHeight * gridY,\n\t\tw: blockWidth,\n\t\th: blockHeight,\n\t\tsprite,\n\t};\n\tif (parsed.subX != null) {\n\t\ttile.tx = parsed.subX * blockWidth;\n\t\ttile.ty = (parsed.subY ?? 0) * blockHeight;\n\t}\n\tstate.animated.push(tile);\n};\n\nexport const renderAnimatedTiles = (state: RenderState, blockWidth: number, blockHeight: number): HTMLCanvasElement => {\n\tconst time = performance.now();\n\tif (\n\t\t!state.buffer ||\n\t\tstate.buffer.width !== blockWidth * (state.canvas!.width / blockWidth) ||\n\t\tstate.buffer.height !== blockHeight * (state.canvas!.height / blockHeight)\n\t) {\n\t\tstate.buffer = document.createElement(\"canvas\");\n\t\tstate.buffer.width = state.canvas!.width;\n\t\tstate.buffer.height = state.canvas!.height;\n\t}\n\tconst bufferCtx = state.buffer.getContext(\"2d\")!;\n\tbufferCtx.clearRect(0, 0, state.buffer.width, state.buffer.height);\n\tbufferCtx.drawImage(state.canvas!, 0, 0);\n\tfor (const tile of state.animated) {\n\t\tconst len = tile.sprite.frames.length;\n\t\tconst frame = tile.sprite.frames[Math.floor((time / 1000) * tile.sprite.fps) % len]?.canvas;\n\t\tif (!frame) continue;\n\t\tif (tile.tx != null && tile.ty != null) {\n\t\t\tbufferCtx.drawImage(frame, tile.tx, tile.ty, blockWidth, blockHeight, tile.x, tile.y, blockWidth, blockHeight);\n\t\t} else {\n\t\t\tbufferCtx.drawImage(frame, tile.x, tile.y, blockWidth, blockHeight);\n\t\t}\n\t}\n\treturn state.buffer;\n};\n","// Convert forward slashes to dashes for safe storage in map data format\nexport const normalizeRefForStorage = (ref: string): string => ref.replace(/\\//g, \"-\");\n// Convert dashes back to forward slashes for sprite reference lookup\nexport const normalizeRefForUsage = (ref: string): string => ref.replace(/-/g, \"/\");\n","/**\n * TileMap - Tile-based map implementation with sprite rendering support.\n */\n\nimport type { Sprite } from \"@al8b/sprites\";\nimport type { MapData, ParsedTile, SpriteDictionary } from \"../data/types\";\nimport { drawTile, ensureCanvas, queueAnimatedTile, type RenderState, renderAnimatedTiles } from \"../drawing/renderer\";\nimport { normalizeRefForStorage, normalizeRefForUsage } from \"../shared/references\";\n\n/**\n * Parse a tile reference string into a cached ParsedTile object.\n * Input format: \"spriteName\" or \"spriteName:subX,subY\"\n */\nfunction parseTileRef(entry: string): ParsedTile {\n\tconst colonIdx = entry.indexOf(\":\");\n\tif (colonIdx === -1) {\n\t\treturn { spriteName: entry };\n\t}\n\tconst spriteName = entry.substring(0, colonIdx);\n\tconst coords = entry.substring(colonIdx + 1);\n\tconst commaIdx = coords.indexOf(\",\");\n\tif (commaIdx === -1) {\n\t\treturn { spriteName, subX: Number.parseInt(coords, 10) };\n\t}\n\treturn {\n\t\tspriteName,\n\t\tsubX: Number.parseInt(coords.substring(0, commaIdx), 10),\n\t\tsubY: Number.parseInt(coords.substring(commaIdx + 1), 10),\n\t};\n}\n\nexport class TileMap {\n\tpublic width: number;\n\tpublic height: number;\n\tpublic block_width: number;\n\tpublic block_height: number;\n\tpublic sprites: SpriteDictionary;\n\tpublic map: (string | null)[] = [];\n\tpublic ready = true;\n\tpublic needs_update = false;\n\tpublic name = \"\";\n\tprivate parsedMap: (ParsedTile | null)[] = [];\n\tprivate readonly renderState: RenderState = {\n\t\tcanvas: null,\n\t\tbuffer: null,\n\t\tanimated: [],\n\t};\n\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\tblock_width: number,\n\t\tblock_height: number,\n\t\tsprites?: Record<string, Sprite>,\n\t) {\n\t\t// Validate map dimensions to ensure positive, finite values\n\t\tif (width <= 0 || height <= 0 || !isFinite(width) || !isFinite(height)) {\n\t\t\tthrow new Error(`Invalid map dimensions: width=${width}, height=${height}`);\n\t\t}\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.block_width = block_width;\n\t\tthis.block_height = block_height;\n\t\tthis.sprites = sprites ?? {};\n\t\tthis.clear();\n\t}\n\n\tclear(): void {\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = null;\n\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tset(x: number, y: number, ref: string | null): void {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Out-of-bounds access is ignored to prevent crashes during development\n\t\t\treturn;\n\t\t}\n\n\t\tlet normalized = ref;\n\t\tif (typeof normalized === \"string\") {\n\t\t\tnormalized = normalizeRefForStorage(normalized);\n\t\t}\n\t\tconst idx = x + y * this.width;\n\t\tthis.map[idx] = normalized;\n\t\tthis.parsedMap[idx] = normalized ? parseTileRef(normalized) : null;\n\t\tthis.needs_update = true;\n\t}\n\n\tget(x: number, y: number): string | number | null {\n\t\t// Validate tile coordinates are within map bounds\n\t\tif (x < 0 || y < 0 || x >= this.width || y >= this.height || !isFinite(x) || !isFinite(y)) {\n\t\t\t// Silent fail: TileMap lacks runtime reference for error reporting\n\t\t\t// Returns 0 for out-of-bounds access to maintain backward compatibility\n\t\t\treturn 0;\n\t\t}\n\t\tlet cell = this.map[x + y * this.width];\n\t\tif (typeof cell === \"string\") {\n\t\t\tcell = normalizeRefForUsage(cell);\n\t\t}\n\t\treturn cell || 0;\n\t}\n\n\tgetCanvas(): HTMLCanvasElement {\n\t\tif (this.renderState.canvas == null || this.needs_update) {\n\t\t\tthis.update();\n\t\t}\n\t\treturn this.renderState.canvas!;\n\t}\n\n\tdraw(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number): void {\n\t\tif (this.renderState.animated.length > 0) {\n\t\t\tconst buffer = renderAnimatedTiles(this.renderState, this.block_width, this.block_height);\n\t\t\tcontext.drawImage(buffer, x, y, w, h);\n\t\t} else {\n\t\t\tcontext.drawImage(this.getCanvas(), x, y, w, h);\n\t\t}\n\t}\n\n\tupdate(): void {\n\t\tthis.needs_update = false;\n\t\tconst context = ensureCanvas(this.renderState, this.width, this.height, this.block_width, this.block_height);\n\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst index = i + (this.height - 1 - j) * this.width;\n\t\t\t\tconst parsed = this.parsedMap[index];\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\tconst sprite =\n\t\t\t\t\tthis.sprites[parsed.spriteName] || this.sprites[normalizeRefForUsage(parsed.spriteName)];\n\t\t\t\tif (!sprite || !sprite.frames[0]) continue;\n\n\t\t\t\tif (sprite.frames.length > 1) {\n\t\t\t\t\tqueueAnimatedTile(this.renderState, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t} else {\n\t\t\t\t\tdrawTile(context, sprite, this.block_width, this.block_height, i, j, parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tloadFile(url: string, loaded?: () => void): void {\n\t\tconst req = new XMLHttpRequest();\n\t\treq.onreadystatechange = () => {\n\t\t\tif (req.readyState === XMLHttpRequest.DONE && req.status === 200) {\n\t\t\t\tthis.load(req.responseText, this.sprites);\n\t\t\t\tthis.update();\n\t\t\t\tloaded?.();\n\t\t\t}\n\t\t};\n\t\treq.open(\"GET\", url);\n\t\treq.send();\n\t}\n\n\tload(data: string, sprites: Record<string, Sprite>): void {\n\t\tconst parsed: MapData = JSON.parse(data);\n\t\tthis.width = parsed.width;\n\t\tthis.height = parsed.height;\n\t\tthis.block_width = parsed.block_width;\n\t\tthis.block_height = parsed.block_height;\n\t\tthis.sprites = sprites ?? this.sprites;\n\n\t\tfor (let j = 0; j < parsed.height; j++) {\n\t\t\tfor (let i = 0; i < parsed.width; i++) {\n\t\t\t\tconst idx = i + j * parsed.width;\n\t\t\t\tconst value = parsed.data[idx];\n\t\t\t\tif (value > 0) {\n\t\t\t\t\tconst ref = parsed.sprites[value] as string;\n\t\t\t\t\tthis.map[idx] = ref;\n\t\t\t\t\tthis.parsedMap[idx] = parseTileRef(ref);\n\t\t\t\t} else {\n\t\t\t\t\tthis.map[idx] = null;\n\t\t\t\t\tthis.parsedMap[idx] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.needs_update = true;\n\t}\n\n\tclone(): TileMap {\n\t\tconst duplicate = new TileMap(this.width, this.height, this.block_width, this.block_height, this.sprites);\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tduplicate.map[idx] = this.map[idx];\n\t\t\t\tduplicate.parsedMap[idx] = this.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tduplicate.needs_update = true;\n\t\treturn duplicate;\n\t}\n\n\tcopyFrom(map: TileMap): TileMap {\n\t\tthis.width = map.width;\n\t\tthis.height = map.height;\n\t\tthis.block_width = map.block_width;\n\t\tthis.block_height = map.block_height;\n\t\tfor (let j = 0; j < this.height; j++) {\n\t\t\tfor (let i = 0; i < this.width; i++) {\n\t\t\t\tconst idx = i + j * this.width;\n\t\t\t\tthis.map[idx] = map.map[idx];\n\t\t\t\tthis.parsedMap[idx] = map.parsedMap[idx];\n\t\t\t}\n\t\t}\n\t\tthis.update();\n\t\treturn this;\n\t}\n}\n\nexport function LoadMap(url: string, sprites?: Record<string, Sprite>, loaded?: () => void): TileMap {\n\tconst map = new TileMap(1, 1, 1, 1, sprites);\n\tmap.ready = false;\n\n\tconst req = new XMLHttpRequest();\n\treq.onreadystatechange = () => {\n\t\tif (req.readyState === XMLHttpRequest.DONE) {\n\t\t\tmap.ready = true;\n\t\t\tif (req.status === 200) {\n\t\t\t\tUpdateMap(map, req.responseText, sprites);\n\t\t\t}\n\t\t\tmap.needs_update = true;\n\t\t\tloaded?.();\n\t\t}\n\t};\n\treq.open(\"GET\", url);\n\treq.send();\n\treturn map;\n}\n\nexport function UpdateMap(map: TileMap, data: string, sprites?: Record<string, Sprite>): TileMap {\n\tmap.load(data, sprites ?? map.sprites);\n\treturn map;\n}\n\nexport function SaveMap(map: TileMap): string {\n\tlet index = 1;\n\tconst list: Array<string | number> = [0];\n\tconst table: Record<string, number> = {};\n\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tif (entry && entry.length > 0 && table[entry] == null) {\n\t\t\t\tlist.push(entry);\n\t\t\t\ttable[entry] = index++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst serialized: number[] = [];\n\tfor (let j = 0; j < map.height; j++) {\n\t\tfor (let i = 0; i < map.width; i++) {\n\t\t\tconst entry = map.map[i + j * map.width];\n\t\t\tserialized[i + j * map.width] = entry && entry.length > 0 ? table[entry] : 0;\n\t\t}\n\t}\n\n\tconst payload: MapData = {\n\t\twidth: map.width,\n\t\theight: map.height,\n\t\tblock_width: map.block_width,\n\t\tblock_height: map.block_height,\n\t\tsprites: list,\n\t\tdata: serialized,\n\t};\n\n\treturn JSON.stringify(payload);\n}\n"],"mappings":"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","names":["ensureCanvas","state","width","height","blockWidth","blockHeight","canvas","document","createElement","expectedWidth","expectedHeight","ctx","getContext","message","Error","clearRect","animated","drawTile","sprite","gridX","gridY","parsed","frames","subX","tx","ty","subY","drawImage","queueAnimatedTile","tile","x","y","w","h","push","renderAnimatedTiles","time","performance","now","buffer","bufferCtx","len","length","frame","Math","floor","fps","normalizeRefForStorage","ref","replace","normalizeRefForUsage","parseTileRef","entry","colonIdx","indexOf","spriteName","substring","coords","commaIdx","subX","Number","parseInt","subY","TileMap","width","height","block_width","block_height","sprites","map","ready","needs_update","name","parsedMap","renderState","canvas","buffer","animated","isFinite","Error","clear","j","i","idx","set","x","y","ref","normalized","normalizeRefForStorage","get","cell","normalizeRefForUsage","getCanvas","update","draw","context","w","h","length","renderAnimatedTiles","drawImage","ensureCanvas","index","parsed","sprite","frames","queueAnimatedTile","drawTile","loadFile","url","loaded","req","XMLHttpRequest","onreadystatechange","readyState","DONE","status","load","responseText","open","send","data","JSON","parse","value","clone","duplicate","copyFrom","LoadMap","UpdateMap","SaveMap","list","table","push","serialized","payload","stringify"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@al8b/map",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "sideEffects": false,
5
5
  "files": [
6
6
  "dist/**/*",
@@ -23,8 +23,7 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@al8b/diagnostics": "^0.1.13",
27
- "@al8b/sprites": "^0.1.13"
26
+ "@al8b/sprites": "^0.1.14"
28
27
  },
29
28
  "keywords": [
30
29
  "map"