sci2-ts 0.0.0-stage → 0.2.2

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 (357) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/LICENSE +21 -0
  3. package/README.md +113 -2
  4. package/apps/viewer/editor.html +43 -0
  5. package/apps/viewer/explore.html +27 -0
  6. package/apps/viewer/index.html +26 -0
  7. package/apps/viewer/package.json +27 -0
  8. package/apps/viewer/play.html +26 -0
  9. package/apps/viewer/plugin.d.ts +18 -0
  10. package/apps/viewer/plugin.js +75 -0
  11. package/apps/viewer/plugin.ts +84 -0
  12. package/apps/viewer/src/adlib.d.ts +20 -0
  13. package/apps/viewer/src/adlib.js +55 -0
  14. package/apps/viewer/src/adlib.ts +62 -0
  15. package/apps/viewer/src/app.d.ts +5 -0
  16. package/apps/viewer/src/app.js +5 -0
  17. package/apps/viewer/src/app.ts +5 -0
  18. package/apps/viewer/src/asset-index.d.ts +22 -0
  19. package/apps/viewer/src/asset-index.js +74 -0
  20. package/apps/viewer/src/asset-index.ts +90 -0
  21. package/apps/viewer/src/audio.d.ts +28 -0
  22. package/apps/viewer/src/audio.js +102 -0
  23. package/apps/viewer/src/audio.ts +110 -0
  24. package/apps/viewer/src/editor-handles.d.ts +63 -0
  25. package/apps/viewer/src/editor-handles.js +332 -0
  26. package/apps/viewer/src/editor-handles.ts +368 -0
  27. package/apps/viewer/src/editor-languages.d.ts +23 -0
  28. package/apps/viewer/src/editor-languages.js +157 -0
  29. package/apps/viewer/src/editor-languages.ts +135 -0
  30. package/apps/viewer/src/editor.css +213 -0
  31. package/apps/viewer/src/editor.d.ts +1 -0
  32. package/apps/viewer/src/editor.js +393 -0
  33. package/apps/viewer/src/editor.ts +412 -0
  34. package/apps/viewer/src/explore/classes.d.ts +7 -0
  35. package/apps/viewer/src/explore/classes.js +63 -0
  36. package/apps/viewer/src/explore/classes.ts +61 -0
  37. package/apps/viewer/src/explore/context.d.ts +33 -0
  38. package/apps/viewer/src/explore/context.js +96 -0
  39. package/apps/viewer/src/explore/context.ts +124 -0
  40. package/apps/viewer/src/explore/explore.css +93 -0
  41. package/apps/viewer/src/explore/explore.d.ts +1 -0
  42. package/apps/viewer/src/explore/explore.js +102 -0
  43. package/apps/viewer/src/explore/explore.ts +104 -0
  44. package/apps/viewer/src/explore/inspect.d.ts +5 -0
  45. package/apps/viewer/src/explore/inspect.js +208 -0
  46. package/apps/viewer/src/explore/inspect.ts +279 -0
  47. package/apps/viewer/src/explore/live.d.ts +22 -0
  48. package/apps/viewer/src/explore/live.js +318 -0
  49. package/apps/viewer/src/explore/live.ts +365 -0
  50. package/apps/viewer/src/explore/map.d.ts +11 -0
  51. package/apps/viewer/src/explore/map.js +182 -0
  52. package/apps/viewer/src/explore/map.ts +193 -0
  53. package/apps/viewer/src/files.d.ts +21 -0
  54. package/apps/viewer/src/files.js +69 -0
  55. package/apps/viewer/src/files.ts +72 -0
  56. package/apps/viewer/src/game-profile.d.ts +19 -0
  57. package/apps/viewer/src/game-profile.js +4 -0
  58. package/apps/viewer/src/game-profile.ts +26 -0
  59. package/apps/viewer/src/main.d.ts +1 -0
  60. package/apps/viewer/src/main.js +473 -0
  61. package/apps/viewer/src/main.ts +527 -0
  62. package/apps/viewer/src/music.d.ts +14 -0
  63. package/apps/viewer/src/music.js +32 -0
  64. package/apps/viewer/src/music.ts +34 -0
  65. package/apps/viewer/src/opl-worklet.d.ts +1 -0
  66. package/apps/viewer/src/opl-worklet.js +47 -0
  67. package/apps/viewer/src/opl-worklet.ts +55 -0
  68. package/apps/viewer/src/overlay.d.ts +31 -0
  69. package/apps/viewer/src/overlay.js +103 -0
  70. package/apps/viewer/src/overlay.ts +123 -0
  71. package/apps/viewer/src/play.css +14 -0
  72. package/apps/viewer/src/play.d.ts +1 -0
  73. package/apps/viewer/src/play.js +179 -0
  74. package/apps/viewer/src/play.ts +177 -0
  75. package/apps/viewer/src/saves.d.ts +10 -0
  76. package/apps/viewer/src/saves.js +50 -0
  77. package/apps/viewer/src/saves.ts +51 -0
  78. package/apps/viewer/src/session.d.ts +91 -0
  79. package/apps/viewer/src/session.js +301 -0
  80. package/apps/viewer/src/session.ts +321 -0
  81. package/apps/viewer/src/sound-preview.d.ts +7 -0
  82. package/apps/viewer/src/sound-preview.js +214 -0
  83. package/apps/viewer/src/sound-preview.ts +228 -0
  84. package/apps/viewer/src/style.css +64 -0
  85. package/apps/viewer/vite.config.ts +25 -0
  86. package/bin/sci-ts.mjs +9 -0
  87. package/lib/994.sc +49 -0
  88. package/lib/995.sc +53 -0
  89. package/lib/996.sc +299 -0
  90. package/lib/997.sc +198 -0
  91. package/lib/998.sc +338 -0
  92. package/lib/999.sc +189 -0
  93. package/lib/resources.d.ts +2 -0
  94. package/lib/resources.js +84 -0
  95. package/lib/resources.ts +88 -0
  96. package/lib/system.sh +62 -0
  97. package/package.json +75 -4
  98. package/packages/content/room.schema.json +267 -0
  99. package/packages/content/src/index.d.ts +3 -0
  100. package/packages/content/src/index.js +3 -0
  101. package/packages/content/src/index.ts +3 -0
  102. package/packages/content/src/room.d.ts +111 -0
  103. package/packages/content/src/room.js +886 -0
  104. package/packages/content/src/room.ts +1011 -0
  105. package/packages/content/src/target.d.ts +80 -0
  106. package/packages/content/src/target.js +1 -0
  107. package/packages/content/src/target.ts +60 -0
  108. package/packages/content/src/yarn.d.ts +90 -0
  109. package/packages/content/src/yarn.js +214 -0
  110. package/packages/content/src/yarn.ts +261 -0
  111. package/packages/sci/src/analysis/game.d.ts +90 -0
  112. package/packages/sci/src/analysis/game.js +221 -0
  113. package/packages/sci/src/analysis/game.ts +305 -0
  114. package/packages/sci/src/analysis/runtime.d.ts +58 -0
  115. package/packages/sci/src/analysis/runtime.js +133 -0
  116. package/packages/sci/src/analysis/runtime.ts +163 -0
  117. package/packages/sci/src/audio/adlib.d.ts +73 -0
  118. package/packages/sci/src/audio/adlib.js +280 -0
  119. package/packages/sci/src/audio/adlib.ts +329 -0
  120. package/packages/sci/src/audio/index.d.ts +43 -0
  121. package/packages/sci/src/audio/index.js +99 -0
  122. package/packages/sci/src/audio/index.ts +122 -0
  123. package/packages/sci/src/audio/midi.d.ts +92 -0
  124. package/packages/sci/src/audio/midi.js +242 -0
  125. package/packages/sci/src/audio/midi.ts +262 -0
  126. package/packages/sci/src/audio/opl2.d.ts +74 -0
  127. package/packages/sci/src/audio/opl2.js +283 -0
  128. package/packages/sci/src/audio/opl2.ts +283 -0
  129. package/packages/sci/src/audio/sol.d.ts +32 -0
  130. package/packages/sci/src/audio/sol.js +81 -0
  131. package/packages/sci/src/audio/sol.ts +100 -0
  132. package/packages/sci/src/audio/write.d.ts +35 -0
  133. package/packages/sci/src/audio/write.js +91 -0
  134. package/packages/sci/src/audio/write.ts +108 -0
  135. package/packages/sci/src/gfx/cel.d.ts +36 -0
  136. package/packages/sci/src/gfx/cel.js +82 -0
  137. package/packages/sci/src/gfx/cel.ts +110 -0
  138. package/packages/sci/src/gfx/palette.d.ts +24 -0
  139. package/packages/sci/src/gfx/palette.js +49 -0
  140. package/packages/sci/src/gfx/palette.ts +56 -0
  141. package/packages/sci/src/gfx/pic-file.d.ts +46 -0
  142. package/packages/sci/src/gfx/pic-file.js +114 -0
  143. package/packages/sci/src/gfx/pic-file.ts +155 -0
  144. package/packages/sci/src/gfx/pic.d.ts +27 -0
  145. package/packages/sci/src/gfx/pic.js +34 -0
  146. package/packages/sci/src/gfx/pic.ts +53 -0
  147. package/packages/sci/src/gfx/quantize.d.ts +42 -0
  148. package/packages/sci/src/gfx/quantize.js +199 -0
  149. package/packages/sci/src/gfx/quantize.ts +218 -0
  150. package/packages/sci/src/gfx/view-file.d.ts +62 -0
  151. package/packages/sci/src/gfx/view-file.js +182 -0
  152. package/packages/sci/src/gfx/view-file.ts +233 -0
  153. package/packages/sci/src/gfx/view.d.ts +28 -0
  154. package/packages/sci/src/gfx/view.js +45 -0
  155. package/packages/sci/src/gfx/view.ts +62 -0
  156. package/packages/sci/src/index.d.ts +38 -0
  157. package/packages/sci/src/index.js +38 -0
  158. package/packages/sci/src/index.ts +38 -0
  159. package/packages/sci/src/motion/pathfinding.d.ts +40 -0
  160. package/packages/sci/src/motion/pathfinding.js +140 -0
  161. package/packages/sci/src/motion/pathfinding.ts +147 -0
  162. package/packages/sci/src/resource/archive.d.ts +16 -0
  163. package/packages/sci/src/resource/archive.js +51 -0
  164. package/packages/sci/src/resource/archive.ts +56 -0
  165. package/packages/sci/src/resource/lzs.d.ts +12 -0
  166. package/packages/sci/src/resource/lzs.js +64 -0
  167. package/packages/sci/src/resource/lzs.ts +66 -0
  168. package/packages/sci/src/resource/manager.d.ts +45 -0
  169. package/packages/sci/src/resource/manager.js +158 -0
  170. package/packages/sci/src/resource/manager.ts +184 -0
  171. package/packages/sci/src/resource/map.d.ts +18 -0
  172. package/packages/sci/src/resource/map.js +34 -0
  173. package/packages/sci/src/resource/map.ts +44 -0
  174. package/packages/sci/src/resource/types.d.ts +37 -0
  175. package/packages/sci/src/resource/types.js +45 -0
  176. package/packages/sci/src/resource/types.ts +57 -0
  177. package/packages/sci/src/script/assembly.d.ts +67 -0
  178. package/packages/sci/src/script/assembly.js +748 -0
  179. package/packages/sci/src/script/assembly.ts +714 -0
  180. package/packages/sci/src/script/compiler.d.ts +75 -0
  181. package/packages/sci/src/script/compiler.js +993 -0
  182. package/packages/sci/src/script/compiler.ts +1040 -0
  183. package/packages/sci/src/script/disasm.d.ts +81 -0
  184. package/packages/sci/src/script/disasm.js +318 -0
  185. package/packages/sci/src/script/disasm.ts +323 -0
  186. package/packages/sci/src/script/kernel-names.d.ts +6 -0
  187. package/packages/sci/src/script/kernel-names.js +165 -0
  188. package/packages/sci/src/script/kernel-names.ts +165 -0
  189. package/packages/sci/src/script/opcodes.d.ts +22 -0
  190. package/packages/sci/src/script/opcodes.js +45 -0
  191. package/packages/sci/src/script/opcodes.ts +72 -0
  192. package/packages/sci/src/script/script.d.ts +56 -0
  193. package/packages/sci/src/script/script.js +63 -0
  194. package/packages/sci/src/script/script.ts +111 -0
  195. package/packages/sci/src/script/sexpr.d.ts +45 -0
  196. package/packages/sci/src/script/sexpr.js +151 -0
  197. package/packages/sci/src/script/sexpr.ts +139 -0
  198. package/packages/sci/src/script/vocab.d.ts +15 -0
  199. package/packages/sci/src/script/vocab.js +44 -0
  200. package/packages/sci/src/script/vocab.ts +48 -0
  201. package/packages/sci/src/script/world.d.ts +25 -0
  202. package/packages/sci/src/script/world.js +65 -0
  203. package/packages/sci/src/script/world.ts +70 -0
  204. package/packages/sci/src/text/font.d.ts +37 -0
  205. package/packages/sci/src/text/font.js +103 -0
  206. package/packages/sci/src/text/font.ts +125 -0
  207. package/packages/sci/src/text/message.d.ts +69 -0
  208. package/packages/sci/src/text/message.js +171 -0
  209. package/packages/sci/src/text/message.ts +205 -0
  210. package/packages/sci/src/vm/kernels/arrays.d.ts +19 -0
  211. package/packages/sci/src/vm/kernels/arrays.js +182 -0
  212. package/packages/sci/src/vm/kernels/arrays.ts +164 -0
  213. package/packages/sci/src/vm/kernels/audio.d.ts +70 -0
  214. package/packages/sci/src/vm/kernels/audio.js +347 -0
  215. package/packages/sci/src/vm/kernels/audio.ts +349 -0
  216. package/packages/sci/src/vm/kernels/core.d.ts +5 -0
  217. package/packages/sci/src/vm/kernels/core.js +87 -0
  218. package/packages/sci/src/vm/kernels/core.ts +91 -0
  219. package/packages/sci/src/vm/kernels/graphics.d.ts +141 -0
  220. package/packages/sci/src/vm/kernels/graphics.js +467 -0
  221. package/packages/sci/src/vm/kernels/graphics.ts +502 -0
  222. package/packages/sci/src/vm/kernels/index.d.ts +8 -0
  223. package/packages/sci/src/vm/kernels/index.js +29 -0
  224. package/packages/sci/src/vm/kernels/index.ts +32 -0
  225. package/packages/sci/src/vm/kernels/lists.d.ts +6 -0
  226. package/packages/sci/src/vm/kernels/lists.js +127 -0
  227. package/packages/sci/src/vm/kernels/lists.ts +111 -0
  228. package/packages/sci/src/vm/kernels/motion.d.ts +7 -0
  229. package/packages/sci/src/vm/kernels/motion.js +204 -0
  230. package/packages/sci/src/vm/kernels/motion.ts +194 -0
  231. package/packages/sci/src/vm/kernels/palette.d.ts +82 -0
  232. package/packages/sci/src/vm/kernels/palette.js +292 -0
  233. package/packages/sci/src/vm/kernels/palette.ts +303 -0
  234. package/packages/sci/src/vm/kernels/saves.d.ts +45 -0
  235. package/packages/sci/src/vm/kernels/saves.js +123 -0
  236. package/packages/sci/src/vm/kernels/saves.ts +149 -0
  237. package/packages/sci/src/vm/kernels/system.d.ts +62 -0
  238. package/packages/sci/src/vm/kernels/system.js +110 -0
  239. package/packages/sci/src/vm/kernels/system.ts +128 -0
  240. package/packages/sci/src/vm/kernels/text.d.ts +13 -0
  241. package/packages/sci/src/vm/kernels/text.js +338 -0
  242. package/packages/sci/src/vm/kernels/text.ts +328 -0
  243. package/packages/sci/src/vm/memory.d.ts +138 -0
  244. package/packages/sci/src/vm/memory.js +168 -0
  245. package/packages/sci/src/vm/memory.ts +266 -0
  246. package/packages/sci/src/vm/replace.d.ts +11 -0
  247. package/packages/sci/src/vm/replace.js +14 -0
  248. package/packages/sci/src/vm/replace.ts +16 -0
  249. package/packages/sci/src/vm/savegame.d.ts +89 -0
  250. package/packages/sci/src/vm/savegame.js +161 -0
  251. package/packages/sci/src/vm/savegame.ts +188 -0
  252. package/packages/sci/src/vm/value.d.ts +17 -0
  253. package/packages/sci/src/vm/value.js +12 -0
  254. package/packages/sci/src/vm/value.ts +25 -0
  255. package/packages/sci/src/vm/vm.d.ts +155 -0
  256. package/packages/sci/src/vm/vm.js +1053 -0
  257. package/packages/sci/src/vm/vm.ts +914 -0
  258. package/tools/adlib-render.d.ts +1 -0
  259. package/tools/adlib-render.js +49 -0
  260. package/tools/adlib-render.ts +42 -0
  261. package/tools/analyze.d.ts +1 -0
  262. package/tools/analyze.js +25 -0
  263. package/tools/analyze.ts +25 -0
  264. package/tools/art/build.d.ts +34 -0
  265. package/tools/art/build.js +175 -0
  266. package/tools/art/build.ts +153 -0
  267. package/tools/art.d.ts +1 -0
  268. package/tools/art.js +31 -0
  269. package/tools/art.ts +29 -0
  270. package/tools/asm.d.ts +1 -0
  271. package/tools/asm.js +61 -0
  272. package/tools/asm.ts +55 -0
  273. package/tools/cli.d.ts +1 -0
  274. package/tools/cli.js +99 -0
  275. package/tools/cli.ts +100 -0
  276. package/tools/editor-server.d.ts +29 -0
  277. package/tools/editor-server.js +162 -0
  278. package/tools/editor-server.ts +176 -0
  279. package/tools/explore.d.ts +1 -0
  280. package/tools/explore.js +305 -0
  281. package/tools/explore.ts +250 -0
  282. package/tools/game/audio.d.ts +19 -0
  283. package/tools/game/audio.js +143 -0
  284. package/tools/game/audio.ts +130 -0
  285. package/tools/game/build.d.ts +46 -0
  286. package/tools/game/build.js +259 -0
  287. package/tools/game/build.ts +283 -0
  288. package/tools/game/defaults.d.ts +26 -0
  289. package/tools/game/defaults.js +82 -0
  290. package/tools/game/defaults.ts +83 -0
  291. package/tools/game/font.d.ts +3 -0
  292. package/tools/game/font.js +124 -0
  293. package/tools/game/font.ts +126 -0
  294. package/tools/game/kit.d.ts +7 -0
  295. package/tools/game/kit.js +7 -0
  296. package/tools/game/kit.ts +10 -0
  297. package/tools/game/new.d.ts +8 -0
  298. package/tools/game/new.js +39 -0
  299. package/tools/game/new.ts +38 -0
  300. package/tools/game/target.d.ts +7 -0
  301. package/tools/game/target.js +41 -0
  302. package/tools/game/target.ts +42 -0
  303. package/tools/game/template/resources.ts.tmpl +43 -0
  304. package/tools/game/template/rooms/1.room.yaml +9 -0
  305. package/tools/game/template/rooms/1.yarn +17 -0
  306. package/tools/game/template/scripts/0.sc +24 -0
  307. package/tools/game.d.ts +1 -0
  308. package/tools/game.js +77 -0
  309. package/tools/game.ts +70 -0
  310. package/tools/mod-build.d.ts +28 -0
  311. package/tools/mod-build.js +173 -0
  312. package/tools/mod-build.ts +179 -0
  313. package/tools/mod-target.d.ts +16 -0
  314. package/tools/mod-target.js +26 -0
  315. package/tools/mod-target.ts +32 -0
  316. package/tools/mod.d.ts +1 -0
  317. package/tools/mod.js +47 -0
  318. package/tools/mod.ts +45 -0
  319. package/tools/msg.d.ts +1 -0
  320. package/tools/msg.js +40 -0
  321. package/tools/msg.ts +36 -0
  322. package/tools/node-files.d.ts +3 -0
  323. package/tools/node-files.js +34 -0
  324. package/tools/node-files.ts +33 -0
  325. package/tools/patches.d.ts +3 -0
  326. package/tools/patches.js +13 -0
  327. package/tools/patches.ts +13 -0
  328. package/tools/pic-build.d.ts +9 -0
  329. package/tools/pic-build.js +26 -0
  330. package/tools/pic-build.ts +27 -0
  331. package/tools/pic.d.ts +1 -0
  332. package/tools/pic.js +64 -0
  333. package/tools/pic.ts +61 -0
  334. package/tools/play.d.ts +1 -0
  335. package/tools/play.js +54 -0
  336. package/tools/play.ts +54 -0
  337. package/tools/png.d.ts +16 -0
  338. package/tools/png.js +124 -0
  339. package/tools/png.ts +123 -0
  340. package/tools/res.d.ts +1 -0
  341. package/tools/res.js +48 -0
  342. package/tools/res.ts +50 -0
  343. package/tools/scenario.d.ts +1 -0
  344. package/tools/scenario.js +74 -0
  345. package/tools/scenario.ts +80 -0
  346. package/tools/site/build.d.ts +8 -0
  347. package/tools/site/build.js +110 -0
  348. package/tools/site/build.ts +116 -0
  349. package/tools/site/markdown.d.ts +11 -0
  350. package/tools/site/markdown.js +88 -0
  351. package/tools/site/markdown.ts +84 -0
  352. package/tools/view-build.d.ts +34 -0
  353. package/tools/view-build.js +47 -0
  354. package/tools/view-build.ts +71 -0
  355. package/tools/view.d.ts +1 -0
  356. package/tools/view.js +54 -0
  357. package/tools/view.ts +52 -0
