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
@@ -0,0 +1,213 @@
1
+ :root {
2
+ --bg: #16161a;
3
+ --panel: #1e1e24;
4
+ --line: #2c2c34;
5
+ --text: #e4e4e8;
6
+ --muted: #8a8a96;
7
+ --accent: #d9a441;
8
+ --error: #ff6b6b;
9
+ --ok: #7bd88f;
10
+ color-scheme: dark;
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ html,
18
+ body {
19
+ margin: 0;
20
+ height: 100%;
21
+ background: var(--bg);
22
+ color: var(--text);
23
+ font: 13px/1.4 system-ui, sans-serif;
24
+ }
25
+
26
+ .editor {
27
+ display: grid;
28
+ grid-template-columns: minmax(360px, 1fr) minmax(400px, 1.4fr);
29
+ /* One row the height of the window: the panels scroll inside, they don't grow it. */
30
+ grid-template-rows: minmax(0, 1fr);
31
+ height: 100vh;
32
+ }
33
+
34
+ .source {
35
+ display: flex;
36
+ flex-direction: column;
37
+ min-width: 0;
38
+ min-height: 0;
39
+ border-right: 1px solid var(--line);
40
+ background: var(--panel);
41
+ }
42
+
43
+ header,
44
+ .bar {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 10px;
48
+ padding: 8px 10px;
49
+ border-bottom: 1px solid var(--line);
50
+ }
51
+
52
+ .spacer {
53
+ flex: 1;
54
+ }
55
+
56
+ select,
57
+ button {
58
+ background: var(--bg);
59
+ color: var(--text);
60
+ border: 1px solid var(--line);
61
+ border-radius: 4px;
62
+ padding: 3px 8px;
63
+ font: inherit;
64
+ }
65
+
66
+ button:hover {
67
+ border-color: var(--accent);
68
+ }
69
+
70
+ #tabs {
71
+ display: flex;
72
+ border-bottom: 1px solid var(--line);
73
+ overflow-x: auto;
74
+ }
75
+
76
+ #tabs button {
77
+ border: 0;
78
+ border-radius: 0;
79
+ border-right: 1px solid var(--line);
80
+ background: transparent;
81
+ color: var(--muted);
82
+ padding: 6px 12px;
83
+ white-space: nowrap;
84
+ }
85
+
86
+ #tabs button.active {
87
+ color: var(--text);
88
+ box-shadow: inset 0 -2px var(--accent);
89
+ }
90
+
91
+ #tabs button.dirty::after {
92
+ content: " •";
93
+ color: var(--accent);
94
+ }
95
+
96
+ #tabs button.has-error {
97
+ color: var(--error);
98
+ }
99
+
100
+ .code {
101
+ flex: 1;
102
+ min-height: 0;
103
+ overflow: hidden;
104
+ }
105
+
106
+ .code .cm-editor {
107
+ height: 100%;
108
+ font-size: 12.5px;
109
+ }
110
+
111
+ .code .cm-scroller {
112
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
113
+ line-height: 1.5;
114
+ }
115
+
116
+ #problems {
117
+ max-height: 30%;
118
+ overflow: auto;
119
+ padding: 8px 10px;
120
+ border-top: 1px solid var(--line);
121
+ background: #2a1c1f;
122
+ color: var(--error);
123
+ font: 12px/1.45 ui-monospace, Menlo, monospace;
124
+ white-space: pre-wrap;
125
+ cursor: pointer;
126
+ }
127
+
128
+ .game {
129
+ display: flex;
130
+ flex-direction: column;
131
+ min-width: 0;
132
+ min-height: 0;
133
+ }
134
+
135
+ .stage {
136
+ flex: 1;
137
+ min-height: 0;
138
+ display: grid;
139
+ place-items: center;
140
+ background: #000;
141
+ container-type: size;
142
+ }
143
+
144
+ /* The canvas itself is 4:3 (mouse coordinates map over its whole box), as big as fits;
145
+ the outlines are an SVG of the same box on top. */
146
+ .screen {
147
+ position: relative;
148
+ width: min(100cqw, 100cqh * 4 / 3);
149
+ aspect-ratio: 4 / 3;
150
+ }
151
+
152
+ .screen canvas {
153
+ display: block;
154
+ width: 100%;
155
+ height: 100%;
156
+ image-rendering: pixelated;
157
+ cursor: none;
158
+ }
159
+
160
+ .screen svg {
161
+ position: absolute;
162
+ inset: 0;
163
+ width: 100%;
164
+ height: 100%;
165
+ }
166
+
167
+ .modes {
168
+ display: inline-flex;
169
+ }
170
+
171
+ .modes button {
172
+ border-radius: 0;
173
+ margin-left: -1px;
174
+ }
175
+
176
+ .modes button:first-child {
177
+ border-radius: 4px 0 0 4px;
178
+ }
179
+
180
+ .modes button:last-child {
181
+ border-radius: 0 4px 4px 0;
182
+ }
183
+
184
+ .modes button.active {
185
+ background: var(--accent);
186
+ border-color: var(--accent);
187
+ color: #000;
188
+ }
189
+
190
+ .legend {
191
+ display: flex;
192
+ gap: 8px;
193
+ font-size: 11px;
194
+ }
195
+
196
+ .legend span::before {
197
+ content: "■ ";
198
+ color: var(--swatch);
199
+ }
200
+
201
+ .bar {
202
+ border-top: 1px solid var(--line);
203
+ border-bottom: 0;
204
+ color: var(--muted);
205
+ }
206
+
207
+ #status.ok {
208
+ color: var(--ok);
209
+ }
210
+
211
+ #status.error {
212
+ color: var(--error);
213
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,393 @@
1
+ import { indentWithTab } from "@codemirror/commands";
2
+ import { yaml as yamlLanguage } from "@codemirror/lang-yaml";
3
+ import { lintGutter, setDiagnostics } from "@codemirror/lint";
4
+ import { EditorState } from "@codemirror/state";
5
+ import { oneDark } from "@codemirror/theme-one-dark";
6
+ import { EditorView, keymap } from "@codemirror/view";
7
+ import { ResourceManager, gameGlobal, drawnItems, graphics, heroInPlay, replaceResources, requestRoom, roomPlane } from "../../../packages/sci/src/index.js";
8
+ import { basicSetup } from "codemirror";
9
+ import { parse as parseYaml } from "yaml";
10
+ import { EditLayer } from "./editor-handles.js";
11
+ import { sciAssembly, sciMessages, sciScript, yarn } from "./editor-languages.js";
12
+ import { httpFiles } from "./files.js";
13
+ import { OVERLAY_COLORS, createOverlay, drawRoomOverlay } from "./overlay.js";
14
+ import { gameProfile } from "./game-profile.js";
15
+ import { GameSession } from "./session.js";
16
+ const $ = (id) => document.getElementById(id);
17
+ const params = new URLSearchParams(location.search);
18
+ const statusEl = $("status");
19
+ const status = (text, kind = "") => {
20
+ statusEl.textContent = text;
21
+ statusEl.className = kind;
22
+ };
23
+ // --- Which mod and room ---------------------------------------------------------------
24
+ // A mod of Sierra's game, or the game of ours the dev server is serving (its rooms/).
25
+ const mods = (await (await fetch("/__editor/mods")).json());
26
+ const editable = mods.filter((m) => m.rooms.length);
27
+ if (!editable.length) {
28
+ status(mods[0]?.kind === "game" ? `${mods[0].name} has no rooms/<n>.room.yaml yet.` : "No mod has a <n>.room.yaml yet (see mods/README.md).", "error");
29
+ throw new Error("nothing to edit");
30
+ }
31
+ const mod = editable.find((m) => m.name === params.get("mod")) ?? editable[0];
32
+ const room = mod.rooms.includes(Number(params.get("room"))) ? Number(params.get("room")) : mod.rooms[0];
33
+ const modSelect = $("mod"), roomSelect = $("room");
34
+ modSelect.append(...editable.map((m) => new Option(m.name, m.name, false, m === mod)));
35
+ roomSelect.append(...mod.rooms.map((r) => new Option(String(r), String(r), false, r === room)));
36
+ const go = (m, r) => {
37
+ const q = new URLSearchParams({ mod: m, ...(r !== undefined ? { room: String(r) } : {}) });
38
+ location.search = q.toString();
39
+ };
40
+ modSelect.onchange = () => go(modSelect.value);
41
+ roomSelect.onchange = () => go(mod.name, Number(roomSelect.value));
42
+ document.title = `${mod.name} ${room} · Room Editor`;
43
+ // --- Files, tabs and the code editor ----------------------------------------------------
44
+ const files = (await (await fetch(`/__editor/files?mod=${mod.name}`)).json());
45
+ const yamlName = `${room}.room.yaml`, yarnName = `${room}.yarn`;
46
+ const scaName = `${room}.sca (compiled)`, msgName = `${room}.msg (compiled)`;
47
+ // A room written as a script (a game of ours): its .sc and message file, not YAML.
48
+ const scriptName = `${room}.sc`, messagesName = `${room}.msg`;
49
+ const scriptMode = files[yamlName] === undefined && files[scriptName] !== undefined;
50
+ const editableNames = scriptMode ? [scriptName, ...(files[messagesName] !== undefined ? [messagesName] : [])] : [yamlName, yarnName];
51
+ const tabNames = scriptMode ? [...editableNames, scaName] : [...editableNames, "flags.yaml", scaName, msgName];
52
+ /** What was last sent to the server, per editable file. */
53
+ const saved = new Map(editableNames.map((n) => [n, files[n] ?? ""]));
54
+ const languageOf = (name) => name.endsWith(".yaml") ? yamlLanguage() : name.endsWith(".yarn") ? yarn : name.endsWith(".sc") ? sciScript
55
+ : name === scaName ? sciAssembly : name === msgName || name === messagesName ? sciMessages : [];
56
+ const theme = EditorView.theme({
57
+ "&": { height: "100%" },
58
+ ".cm-content": { fontFamily: "inherit" },
59
+ ".cm-gutters": { fontFamily: "inherit" },
60
+ });
61
+ function makeState(name, text) {
62
+ const isEditable = editableNames.includes(name);
63
+ return EditorState.create({
64
+ doc: text,
65
+ extensions: [
66
+ basicSetup,
67
+ oneDark,
68
+ theme,
69
+ languageOf(name),
70
+ lintGutter(),
71
+ keymap.of([indentWithTab, { key: "Mod-s", run: () => (void build(), true) }]),
72
+ EditorState.readOnly.of(!isEditable),
73
+ EditorView.editable.of(isEditable),
74
+ EditorView.updateListener.of((u) => {
75
+ if (u.docChanged && isEditable) {
76
+ renderTabs();
77
+ scheduleBuild();
78
+ }
79
+ }),
80
+ ],
81
+ });
82
+ }
83
+ const states = new Map(tabNames.map((n) => [
84
+ n,
85
+ makeState(n, n === "flags.yaml" ? files[n] ?? "# No variables yet: <<set $name to true>> in Yarn adds them here.\n" : editableNames.includes(n) ? files[n] ?? "" : "Builds on save."),
86
+ ]));
87
+ let current = editableNames[0];
88
+ const view = new EditorView({ parent: $("code"), state: states.get(current) });
89
+ let error;
90
+ const textOf = (name) => (name === current ? view.state : states.get(name)).doc.toString();
91
+ const tabs = $("tabs"), problems = $("problems");
92
+ function renderTabs() {
93
+ tabs.replaceChildren(...tabNames.map((name) => {
94
+ const b = document.createElement("button");
95
+ b.type = "button";
96
+ b.textContent = name;
97
+ b.classList.toggle("active", name === current);
98
+ b.classList.toggle("dirty", editableNames.includes(name) && textOf(name) !== saved.get(name));
99
+ b.classList.toggle("has-error", error?.file === name);
100
+ b.onclick = () => show(name);
101
+ return b;
102
+ }));
103
+ }
104
+ /** The error as a diagnostic on its line, in the tab it belongs to. */
105
+ function diagnostics(name, state) {
106
+ if (!error || error.file !== name || !error.line || error.line > state.doc.lines)
107
+ return [];
108
+ const line = state.doc.line(error.line);
109
+ return [{ from: line.from, to: Math.max(line.to, line.from), severity: "error", message: error.message.replace(/^[^:]+:\d+:\s*/, "") }];
110
+ }
111
+ function show(name, line) {
112
+ states.set(current, view.state);
113
+ current = name;
114
+ view.setState(states.get(name));
115
+ view.dispatch(setDiagnostics(view.state, diagnostics(name, view.state)));
116
+ if (line && line <= view.state.doc.lines) {
117
+ const pos = view.state.doc.line(line).from;
118
+ view.dispatch({ selection: { anchor: pos }, effects: EditorView.scrollIntoView(pos, { y: "center" }) });
119
+ view.focus();
120
+ }
121
+ renderTabs();
122
+ }
123
+ /**
124
+ * Replaces an editable file's text with the smallest change (so the cursor, scrolling and
125
+ * undo behave), shown in its tab: what dragging and drawing on the game do to the YAML.
126
+ */
127
+ function editText(name, text) {
128
+ if (current !== name)
129
+ show(name);
130
+ const old = view.state.doc.toString();
131
+ if (old === text)
132
+ return;
133
+ let from = 0;
134
+ while (from < old.length && from < text.length && old[from] === text[from])
135
+ from++;
136
+ let oldEnd = old.length, newEnd = text.length;
137
+ while (oldEnd > from && newEnd > from && old[oldEnd - 1] === text[newEnd - 1])
138
+ (oldEnd--, newEnd--);
139
+ view.dispatch({
140
+ changes: { from, to: oldEnd, insert: text.slice(from, newEnd) },
141
+ effects: EditorView.scrollIntoView(from, { y: "nearest", yMargin: 40 }),
142
+ });
143
+ }
144
+ /** Replaces a read-only tab's text (flags, compiled output). */
145
+ function setView(name, text) {
146
+ const state = makeState(name, text);
147
+ if (name === current)
148
+ view.setState(state);
149
+ else
150
+ states.set(name, state);
151
+ }
152
+ window.addEventListener("keydown", (e) => {
153
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "s") {
154
+ e.preventDefault();
155
+ void build();
156
+ }
157
+ });
158
+ problems.onclick = () => error?.file && editableNames.includes(error.file) && show(error.file, error.line);
159
+ $("save").onclick = () => void build();
160
+ renderTabs();
161
+ // --- Building -------------------------------------------------------------------------
162
+ const auto = $("auto");
163
+ let timer;
164
+ /** While a drag or brush stroke is in progress, builds wait for it to end. */
165
+ let held = false;
166
+ function scheduleBuild() {
167
+ clearTimeout(timer);
168
+ if (auto.checked && !held)
169
+ timer = setTimeout(() => void build(), 700);
170
+ }
171
+ let building;
172
+ let again = false;
173
+ async function build() {
174
+ clearTimeout(timer);
175
+ if (building) {
176
+ again = true;
177
+ return building;
178
+ }
179
+ building = (async () => {
180
+ const sent = new Map(editableNames.map((n) => [n, textOf(n)]));
181
+ status("Building…");
182
+ const body = { files: Object.fromEntries([...sent].filter(([name, t]) => t !== "" || files[name] !== undefined)) };
183
+ let result;
184
+ try {
185
+ result = await (await fetch(`/__editor/save?mod=${mod.name}`, { method: "POST", body: JSON.stringify(body) })).json();
186
+ }
187
+ catch (e) {
188
+ result = { ok: false, error: { message: `The dev server didn't answer: ${e.message}` } };
189
+ }
190
+ for (const [name, t] of sent)
191
+ saved.set(name, t);
192
+ applyResult(result);
193
+ })();
194
+ await building;
195
+ building = undefined;
196
+ if (again) {
197
+ again = false;
198
+ await build();
199
+ }
200
+ }
201
+ function applyResult(result) {
202
+ error = result.ok ? undefined : result.error;
203
+ problems.hidden = result.ok;
204
+ problems.textContent = error ? `${error.message}${error.file && editableNames.includes(error.file) ? "\n(click to go there)" : ""}` : "";
205
+ view.dispatch(setDiagnostics(view.state, diagnostics(current, view.state)));
206
+ if (result.ok) {
207
+ if (result.reload) {
208
+ // The game's selector or class tables changed: the running game can't take that.
209
+ status("Rebuilt: reloading…");
210
+ location.reload();
211
+ return;
212
+ }
213
+ if (result.flags !== undefined && tabNames.includes("flags.yaml"))
214
+ setView("flags.yaml", result.flags);
215
+ setView(scaName, result.generated?.[`${room}.sca`] ?? "");
216
+ if (tabNames.includes(msgName))
217
+ setView(msgName, result.generated?.[`${room}.msg`] ?? "");
218
+ readExits();
219
+ const resources = (result.resources ?? []).map((r) => ({ type: r.type, number: r.number, data: Uint8Array.from(atob(r.data), (c) => c.charCodeAt(0)) }));
220
+ if (session) {
221
+ replaceResources(session.vm, resources);
222
+ restartScene();
223
+ }
224
+ else
225
+ for (const r of resources)
226
+ rm.override(r, r.data);
227
+ status(`Built in ${result.ms} ms`, "ok");
228
+ }
229
+ else
230
+ status("Build failed: see below", "error");
231
+ renderTabs();
232
+ }
233
+ /** The room's exits, by name, from the YAML that built (they're outlined in their own colour). */
234
+ let exits = new Set();
235
+ function readExits() {
236
+ try {
237
+ exits = new Set(Object.keys(parseYaml(saved.get(yamlName) ?? "")?.exits ?? {}));
238
+ }
239
+ catch {
240
+ /* keep the last good set */
241
+ }
242
+ }
243
+ // --- The game -------------------------------------------------------------------------
244
+ // A mod's resources come from the builds (swapped in memory), not from out/mods; a game's
245
+ // from out/games, with the builds' changes swapped in.
246
+ const rm = await ResourceManager.open(httpFiles, ["", "PATCHES"]);
247
+ let session;
248
+ await build();
249
+ while (error) {
250
+ // Nothing to run until the mod builds: wait for the next successful save.
251
+ await new Promise((r) => setTimeout(r, 300));
252
+ }
253
+ session = await GameSession.create({
254
+ canvas: $("screen"), rm, mods: mod.kind === "mod" ? [mod.name] : [], saveNamespace: `editor:${mod.name}`, muted: true,
255
+ });
256
+ const vm = session.vm;
257
+ /**
258
+ * The game just before entering the room: a hero, outside it (a new one, made by the quick
259
+ * route, awake in the first cave; or one made by hand if that didn't work out).
260
+ */
261
+ let base;
262
+ let entering = false;
263
+ let readyAt = 0;
264
+ const quickStart = gameProfile(vm)?.quickStart(vm);
265
+ if (quickStart) {
266
+ status("Creating a hero…");
267
+ session.start({
268
+ fastForward: {
269
+ ...quickStart,
270
+ done: () => {
271
+ entering = true;
272
+ readyAt = performance.now();
273
+ },
274
+ },
275
+ });
276
+ }
277
+ else {
278
+ // No quick route for this game: the player makes a character, then it goes to the room.
279
+ session.start();
280
+ entering = true;
281
+ readyAt = performance.now();
282
+ }
283
+ function restartScene() {
284
+ if (!base || !session)
285
+ return;
286
+ session.restore(base);
287
+ entering = true;
288
+ }
289
+ $("restart").onclick = restartScene;
290
+ const roomNow = () => gameGlobal(vm, "curRoomNum");
291
+ session.onDraw.push(() => {
292
+ if (!entering || session.fastForwarding)
293
+ return;
294
+ if (roomNow() === room) {
295
+ entering = false;
296
+ if (!error)
297
+ status(`In room ${room}`, "ok");
298
+ }
299
+ else if (heroInPlay(vm)) {
300
+ base ??= session.snapshot();
301
+ requestRoom(vm, room);
302
+ }
303
+ else if (performance.now() - readyAt > 4000) {
304
+ status("No hero yet: finish creating one in the game (Play), then it goes to the room", "error");
305
+ }
306
+ });
307
+ // Outlines: features, exits, sprites, walk areas, by name (on by default).
308
+ const overlay = createOverlay();
309
+ $("screen-box").append(overlay);
310
+ const outlines = $("outlines");
311
+ $("legend").replaceChildren(...[["features", OVERLAY_COLORS.feature], ["exits", OVERLAY_COLORS.exit], ["props", OVERLAY_COLORS.sprite], ["hero", OVERLAY_COLORS.hero], ["walkable", OVERLAY_COLORS.polygons[3]], ["obstacles", OVERLAY_COLORS.polygons[2]]].map(([name, color]) => {
312
+ const s = document.createElement("span");
313
+ s.textContent = name;
314
+ s.style.setProperty("--swatch", color);
315
+ return s;
316
+ }));
317
+ const legend = $("legend");
318
+ const drawOutlines = () => {
319
+ legend.style.display = outlines.checked ? "" : "none";
320
+ if (!outlines.checked || roomNow() !== room)
321
+ overlay.replaceChildren();
322
+ else {
323
+ drawRoomOverlay(overlay, vm, { sprites: true, features: true, polygons: true, paths: true, isExit: (n) => exits.has(n) });
324
+ // A dialog (a plane above the room's and the status bar's) is what matters then.
325
+ overlay.style.opacity = dialogOpen() ? "0.12" : "1";
326
+ }
327
+ };
328
+ function dialogOpen() {
329
+ const g = graphics(vm);
330
+ const roomPriority = roomPlane(vm) ? g.prop(roomPlane(vm).address, "priority") : 0;
331
+ return [...g.planes].some((p) => vm.memory.object(p) && g.prop(p, "priority") > roomPriority + 1);
332
+ }
333
+ outlines.onchange = drawOutlines;
334
+ session.onDraw.push(drawOutlines);
335
+ readExits();
336
+ // Moving things by dragging them, and painting walk areas: edits to the YAML.
337
+ const roomOffset = () => {
338
+ const plane = roomPlane(vm);
339
+ return plane ? [plane.left, plane.top] : [0, 10];
340
+ };
341
+ const editLayer = new EditLayer({
342
+ yaml: () => textOf(yamlName),
343
+ setYaml: (text) => editText(yamlName, text),
344
+ roomOffset,
345
+ spriteBox: (name) => {
346
+ const [ox, oy] = roomOffset();
347
+ const d = drawnItems(vm).find((item) => item.item.name === name);
348
+ return d && { x: d.x - ox, y: d.y - oy, w: d.w, h: d.h };
349
+ },
350
+ previewProp: (name, [x, y]) => {
351
+ const prop = [...vm.memory.objects.values()].find((o) => o.name === name && !o.isClone);
352
+ if (!prop)
353
+ return;
354
+ vm.setProp(prop, "x", x);
355
+ vm.setProp(prop, "y", y);
356
+ },
357
+ holdBuild: (hold) => {
358
+ held = hold;
359
+ if (hold)
360
+ clearTimeout(timer);
361
+ else if (auto.checked)
362
+ void build();
363
+ },
364
+ });
365
+ $("screen-box").append(editLayer.svg);
366
+ const modeButtons = [...document.querySelectorAll("#modes button")];
367
+ const setMode = (mode) => {
368
+ editLayer.setMode(mode);
369
+ for (const b of modeButtons)
370
+ b.classList.toggle("active", b.dataset.mode === mode);
371
+ // The game's own outlines step back while the handles are up.
372
+ overlay.style.filter = mode === "play" ? "" : "opacity(0.35)";
373
+ };
374
+ for (const b of modeButtons)
375
+ b.onclick = () => setMode(b.dataset.mode);
376
+ // Dragging and drawing change YAML: a room written as a script is edited as text.
377
+ if (scriptMode)
378
+ for (const b of modeButtons)
379
+ b.hidden = b.dataset.mode !== "play";
380
+ setMode("play");
381
+ // Handles follow what's drawn (props move, the YAML changes); not mid-drag, which redraws itself.
382
+ session.onDraw.push(() => {
383
+ if (editLayer.mode === "play")
384
+ return;
385
+ if (!held)
386
+ editLayer.render();
387
+ editLayer.svg.style.opacity = !held && dialogOpen() ? "0.3" : "1";
388
+ });
389
+ const sound = $("sound");
390
+ sound.onclick = () => {
391
+ session.setMuted(!session.muted);
392
+ sound.textContent = session.muted ? "Sound off" : "Sound on";
393
+ };