picopilot 0.0.0 → 0.1.0

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.
Files changed (334) hide show
  1. package/LICENSE +661 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +4 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/cli.d.ts +15 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +59 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/audio.d.ts +17 -0
  11. package/dist/commands/audio.d.ts.map +1 -0
  12. package/dist/commands/audio.js +315 -0
  13. package/dist/commands/audio.js.map +1 -0
  14. package/dist/commands/export.d.ts +36 -0
  15. package/dist/commands/export.d.ts.map +1 -0
  16. package/dist/commands/export.js +257 -0
  17. package/dist/commands/export.js.map +1 -0
  18. package/dist/commands/gfx.d.ts +28 -0
  19. package/dist/commands/gfx.d.ts.map +1 -0
  20. package/dist/commands/gfx.js +521 -0
  21. package/dist/commands/gfx.js.map +1 -0
  22. package/dist/commands/init.d.ts +34 -0
  23. package/dist/commands/init.d.ts.map +1 -0
  24. package/dist/commands/init.js +151 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/lint.d.ts +20 -0
  27. package/dist/commands/lint.d.ts.map +1 -0
  28. package/dist/commands/lint.js +90 -0
  29. package/dist/commands/lint.js.map +1 -0
  30. package/dist/commands/minify.d.ts +20 -0
  31. package/dist/commands/minify.d.ts.map +1 -0
  32. package/dist/commands/minify.js +129 -0
  33. package/dist/commands/minify.js.map +1 -0
  34. package/dist/commands/music.d.ts +13 -0
  35. package/dist/commands/music.d.ts.map +1 -0
  36. package/dist/commands/music.js +241 -0
  37. package/dist/commands/music.js.map +1 -0
  38. package/dist/commands/playtest.d.ts +29 -0
  39. package/dist/commands/playtest.d.ts.map +1 -0
  40. package/dist/commands/playtest.js +631 -0
  41. package/dist/commands/playtest.js.map +1 -0
  42. package/dist/commands/run.d.ts +30 -0
  43. package/dist/commands/run.d.ts.map +1 -0
  44. package/dist/commands/run.js +202 -0
  45. package/dist/commands/run.js.map +1 -0
  46. package/dist/commands/serve.d.ts +50 -0
  47. package/dist/commands/serve.d.ts.map +1 -0
  48. package/dist/commands/serve.js +213 -0
  49. package/dist/commands/serve.js.map +1 -0
  50. package/dist/commands/sfx.d.ts +13 -0
  51. package/dist/commands/sfx.d.ts.map +1 -0
  52. package/dist/commands/sfx.js +254 -0
  53. package/dist/commands/sfx.js.map +1 -0
  54. package/dist/commands/tokens.d.ts +36 -0
  55. package/dist/commands/tokens.d.ts.map +1 -0
  56. package/dist/commands/tokens.js +129 -0
  57. package/dist/commands/tokens.js.map +1 -0
  58. package/dist/commands/verify.d.ts +57 -0
  59. package/dist/commands/verify.d.ts.map +1 -0
  60. package/dist/commands/verify.js +213 -0
  61. package/dist/commands/verify.js.map +1 -0
  62. package/dist/commands/version.d.ts +12 -0
  63. package/dist/commands/version.d.ts.map +1 -0
  64. package/dist/commands/version.js +24 -0
  65. package/dist/commands/version.js.map +1 -0
  66. package/dist/engine/audio/index.d.ts +14 -0
  67. package/dist/engine/audio/index.d.ts.map +1 -0
  68. package/dist/engine/audio/index.js +14 -0
  69. package/dist/engine/audio/index.js.map +1 -0
  70. package/dist/engine/audio/music.d.ts +116 -0
  71. package/dist/engine/audio/music.d.ts.map +1 -0
  72. package/dist/engine/audio/music.js +138 -0
  73. package/dist/engine/audio/music.js.map +1 -0
  74. package/dist/engine/audio/section.d.ts +34 -0
  75. package/dist/engine/audio/section.d.ts.map +1 -0
  76. package/dist/engine/audio/section.js +67 -0
  77. package/dist/engine/audio/section.js.map +1 -0
  78. package/dist/engine/audio/sfx.d.ts +141 -0
  79. package/dist/engine/audio/sfx.d.ts.map +1 -0
  80. package/dist/engine/audio/sfx.js +506 -0
  81. package/dist/engine/audio/sfx.js.map +1 -0
  82. package/dist/engine/cart/cart.d.ts +75 -0
  83. package/dist/engine/cart/cart.d.ts.map +1 -0
  84. package/dist/engine/cart/cart.js +153 -0
  85. package/dist/engine/cart/cart.js.map +1 -0
  86. package/dist/engine/cart/errors.d.ts +29 -0
  87. package/dist/engine/cart/errors.d.ts.map +1 -0
  88. package/dist/engine/cart/errors.js +26 -0
  89. package/dist/engine/cart/errors.js.map +1 -0
  90. package/dist/engine/cart/gfx.d.ts +79 -0
  91. package/dist/engine/cart/gfx.d.ts.map +1 -0
  92. package/dist/engine/cart/gfx.js +171 -0
  93. package/dist/engine/cart/gfx.js.map +1 -0
  94. package/dist/engine/cart/index.d.ts +15 -0
  95. package/dist/engine/cart/index.d.ts.map +1 -0
  96. package/dist/engine/cart/index.js +15 -0
  97. package/dist/engine/cart/index.js.map +1 -0
  98. package/dist/engine/cart/map.d.ts +46 -0
  99. package/dist/engine/cart/map.d.ts.map +1 -0
  100. package/dist/engine/cart/map.js +118 -0
  101. package/dist/engine/cart/map.js.map +1 -0
  102. package/dist/engine/config/config.d.ts +74 -0
  103. package/dist/engine/config/config.d.ts.map +1 -0
  104. package/dist/engine/config/config.js +103 -0
  105. package/dist/engine/config/config.js.map +1 -0
  106. package/dist/engine/config/index.d.ts +7 -0
  107. package/dist/engine/config/index.d.ts.map +1 -0
  108. package/dist/engine/config/index.js +7 -0
  109. package/dist/engine/config/index.js.map +1 -0
  110. package/dist/engine/gfx/grid.d.ts +51 -0
  111. package/dist/engine/gfx/grid.d.ts.map +1 -0
  112. package/dist/engine/gfx/grid.js +116 -0
  113. package/dist/engine/gfx/grid.js.map +1 -0
  114. package/dist/engine/gfx/index.d.ts +17 -0
  115. package/dist/engine/gfx/index.d.ts.map +1 -0
  116. package/dist/engine/gfx/index.js +17 -0
  117. package/dist/engine/gfx/index.js.map +1 -0
  118. package/dist/engine/gfx/label.d.ts +34 -0
  119. package/dist/engine/gfx/label.d.ts.map +1 -0
  120. package/dist/engine/gfx/label.js +190 -0
  121. package/dist/engine/gfx/label.js.map +1 -0
  122. package/dist/engine/gfx/overlap.d.ts +42 -0
  123. package/dist/engine/gfx/overlap.d.ts.map +1 -0
  124. package/dist/engine/gfx/overlap.js +57 -0
  125. package/dist/engine/gfx/overlap.js.map +1 -0
  126. package/dist/engine/gfx/palette.d.ts +29 -0
  127. package/dist/engine/gfx/palette.d.ts.map +1 -0
  128. package/dist/engine/gfx/palette.js +46 -0
  129. package/dist/engine/gfx/palette.js.map +1 -0
  130. package/dist/engine/gfx/png.d.ts +31 -0
  131. package/dist/engine/gfx/png.d.ts.map +1 -0
  132. package/dist/engine/gfx/png.js +165 -0
  133. package/dist/engine/gfx/png.js.map +1 -0
  134. package/dist/engine/gfx/render.d.ts +42 -0
  135. package/dist/engine/gfx/render.d.ts.map +1 -0
  136. package/dist/engine/gfx/render.js +72 -0
  137. package/dist/engine/gfx/render.js.map +1 -0
  138. package/dist/engine/index.d.ts +22 -0
  139. package/dist/engine/index.d.ts.map +1 -0
  140. package/dist/engine/index.js +22 -0
  141. package/dist/engine/index.js.map +1 -0
  142. package/dist/engine/pico8/adapter.d.ts +267 -0
  143. package/dist/engine/pico8/adapter.d.ts.map +1 -0
  144. package/dist/engine/pico8/adapter.js +37 -0
  145. package/dist/engine/pico8/adapter.js.map +1 -0
  146. package/dist/engine/pico8/drive.d.ts +308 -0
  147. package/dist/engine/pico8/drive.d.ts.map +1 -0
  148. package/dist/engine/pico8/drive.js +581 -0
  149. package/dist/engine/pico8/drive.js.map +1 -0
  150. package/dist/engine/pico8/harness.d.ts +146 -0
  151. package/dist/engine/pico8/harness.d.ts.map +1 -0
  152. package/dist/engine/pico8/harness.js +251 -0
  153. package/dist/engine/pico8/harness.js.map +1 -0
  154. package/dist/engine/pico8/index.d.ts +21 -0
  155. package/dist/engine/pico8/index.d.ts.map +1 -0
  156. package/dist/engine/pico8/index.js +21 -0
  157. package/dist/engine/pico8/index.js.map +1 -0
  158. package/dist/engine/pico8/sentinel.d.ts +63 -0
  159. package/dist/engine/pico8/sentinel.d.ts.map +1 -0
  160. package/dist/engine/pico8/sentinel.js +86 -0
  161. package/dist/engine/pico8/sentinel.js.map +1 -0
  162. package/dist/engine/pico8/session-daemon-entry.d.ts +10 -0
  163. package/dist/engine/pico8/session-daemon-entry.d.ts.map +1 -0
  164. package/dist/engine/pico8/session-daemon-entry.js +11 -0
  165. package/dist/engine/pico8/session-daemon-entry.js.map +1 -0
  166. package/dist/engine/pico8/session-daemon-main.d.ts +44 -0
  167. package/dist/engine/pico8/session-daemon-main.d.ts.map +1 -0
  168. package/dist/engine/pico8/session-daemon-main.js +71 -0
  169. package/dist/engine/pico8/session-daemon-main.js.map +1 -0
  170. package/dist/engine/pico8/session-daemon.d.ts +68 -0
  171. package/dist/engine/pico8/session-daemon.d.ts.map +1 -0
  172. package/dist/engine/pico8/session-daemon.js +264 -0
  173. package/dist/engine/pico8/session-daemon.js.map +1 -0
  174. package/dist/engine/pico8/session.d.ts +140 -0
  175. package/dist/engine/pico8/session.d.ts.map +1 -0
  176. package/dist/engine/pico8/session.js +250 -0
  177. package/dist/engine/pico8/session.js.map +1 -0
  178. package/dist/engine/pico8/shell.d.ts +103 -0
  179. package/dist/engine/pico8/shell.d.ts.map +1 -0
  180. package/dist/engine/pico8/shell.js +324 -0
  181. package/dist/engine/pico8/shell.js.map +1 -0
  182. package/dist/engine/pico8/supervisor.d.ts +137 -0
  183. package/dist/engine/pico8/supervisor.d.ts.map +1 -0
  184. package/dist/engine/pico8/supervisor.js +140 -0
  185. package/dist/engine/pico8/supervisor.js.map +1 -0
  186. package/dist/engine/scaffold/index.d.ts +8 -0
  187. package/dist/engine/scaffold/index.d.ts.map +1 -0
  188. package/dist/engine/scaffold/index.js +8 -0
  189. package/dist/engine/scaffold/index.js.map +1 -0
  190. package/dist/engine/scaffold/scaffold.d.ts +49 -0
  191. package/dist/engine/scaffold/scaffold.d.ts.map +1 -0
  192. package/dist/engine/scaffold/scaffold.js +217 -0
  193. package/dist/engine/scaffold/scaffold.js.map +1 -0
  194. package/dist/engine/shrinko/adapter.d.ts +167 -0
  195. package/dist/engine/shrinko/adapter.d.ts.map +1 -0
  196. package/dist/engine/shrinko/adapter.js +107 -0
  197. package/dist/engine/shrinko/adapter.js.map +1 -0
  198. package/dist/engine/shrinko/index.d.ts +13 -0
  199. package/dist/engine/shrinko/index.d.ts.map +1 -0
  200. package/dist/engine/shrinko/index.js +13 -0
  201. package/dist/engine/shrinko/index.js.map +1 -0
  202. package/dist/engine/shrinko/shell.d.ts +69 -0
  203. package/dist/engine/shrinko/shell.d.ts.map +1 -0
  204. package/dist/engine/shrinko/shell.js +156 -0
  205. package/dist/engine/shrinko/shell.js.map +1 -0
  206. package/dist/engine/skills/index.d.ts +8 -0
  207. package/dist/engine/skills/index.d.ts.map +1 -0
  208. package/dist/engine/skills/index.js +8 -0
  209. package/dist/engine/skills/index.js.map +1 -0
  210. package/dist/engine/skills/skills.d.ts +104 -0
  211. package/dist/engine/skills/skills.d.ts.map +1 -0
  212. package/dist/engine/skills/skills.js +149 -0
  213. package/dist/engine/skills/skills.js.map +1 -0
  214. package/dist/index.d.ts +10 -0
  215. package/dist/index.d.ts.map +1 -0
  216. package/dist/index.js +10 -0
  217. package/dist/index.js.map +1 -0
  218. package/dist/version.d.ts +2 -0
  219. package/dist/version.d.ts.map +1 -0
  220. package/dist/version.js +19 -0
  221. package/dist/version.js.map +1 -0
  222. package/package.json +48 -2
  223. package/src/bin.ts +4 -0
  224. package/src/cli.ts +61 -0
  225. package/src/commands/audio.test.ts +280 -0
  226. package/src/commands/audio.ts +410 -0
  227. package/src/commands/export.test.ts +310 -0
  228. package/src/commands/export.ts +316 -0
  229. package/src/commands/gfx-io.test.ts +178 -0
  230. package/src/commands/gfx.test.ts +470 -0
  231. package/src/commands/gfx.ts +645 -0
  232. package/src/commands/init.test.ts +460 -0
  233. package/src/commands/init.ts +193 -0
  234. package/src/commands/lint.test.ts +137 -0
  235. package/src/commands/lint.ts +117 -0
  236. package/src/commands/minify.test.ts +166 -0
  237. package/src/commands/minify.ts +156 -0
  238. package/src/commands/music.test.ts +272 -0
  239. package/src/commands/music.ts +288 -0
  240. package/src/commands/playtest.test.ts +317 -0
  241. package/src/commands/playtest.ts +773 -0
  242. package/src/commands/run.test.ts +238 -0
  243. package/src/commands/run.ts +259 -0
  244. package/src/commands/serve.test.ts +218 -0
  245. package/src/commands/serve.ts +272 -0
  246. package/src/commands/sfx.test.ts +349 -0
  247. package/src/commands/sfx.ts +300 -0
  248. package/src/commands/tokens.test.ts +212 -0
  249. package/src/commands/tokens.ts +168 -0
  250. package/src/commands/verify.test.ts +238 -0
  251. package/src/commands/verify.ts +256 -0
  252. package/src/commands/version.test.ts +70 -0
  253. package/src/commands/version.ts +24 -0
  254. package/src/engine/audio/index.ts +47 -0
  255. package/src/engine/audio/music.test.ts +188 -0
  256. package/src/engine/audio/music.ts +218 -0
  257. package/src/engine/audio/section.test.ts +124 -0
  258. package/src/engine/audio/section.ts +79 -0
  259. package/src/engine/audio/sfx.test.ts +410 -0
  260. package/src/engine/audio/sfx.ts +694 -0
  261. package/src/engine/cart/cart.test.ts +304 -0
  262. package/src/engine/cart/cart.ts +211 -0
  263. package/src/engine/cart/errors.ts +39 -0
  264. package/src/engine/cart/fixtures/crlf.p8 +6 -0
  265. package/src/engine/cart/fixtures/full.p8 +174 -0
  266. package/src/engine/cart/fixtures/header-only.p8 +2 -0
  267. package/src/engine/cart/fixtures/malformed-no-header.p8 +2 -0
  268. package/src/engine/cart/fixtures/minimal.p8 +10 -0
  269. package/src/engine/cart/fixtures/no-trailing-newline.p8 +6 -0
  270. package/src/engine/cart/gfx.ts +195 -0
  271. package/src/engine/cart/index.ts +27 -0
  272. package/src/engine/cart/map.ts +134 -0
  273. package/src/engine/config/config.ts +115 -0
  274. package/src/engine/config/index.ts +12 -0
  275. package/src/engine/gfx/grid.test.ts +128 -0
  276. package/src/engine/gfx/grid.ts +136 -0
  277. package/src/engine/gfx/index.ts +34 -0
  278. package/src/engine/gfx/label.test.ts +105 -0
  279. package/src/engine/gfx/label.ts +229 -0
  280. package/src/engine/gfx/overlap.test.ts +71 -0
  281. package/src/engine/gfx/overlap.ts +66 -0
  282. package/src/engine/gfx/palette.test.ts +35 -0
  283. package/src/engine/gfx/palette.ts +54 -0
  284. package/src/engine/gfx/png.test.ts +111 -0
  285. package/src/engine/gfx/png.ts +196 -0
  286. package/src/engine/gfx/render.test.ts +180 -0
  287. package/src/engine/gfx/render.ts +106 -0
  288. package/src/engine/index.ts +21 -0
  289. package/src/engine/pico8/adapter.ts +303 -0
  290. package/src/engine/pico8/drive.test.ts +415 -0
  291. package/src/engine/pico8/drive.ts +729 -0
  292. package/src/engine/pico8/harness.test.ts +181 -0
  293. package/src/engine/pico8/harness.ts +354 -0
  294. package/src/engine/pico8/index.ts +20 -0
  295. package/src/engine/pico8/sentinel.test.ts +80 -0
  296. package/src/engine/pico8/sentinel.ts +93 -0
  297. package/src/engine/pico8/session-daemon-entry.ts +12 -0
  298. package/src/engine/pico8/session-daemon-main.ts +98 -0
  299. package/src/engine/pico8/session-daemon.test.ts +212 -0
  300. package/src/engine/pico8/session-daemon.ts +306 -0
  301. package/src/engine/pico8/session.test.ts +239 -0
  302. package/src/engine/pico8/session.ts +321 -0
  303. package/src/engine/pico8/shell.test.ts +583 -0
  304. package/src/engine/pico8/shell.ts +412 -0
  305. package/src/engine/pico8/supervisor.test.ts +105 -0
  306. package/src/engine/pico8/supervisor.ts +197 -0
  307. package/src/engine/scaffold/index.ts +15 -0
  308. package/src/engine/scaffold/scaffold.ts +236 -0
  309. package/src/engine/shrinko/adapter.ts +241 -0
  310. package/src/engine/shrinko/fixtures/count-no-compressed.txt +2 -0
  311. package/src/engine/shrinko/fixtures/count-over-budget.txt +3 -0
  312. package/src/engine/shrinko/fixtures/count-under-budget.txt +3 -0
  313. package/src/engine/shrinko/index.ts +33 -0
  314. package/src/engine/shrinko/shell.ts +224 -0
  315. package/src/engine/shrinko/shrinko.test.ts +182 -0
  316. package/src/engine/skills/index.ts +14 -0
  317. package/src/engine/skills/skills.test.ts +376 -0
  318. package/src/engine/skills/skills.ts +205 -0
  319. package/src/index.ts +13 -0
  320. package/src/skills/game-design-reference/SKILL.md +70 -0
  321. package/src/skills/game-jam/SKILL.md +45 -0
  322. package/src/skills/picopilot-art/SKILL.md +62 -0
  323. package/src/skills/picopilot-audio/SKILL.md +194 -0
  324. package/src/skills/picopilot-code/SKILL.md +97 -0
  325. package/src/skills/picopilot-code/reference/README.md +42 -0
  326. package/src/skills/picopilot-code/reference/mode7-racing.md +95 -0
  327. package/src/skills/picopilot-code/reference/platformer.md +119 -0
  328. package/src/skills/picopilot-code/reference/puzzle-grid.md +106 -0
  329. package/src/skills/picopilot-code/reference/rpg-menus-dialog.md +122 -0
  330. package/src/skills/picopilot-code/reference/top-down-adventure.md +117 -0
  331. package/src/skills/picopilot-code/reference/twin-stick-arcade.md +128 -0
  332. package/src/skills/picopilot-debug/SKILL.md +195 -0
  333. package/src/skills/picopilot-overview/SKILL.md +52 -0
  334. package/src/version.ts +22 -0