@@ -0,0 +1,467 @@
1
+ import { ResourceType } from "../../resource/types.js";
2
+ import { emptyPalette, mergePalette, parseHunkPalette } from "../../gfx/palette.js";
3
+ import { parsePic } from "../../gfx/pic.js";
4
+ import { parseView } from "../../gfx/view.js";
5
+ import { INFO_VIEW_CHANGED } from "../vm.js";
6
+ import { ObjectSlot } from "../../script/script.js";
7
+ import { paletteEffects } from "./palette.js";
8
+ import { input } from "./system.js";
9
+ import { NULL, bool, fromInt, toSigned } from "../value.js";
10
+ export const SCREEN_WIDTH = 320;
11
+ export const SCREEN_HEIGHT = 200;
12
+ /**
13
+ * SCI32 graphics model: the scripts create Plane objects (layers with a rect, a background
14
+ * pic or colour, and a priority) and register View-like objects as screen items in a plane.
15
+ * Each FrameOut composes planes by priority, then items within a plane by priority.
16
+ * The kernel only remembers *which* objects are registered; their current state (x, y,
17
+ * view, loop, cel...) is read from the objects' properties at compose time.
18
+ */
19
+ export class Graphics {
20
+ vm;
21
+ planes = new Set();
22
+ items = new Set();
23
+ frames = 0;
24
+ views = new Map();
25
+ pics = new Map();
26
+ basePalette;
27
+ /** Called on every FrameOut with the composed frame. */
28
+ onFrame;
29
+ /** Drops parsed views and pictures (their resources were replaced). */
30
+ forgetResources() {
31
+ this.views.clear();
32
+ this.pics.clear();
33
+ }
34
+ constructor(vm) {
35
+ this.vm = vm;
36
+ const res = { type: ResourceType.Palette, number: 999 };
37
+ const base = vm.resources.has(res) ? (parseHunkPalette(vm.resources.loadSync(res).data) ?? emptyPalette()) : emptyPalette();
38
+ // SCI reserves colour 255 as white; palette 999 doesn't define it but UI text uses it.
39
+ base.rgb.set([255, 255, 255], 255 * 3);
40
+ base.used[255] = 1;
41
+ this.basePalette = base;
42
+ }
43
+ /**
44
+ * View numbers are unsigned (SCI32's system views live at 64xxx), but scripts store them
45
+ * in signed properties; only 0xFFFF (-1) means "no view".
46
+ */
47
+ view(n) {
48
+ n &= 0xffff;
49
+ if (n === 0xffff)
50
+ return undefined;
51
+ if (!this.views.has(n)) {
52
+ const id = { type: ResourceType.View, number: n };
53
+ this.views.set(n, this.vm.resources.has(id) ? parseView(this.vm.resources.loadSync(id).data) : null);
54
+ }
55
+ return this.views.get(n) ?? undefined;
56
+ }
57
+ pic(n) {
58
+ if (!this.pics.has(n)) {
59
+ const id = { type: ResourceType.Pic, number: n };
60
+ this.pics.set(n, this.vm.resources.has(id) ? parsePic(this.vm.resources.loadSync(id).data) : null);
61
+ }
62
+ return this.pics.get(n) ?? undefined;
63
+ }
64
+ cel(viewNo, loopNo, celNo) {
65
+ const view = this.view(viewNo);
66
+ if (!view || !view.loops.length)
67
+ return undefined;
68
+ const loop = view.loops[Math.min(Math.max(loopNo, 0), view.loops.length - 1)];
69
+ if (!loop.cels.length)
70
+ return undefined;
71
+ return { cel: loop.cels[Math.min(Math.max(celNo, 0), loop.cels.length - 1)], mirror: loop.mirror };
72
+ }
73
+ /** Reads a numeric property (signed), 0 if absent. */
74
+ prop(obj, name) {
75
+ return toSigned(this.vm.getProp(obj, name) ?? 0);
76
+ }
77
+ /** Palette built from every palette submitted so far (base, scripts, pics, views). */
78
+ source;
79
+ /** Picture each plane last submitted, and view each item last submitted. */
80
+ planePictures = new Map();
81
+ itemViews = new Map();
82
+ /** The palette after PalVary/PalCycle (before fades), as of the last frame. */
83
+ currentPalette = emptyPalette();
84
+ /** Active show-style transitions, by plane. */
85
+ transitions = new Map();
86
+ /** The mouse cursor: a view cel drawn on top at the mouse position (its origin = hotspot). */
87
+ cursor = { view: -1, loop: 0, cel: 0, visible: true, x: 160, y: 100 };
88
+ /** Palette range affected by fade show styles (SetPalStyleRange). */
89
+ styleRange = { from: 0, to: 255 };
90
+ lastFrame;
91
+ get sourcePalette() {
92
+ return this.source;
93
+ }
94
+ set sourcePalette(pal) {
95
+ this.source = pal;
96
+ }
97
+ submitPalette(pal) {
98
+ this.source = mergePalette(this.source ?? this.basePalette, pal);
99
+ }
100
+ /** Forgets submitted palettes (restart/restore); they're resubmitted as things are drawn. */
101
+ resetPalette() {
102
+ this.source = undefined;
103
+ this.planePictures.clear();
104
+ this.itemViews.clear();
105
+ }
106
+ /**
107
+ * Where a view item's cel lands, in its plane's coordinates. Scripts scale cels for
108
+ * perspective (the hero shrinks with distance) and to stretch UI backgrounds (dialog boxes)
109
+ * to fit their contents.
110
+ */
111
+ viewRect(item) {
112
+ const p = (n) => this.prop(item, n);
113
+ const found = this.cel(p("view"), p("loop"), p("cel"));
114
+ if (!found || found.cel.width === 0)
115
+ return undefined;
116
+ const { cel, mirror } = found;
117
+ const scaled = (p("scaleSignal") & SCALE_SIGNAL_DO_SCALING) !== 0;
118
+ const sx = scaled ? Math.max(p("scaleX"), 1) / 128 : 1;
119
+ const sy = scaled ? Math.max(p("scaleY"), 1) / 128 : 1;
120
+ const origin = celOrigin(cel, mirror);
121
+ return {
122
+ cel, mirror, sx, sy,
123
+ x: p("x") - Math.floor(origin.x * sx),
124
+ y: p("y") - p("z") - Math.floor(origin.y * sy),
125
+ width: Math.max(1, Math.floor(cel.width * sx)),
126
+ height: Math.max(1, Math.floor(cel.height * sy)),
127
+ };
128
+ }
129
+ /**
130
+ * Composes the current frame in three phases, because remap colours (shadows) depend on
131
+ * the final palette: collect what each plane draws (submitting palettes as pictures and
132
+ * views change, like the original), apply palette effects, then blit.
133
+ */
134
+ compose() {
135
+ const pixels = new Uint8Array(SCREEN_WIDTH * SCREEN_HEIGHT);
136
+ const p = (o, n) => this.prop(o, n);
137
+ this.source ??= this.basePalette;
138
+ // Phase 1: what to draw. Priority -1 marks a hidden plane; an empty rect is off-screen.
139
+ const layers = [];
140
+ const planes = [...this.planes]
141
+ .filter((pl) => this.vm.memory.object(pl) && p(pl, "priority") !== -1)
142
+ .sort((a, b) => p(a, "priority") - p(b, "priority"));
143
+ for (const plane of planes) {
144
+ const left = p(plane, "inLeft"), top = p(plane, "inTop");
145
+ const clip = { left, top, right: p(plane, "inRight"), bottom: p(plane, "inBottom") };
146
+ if (clip.right <= clip.left || clip.bottom <= clip.top)
147
+ continue;
148
+ const draws = [];
149
+ let fill;
150
+ const picture = p(plane, "picture");
151
+ const pic = picture >= 0 ? this.pic(picture) : undefined;
152
+ if (pic) {
153
+ if (this.planePictures.get(plane) !== picture) {
154
+ this.planePictures.set(plane, picture);
155
+ this.submitPalette(pic.palette);
156
+ }
157
+ for (const cel of pic.cels)
158
+ draws.push({ cel, mirror: false, x: left + cel.x, y: top + cel.y, priority: cel.priority, order: -1 });
159
+ }
160
+ else if (picture !== -2)
161
+ fill = p(plane, "back") & 0xff;
162
+ let order = 0;
163
+ for (const item of this.items) {
164
+ if (!this.vm.memory.object(item) || this.vm.getProp(item, "plane") !== plane)
165
+ continue;
166
+ const priority = p(item, "fixPriority") ? p(item, "priority") : p(item, "y");
167
+ // Script-drawn bitmaps (text boxes) replace the view cel when `bitmap` is set.
168
+ const bitmap = this.vm.memory.bitmap(this.vm.getProp(item, "bitmap") ?? 0);
169
+ if (bitmap) {
170
+ const cel = { width: bitmap.width, height: bitmap.height, displaceX: 0, displaceY: 0, skipColor: bitmap.skipColor, pixels: bitmap.pixels };
171
+ draws.push({ cel, mirror: false, x: left + p(item, "x") - bitmap.originX, y: top + p(item, "y") - bitmap.originY, priority, order: order++ });
172
+ continue;
173
+ }
174
+ const viewNo = p(item, "view");
175
+ const rect = this.viewRect(item);
176
+ if (!rect)
177
+ continue;
178
+ const { cel, mirror, sx, sy } = rect;
179
+ const x = left + rect.x, y = top + rect.y, w = rect.width, h = rect.height;
180
+ if (x > clip.right || y > clip.bottom || x + w <= clip.left || y + h <= clip.top)
181
+ continue;
182
+ // A view's palette is submitted when it's first drawn (or the item's view changes).
183
+ if (this.itemViews.get(item) !== viewNo) {
184
+ this.itemViews.set(item, viewNo);
185
+ this.submitPalette(this.view(viewNo)?.palette);
186
+ }
187
+ draws.push({ cel, mirror, x, y, priority, order: order++, sx, sy });
188
+ }
189
+ draws.sort((a, b) => a.priority - b.priority || a.order - b.order);
190
+ layers.push({ clip, fill, draws });
191
+ }
192
+ // Phase 2: palette effects and remap tables.
193
+ const fx = paletteEffects(this.vm);
194
+ fx.update(Math.floor((this.vm.clock() * 60) / 1000));
195
+ const { current, output, remap } = fx.apply(this.source);
196
+ this.currentPalette = current;
197
+ // Fade show styles dim the style range of the palette (held at black after a fade-out).
198
+ const now = this.vm.clock();
199
+ const progress = (t) => (t.duration <= 0 ? 1 : Math.min(1, (now - t.start) / t.duration));
200
+ let fadedOutput = output;
201
+ for (const t of this.transitions.values()) {
202
+ if (t.style !== ShowStyle.FadeOut && t.style !== ShowStyle.FadeIn)
203
+ continue;
204
+ const level = t.style === ShowStyle.FadeOut ? 1 - progress(t) : progress(t);
205
+ if (fadedOutput === output)
206
+ fadedOutput = { rgb: output.rgb.slice(), used: output.used };
207
+ for (let i = this.styleRange.from; i <= this.styleRange.to; i++)
208
+ for (let k = 0; k < 3; k++)
209
+ fadedOutput.rgb[i * 3 + k] = Math.round(fadedOutput.rgb[i * 3 + k] * level);
210
+ }
211
+ // Phase 3: blit.
212
+ for (const { clip, fill, draws } of layers) {
213
+ if (fill !== undefined) {
214
+ for (let y = Math.max(clip.top, 0); y <= Math.min(clip.bottom, SCREEN_HEIGHT - 1); y++) {
215
+ pixels.fill(fill, y * SCREEN_WIDTH + Math.max(clip.left, 0), y * SCREEN_WIDTH + Math.min(clip.right, SCREEN_WIDTH - 1) + 1);
216
+ }
217
+ }
218
+ for (const d of draws)
219
+ blit(pixels, d, clip, remap);
220
+ }
221
+ // The cursor goes on top of everything.
222
+ const cur = this.cursor;
223
+ if (cur.visible && cur.view >= 0) {
224
+ const found = this.cel(cur.view, cur.loop, cur.cel);
225
+ if (found) {
226
+ const o = celOrigin(found.cel, found.mirror);
227
+ blit(pixels, { cel: found.cel, mirror: found.mirror, x: cur.x - o.x, y: cur.y - o.y, priority: 0, order: 0 }, { left: 0, top: 0, right: SCREEN_WIDTH - 1, bottom: SCREEN_HEIGHT - 1 }, remap);
228
+ }
229
+ }
230
+ const frame = { pixels, palette: fadedOutput, width: SCREEN_WIDTH, height: SCREEN_HEIGHT };
231
+ // Wipes, shutters, irises and dissolves reveal the new image over the old one.
232
+ for (const [plane, t] of this.transitions) {
233
+ const p = progress(t);
234
+ if (t.style === ShowStyle.FadeOut || t.style === ShowStyle.FadeIn) {
235
+ if (p >= 1 && t.style === ShowStyle.FadeIn)
236
+ this.transitions.delete(plane);
237
+ continue;
238
+ }
239
+ if (t.from && p < 1) {
240
+ const rgb = (frame.rgb ??= frameRgb(frame).slice());
241
+ for (let y = Math.max(t.rect.top, 0); y <= Math.min(t.rect.bottom, SCREEN_HEIGHT - 1); y++) {
242
+ for (let x = Math.max(t.rect.left, 0); x <= Math.min(t.rect.right, SCREEN_WIDTH - 1); x++) {
243
+ if (!revealed(t.style, x, y, t.rect, p)) {
244
+ const i = (y * SCREEN_WIDTH + x) * 3;
245
+ rgb.set(t.from.subarray(i, i + 3), i);
246
+ }
247
+ }
248
+ }
249
+ }
250
+ if (p >= 1)
251
+ this.transitions.delete(plane);
252
+ }
253
+ this.lastFrame = frame;
254
+ return frame;
255
+ }
256
+ }
257
+ /** SCI32 show styles (SetShowStyle): how a plane's new content appears. */
258
+ export const ShowStyle = {
259
+ None: 0, HShutterOut: 1, HShutterIn: 2, VShutterOut: 3, VShutterIn: 4, WipeLeft: 5, WipeRight: 6,
260
+ WipeUp: 7, WipeDown: 8, IrisOut: 9, IrisIn: 10, DissolveNoMorph: 11, Dissolve: 12, FadeOut: 13, FadeIn: 14,
261
+ };
262
+ /** Converts an indexed frame to RGB. */
263
+ export function frameRgb(f) {
264
+ if (f.rgb)
265
+ return f.rgb;
266
+ const rgb = new Uint8Array(f.width * f.height * 3);
267
+ for (let i = 0; i < f.pixels.length; i++)
268
+ rgb.set(f.palette.rgb.subarray(f.pixels[i] * 3, f.pixels[i] * 3 + 3), i * 3);
269
+ return rgb;
270
+ }
271
+ /** Deterministic per-pixel order for dissolves. */
272
+ const DISSOLVE = (() => {
273
+ const order = new Float32Array(SCREEN_WIDTH * SCREEN_HEIGHT);
274
+ let seed = 0x9e3779b9;
275
+ for (let i = 0; i < order.length; i++) {
276
+ seed = (seed * 1664525 + 1013904223) >>> 0;
277
+ order[i] = seed / 0x100000000;
278
+ }
279
+ return order;
280
+ })();
281
+ /** True where the *new* image shows at progress p (0..1) for a wipe-like style. */
282
+ function revealed(style, x, y, r, p) {
283
+ const w = r.right - r.left + 1, h = r.bottom - r.top + 1;
284
+ const fx = (x - r.left) / w, fy = (y - r.top) / h;
285
+ const cx = Math.abs(fx - 0.5) * 2, cy = Math.abs(fy - 0.5) * 2; // 0 at centre, 1 at edges
286
+ switch (style) {
287
+ case ShowStyle.HShutterOut: return cx <= p;
288
+ case ShowStyle.HShutterIn: return cx >= 1 - p;
289
+ case ShowStyle.VShutterOut: return cy <= p;
290
+ case ShowStyle.VShutterIn: return cy >= 1 - p;
291
+ case ShowStyle.WipeLeft: return fx >= 1 - p;
292
+ case ShowStyle.WipeRight: return fx <= p;
293
+ case ShowStyle.WipeUp: return fy >= 1 - p;
294
+ case ShowStyle.WipeDown: return fy <= p;
295
+ case ShowStyle.IrisOut: return Math.max(cx, cy) <= p;
296
+ case ShowStyle.IrisIn: return Math.max(cx, cy) >= 1 - p;
297
+ default: return DISSOLVE[y * SCREEN_WIDTH + x] <= p; // dissolves
298
+ }
299
+ }
300
+ /** scaleSignal bit 0: scale the cel by scaleX/128, scaleY/128. */
301
+ const SCALE_SIGNAL_DO_SCALING = 1;
302
+ /** View cel anchor point, relative to the cel's top-left. */
303
+ export function celOrigin(cel, mirror = false) {
304
+ const x = (cel.width >> 1) - cel.displaceX;
305
+ return { x: mirror ? cel.width - 1 - x : x, y: cel.height - cel.displaceY - 1 };
306
+ }
307
+ function blit(dst, d, clip, remap) {
308
+ const { cel } = d;
309
+ const sx = d.sx ?? 1, sy = d.sy ?? 1;
310
+ const w = Math.max(1, Math.floor(cel.width * sx)), h = Math.max(1, Math.floor(cel.height * sy));
311
+ for (let dy = 0; dy < h; dy++) {
312
+ const y = d.y + dy;
313
+ if (y < Math.max(clip.top, 0) || y > Math.min(clip.bottom, SCREEN_HEIGHT - 1))
314
+ continue;
315
+ const cy = Math.min(cel.height - 1, Math.floor(dy / sy));
316
+ for (let dx = 0; dx < w; dx++) {
317
+ const x = d.x + dx;
318
+ if (x < Math.max(clip.left, 0) || x > Math.min(clip.right, SCREEN_WIDTH - 1))
319
+ continue;
320
+ const cx = Math.min(cel.width - 1, Math.floor(dx / sx));
321
+ const c = cel.pixels[cy * cel.width + (d.mirror ? cel.width - 1 - cx : cx)];
322
+ if (c === cel.skipColor)
323
+ continue;
324
+ const table = remap.get(c);
325
+ if (table) {
326
+ // Remap colour: transform what's already there (e.g. darken it for a shadow).
327
+ const i = y * SCREEN_WIDTH + x;
328
+ dst[i] = table[dst[i]];
329
+ continue;
330
+ }
331
+ // Placeholder remap colours with no active remap would show as magenta/teal: skip.
332
+ if (c === 253 || c === 254)
333
+ continue;
334
+ dst[y * SCREEN_WIDTH + x] = c;
335
+ }
336
+ }
337
+ }
338
+ const gfxOf = new WeakMap();
339
+ export const graphics = (vm) => {
340
+ let g = gfxOf.get(vm);
341
+ if (!g) {
342
+ const created = (g = new Graphics(vm));
343
+ gfxOf.set(vm, created);
344
+ vm.hostRoots.push(() => [...created.planes, ...created.items]);
345
+ }
346
+ return g;
347
+ };
348
+ function clearViewChanged(vm, obj) {
349
+ const o = vm.memory.object(obj);
350
+ if (o)
351
+ o.vars[ObjectSlot.Info] = o.vars[ObjectSlot.Info] & ~INFO_VIEW_CHANGED & 0xffff;
352
+ }
353
+ /** Writes nsLeft/nsTop/nsRight/nsBottom from the object's current (scaled) cel and position. */
354
+ function setNowSeen(vm, obj) {
355
+ if (vm.getProp(obj, "nsLeft") === undefined)
356
+ return;
357
+ const r = graphics(vm).viewRect(obj);
358
+ if (!r)
359
+ return;
360
+ vm.setProp(obj, "nsLeft", fromInt(r.x));
361
+ vm.setProp(obj, "nsTop", fromInt(r.y));
362
+ vm.setProp(obj, "nsRight", fromInt(r.x + r.width - 1));
363
+ vm.setProp(obj, "nsBottom", fromInt(r.y + r.height - 1));
364
+ }
365
+ export const graphicsKernels = {
366
+ AddPlane: (vm, [plane = 0]) => void graphics(vm).planes.add(plane),
367
+ UpdatePlane: (vm, [plane = 0]) => void graphics(vm).planes.add(plane),
368
+ DeletePlane: (vm, [plane = 0]) => void graphics(vm).planes.delete(plane),
369
+ RepaintPlane: () => 0,
370
+ GetHighPlanePri: (vm) => {
371
+ const g = graphics(vm);
372
+ return fromInt(Math.max(0, ...[...g.planes].filter((p) => vm.memory.object(p)).map((p) => g.prop(p, "priority"))));
373
+ },
374
+ // Taking the object's current state clears its "changed since drawn" flag (SSCI's
375
+ // ScreenItem::setFromObject), which scripts set again by writing drawing properties.
376
+ AddScreenItem: (vm, [obj = 0]) => void (graphics(vm).items.add(obj), clearViewChanged(vm, obj)),
377
+ UpdateScreenItem: (vm, [obj = 0]) => void (graphics(vm).items.add(obj), clearViewChanged(vm, obj)),
378
+ DeleteScreenItem: (vm, [obj = 0]) => void graphics(vm).items.delete(obj),
379
+ FrameOut: (vm) => {
380
+ const g = graphics(vm);
381
+ g.frames++;
382
+ const mouse = input(vm);
383
+ g.cursor.x = mouse.x;
384
+ g.cursor.y = mouse.y;
385
+ for (const hook of vm.frameHooks)
386
+ hook();
387
+ g.onFrame?.(g.compose());
388
+ vm.yieldRequested = true;
389
+ },
390
+ SetNowSeen: (vm, [obj = 0]) => setNowSeen(vm, obj),
391
+ NumLoops: (vm, [obj = 0]) => graphics(vm).view(graphics(vm).prop(obj, "view"))?.loops.length ?? 0,
392
+ NumCels: (vm, [obj = 0]) => {
393
+ const g = graphics(vm);
394
+ const view = g.view(g.prop(obj, "view"));
395
+ const loop = view?.loops[g.prop(obj, "loop")];
396
+ return loop?.cels.length ?? 0;
397
+ },
398
+ CelWide: (vm, [view = 0, loop = 0, cel = 0]) => graphics(vm).cel(view, toSigned(loop), toSigned(cel))?.cel.width ?? 0,
399
+ CelHigh: (vm, [view = 0, loop = 0, cel = 0]) => graphics(vm).cel(view, toSigned(loop), toSigned(cel))?.cel.height ?? 0,
400
+ BaseSetter: (vm, [obj = 0]) => {
401
+ const g = graphics(vm);
402
+ const found = g.cel(g.prop(obj, "view"), g.prop(obj, "loop"), g.prop(obj, "cel"));
403
+ if (!found)
404
+ return;
405
+ const left = g.prop(obj, "x") - celOrigin(found.cel, found.mirror).x;
406
+ const bottom = g.prop(obj, "y") + 1;
407
+ vm.setProp(obj, "brLeft", fromInt(left));
408
+ vm.setProp(obj, "brRight", fromInt(left + found.cel.width - 1));
409
+ vm.setProp(obj, "brBottom", fromInt(bottom));
410
+ vm.setProp(obj, "brTop", fromInt(bottom - g.prop(obj, "yStep")));
411
+ },
412
+ IsOnMe: (vm, [x = 0, y = 0, obj = 0]) => {
413
+ const g = graphics(vm);
414
+ const px = toSigned(x), py = toSigned(y);
415
+ // Like SSCI, a screen item is hit where its cel is actually drawn (scaled, current
416
+ // position): its ns* props can be stale, since only SetNowSeen updates them.
417
+ const r = g.items.has(obj) ? g.viewRect(obj) : undefined;
418
+ if (r)
419
+ return bool(px >= r.x && px < r.x + r.width && py >= r.y && py < r.y + r.height);
420
+ return bool(px >= g.prop(obj, "nsLeft") && px <= g.prop(obj, "nsRight") && py >= g.prop(obj, "nsTop") && py <= g.prop(obj, "nsBottom"));
421
+ },
422
+ IsHiRes: () => 0,
423
+ SetVideoMode: () => 0,
424
+ // SetShowStyle(style, plane, seconds, backColor, priority, animate, refFrame, ...)
425
+ SetShowStyle: (vm, [style = 0, plane = 0, seconds = 0]) => {
426
+ const g = graphics(vm);
427
+ if (style === ShowStyle.None) {
428
+ g.transitions.delete(plane);
429
+ return;
430
+ }
431
+ const rect = vm.memory.object(plane)
432
+ ? { left: g.prop(plane, "inLeft"), top: g.prop(plane, "inTop"), right: g.prop(plane, "inRight"), bottom: g.prop(plane, "inBottom") }
433
+ : { left: 0, top: 0, right: SCREEN_WIDTH - 1, bottom: SCREEN_HEIGHT - 1 };
434
+ g.transitions.set(plane, {
435
+ style,
436
+ start: vm.clock(),
437
+ duration: Math.max(0, toSigned(seconds)) * 1000,
438
+ rect,
439
+ from: g.lastFrame && frameRgb(g.lastFrame).slice(),
440
+ });
441
+ },
442
+ ShowStylePercent: () => 0,
443
+ SetPalStyleRange: (vm, [from = 0, to = 255]) => void (graphics(vm).styleRange = { from, to }),
444
+ // SetCursor: (show) 1 arg, (x, y) warp, (view, loop, cel) shape, (l, t, r, b) restrict.
445
+ SetCursor: (vm, args) => {
446
+ const c = graphics(vm).cursor;
447
+ const [a = 0, b = 0, d = 0] = args.map(toSigned);
448
+ if (args.length === 1) {
449
+ if (a !== -2)
450
+ c.visible = a !== 0;
451
+ }
452
+ else if (args.length === 2) {
453
+ vm.moveMouse?.(a, b);
454
+ }
455
+ else if (args.length === 3) {
456
+ Object.assign(c, { view: a, loop: b, cel: d });
457
+ }
458
+ },
459
+ HaveMouse: () => 1,
460
+ ShakeScreen: () => 0,
461
+ SetScroll: () => 0,
462
+ AddMagnify: () => 0,
463
+ DeleteMagnify: () => 0,
464
+ GetHighItemPri: () => 0,
465
+ SetFontRes: () => 0,
466
+ NULL_: () => NULL,
467
+ };