sci2-ts 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. package/CHANGELOG.md +72 -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
package/CHANGELOG.md ADDED
@@ -0,0 +1,72 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
+ versions follow [Semantic Versioning](https://semver.org/), and the entries are generated
5
+ from commit messages ([Conventional Commits](https://www.conventionalcommits.org/)).
6
+
7
+ ## [0.2.1] - 2026-10-01
8
+
9
+ ### Fixed
10
+
11
+ - **tools:** Publish as sci2-ts
12
+
13
+ ## [0.2.0] - 2026-10-01
14
+
15
+ ### Added
16
+
17
+ - **tools:** An art tool for PNGs exported from a paint program (pnpm art)
18
+ - **tools:** Sci-ts as a package, with the sci-ts command
19
+ - **tools:** Publish sci-ts to npm
20
+
21
+ ### Fixed
22
+
23
+ - **viewer:** Play games without an AdLib bank or a SoundFont
24
+ - **viewer:** A neutral sample text in the font preview
25
+
26
+ ### Changed
27
+
28
+ - **game:** Sherlock moves to its own repository, sci-sherlock
29
+
30
+ ### Documentation
31
+
32
+ - A game in its own repository, and the art tool
33
+ - The README shows the tools, and the package is on npm
34
+
35
+ ## [0.1.0] - 2026-10-01
36
+
37
+ ### Added
38
+
39
+ - SCI2 engine, content compiler, viewer and tools
40
+ - **sci:** Resource archive, vocab and font writers
41
+ - **tools:** Build games from nothing with pnpm game build
42
+ - **sci:** Script compiler
43
+ - **sci:** Global declarations and computed selectors
44
+ - **lib:** The class library
45
+ - **game:** Hello on the class library
46
+ - **lib:** Talkers, portraits, conversations, flags and cutscenes
47
+ - **tools:** YAML and Yarn rooms for the class library, and the editor for our games
48
+ - **game:** Hello's road as a YAML room with someone to talk to
49
+ - **sci:** Write sound resources, SOL clips and sound effects
50
+ - **sci:** Switchto, @[buf i], selectors as values, and warnings for unknown sends
51
+ - **tools:** Music and effects, new games, a static site, and editing scripts live
52
+ - **game:** Music for hello, and a lantern that chimes
53
+
54
+ ### Changed
55
+
56
+ - **tools:** Games/hello in the script language
57
+
58
+ ### Documentation
59
+
60
+ - README, how SCI works, the plan and the Sherlock Holmes teaser
61
+ - Making a game, and milestone 1 done
62
+ - The script language, and milestone 2 done
63
+ - The class library, and milestone 3 done
64
+ - Rooms as YAML and Yarn, and milestone 4 done
65
+ - Building a game from nothing, sound, the site and the tooling
66
+
67
+ ### Maintenance
68
+
69
+ - Workspace, typecheck, tests and CI
70
+ - Build every game, and publish the site to GitHub Pages by hand
71
+
72
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rafael Lüder
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,114 @@
1
- # Temporary Holding Version
1
+ # sci-ts
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An engine for Sierra's SCI2 adventure games, written from scratch in TypeScript, and the
4
+ tools to make new games with it: scripts in SCI's own Lisp-like language on a class library
5
+ of our own, rooms written as YAML and [Yarn](https://docs.yarnspinner.dev), a live editor,
6
+ and writers for every kind of resource.
7
+
8
+ ![A conversation in games/hello: the traveller's portrait beside his line](docs/screenshots/conversation-portrait.png)
9
+
10
+ It runs in the browser and in Node. It was built by making one game, *Quest for Glory IV:
11
+ Shadows of Darkness* (CD), play from its title screen on, and it now builds games that need
12
+ nothing from Sierra: `games/hello` here, and a Sherlock Holmes teaser in progress.
13
+
14
+ > **No game data here.** This repository contains no files, scripts, art, text or music from
15
+ > any Sierra game, and none are needed to build, test or use it. To play a Sierra game, point
16
+ > the tools at your own copy. Sierra, Quest for Glory and other names are trademarks of their
17
+ > owners and are used only to say what the software works with.
18
+
19
+ ## Make a game
20
+
21
+ ```sh
22
+ mkdir night-walk && cd night-walk
23
+ npm init -y && npm install --save-dev sci2-ts
24
+ npx sci-ts new . # a room in YAML and Yarn, a hero, placeholder art
25
+ npx sci-ts play # build it and play it in the browser
26
+ npx sci-ts edit # the live editor for its rooms
27
+ ```
28
+
29
+ A game is a folder: scripts (`scripts/<n>.sc`), rooms as data (`rooms/<n>.room.yaml` and
30
+ `.yarn`), message files, music (MIDI files), sound effects (WAV files), and art drawn in code
31
+ or exported from a paint program. `sci-ts build` turns it into the `RESOURCE.MAP` and
32
+ `RESOURCE.000` an SCI interpreter opens. See [docs/games.md](docs/games.md).
33
+
34
+ ```lisp
35
+ (instance lantern of Prop
36
+ (properties noun N_LANTERN view 100 x 160 y 172 cycleSpeed 12)
37
+
38
+ ;; It chimes when touched, then says its line.
39
+ (method (doVerb verb)
40
+ (if (== verb V_DO) (sfx number: 50 play:))
41
+ (super doVerb: verb)))
42
+ ```
43
+
44
+ ![games/hello: the hero on the hill, looking at the moon](docs/screenshots/hello.png)
45
+
46
+ | | |
47
+ |---|---|
48
+ | [docs/games.md](docs/games.md) | a game's folder, the build, the `sci-ts` command and the package's imports |
49
+ | [docs/language.md](docs/language.md) | the script language |
50
+ | [docs/library.md](docs/library.md) | the class library: rooms, actors, walking, talking, conversations, flags, sound |
51
+ | [docs/rooms.md](docs/rooms.md) | rooms as YAML and Yarn: features, exits, characters, topic menus, cutscenes |
52
+ | [docs/art.md](docs/art.md) | art from a paint program: the manifest, the palette, the checks |
53
+
54
+ ## The tools
55
+
56
+ **The player** plays a game in the page: saved games, a link to any room
57
+ (`play.html#room/2`), music through a SoundFont or an emulated AdLib card.
58
+
59
+ ![A topic menu: talking to the traveller](docs/screenshots/conversation-menu.png)
60
+
61
+ **The live editor** shows a room's YAML and Yarn, or its script, next to the running game.
62
+ Saving (or a pause in typing) rebuilds the game and walks into the room again; errors are
63
+ marked on their line. Outlines name the features, exits, props and walkable floor; in Move
64
+ mode things are dragged into place and floors painted with a brush, as edits to the YAML.
65
+
66
+ ![The editor with a room written as YAML](docs/screenshots/editor-yaml.png)
67
+
68
+ ![The editor with a room written as a script](docs/screenshots/editor-script.png)
69
+
70
+ **The resource viewer** lists every resource with a thumbnail and the name the game's
71
+ scripts give it, searchable, with a player for sounds.
72
+
73
+ ![The resource viewer: the hero's walking loops](docs/screenshots/resource-viewer.png)
74
+
75
+ **The explorer** maps a game's rooms and how they connect, its classes and objects, and
76
+ inspects a running game.
77
+
78
+ ![The explorer's room map](docs/screenshots/explorer.png)
79
+
80
+ **On the command line:** `sci-ts build`, `play`, `edit`, `new`, `site` (the game and its
81
+ docs as a static site) and `art` (check and build exported art). The compiler warns about
82
+ sends no class defines; `editors/vscode` highlights the language in VS Code.
83
+
84
+ ## Inside
85
+
86
+ | | |
87
+ |---|---|
88
+ | `packages/sci` | the engine: resources, the SCI2 virtual machine and its kernel, graphics, text, motion, sound, saves; readers and byte-exact writers for every resource; the script compiler and assembler |
89
+ | `packages/content` | the room compiler: YAML and Yarn to scripts and messages |
90
+ | `lib` | the class library, in the script language |
91
+ | `apps/viewer` | the player, editor, resource viewer and explorer |
92
+ | `tools` | the `sci-ts` command, the game build, the art tool, and tools for reading SCI2 games |
93
+ | `games/hello` | the example game |
94
+
95
+ How SCI works, as far as we found out by running a game: [docs/sci](docs/sci/README.md).
96
+
97
+ ## Developing sci-ts
98
+
99
+ ```sh
100
+ pnpm install
101
+ pnpm check # typecheck and tests; no game files needed
102
+ pnpm game build games/hello
103
+ SCI_GAME=out/games/hello pnpm viewer
104
+ ```
105
+
106
+ With an SCI2 game of your own in `original/` (or `SCI_GAME=<dir>`), `pnpm viewer` opens it,
107
+ and `pnpm res`, `pnpm asm`, `pnpm msg` and `pnpm play` read, disassemble and run it.
108
+
109
+ See [docs/plan.md](docs/plan.md) for where this is going and [CONTRIBUTING.md](CONTRIBUTING.md)
110
+ for how to help. Changes are in [CHANGELOG.md](CHANGELOG.md), generated from commit messages.
111
+
112
+ ## License
113
+
114
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,43 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Room Editor</title>
7
+ <link rel="stylesheet" href="/src/editor.css" />
8
+ </head>
9
+ <body>
10
+ <main class="editor">
11
+ <section class="source">
12
+ <header>
13
+ <label>Mod <select id="mod"></select></label>
14
+ <label>Room <select id="room"></select></label>
15
+ <span class="spacer"></span>
16
+ <label class="auto" title="Rebuild a moment after you stop typing"><input id="auto" type="checkbox" checked /> live</label>
17
+ <button id="save" type="button" title="Save and rebuild (⌘S / Ctrl+S)">Save ⌘S</button>
18
+ </header>
19
+ <nav id="tabs"></nav>
20
+ <div class="code" id="code"></div>
21
+ <div id="problems" hidden></div>
22
+ </section>
23
+ <section class="game">
24
+ <div class="stage"><div class="screen" id="screen-box"><canvas id="screen" width="320" height="200"></canvas></div></div>
25
+ <div class="bar">
26
+ <span id="status">Loading…</span>
27
+ <span class="spacer"></span>
28
+ <span class="modes" id="modes" role="radiogroup" aria-label="What the mouse does on the game">
29
+ <button type="button" data-mode="play" title="Clicks go to the game">Play</button>
30
+ <button type="button" data-mode="move" title="Drag features, exits, props, the hero's points and floor corners; Alt-click a corner to remove it">Move</button>
31
+ <button type="button" data-mode="floor" title="Paint the walkable floor's outline">Draw floor</button>
32
+ <button type="button" data-mode="obstacle" title="Paint an obstacle the hero walks around">Draw obstacle</button>
33
+ </span>
34
+ <label title="Outline features, exits, sprites and walk areas"><input id="outlines" type="checkbox" checked /> Outlines</label>
35
+ <span class="legend" id="legend"></span>
36
+ <button id="restart" type="button" title="Back to the moment before entering the room, then in again">Restart scene</button>
37
+ <button id="sound" type="button">Sound off</button>
38
+ </div>
39
+ </section>
40
+ </main>
41
+ <script type="module" src="/src/editor.ts"></script>
42
+ </body>
43
+ </html>
@@ -0,0 +1,27 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>SCI Explorer</title>
7
+ <link rel="stylesheet" href="/src/explore/explore.css" />
8
+ </head>
9
+ <body>
10
+ <header>
11
+ <strong>SCI Explorer</strong>
12
+ <nav>
13
+ <a href="#/map" data-tab="map">Room map</a>
14
+ <a href="#/classes" data-tab="classes">Classes</a>
15
+ <a href="#/live" data-tab="live">Live</a>
16
+ <a href="/index.html">Resources</a>
17
+ <a href="/play.html">Play</a>
18
+ </nav>
19
+ <input id="search" type="search" placeholder="Jump to room, class or object…" list="search-options" autocomplete="off" />
20
+ <datalist id="search-options"></datalist>
21
+ <span id="status"></span>
22
+ </header>
23
+ <main id="stage"></main>
24
+ <aside id="inspector"><p class="meta">Select a room or class.</p></aside>
25
+ <script type="module" src="/src/explore/explore.ts"></script>
26
+ </body>
27
+ </html>
@@ -0,0 +1,26 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>SCI Resources</title>
7
+ <link rel="stylesheet" href="/src/style.css" />
8
+ </head>
9
+ <body>
10
+ <aside>
11
+ <select id="type"></select>
12
+ <input id="search" type="search" placeholder="Search: number, name, description ( / )" autocomplete="off" spellcheck="false" />
13
+ <div class="keys" id="index-status">Indexing the scripts…</div>
14
+ <label class="opt"><input type="checkbox" id="aspect" checked /> 4:3 aspect</label>
15
+ <div class="keys">↑↓ resources · ←→ type · PgUp/PgDn · Home/End · / search</div>
16
+ <ul id="list"></ul>
17
+ </aside>
18
+ <main>
19
+ <h1 id="title">Pick a resource</h1>
20
+ <div id="uses"></div>
21
+ <div id="view"></div>
22
+ <details><summary>Hex</summary><pre id="hex"></pre></details>
23
+ </main>
24
+ <script type="module" src="/src/main.ts"></script>
25
+ </body>
26
+ </html>
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@sci-ts/viewer",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "vite build"
8
+ },
9
+ "dependencies": {
10
+ "@codemirror/commands": "^6.11.1",
11
+ "@codemirror/lang-yaml": "^6.1.3",
12
+ "@codemirror/language": "^6.12.4",
13
+ "@codemirror/lint": "^6.9.7",
14
+ "@codemirror/state": "^6.7.6",
15
+ "@codemirror/theme-one-dark": "^6.1.3",
16
+ "@codemirror/view": "^6.43.13",
17
+ "@sci-ts/sci": "workspace:*",
18
+ "codemirror": "^6.0.2",
19
+ "cytoscape": "^3.34.3",
20
+ "spessasynth_lib": "^4.3.14",
21
+ "yaml": "^2.9.1"
22
+ },
23
+ "devDependencies": {
24
+ "sirv": "^3.0.2",
25
+ "vite": "^8.3.1"
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>SCI Player</title>
7
+ <link rel="stylesheet" href="/src/play.css" />
8
+ </head>
9
+ <body>
10
+ <main>
11
+ <canvas id="screen" width="320" height="200"></canvas>
12
+ <div class="bar">
13
+ <div id="status">Loading game files…</div>
14
+ <details id="mods">
15
+ <summary>Mods</summary>
16
+ <div id="mod-list"></div>
17
+ <button id="mods-apply" type="button">Apply (restarts the game)</button>
18
+ </details>
19
+ <button id="export-saves" type="button" title="Download every saved game as JSON (for tools/explore.ts loadsaves:)">Export saves</button>
20
+ <button id="music" type="button">Music</button>
21
+ </div>
22
+ <pre id="error" hidden></pre>
23
+ </main>
24
+ <script type="module" src="/src/play.ts"></script>
25
+ </body>
26
+ </html>
@@ -0,0 +1,18 @@
1
+ import type { Plugin } from "vite";
2
+ export interface SciGameOptions {
3
+ /** The built game's folder (RESOURCE.MAP, RESOURCE.000...), served as /game/. */
4
+ game: string;
5
+ /** Built mods (one folder each), served as /game/MODS/<name> and listed at /__mods. */
6
+ mods?: string;
7
+ /** SoundFonts for music, served as /soundfonts/ (GeneralUser-GS.sf2 is the one used). */
8
+ soundfonts?: string;
9
+ /** The live editor's routes (/__editor): edits and rebuilds the game being served. */
10
+ editor?: boolean;
11
+ }
12
+ /**
13
+ * A Vite plugin that serves an SCI game to pages using the player (`sci-ts/viewer`): the
14
+ * game's files under /game/ (Range requests too), directory listings at /__list, and
15
+ * optionally mods, SoundFonts and the editor's routes. Development only; a built site
16
+ * carries the game itself (see `sci-ts site`).
17
+ */
18
+ export declare function sciGame(options: SciGameOptions): Plugin;
@@ -0,0 +1,75 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ const EDITOR_SERVER = resolve(import.meta.dirname, "../../tools/editor-server.ts");
4
+ /**
5
+ * A Vite plugin that serves an SCI game to pages using the player (`sci-ts/viewer`): the
6
+ * game's files under /game/ (Range requests too), directory listings at /__list, and
7
+ * optionally mods, SoundFonts and the editor's routes. Development only; a built site
8
+ * carries the game itself (see `sci-ts site`).
9
+ */
10
+ export function sciGame(options) {
11
+ const game = resolve(options.game);
12
+ const mods = options.mods && resolve(options.mods);
13
+ return {
14
+ name: "sci-game",
15
+ // Pages outside this repository import the player's sources from it.
16
+ config: () => ({ server: { fs: { allow: [resolve(import.meta.dirname, "../.."), process.cwd()] } } }),
17
+ async configureServer(server) {
18
+ const sirv = (await import("sirv")).default;
19
+ // Before /game: mods look like a MODS folder inside the game, as patch directories.
20
+ if (mods) {
21
+ server.middlewares.use("/game/MODS", sirv(mods, { dev: true }));
22
+ server.middlewares.use("/__mods", async (_req, res) => {
23
+ const entries = await readdir(mods, { withFileTypes: true }).catch(() => []);
24
+ res.setHeader("content-type", "application/json");
25
+ res.end(JSON.stringify(entries.filter((e) => e.isDirectory()).map((e) => e.name).sort()));
26
+ });
27
+ }
28
+ // A file that isn't there is a 404, not the page (Vite's fallback for unknown paths):
29
+ // the player asks for files a game may not have (RESOURCE.SFX, the SoundFont).
30
+ const notFound = (_req, res) => ((res.statusCode = 404), res.end());
31
+ server.middlewares.use("/game", sirv(game, { dev: true }));
32
+ server.middlewares.use("/game", notFound);
33
+ if (options.soundfonts)
34
+ server.middlewares.use("/soundfonts", sirv(resolve(options.soundfonts), { dev: true }));
35
+ server.middlewares.use("/soundfonts", notFound);
36
+ if (options.editor) {
37
+ // Loaded through Vite so edits to the tools take effect; the game stays loaded in it.
38
+ const editor = () => server.ssrLoadModule(EDITOR_SERVER);
39
+ server.middlewares.use("/__editor", async (req, res) => {
40
+ const url = new URL(req.url ?? "", "http://x");
41
+ const json = (status, body) => {
42
+ res.statusCode = status;
43
+ res.setHeader("content-type", "application/json");
44
+ res.end(JSON.stringify(body));
45
+ };
46
+ try {
47
+ const mod = url.searchParams.get("mod") ?? "";
48
+ if (req.method === "GET" && url.pathname === "/mods")
49
+ return json(200, (await editor()).listMods());
50
+ if (req.method === "GET" && url.pathname === "/files")
51
+ return json(200, (await editor()).readFiles(mod));
52
+ if (req.method === "POST" && url.pathname === "/save") {
53
+ let body = "";
54
+ for await (const chunk of req)
55
+ body += chunk;
56
+ const { files } = JSON.parse(body);
57
+ return json(200, await (await editor()).save(mod, files));
58
+ }
59
+ json(404, { error: "unknown editor route" });
60
+ }
61
+ catch (e) {
62
+ json(500, { error: e.message });
63
+ }
64
+ });
65
+ }
66
+ server.middlewares.use("/__list", async (req, res) => {
67
+ const dir = new URL(req.url ?? "", "http://x").searchParams.get("dir") ?? "";
68
+ const base = mods && (dir === "MODS" || dir.startsWith("MODS/")) ? resolve(mods, dir.slice(5)) : resolve(game, dir);
69
+ const names = await readdir(base).catch(() => []);
70
+ res.setHeader("content-type", "application/json");
71
+ res.end(JSON.stringify(names));
72
+ });
73
+ },
74
+ };
75
+ }
@@ -0,0 +1,84 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import type { Plugin } from "vite";
4
+
5
+ export interface SciGameOptions {
6
+ /** The built game's folder (RESOURCE.MAP, RESOURCE.000...), served as /game/. */
7
+ game: string;
8
+ /** Built mods (one folder each), served as /game/MODS/<name> and listed at /__mods. */
9
+ mods?: string;
10
+ /** SoundFonts for music, served as /soundfonts/ (GeneralUser-GS.sf2 is the one used). */
11
+ soundfonts?: string;
12
+ /** The live editor's routes (/__editor): edits and rebuilds the game being served. */
13
+ editor?: boolean;
14
+ }
15
+
16
+ const EDITOR_SERVER = resolve(import.meta.dirname, "../../tools/editor-server.ts");
17
+
18
+ /**
19
+ * A Vite plugin that serves an SCI game to pages using the player (`sci-ts/viewer`): the
20
+ * game's files under /game/ (Range requests too), directory listings at /__list, and
21
+ * optionally mods, SoundFonts and the editor's routes. Development only; a built site
22
+ * carries the game itself (see `sci-ts site`).
23
+ */
24
+ export function sciGame(options: SciGameOptions): Plugin {
25
+ const game = resolve(options.game);
26
+ const mods = options.mods && resolve(options.mods);
27
+ return {
28
+ name: "sci-game",
29
+ // Pages outside this repository import the player's sources from it.
30
+ config: () => ({ server: { fs: { allow: [resolve(import.meta.dirname, "../.."), process.cwd()] } } }),
31
+ async configureServer(server) {
32
+ const sirv = (await import("sirv")).default;
33
+ // Before /game: mods look like a MODS folder inside the game, as patch directories.
34
+ if (mods) {
35
+ server.middlewares.use("/game/MODS", sirv(mods, { dev: true }));
36
+ server.middlewares.use("/__mods", async (_req, res) => {
37
+ const entries = await readdir(mods, { withFileTypes: true }).catch(() => []);
38
+ res.setHeader("content-type", "application/json");
39
+ res.end(JSON.stringify(entries.filter((e) => e.isDirectory()).map((e) => e.name).sort()));
40
+ });
41
+ }
42
+ // A file that isn't there is a 404, not the page (Vite's fallback for unknown paths):
43
+ // the player asks for files a game may not have (RESOURCE.SFX, the SoundFont).
44
+ const notFound = (_req: unknown, res: { statusCode: number; end: () => void }) => ((res.statusCode = 404), res.end());
45
+ server.middlewares.use("/game", sirv(game, { dev: true }));
46
+ server.middlewares.use("/game", notFound);
47
+ if (options.soundfonts) server.middlewares.use("/soundfonts", sirv(resolve(options.soundfonts), { dev: true }));
48
+ server.middlewares.use("/soundfonts", notFound);
49
+ if (options.editor) {
50
+ // Loaded through Vite so edits to the tools take effect; the game stays loaded in it.
51
+ const editor = () => server.ssrLoadModule(EDITOR_SERVER) as Promise<typeof import("../../tools/editor-server.ts")>;
52
+ server.middlewares.use("/__editor", async (req, res) => {
53
+ const url = new URL(req.url ?? "", "http://x");
54
+ const json = (status: number, body: unknown) => {
55
+ res.statusCode = status;
56
+ res.setHeader("content-type", "application/json");
57
+ res.end(JSON.stringify(body));
58
+ };
59
+ try {
60
+ const mod = url.searchParams.get("mod") ?? "";
61
+ if (req.method === "GET" && url.pathname === "/mods") return json(200, (await editor()).listMods());
62
+ if (req.method === "GET" && url.pathname === "/files") return json(200, (await editor()).readFiles(mod));
63
+ if (req.method === "POST" && url.pathname === "/save") {
64
+ let body = "";
65
+ for await (const chunk of req) body += chunk;
66
+ const { files } = JSON.parse(body) as { files: Record<string, string> };
67
+ return json(200, await (await editor()).save(mod, files));
68
+ }
69
+ json(404, { error: "unknown editor route" });
70
+ } catch (e) {
71
+ json(500, { error: (e as Error).message });
72
+ }
73
+ });
74
+ }
75
+ server.middlewares.use("/__list", async (req, res) => {
76
+ const dir = new URL(req.url ?? "", "http://x").searchParams.get("dir") ?? "";
77
+ const base = mods && (dir === "MODS" || dir.startsWith("MODS/")) ? resolve(mods, dir.slice(5)) : resolve(game, dir);
78
+ const names = await readdir(base).catch(() => []);
79
+ res.setHeader("content-type", "application/json");
80
+ res.end(JSON.stringify(names));
81
+ });
82
+ },
83
+ };
84
+ }
@@ -0,0 +1,20 @@
1
+ import { type AdLibBank, type MidiOutput } from "../../../packages/sci/src/index.ts";
2
+ /**
3
+ * AdLib music: Sierra's driver runs here and batches OPL register writes to the chip
4
+ * emulator in an AudioWorklet (flushed once per microtask, i.e. per burst of MIDI events).
5
+ */
6
+ export declare class AdLibOutput implements MidiOutput {
7
+ private readonly node;
8
+ private readonly bank;
9
+ private driver;
10
+ private queue;
11
+ private flushScheduled;
12
+ private constructor();
13
+ static create(ctx: AudioContext, bank: AdLibBank): Promise<AdLibOutput>;
14
+ private newDriver;
15
+ /** Silences the chip and starts from a clean driver (used when switching devices). */
16
+ reset(): void;
17
+ send(bytes: number[]): void;
18
+ claimVoices(channel: number, count: number): void;
19
+ setMasterVolume(volume: number): void;
20
+ }
@@ -0,0 +1,55 @@
1
+ import { AdLibDriver } from "../../../packages/sci/src/index.js";
2
+ import workletUrl from "./opl-worklet.ts?worker&url";
3
+ /**
4
+ * AdLib music: Sierra's driver runs here and batches OPL register writes to the chip
5
+ * emulator in an AudioWorklet (flushed once per microtask, i.e. per burst of MIDI events).
6
+ */
7
+ export class AdLibOutput {
8
+ node;
9
+ bank;
10
+ driver;
11
+ queue = [];
12
+ flushScheduled = false;
13
+ constructor(node, bank) {
14
+ this.node = node;
15
+ this.bank = bank;
16
+ this.driver = this.newDriver();
17
+ }
18
+ static async create(ctx, bank) {
19
+ await ctx.audioWorklet.addModule(workletUrl);
20
+ const node = new AudioWorkletNode(ctx, "opl2", { outputChannelCount: [2] });
21
+ node.connect(ctx.destination);
22
+ return new AdLibOutput(node, bank);
23
+ }
24
+ newDriver() {
25
+ const chip = {
26
+ write: (reg, value) => {
27
+ this.queue.push(reg, value);
28
+ if (!this.flushScheduled) {
29
+ this.flushScheduled = true;
30
+ queueMicrotask(() => {
31
+ this.flushScheduled = false;
32
+ this.node.port.postMessage(this.queue);
33
+ this.queue = [];
34
+ });
35
+ }
36
+ },
37
+ };
38
+ return new AdLibDriver(chip, this.bank);
39
+ }
40
+ /** Silences the chip and starts from a clean driver (used when switching devices). */
41
+ reset() {
42
+ this.queue = [];
43
+ this.node.port.postMessage("reset");
44
+ this.driver = this.newDriver();
45
+ }
46
+ send(bytes) {
47
+ this.driver.send(bytes);
48
+ }
49
+ claimVoices(channel, count) {
50
+ this.driver.claimVoices(channel, count);
51
+ }
52
+ setMasterVolume(volume) {
53
+ this.driver.setMasterVolume(volume);
54
+ }
55
+ }