@@ -0,0 +1,118 @@
1
+ import { CartRangeError } from './errors.js';
2
+ /**
3
+ * Addressable view over a cart's `__map__` section: the tile map as a grid of
4
+ * 8-bit tile indices, NOT an opaque hex string.
5
+ *
6
+ * The text `__map__` stores the base map: up to 32 rows of 128 tiles, each tile
7
+ * a byte written as two hex chars (`grid[y][x]`). Rows 32..63 (the shared half)
8
+ * are NOT stored here in the text format (they live in `__gfx__`'s upper bank
9
+ ), see `gfx.ts` {@link mapRowsForSprite}; this view models the `__map__` body
10
+ * as written. The 0x1000 overlap check reads a shared-bank sprite's aliased map
11
+ * region through {@link nonZeroTilesInRows} to decide whether a write clobbers
12
+ * real tiles.
13
+ *
14
+ * Reads/writes go through the parsed grid; call {@link commit} to write it back
15
+ * into the cart's `__map__` body. A missing/short section reads as zero tiles.
16
+ */
17
+ export const MAP_WIDTH = 128;
18
+ export const MAP_ROWS = 32;
19
+ export class MapData {
20
+ /** `tiles[y][x]` is the 0..255 tile index at map cell (x, y). */
21
+ tiles;
22
+ newline;
23
+ constructor(tiles, newline) {
24
+ this.tiles = tiles;
25
+ this.newline = newline;
26
+ }
27
+ /** Builds an addressable map from a cart's `__map__` body (all-zero if absent). */
28
+ static fromBody(body) {
29
+ const newline = body !== undefined && /\r\n/.test(body) ? '\r\n' : '\n';
30
+ const lines = body === undefined ? [] : body.split(/\r?\n/);
31
+ const tiles = [];
32
+ for (let y = 0; y < MAP_ROWS; y++) {
33
+ const line = lines[y] ?? '';
34
+ const row = [];
35
+ for (let x = 0; x < MAP_WIDTH; x++) {
36
+ const hi = line[x * 2];
37
+ const lo = line[x * 2 + 1];
38
+ row.push(hi === undefined || lo === undefined ? 0 : hexByte(hi, lo));
39
+ }
40
+ tiles.push(row);
41
+ }
42
+ return new MapData(tiles, newline);
43
+ }
44
+ /** Reads the tile index (0..255) at map cell (x, y). */
45
+ getTile(x, y) {
46
+ this.assertCell(x, y);
47
+ return this.tiles[y]?.[x] ?? 0;
48
+ }
49
+ /** Writes the tile index (0..255) at map cell (x, y). */
50
+ setTile(x, y, value) {
51
+ this.assertCell(x, y);
52
+ if (!Number.isInteger(value) || value < 0 || value > 255) {
53
+ throw new CartRangeError(`tile value ${value} out of range 0..255`);
54
+ }
55
+ const row = this.tiles[y];
56
+ if (row !== undefined)
57
+ row[x] = value;
58
+ }
59
+ /** Reads one full map row (128 tile indices) at row `y`. */
60
+ getRow(y) {
61
+ if (!Number.isInteger(y) || y < 0 || y >= MAP_ROWS) {
62
+ throw new CartRangeError(`map row ${y} out of range 0..${MAP_ROWS - 1}`);
63
+ }
64
+ return (this.tiles[y] ?? []).slice();
65
+ }
66
+ /**
67
+ * Counts non-zero tiles across a half-open row range `[start, end)`, clamped
68
+ * to the stored `__map__` rows (0..31). Shared-bank rows (32..63) that the
69
+ * text `__map__` does not store contribute zero. This is the primitive the
70
+ * gfx/map overlap smart-refuse uses to decide whether a shared-bank sprite
71
+ * write would clobber real map data.
72
+ */
73
+ nonZeroTilesInRows(start, end) {
74
+ let count = 0;
75
+ for (let y = Math.max(0, start); y < Math.min(MAP_ROWS, end); y++) {
76
+ for (let x = 0; x < MAP_WIDTH; x++)
77
+ if ((this.tiles[y]?.[x] ?? 0) !== 0)
78
+ count++;
79
+ }
80
+ return count;
81
+ }
82
+ /** Serializes the grid back to a `__map__` body (32 lines of 256 hex chars). */
83
+ toBody() {
84
+ const lines = [];
85
+ for (let y = 0; y < MAP_ROWS; y++) {
86
+ let line = '';
87
+ for (let x = 0; x < MAP_WIDTH; x++)
88
+ line += byteHex(this.tiles[y]?.[x] ?? 0);
89
+ lines.push(line);
90
+ }
91
+ return lines.join(this.newline) + this.newline;
92
+ }
93
+ /** Writes the grid back into the cart's `__map__` section. */
94
+ commit(cart) {
95
+ cart.setSection('map', this.toBody());
96
+ }
97
+ assertCell(x, y) {
98
+ if (!Number.isInteger(x) ||
99
+ !Number.isInteger(y) ||
100
+ x < 0 ||
101
+ y < 0 ||
102
+ x >= MAP_WIDTH ||
103
+ y >= MAP_ROWS) {
104
+ throw new CartRangeError(`map cell (${x}, ${y}) out of range 0..${MAP_WIDTH - 1} x 0..${MAP_ROWS - 1}`);
105
+ }
106
+ }
107
+ }
108
+ function hexByte(hi, lo) {
109
+ const v = Number.parseInt(`${hi}${lo}`, 16);
110
+ if (Number.isNaN(v)) {
111
+ throw new CartRangeError(`invalid hex byte "${hi}${lo}" in __map__`);
112
+ }
113
+ return v;
114
+ }
115
+ function byteHex(value) {
116
+ return value.toString(16).padStart(2, '0');
117
+ }
118
+ //# sourceMappingURL=map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.js","sourceRoot":"","sources":["../../../src/engine/cart/map.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { z } from 'incur';
2
+ /**
3
+ * The per-cart `picopilot.json` config, US #10.
4
+ *
5
+ * picopilot's config is incur's built-in `config: { files: ['picopilot.json'] }`
6
+ * mechanism (declared in `createCli`): a JSON file whose values become option
7
+ * DEFAULTS for the matching command, with precedence `argv > config > zod
8
+ * defaults`. incur keys config by command path, a command's options live under
9
+ * `commands.<group>.commands.<name>.options.<key>`, so a policy an agent sets
10
+ * once (like `allowMapOverlap`) is stored where the command that reads it looks.
11
+ *
12
+ * This module is the single source of truth for the config SHAPE: `init`
13
+ * scaffolds a file from {@link defaultConfigFile}, and the later `gfx set`
14
+ * command reads `allowMapOverlap` through incur's config layer (its own
15
+ * `--allow-map-overlap` option default). Keeping the shape here, rather than
16
+ * inlining a blob in `init`, means the scaffolded file and the reader cannot
17
+ * drift.
18
+ */
19
+ /**
20
+ * The `gfx set` options that `picopilot.json` may set as defaults.
21
+ *
22
+ * `allowMapOverlap` authorises `gfx set` to write sprites 128-255 even when the
23
+ * overlapping `__map__` rows hold real tiles (see ADR-0004). It is a rarely /
24
+ * once-set PROJECT setting: OFF by default (the smart-refuse is the safety), so
25
+ * a fresh cart never silently loses map data. The matching invocation flag is
26
+ * `--allow-map-overlap`; `argv > config > default` means the flag always wins.
27
+ */
28
+ export declare const GfxSetOptions: z.ZodObject<{
29
+ allowMapOverlap: z.ZodDefault<z.ZodBoolean>;
30
+ }, z.core.$strip>;
31
+ /**
32
+ * The full `picopilot.json` document shape, mirroring incur's config tree
33
+ * (`commands.<path>.options`). Only the keys picopilot actually reads are
34
+ * modelled; the schema is `.strict()`-free on purpose so a human/agent may add
35
+ * config for commands this version does not yet know about without a parse
36
+ * failure.
37
+ */
38
+ export declare const PicopilotConfig: z.ZodObject<{
39
+ $schema: z.ZodOptional<z.ZodString>;
40
+ commands: z.ZodOptional<z.ZodObject<{
41
+ gfx: z.ZodOptional<z.ZodObject<{
42
+ commands: z.ZodOptional<z.ZodObject<{
43
+ set: z.ZodOptional<z.ZodObject<{
44
+ options: z.ZodOptional<z.ZodObject<{
45
+ allowMapOverlap: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
46
+ }, z.core.$strip>>;
47
+ }, z.core.$strip>>;
48
+ }, z.core.$strip>>;
49
+ }, z.core.$strip>>;
50
+ }, z.core.$strip>>;
51
+ }, z.core.$strip>;
52
+ export type PicopilotConfig = z.infer<typeof PicopilotConfig>;
53
+ /**
54
+ * The default `picopilot.json` written by `init`: valid, minimal, and
55
+ * self-documenting via `$schema`. It sets `allowMapOverlap: false` explicitly
56
+ * so the one policy knob an agent is likely to flip is visible in the file (an
57
+ * agent is FREE to read/write this to match a higher-level instruction, US #10)
58
+ * rather than hidden behind a zod default.
59
+ */
60
+ export declare const defaultConfig: PicopilotConfig;
61
+ /** The default `picopilot.json` serialized as it is written to disk (trailing newline). */
62
+ export declare function defaultConfigFile(): string;
63
+ /**
64
+ * Reads `allowMapOverlap` out of a parsed `picopilot.json` tree, tolerating a
65
+ * partially-shaped or absent config. Returns `false` when the key is missing or
66
+ * the file is malformed enough that the value cannot be found, the SAFE default
67
+ * (the smart-refuse still guards the genuine data-loss corner).
68
+ *
69
+ * `gfx set` normally gets this value through incur's config → option-default
70
+ * layer; this helper exists for callers that hold a raw config object (and to
71
+ * keep the "where allowMapOverlap lives" knowledge in one place).
72
+ */
73
+ export declare function readAllowMapOverlap(config: unknown): boolean;
74
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/engine/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,OAAO,CAAC;AAExB;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa;;iBAOxB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;iBAsB1B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,eAa3B,CAAC;AAEF,2FAA2F;AAC3F,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAM5D"}
@@ -0,0 +1,103 @@
1
+ import { z } from 'incur';
2
+ /**
3
+ * The per-cart `picopilot.json` config, US #10.
4
+ *
5
+ * picopilot's config is incur's built-in `config: { files: ['picopilot.json'] }`
6
+ * mechanism (declared in `createCli`): a JSON file whose values become option
7
+ * DEFAULTS for the matching command, with precedence `argv > config > zod
8
+ * defaults`. incur keys config by command path, a command's options live under
9
+ * `commands.<group>.commands.<name>.options.<key>`, so a policy an agent sets
10
+ * once (like `allowMapOverlap`) is stored where the command that reads it looks.
11
+ *
12
+ * This module is the single source of truth for the config SHAPE: `init`
13
+ * scaffolds a file from {@link defaultConfigFile}, and the later `gfx set`
14
+ * command reads `allowMapOverlap` through incur's config layer (its own
15
+ * `--allow-map-overlap` option default). Keeping the shape here, rather than
16
+ * inlining a blob in `init`, means the scaffolded file and the reader cannot
17
+ * drift.
18
+ */
19
+ /**
20
+ * The `gfx set` options that `picopilot.json` may set as defaults.
21
+ *
22
+ * `allowMapOverlap` authorises `gfx set` to write sprites 128-255 even when the
23
+ * overlapping `__map__` rows hold real tiles (see ADR-0004). It is a rarely /
24
+ * once-set PROJECT setting: OFF by default (the smart-refuse is the safety), so
25
+ * a fresh cart never silently loses map data. The matching invocation flag is
26
+ * `--allow-map-overlap`; `argv > config > default` means the flag always wins.
27
+ */
28
+ export const GfxSetOptions = z.object({
29
+ allowMapOverlap: z
30
+ .boolean()
31
+ .default(false)
32
+ .describe('Authorise `gfx set` to overwrite __map__ tiles that alias sprites 128-255 (ADR-0004). Off by default; the smart-refuse is the safety.'),
33
+ });
34
+ /**
35
+ * The full `picopilot.json` document shape, mirroring incur's config tree
36
+ * (`commands.<path>.options`). Only the keys picopilot actually reads are
37
+ * modelled; the schema is `.strict()`-free on purpose so a human/agent may add
38
+ * config for commands this version does not yet know about without a parse
39
+ * failure.
40
+ */
41
+ export const PicopilotConfig = z.object({
42
+ $schema: z.string().optional().describe('JSON Schema reference (optional).'),
43
+ commands: z
44
+ .object({
45
+ gfx: z
46
+ .object({
47
+ commands: z
48
+ .object({
49
+ set: z
50
+ .object({
51
+ options: GfxSetOptions.partial().optional(),
52
+ })
53
+ .optional(),
54
+ })
55
+ .optional(),
56
+ })
57
+ .optional(),
58
+ })
59
+ .optional()
60
+ .describe('Per-command option defaults, keyed by command path (incur config layout).'),
61
+ });
62
+ /**
63
+ * The default `picopilot.json` written by `init`: valid, minimal, and
64
+ * self-documenting via `$schema`. It sets `allowMapOverlap: false` explicitly
65
+ * so the one policy knob an agent is likely to flip is visible in the file (an
66
+ * agent is FREE to read/write this to match a higher-level instruction, US #10)
67
+ * rather than hidden behind a zod default.
68
+ */
69
+ export const defaultConfig = {
70
+ $schema: './picopilot.schema.json',
71
+ commands: {
72
+ gfx: {
73
+ commands: {
74
+ set: {
75
+ options: {
76
+ allowMapOverlap: false,
77
+ },
78
+ },
79
+ },
80
+ },
81
+ },
82
+ };
83
+ /** The default `picopilot.json` serialized as it is written to disk (trailing newline). */
84
+ export function defaultConfigFile() {
85
+ return `${JSON.stringify(defaultConfig, null, 2)}\n`;
86
+ }
87
+ /**
88
+ * Reads `allowMapOverlap` out of a parsed `picopilot.json` tree, tolerating a
89
+ * partially-shaped or absent config. Returns `false` when the key is missing or
90
+ * the file is malformed enough that the value cannot be found, the SAFE default
91
+ * (the smart-refuse still guards the genuine data-loss corner).
92
+ *
93
+ * `gfx set` normally gets this value through incur's config → option-default
94
+ * layer; this helper exists for callers that hold a raw config object (and to
95
+ * keep the "where allowMapOverlap lives" knowledge in one place).
96
+ */
97
+ export function readAllowMapOverlap(config) {
98
+ const parsed = PicopilotConfig.safeParse(config);
99
+ if (!parsed.success)
100
+ return false;
101
+ return (parsed.data.commands?.gfx?.commands?.set?.options?.allowMapOverlap ?? false);
102
+ }
103
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/engine/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,OAAO,CAAC;AAExB;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,eAAe,EAAE,CAAC;SAChB,OAAO,EAAE;SACT,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CACR,uIAAuI,CACvI;CACF,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;IAC5E,QAAQ,EAAE,CAAC;SACT,MAAM,CAAC;QACP,GAAG,EAAE,CAAC;aACJ,MAAM,CAAC;YACP,QAAQ,EAAE,CAAC;iBACT,MAAM,CAAC;gBACP,GAAG,EAAE,CAAC;qBACJ,MAAM,CAAC;oBACP,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;iBAC3C,CAAC;qBACD,QAAQ,EAAE;aACZ,CAAC;iBACD,QAAQ,EAAE;SACZ,CAAC;aACD,QAAQ,EAAE;KACZ,CAAC;SACD,QAAQ,EAAE;SACV,QAAQ,CACR,2EAA2E,CAC3E;CACF,CAAC,CAAC;AAIH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC7C,OAAO,EAAE,yBAAyB;IAClC,QAAQ,EAAE;QACT,GAAG,EAAE;YACJ,QAAQ,EAAE;gBACT,GAAG,EAAE;oBACJ,OAAO,EAAE;wBACR,eAAe,EAAE,KAAK;qBACtB;iBACD;aACD;SACD;KACD;CACD,CAAC;AAEF,2FAA2F;AAC3F,MAAM,UAAU,iBAAiB;IAChC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAe;IAClD,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,CACN,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,eAAe,IAAI,KAAK,CAC3E,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The per-cart `picopilot.json` config model: the schema `init` scaffolds from
3
+ * and the later `gfx set` reads `allowMapOverlap` through (via incur's config
4
+ * layer). See `config.ts` for why the shape lives here as one source of truth.
5
+ */
6
+ export { defaultConfig, defaultConfigFile, GfxSetOptions, PicopilotConfig, readAllowMapOverlap, } from './config.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/config/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACN,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,mBAAmB,GACnB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The per-cart `picopilot.json` config model: the schema `init` scaffolds from
3
+ * and the later `gfx set` reads `allowMapOverlap` through (via incur's config
4
+ * layer). See `config.ts` for why the shape lives here as one source of truth.
5
+ */
6
+ export { defaultConfig, defaultConfigFile, GfxSetOptions, PicopilotConfig, readAllowMapOverlap, } from './config.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/config/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACN,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,mBAAmB,GACnB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The shrinko-free char-grid codec: converts a sprite's per-pixel colour
3
+ * nibbles (as {@link import('../cart/index.js').GfxSheet} reads/writes them)
4
+ * to and from a readable CHAR GRID, and back, as an IDENTITY.
5
+ *
6
+ * The char grid is the "SVG for pixels" (CONTEXT.md `char grid`): the EDIT
7
+ * surface an agent reads with `gfx show` and writes with `gfx set`. Each pixel
8
+ * is one character:
9
+ *
10
+ * - `.` = transparent = colour index 0 (PICO-8's index-0 doubles as the default
11
+ * transparent colour; the grid renders it as `.` so an agent SEES the empty
12
+ * pixels rather than a wall of `0`s).
13
+ * - `0`..`f` (lowercase hex) = colour indices 1..15 (`__gfx__` stores nibbles
14
+ * lowercase, so decode is case-insensitive but encode is lowercase to match
15
+ * what PICO-8 writes).
16
+ *
17
+ * The grid is 8 lines of 8 characters (one 8x8 sprite). The round-trip
18
+ * `nibbles → grid → nibbles` is an identity for every valid sprite (asserted in
19
+ * the tests), because `.` maps to 0 and 0 maps to `.` with no other collision.
20
+ *
21
+ * This codec is deliberately SEPARABLE from the cart model and the commands: it
22
+ * only knows nibble grids and strings, so the later `gfx render` task can reuse
23
+ * it and a test can round-trip it without a cart.
24
+ */
25
+ /** The width/height (in pixels) of one sprite; a char grid is this square. */
26
+ export declare const GRID_SIZE = 8;
27
+ /** The character that renders colour index 0 (transparent) in a char grid. */
28
+ export declare const TRANSPARENT_CHAR = ".";
29
+ /**
30
+ * Encodes an 8x8 grid of colour nibbles (0..15) into a char-grid string: 8
31
+ * lines of 8 chars, `\n`-joined, no trailing newline. Index 0 → `.`; 1..15 →
32
+ * the lowercase hex digit.
33
+ *
34
+ * @throws {GfxGridError} if the grid is not 8x8 or a cell is out of 0..15.
35
+ */
36
+ export declare function nibblesToGrid(grid: number[][]): string;
37
+ /**
38
+ * Decodes a char-grid string back into an 8x8 grid of colour nibbles (0..15),
39
+ * the inverse of {@link nibblesToGrid}. Accepts `.` (→ 0) and hex digits
40
+ * (case-insensitive). Blank lines / trailing whitespace-only content are
41
+ * tolerated so an agent may paste a grid with a trailing newline; the parsed
42
+ * content must still be exactly 8 rows of 8 cells.
43
+ *
44
+ * @throws {GfxGridError} if the parsed grid is not 8x8 or a char is invalid.
45
+ */
46
+ export declare function gridToNibbles(text: string): number[][];
47
+ /** A malformed char grid or nibble grid (wrong shape or an invalid cell/char). */
48
+ export declare class GfxGridError extends Error {
49
+ constructor(message: string);
50
+ }
51
+ //# sourceMappingURL=grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/engine/gfx/grid.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,SAAS,IAAc,CAAC;AAErC,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAStD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CA4BtD;AAED,kFAAkF;AAClF,qBAAa,YAAa,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI3B"}
@@ -0,0 +1,116 @@
1
+ import { SPRITE_SIZE } from '../cart/index.js';
2
+ /**
3
+ * The shrinko-free char-grid codec: converts a sprite's per-pixel colour
4
+ * nibbles (as {@link import('../cart/index.js').GfxSheet} reads/writes them)
5
+ * to and from a readable CHAR GRID, and back, as an IDENTITY.
6
+ *
7
+ * The char grid is the "SVG for pixels" (CONTEXT.md `char grid`): the EDIT
8
+ * surface an agent reads with `gfx show` and writes with `gfx set`. Each pixel
9
+ * is one character:
10
+ *
11
+ * - `.` = transparent = colour index 0 (PICO-8's index-0 doubles as the default
12
+ * transparent colour; the grid renders it as `.` so an agent SEES the empty
13
+ * pixels rather than a wall of `0`s).
14
+ * - `0`..`f` (lowercase hex) = colour indices 1..15 (`__gfx__` stores nibbles
15
+ * lowercase, so decode is case-insensitive but encode is lowercase to match
16
+ * what PICO-8 writes).
17
+ *
18
+ * The grid is 8 lines of 8 characters (one 8x8 sprite). The round-trip
19
+ * `nibbles → grid → nibbles` is an identity for every valid sprite (asserted in
20
+ * the tests), because `.` maps to 0 and 0 maps to `.` with no other collision.
21
+ *
22
+ * This codec is deliberately SEPARABLE from the cart model and the commands: it
23
+ * only knows nibble grids and strings, so the later `gfx render` task can reuse
24
+ * it and a test can round-trip it without a cart.
25
+ */
26
+ /** The width/height (in pixels) of one sprite; a char grid is this square. */
27
+ export const GRID_SIZE = SPRITE_SIZE;
28
+ /** The character that renders colour index 0 (transparent) in a char grid. */
29
+ export const TRANSPARENT_CHAR = '.';
30
+ /**
31
+ * Encodes an 8x8 grid of colour nibbles (0..15) into a char-grid string: 8
32
+ * lines of 8 chars, `\n`-joined, no trailing newline. Index 0 → `.`; 1..15 →
33
+ * the lowercase hex digit.
34
+ *
35
+ * @throws {GfxGridError} if the grid is not 8x8 or a cell is out of 0..15.
36
+ */
37
+ export function nibblesToGrid(grid) {
38
+ assertNibbleGrid(grid);
39
+ const lines = [];
40
+ for (let r = 0; r < GRID_SIZE; r++) {
41
+ let line = '';
42
+ for (let c = 0; c < GRID_SIZE; c++)
43
+ line += nibbleToChar(grid[r]?.[c] ?? 0);
44
+ lines.push(line);
45
+ }
46
+ return lines.join('\n');
47
+ }
48
+ /**
49
+ * Decodes a char-grid string back into an 8x8 grid of colour nibbles (0..15),
50
+ * the inverse of {@link nibblesToGrid}. Accepts `.` (→ 0) and hex digits
51
+ * (case-insensitive). Blank lines / trailing whitespace-only content are
52
+ * tolerated so an agent may paste a grid with a trailing newline; the parsed
53
+ * content must still be exactly 8 rows of 8 cells.
54
+ *
55
+ * @throws {GfxGridError} if the parsed grid is not 8x8 or a char is invalid.
56
+ */
57
+ export function gridToNibbles(text) {
58
+ // Split on either newline style; drop a single trailing empty line (a paste
59
+ // with a final newline) but keep interior blank lines so a genuine malformed
60
+ // grid (too few rows) is caught rather than silently accepted.
61
+ const rawLines = text.split(/\r?\n/);
62
+ while (rawLines.length > 0 && rawLines[rawLines.length - 1] === '')
63
+ rawLines.pop();
64
+ if (rawLines.length !== GRID_SIZE) {
65
+ throw new GfxGridError(`char grid must have ${GRID_SIZE} rows, got ${rawLines.length}`);
66
+ }
67
+ const grid = [];
68
+ for (let r = 0; r < GRID_SIZE; r++) {
69
+ const line = rawLines[r] ?? '';
70
+ if (line.length !== GRID_SIZE) {
71
+ throw new GfxGridError(`char grid row ${r} must have ${GRID_SIZE} cells, got ${line.length}`);
72
+ }
73
+ const row = [];
74
+ for (let c = 0; c < GRID_SIZE; c++)
75
+ row.push(charToNibble(line[c], r, c));
76
+ grid.push(row);
77
+ }
78
+ return grid;
79
+ }
80
+ /** A malformed char grid or nibble grid (wrong shape or an invalid cell/char). */
81
+ export class GfxGridError extends Error {
82
+ constructor(message) {
83
+ super(message);
84
+ this.name = 'GfxGridError';
85
+ }
86
+ }
87
+ function nibbleToChar(n) {
88
+ return n === 0 ? TRANSPARENT_CHAR : n.toString(16);
89
+ }
90
+ function charToNibble(ch, r, c) {
91
+ if (ch === TRANSPARENT_CHAR)
92
+ return 0;
93
+ const v = Number.parseInt(ch, 16);
94
+ if (Number.isNaN(v) || v < 0 || v > 15) {
95
+ throw new GfxGridError(`invalid char "${ch}" at (${c}, ${r}); expected "${TRANSPARENT_CHAR}" or a hex digit 0-f`);
96
+ }
97
+ return v;
98
+ }
99
+ function assertNibbleGrid(grid) {
100
+ if (grid.length !== GRID_SIZE) {
101
+ throw new GfxGridError(`nibble grid must have ${GRID_SIZE} rows, got ${grid.length}`);
102
+ }
103
+ for (let r = 0; r < GRID_SIZE; r++) {
104
+ const row = grid[r];
105
+ if (row === undefined || row.length !== GRID_SIZE) {
106
+ throw new GfxGridError(`nibble grid row ${r} must have ${GRID_SIZE} cells`);
107
+ }
108
+ for (let c = 0; c < GRID_SIZE; c++) {
109
+ const v = row[c] ?? 0;
110
+ if (!Number.isInteger(v) || v < 0 || v > 15) {
111
+ throw new GfxGridError(`nibble grid cell (${c}, ${r}) = ${v} out of range 0..15`);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ //# sourceMappingURL=grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.js","sourceRoot":"","sources":["../../../src/engine/gfx/grid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `engine/gfx` — the shrinko-free char-grid codec (`__gfx__` nibbles ↔ char
3
+ * grid) plus the gfx/map overlap smart-refuse decision.
4
+ *
5
+ * This module OWNS the char-grid representation (the "SVG for pixels" EDIT
6
+ * surface), the overlap safety logic, and the palette-accurate PNG render (the
7
+ * JUDGE surface). The `gfx show`/`gfx set`/`gfx render` commands are thin wiring
8
+ * over it. It is separable from the cart model (it only speaks nibble grids,
9
+ * strings, and RGB buffers) so it round-trips in isolation.
10
+ */
11
+ export { GRID_SIZE, GfxGridError, gridToNibbles, nibblesToGrid, TRANSPARENT_CHAR, } from './grid.js';
12
+ export { LABEL_SIZE, LabelError, labelHexFromPng, nearestPaletteIndex, } from './label.js';
13
+ export { decideOverlap, type OverlapDecision, spriteHasData } from './overlap.js';
14
+ export { PICO8_PALETTE, type PaletteColor, paletteColor } from './palette.js';
15
+ export { encodePng, type RgbImage } from './png.js';
16
+ export { RENDER_TARGET_PX, renderSheetPng, renderSpritePng, SHEET_RENDER_SCALE, SPRITE_RENDER_SCALE, upscale, } from './render.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/gfx/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACN,SAAS,EACT,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,mBAAmB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,aAAa,EAAE,KAAK,eAAe,EAAE,aAAa,EAAC,MAAM,cAAc,CAAC;AAChF,OAAO,EAAC,aAAa,EAAE,KAAK,YAAY,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAC,SAAS,EAAE,KAAK,QAAQ,EAAC,MAAM,UAAU,CAAC;AAClD,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,OAAO,GACP,MAAM,aAAa,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `engine/gfx` — the shrinko-free char-grid codec (`__gfx__` nibbles ↔ char
3
+ * grid) plus the gfx/map overlap smart-refuse decision.
4
+ *
5
+ * This module OWNS the char-grid representation (the "SVG for pixels" EDIT
6
+ * surface), the overlap safety logic, and the palette-accurate PNG render (the
7
+ * JUDGE surface). The `gfx show`/`gfx set`/`gfx render` commands are thin wiring
8
+ * over it. It is separable from the cart model (it only speaks nibble grids,
9
+ * strings, and RGB buffers) so it round-trips in isolation.
10
+ */
11
+ export { GRID_SIZE, GfxGridError, gridToNibbles, nibblesToGrid, TRANSPARENT_CHAR, } from './grid.js';
12
+ export { LABEL_SIZE, LabelError, labelHexFromPng, nearestPaletteIndex, } from './label.js';
13
+ export { decideOverlap, spriteHasData } from './overlap.js';
14
+ export { PICO8_PALETTE, paletteColor } from './palette.js';
15
+ export { encodePng } from './png.js';
16
+ export { RENDER_TARGET_PX, renderSheetPng, renderSpritePng, SHEET_RENDER_SCALE, SPRITE_RENDER_SCALE, upscale, } from './render.js';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/gfx/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACN,SAAS,EACT,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,mBAAmB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,aAAa,EAAwB,aAAa,EAAC,MAAM,cAAc,CAAC;AAChF,OAAO,EAAC,aAAa,EAAqB,YAAY,EAAC,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAC,SAAS,EAAgB,MAAM,UAAU,CAAC;AAClD,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,OAAO,GACP,MAAM,aAAa,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The `__label__` builder: turn a 128x128 image into the hex a `.p8` cart's
3
+ * label section holds, so `picopilot export --label <png>` can give a labelless
4
+ * cart a loading splash WITHOUT an interactive PICO-8 (F7) capture.
5
+ *
6
+ * A PICO-8 label is a 128x128 grid of palette indices (0..15), serialised as 128
7
+ * rows of 128 hex nibbles. This module decodes a PNG to RGB (via Node's stdlib
8
+ * `zlib` inflate, no npm dependency), maps each pixel to the NEAREST PICO-8
9
+ * palette colour, and emits that hex. It is the inverse direction of the
10
+ * palette-accurate PNG RENDER in `render.ts`/`png.ts` (which goes indices -> RGB).
11
+ *
12
+ * It intentionally supports only the PNG subset a caller realistically produces
13
+ * for a 128x128 label: 8-bit, non-interlaced, colour types 2 (RGB) and 6 (RGBA),
14
+ * and (via the palette map) 0/3 are rejected with a clear error rather than
15
+ * guessed at. Anything else fails loud with a structured {@link LabelError}.
16
+ */
17
+ /** The fixed PICO-8 label dimensions: a 128x128 image. */
18
+ export declare const LABEL_SIZE = 128;
19
+ /** A structured label-build failure (bad PNG, wrong size, unsupported format). */
20
+ export declare class LabelError extends Error {
21
+ readonly code: 'not-a-png' | 'unsupported-png' | 'wrong-size' | 'truncated-png';
22
+ constructor(code: 'not-a-png' | 'unsupported-png' | 'wrong-size' | 'truncated-png', message: string);
23
+ }
24
+ /** The nearest PICO-8 palette index (by squared RGB distance) for an RGB triple. */
25
+ export declare function nearestPaletteIndex(r: number, g: number, b: number): number;
26
+ /**
27
+ * Builds the `__label__` hex body (128 rows of 128 nibbles, newline-joined, with
28
+ * a trailing newline) from a 128x128 PNG. Each pixel is mapped to its nearest
29
+ * PICO-8 palette colour. The PNG MUST be exactly 128x128 ({@link LABEL_SIZE});
30
+ * anything else is a {@link LabelError} (`wrong-size`) so a mis-sized image fails
31
+ * loud instead of being silently cropped or stretched.
32
+ */
33
+ export declare function labelHexFromPng(pngBytes: Uint8Array): string;
34
+ //# sourceMappingURL=label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../src/engine/gfx/label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,0DAA0D;AAC1D,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B,kFAAkF;AAClF,qBAAa,UAAW,SAAQ,KAAK;IAEnC,QAAQ,CAAC,IAAI,EACZ,WAAW,GAAG,iBAAiB,GAAG,YAAY,GAAG,eAAe;gBADxD,IAAI,EACZ,WAAW,GAAG,iBAAiB,GAAG,YAAY,GAAG,eAAe,EACjE,OAAO,EAAE,MAAM;CAKhB;AAsJD,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAe3E;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAkB5D"}