@lewismoten/gamemaker-sandbox-tools 0.0.0-stage → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +24 -0
  3. package/NOTICE.md +121 -0
  4. package/README.md +362 -2
  5. package/dist/audio-files.js +82 -0
  6. package/dist/audio.js +25 -0
  7. package/dist/block-merge.js +43 -0
  8. package/dist/color-reduce.js +44 -0
  9. package/dist/colors.js +87 -0
  10. package/dist/delta.js +75 -0
  11. package/dist/disk-diff.js +220 -0
  12. package/dist/drawing.js +85 -0
  13. package/dist/embroidery/compile.js +85 -0
  14. package/dist/embroidery/dst.js +79 -0
  15. package/dist/embroidery/exp.js +26 -0
  16. package/dist/embroidery/fill.js +49 -0
  17. package/dist/embroidery/geom.js +82 -0
  18. package/dist/embroidery/pes.js +79 -0
  19. package/dist/embroidery/satin.js +29 -0
  20. package/dist/embroidery/simulate.js +30 -0
  21. package/dist/embroidery/strokeFont.js +253 -0
  22. package/dist/embroidery/svg.js +24 -0
  23. package/dist/embroidery/types.js +13 -0
  24. package/dist/embroidery/units.js +26 -0
  25. package/dist/embroidery/validate.js +15 -0
  26. package/dist/formats/blocks.js +1 -0
  27. package/dist/formats/character.js +1 -0
  28. package/dist/formats/cmf.js +1 -0
  29. package/dist/formats/fli.js +1 -0
  30. package/dist/formats/fliLimits.js +1 -0
  31. package/dist/formats/game.js +1 -0
  32. package/dist/formats/gif.js +1 -0
  33. package/dist/formats/graphicScripts.js +1 -0
  34. package/dist/formats/map.js +1 -0
  35. package/dist/formats/monster.js +1 -0
  36. package/dist/formats/palette.js +1 -0
  37. package/dist/formats/recording.js +1 -0
  38. package/dist/formats/sound.js +1 -0
  39. package/dist/formats/voc.js +1 -0
  40. package/dist/gif-shrink.js +56 -0
  41. package/dist/image/png.js +42 -0
  42. package/dist/images.js +88 -0
  43. package/dist/kit/actors.js +19 -0
  44. package/dist/kit/align.js +204 -0
  45. package/dist/kit/blit.js +45 -0
  46. package/dist/kit/blockRoles.js +97 -0
  47. package/dist/kit/clips.js +40 -0
  48. package/dist/kit/coverPaint.js +18 -0
  49. package/dist/kit/disk.js +76 -0
  50. package/dist/kit/fadeOut.js +13 -0
  51. package/dist/kit/fliClips.js +133 -0
  52. package/dist/kit/fliShrink.js +277 -0
  53. package/dist/kit/gameSim.js +235 -0
  54. package/dist/kit/hopBot.js +142 -0
  55. package/dist/kit/levelGen.js +1423 -0
  56. package/dist/kit/levelGrid.js +33 -0
  57. package/dist/kit/levelPicture.js +27 -0
  58. package/dist/kit/menuVersion.js +19 -0
  59. package/dist/kit/microText.js +35 -0
  60. package/dist/kit/mumble.js +74 -0
  61. package/dist/kit/music.js +8 -0
  62. package/dist/kit/palRamps.js +32 -0
  63. package/dist/kit/palette.js +49 -0
  64. package/dist/kit/pcText.js +72 -0
  65. package/dist/kit/photo.js +150 -0
  66. package/dist/kit/pixels.js +79 -0
  67. package/dist/kit/png.js +40 -0
  68. package/dist/kit/quantize.js +72 -0
  69. package/dist/kit/raytrace.js +379 -0
  70. package/dist/kit/recordedDemo.js +10 -0
  71. package/dist/kit/reel.js +77 -0
  72. package/dist/kit/road3d.js +67 -0
  73. package/dist/kit/routeChecks.js +8 -0
  74. package/dist/kit/saveSlots.js +11 -0
  75. package/dist/kit/sceneInit.js +13 -0
  76. package/dist/kit/sceneSim.js +112 -0
  77. package/dist/kit/screenText.js +15 -0
  78. package/dist/kit/sdftext.js +237 -0
  79. package/dist/kit/shading.js +69 -0
  80. package/dist/kit/song.js +251 -0
  81. package/dist/kit/songFx.js +179 -0
  82. package/dist/kit/sound.js +52 -0
  83. package/dist/kit/synthParts.js +20 -0
  84. package/dist/kit/tar.js +53 -0
  85. package/dist/kit/textBounds.js +84 -0
  86. package/dist/kit/textfit.js +246 -0
  87. package/dist/kit/transforms.js +57 -0
  88. package/dist/kit/tune.js +21 -0
  89. package/dist/look-alike.js +50 -0
  90. package/dist/menu-version.js +4 -0
  91. package/dist/mesh/colorBodies.js +137 -0
  92. package/dist/mesh/decimate.js +207 -0
  93. package/dist/mesh/meshCheck.js +51 -0
  94. package/dist/mesh/overhang.js +41 -0
  95. package/dist/mesh/sampledField.js +12 -0
  96. package/dist/mesh/stl.js +36 -0
  97. package/dist/mesh/surfaceNets.js +79 -0
  98. package/dist/mesh/tetraMesh.js +62 -0
  99. package/dist/mesh/threeMf.js +58 -0
  100. package/dist/mesh/zipFile.js +52 -0
  101. package/dist/microFont.js +101 -0
  102. package/dist/music/compose.js +72 -0
  103. package/dist/pack/meshPack.js +198 -0
  104. package/dist/pack/threeMfPack.js +92 -0
  105. package/dist/palette-reserve.js +116 -0
  106. package/dist/palettes.js +138 -0
  107. package/dist/release-notes.js +40 -0
  108. package/dist/simulation.js +18 -0
  109. package/dist/text.js +74 -0
  110. package/dist/types/audio.d.ts +4 -0
  111. package/dist/types/audioFiles.d.ts +22 -0
  112. package/dist/types/audioWav.d.ts +4 -0
  113. package/dist/types/blockMerge.d.ts +24 -0
  114. package/dist/types/colorReduce.d.ts +23 -0
  115. package/dist/types/colors.d.ts +26 -0
  116. package/dist/types/delta.d.ts +32 -0
  117. package/dist/types/diskDiff.d.ts +47 -0
  118. package/dist/types/drawing.d.ts +15 -0
  119. package/dist/types/embroidery/compile.d.ts +3 -0
  120. package/dist/types/embroidery/dst.d.ts +13 -0
  121. package/dist/types/embroidery/exp.d.ts +8 -0
  122. package/dist/types/embroidery/fill.d.ts +6 -0
  123. package/dist/types/embroidery/geom.d.ts +19 -0
  124. package/dist/types/embroidery/pes.d.ts +10 -0
  125. package/dist/types/embroidery/satin.d.ts +11 -0
  126. package/dist/types/embroidery/simulate.d.ts +19 -0
  127. package/dist/types/embroidery/strokeFont.d.ts +20 -0
  128. package/dist/types/embroidery/svg.d.ts +3 -0
  129. package/dist/types/embroidery/types.d.ts +70 -0
  130. package/dist/types/embroidery/units.d.ts +10 -0
  131. package/dist/types/embroidery/validate.d.ts +22 -0
  132. package/dist/types/formats/blocks.d.ts +1 -0
  133. package/dist/types/formats/character.d.ts +1 -0
  134. package/dist/types/formats/cmf.d.ts +1 -0
  135. package/dist/types/formats/fli.d.ts +1 -0
  136. package/dist/types/formats/fliLimits.d.ts +1 -0
  137. package/dist/types/formats/game.d.ts +1 -0
  138. package/dist/types/formats/gif.d.ts +1 -0
  139. package/dist/types/formats/graphicScripts.d.ts +1 -0
  140. package/dist/types/formats/map.d.ts +1 -0
  141. package/dist/types/formats/monster.d.ts +1 -0
  142. package/dist/types/formats/palette.d.ts +1 -0
  143. package/dist/types/formats/recording.d.ts +1 -0
  144. package/dist/types/formats/sound.d.ts +1 -0
  145. package/dist/types/formats/voc.d.ts +1 -0
  146. package/dist/types/gifShrink.d.ts +8 -0
  147. package/dist/types/image/animatedGif.d.ts +9 -0
  148. package/dist/types/image/png.d.ts +10 -0
  149. package/dist/types/image/surface.d.ts +26 -0
  150. package/dist/types/images.d.ts +13 -0
  151. package/dist/types/kit/actors.d.ts +7 -0
  152. package/dist/types/kit/align.d.ts +93 -0
  153. package/dist/types/kit/audioEvents.d.ts +26 -0
  154. package/dist/types/kit/blit.d.ts +20 -0
  155. package/dist/types/kit/blockRoles.d.ts +243 -0
  156. package/dist/types/kit/clips.d.ts +36 -0
  157. package/dist/types/kit/coverPaint.d.ts +7 -0
  158. package/dist/types/kit/disk.d.ts +15 -0
  159. package/dist/types/kit/fadeOut.d.ts +12 -0
  160. package/dist/types/kit/fliClips.d.ts +32 -0
  161. package/dist/types/kit/fliShrink.d.ts +149 -0
  162. package/dist/types/kit/gameSim.d.ts +112 -0
  163. package/dist/types/kit/hopBot.d.ts +72 -0
  164. package/dist/types/kit/laserClearance.d.ts +16 -0
  165. package/dist/types/kit/laserJoints.d.ts +39 -0
  166. package/dist/types/kit/laserLayout.d.ts +54 -0
  167. package/dist/types/kit/laserPixels.d.ts +52 -0
  168. package/dist/types/kit/laserPreview.d.ts +9 -0
  169. package/dist/types/kit/laserSvg.d.ts +35 -0
  170. package/dist/types/kit/levelGen/castle.d.ts +17 -0
  171. package/dist/types/kit/levelGen/dungeon.d.ts +11 -0
  172. package/dist/types/kit/levelGen/forest.d.ts +17 -0
  173. package/dist/types/kit/levelGen/furnish.d.ts +31 -0
  174. package/dist/types/kit/levelGen/grid.d.ts +46 -0
  175. package/dist/types/kit/levelGen/index.d.ts +138 -0
  176. package/dist/types/kit/levelGen/interior.d.ts +18 -0
  177. package/dist/types/kit/levelGen/jumpModel.d.ts +78 -0
  178. package/dist/types/kit/levelGen/needs.d.ts +44 -0
  179. package/dist/types/kit/levelGen/overworld.d.ts +21 -0
  180. package/dist/types/kit/levelGen/platform.d.ts +59 -0
  181. package/dist/types/kit/levelGen/platformCap.d.ts +13 -0
  182. package/dist/types/kit/levelGen/platformReach.d.ts +37 -0
  183. package/dist/types/kit/levelGen/props.d.ts +37 -0
  184. package/dist/types/kit/levelGen/town.d.ts +24 -0
  185. package/dist/types/kit/levelGrid.d.ts +16 -0
  186. package/dist/types/kit/levelPicture.d.ts +9 -0
  187. package/dist/types/kit/menuVersion.d.ts +7 -0
  188. package/dist/types/kit/microText.d.ts +18 -0
  189. package/dist/types/kit/mumble.d.ts +41 -0
  190. package/dist/types/kit/music.d.ts +1 -0
  191. package/dist/types/kit/palMerge.d.ts +10 -0
  192. package/dist/types/kit/palRamps.d.ts +28 -0
  193. package/dist/types/kit/palette.d.ts +16 -0
  194. package/dist/types/kit/pcText.d.ts +19 -0
  195. package/dist/types/kit/photo.d.ts +69 -0
  196. package/dist/types/kit/pixels.d.ts +26 -0
  197. package/dist/types/kit/png.d.ts +7 -0
  198. package/dist/types/kit/quantize.d.ts +20 -0
  199. package/dist/types/kit/raytrace.d.ts +169 -0
  200. package/dist/types/kit/recordedDemo.d.ts +1 -0
  201. package/dist/types/kit/reel.d.ts +34 -0
  202. package/dist/types/kit/road3d.d.ts +71 -0
  203. package/dist/types/kit/routeChecks.d.ts +6 -0
  204. package/dist/types/kit/saveSlots.d.ts +17 -0
  205. package/dist/types/kit/sceneInit.d.ts +11 -0
  206. package/dist/types/kit/sceneSim.d.ts +84 -0
  207. package/dist/types/kit/screenText.d.ts +3 -0
  208. package/dist/types/kit/sdftext.d.ts +34 -0
  209. package/dist/types/kit/shading.d.ts +23 -0
  210. package/dist/types/kit/shareBlocks.d.ts +20 -0
  211. package/dist/types/kit/song.d.ts +89 -0
  212. package/dist/types/kit/songFx.d.ts +99 -0
  213. package/dist/types/kit/sound.d.ts +29 -0
  214. package/dist/types/kit/synthParts.d.ts +7 -0
  215. package/dist/types/kit/tar.d.ts +13 -0
  216. package/dist/types/kit/textBounds.d.ts +51 -0
  217. package/dist/types/kit/textfit.d.ts +74 -0
  218. package/dist/types/kit/transforms.d.ts +8 -0
  219. package/dist/types/kit/tune.d.ts +6 -0
  220. package/dist/types/lookAlike.d.ts +31 -0
  221. package/dist/types/menuVersionEntry.d.ts +1 -0
  222. package/dist/types/mesh/colorBodies.d.ts +18 -0
  223. package/dist/types/mesh/decimate.d.ts +10 -0
  224. package/dist/types/mesh/fieldTrinket.d.ts +40 -0
  225. package/dist/types/mesh/figureShapes.d.ts +24 -0
  226. package/dist/types/mesh/mesh.d.ts +9 -0
  227. package/dist/types/mesh/meshCheck.d.ts +22 -0
  228. package/dist/types/mesh/overhang.d.ts +24 -0
  229. package/dist/types/mesh/pixelFigure.d.ts +41 -0
  230. package/dist/types/mesh/sampledField.d.ts +3 -0
  231. package/dist/types/mesh/sdfShapes.d.ts +20 -0
  232. package/dist/types/mesh/stl.d.ts +9 -0
  233. package/dist/types/mesh/strokeText.d.ts +8 -0
  234. package/dist/types/mesh/surfaceNets.d.ts +14 -0
  235. package/dist/types/mesh/tetraMesh.d.ts +4 -0
  236. package/dist/types/mesh/threeMf.d.ts +12 -0
  237. package/dist/types/mesh/trinketKinds.d.ts +72 -0
  238. package/dist/types/mesh/trinketTool.d.ts +26 -0
  239. package/dist/types/mesh/voxel.d.ts +10 -0
  240. package/dist/types/mesh/zipFile.d.ts +13 -0
  241. package/dist/types/microFont.d.ts +6 -0
  242. package/dist/types/music/compose.d.ts +51 -0
  243. package/dist/types/pack/meshPack.d.ts +26 -0
  244. package/dist/types/pack/threeMfPack.d.ts +11 -0
  245. package/dist/types/palettePresetData.d.ts +4 -0
  246. package/dist/types/palettePresets.d.ts +12 -0
  247. package/dist/types/paletteReserve.d.ts +52 -0
  248. package/dist/types/palettes.d.ts +4 -0
  249. package/dist/types/releaseNotes.d.ts +3 -0
  250. package/dist/types/simulation.d.ts +6 -0
  251. package/dist/types/text.d.ts +41 -0
  252. package/package.json +190 -4
@@ -0,0 +1,277 @@
1
+ import { parseFli as W, newFliCanvas as at, applyFliFrame as ot, encodeFli as rt } from "@lewismoten/gamemaker-sandbox-engine/formats/fli";
2
+ function ct(t) {
3
+ const s = W(t), f = at(s), e = [], n = [];
4
+ for (let o = 0; o < s.frameCount; o++)
5
+ ot(s, o, f), e.push(Uint8Array.from(f.pixels)), n.push(Uint8Array.from(f.palette));
6
+ return { frames: e, palettes: n, width: s.width, height: s.height, speed: s.speed };
7
+ }
8
+ function Q(t, s = {}) {
9
+ return rt(t.frames, t.palettes[0], {
10
+ width: t.width,
11
+ height: t.height,
12
+ speed: t.speed,
13
+ palettes: t.palettes,
14
+ runs: !0,
15
+ noLoopFrame: !0,
16
+ sparsePalettes: s.sparsePalettes
17
+ });
18
+ }
19
+ const lt = { 4: "COLOR256", 7: "SS2", 11: "COLOR64", 12: "LC", 13: "BLACK", 15: "BRUN", 16: "COPY", 18: "PSTAMP" };
20
+ function wt(t) {
21
+ const s = W(t), f = ct(t), e = new DataView(t.buffer, t.byteOffset, t.byteLength), n = [], o = {};
22
+ for (let i = 0; i < s.frameCount; i++) {
23
+ const p = s.frames[i], u = { header: 16 };
24
+ let d = p + 16;
25
+ for (let x = 0, M = e.getUint16(p + 6, !0); x < M; x++) {
26
+ const P = e.getUint32(d, !0), S = lt[e.getUint16(d + 4, !0)] ?? String(e.getUint16(d + 4, !0));
27
+ u[S] = (u[S] ?? 0) + P, d += P;
28
+ }
29
+ let y = 0;
30
+ const b = f.frames[i - 1];
31
+ if (!b) y = f.frames[i].length;
32
+ else for (let x = 0; x < b.length; x++) b[x] !== f.frames[i][x] && y++;
33
+ for (const [x, M] of Object.entries(u)) o[x] = (o[x] ?? 0) + M;
34
+ n.push({ frame: i, bytes: e.getUint32(p, !0), chunks: u, changedPixels: y });
35
+ }
36
+ const c = [], h = t.length, r = [...n].sort((i, p) => p.bytes - i.bytes)[0];
37
+ r && r.bytes > h * 0.25 && c.push(
38
+ `Frame ${r.frame + 1} is ${Math.round(r.bytes / h * 100)}% of the file: a whole new picture. Dithered or speckled art costs the most there; flatter shading costs less.`
39
+ );
40
+ const l = o.COLOR64 ?? 0;
41
+ l > h * 0.04 && c.push(
42
+ `Palette changes take ${Math.round(l / h * 100)}%: fades resend colors a frame does not draw (sparse palettes, a smaller palette).`
43
+ );
44
+ const a = n.filter((i) => i.frame > 0 && i.changedPixels > 0 && i.bytes / i.changedPixels > 1.5 && i.changedPixels > 400);
45
+ a.length > n.length * 0.2 && c.push(
46
+ `${a.length} frames change many pixels by a little (noise or dither that moves): holding still areas still, or snapping near colors, cuts them.`
47
+ );
48
+ const m = n.filter((i) => i.frame > 0 && i.changedPixels === 0).length;
49
+ return m > n.length * 0.3 && c.push(`${m} frames repeat the one before: they cost their header only, but dropping frames and lengthening delays would save more.`), { totalBytes: h, frames: n, byChunk: o, findings: c };
50
+ }
51
+ const F = (t, s, f) => Math.round((t[s * 3 + f] & 63) * 255 / 63);
52
+ function Z(t, s, f = (e) => e) {
53
+ let e = 0, n = 0, o = 0, c = 0;
54
+ for (let h = 0; h < t.frames.length; h++) {
55
+ const r = t.frames[h], l = t.palettes[h], a = Math.min(s.frames.length - 1, f(h)), m = s.frames[a], i = s.palettes[a];
56
+ for (let p = 0; p < r.length; p++) {
57
+ if (c++, r[p] === m[p] && l === i) continue;
58
+ let u = 0;
59
+ for (let d = 0; d < 3; d++) u = Math.max(u, Math.abs(F(l, r[p], d) - F(i, m[p], d)));
60
+ u && o++, e += u, n = Math.max(n, u);
61
+ }
62
+ }
63
+ return { mean: c ? e / c : 0, worst: n, changedShare: c ? o / c : 0 };
64
+ }
65
+ function C(t, s, f, e, n, o, c) {
66
+ const h = Q(f, o).length, r = Q(e, { ...o, ...n }).length;
67
+ return {
68
+ name: t,
69
+ lossless: s,
70
+ frames: e,
71
+ encode: { ...o, ...n },
72
+ bytesBefore: h,
73
+ bytesAfter: r,
74
+ savedBytes: h - r,
75
+ visibility: s ? { mean: 0, worst: 0, changedShare: 0 } : Z(f, e, c)
76
+ };
77
+ }
78
+ const N = (t, s, f) => Math.max(...[0, 1, 2].map((e) => Math.abs(F(t, s, e) - F(t, f, e))));
79
+ function X(t) {
80
+ const s = (f) => f.reduce((e, n) => e + n, 0);
81
+ return t.palettes.reduce((f, e) => s(e) > s(f) ? e : f);
82
+ }
83
+ function ft(t, s, f = {}) {
84
+ const { width: e, height: n } = t, o = X(t), c = t.frames.map((h) => {
85
+ const r = Uint8Array.from(h), l = (a, m, i) => {
86
+ r[m] === r[i] && r[a] !== r[m] && N(o, r[a], r[m]) <= s.tolerance && (r[a] = r[m]);
87
+ };
88
+ for (let a = 0; a < n; a++) for (let m = 1; m < e - 1; m++) l(a * e + m, a * e + m - 1, a * e + m + 1);
89
+ for (let a = 1; a < n - 1; a++) for (let m = 0; m < e; m++) l(a * e + m, (a - 1) * e + m, (a + 1) * e + m);
90
+ return r;
91
+ });
92
+ return C("flatten dithering", !1, t, { ...t, frames: c }, {}, f);
93
+ }
94
+ function ht(t, s = {}) {
95
+ return C("sparse palettes", !0, t, t, { sparsePalettes: !0 }, s);
96
+ }
97
+ function it(t, s, f = {}) {
98
+ const e = t.frames.map((n) => Uint8Array.from(n));
99
+ for (let n = 1; n < e.length; n++) {
100
+ if (t.palettes[n] !== t.palettes[n - 1] && t.palettes[n].some((r, l) => r !== t.palettes[n - 1][l])) continue;
101
+ const o = t.palettes[n], c = e[n - 1], h = e[n];
102
+ for (let r = 0; r < h.length; r++) h[r] !== c[r] && N(o, h[r], c[r]) <= s.tolerance && (h[r] = c[r]);
103
+ }
104
+ return C("hold still areas still", !1, t, { ...t, frames: e }, {}, f);
105
+ }
106
+ function pt(t, s, f) {
107
+ let e = f[0], n = 1 / 0;
108
+ for (const o of f) {
109
+ const c = [0, 1, 2].reduce((h, r) => h + (F(t, s, r) - F(t, o, r)) ** 2, 0);
110
+ c < n && ([e, n] = [o, c]);
111
+ }
112
+ return e;
113
+ }
114
+ function mt(t, s, f = {}) {
115
+ const e = X(t), n = new Array(256).fill(0);
116
+ for (const l of t.frames) for (let a = 0; a < l.length; a += 7) n[l[a]]++;
117
+ const o = n.map((l, a) => a).sort((l, a) => n[a] - n[l]), c = Array.from({ length: 256 }, (l, a) => a), h = [];
118
+ for (const l of o) {
119
+ if (!n[l]) continue;
120
+ const a = h.find((m) => N(e, l, m) <= s.tolerance);
121
+ a === void 0 ? h.push(l) : c[l] = a;
122
+ }
123
+ const r = t.frames.map((l) => l.map((a) => c[a]));
124
+ return C("snap near colors", !1, t, { ...t, frames: r }, {}, f);
125
+ }
126
+ function ut(t, s, f = {}) {
127
+ const e = X(t), n = new Array(256).fill(0);
128
+ for (const l of t.frames) for (let a = 0; a < l.length; a++) n[l[a]]++;
129
+ const o = n.map((l, a) => a).filter((l) => n[l]).sort((l, a) => n[a] - n[l]), c = o.slice(0, Math.max(1, s.colors)), h = Array.from({ length: 256 }, (l, a) => a);
130
+ for (const l of o.slice(c.length)) h[l] = pt(e, l, c);
131
+ const r = t.frames.map((l) => l.map((a) => h[a]));
132
+ return C("fewer colors", !1, t, { ...t, frames: r }, {}, f);
133
+ }
134
+ function dt(t, s = {}) {
135
+ const f = (o) => o % 2 === 0 || o === t.frames.length - 1, e = t.frames.map((o, c) => c).filter(f), n = { ...t, frames: e.map((o) => t.frames[o]), palettes: e.map((o) => t.palettes[o]), speed: Math.min(255, t.speed * 2) };
136
+ return C("drop every other frame", !1, t, n, {}, s, (o) => o >= t.frames.length - 1 ? e.length - 1 : o >> 1);
137
+ }
138
+ function At(t, s) {
139
+ const f = [];
140
+ let e = t, n = {};
141
+ for (const o of s) {
142
+ const c = o.step === "sparsePalettes" ? ht(e, n) : o.step === "holdStill" ? it(e, o, n) : o.step === "snapColors" ? mt(e, o, n) : o.step === "flattenDither" ? ft(e, o, n) : o.step === "maxColors" ? ut(e, o, n) : o.step === "onePalette" ? xt(e, o, n) : o.step === "frame" ? bt(e, o, n) : dt(e, n);
143
+ f.push(c), e = c.frames, n = c.encode;
144
+ }
145
+ return f;
146
+ }
147
+ function Ct(t, s) {
148
+ const f = t.frames.length, e = s.frames.length;
149
+ return Z(t, s, e === f ? (n) => n : (n) => n >= f - 1 ? e - 1 : n >> 1);
150
+ }
151
+ const K = (t, s) => (t[s * 3] & 63) << 12 | (t[s * 3 + 1] & 63) << 6 | t[s * 3 + 2] & 63;
152
+ function yt(t, s, f) {
153
+ const e = (c, h) => c >> 12 - 6 * h & 63, n = [t], o = (c) => {
154
+ let h = 0, r = 0;
155
+ for (let l = 0; l < 3; l++) {
156
+ const a = c.map((i) => e(i, l)), m = Math.max(...a) - Math.min(...a);
157
+ m > h && ([h, r] = [m, l]);
158
+ }
159
+ return { best: h, at: r };
160
+ };
161
+ for (; n.length < f; ) {
162
+ let c = -1, h = 0;
163
+ if (n.forEach((p, u) => {
164
+ if (p.length < 2) return;
165
+ const d = o(p).best * p.reduce((y, b) => y + s.get(b), 0);
166
+ d > h && ([c, h] = [u, d]);
167
+ }), c < 0) break;
168
+ const r = n[c], { at: l } = o(r);
169
+ r.sort((p, u) => e(p, l) - e(u, l));
170
+ const a = r.reduce((p, u) => p + s.get(u), 0) / 2;
171
+ let m = 0, i = 1;
172
+ for (let p = 0; p < r.length - 1 && (m += s.get(r[p]), i = p + 1, !(m >= a)); p++)
173
+ ;
174
+ n.splice(c, 1, r.slice(0, i), r.slice(i));
175
+ }
176
+ return n.map((c) => {
177
+ const h = c.reduce((l, a) => l + s.get(a), 0) || 1, r = [0, 1, 2].map((l) => Math.round(c.reduce((a, m) => a + e(m, l) * s.get(m), 0) / h));
178
+ return r[0] << 12 | r[1] << 6 | r[2];
179
+ });
180
+ }
181
+ function xt(t, s, f = {}) {
182
+ const e = Math.max(1, Math.min(256, Math.round(s.colors))), n = /* @__PURE__ */ new Map();
183
+ t.frames.forEach((i, p) => {
184
+ const u = t.palettes[p], d = new Uint32Array(256);
185
+ for (let y = 0; y < i.length; y++) d[i[y]]++;
186
+ for (let y = 0; y < 256; y++) d[y] && n.set(K(u, y), (n.get(K(u, y)) ?? 0) + d[y]);
187
+ });
188
+ const o = [...n.keys()], c = o.length <= e ? o : yt(o, n, e), h = new Uint8Array(768);
189
+ c.forEach((i, p) => {
190
+ h[p * 3] = i >> 12, h[p * 3 + 1] = i >> 6 & 63, h[p * 3 + 2] = i & 63;
191
+ });
192
+ const r = /* @__PURE__ */ new Map(), l = (i) => {
193
+ let p = r.get(i);
194
+ if (p !== void 0) return p;
195
+ let u = 1 / 0;
196
+ return p = 0, c.forEach((d, y) => {
197
+ const b = [12, 6, 0].reduce((x, M) => x + ((i >> M & 63) - (d >> M & 63)) ** 2, 0);
198
+ b < u && ([p, u] = [y, b]);
199
+ }), r.set(i, p), p;
200
+ }, a = t.frames.map((i, p) => {
201
+ const u = t.palettes[p], d = Uint8Array.from({ length: 256 }, (y, b) => l(K(u, b)));
202
+ return i.map((y) => d[y]);
203
+ }), m = { ...t, frames: a, palettes: a.map(() => h) };
204
+ return C("one palette", o.length <= e, t, m, {}, f);
205
+ }
206
+ function Mt(t) {
207
+ let s = 0, f = 1 / 0;
208
+ for (let e = 0; e < 256; e++) {
209
+ const n = (t[e * 3] & 63) + (t[e * 3 + 1] & 63) + (t[e * 3 + 2] & 63);
210
+ if (n < f && ([s, f] = [e, n]), e === 0 && n === 0) return 0;
211
+ }
212
+ return s;
213
+ }
214
+ function bt(t, s, f = {}) {
215
+ const { width: e, height: n } = t, o = (M, P) => Math.max(0, Math.min(P, Math.round(M) || 0)), c = o(s.left, e - 1), h = o(s.right, e - 1 - c), r = o(s.top, n - 1), l = o(s.bottom, n - 1 - r), a = Math.max(0.05, Math.min(8, s.zoom || 1));
216
+ if (!c && !h && !r && !l && a === 1 && !s.panX && !s.panY) return C("frame it", !0, t, t, {}, f);
217
+ const i = Mt(t.palettes[0]), p = e - c - h, u = n - r - l, d = a === 1 && !s.panX && !s.panY, y = d ? e / 2 : c + p / 2, b = d ? n / 2 : r + u / 2, x = t.frames.map((M, P) => {
218
+ const S = new Uint8Array(e * n).fill(i), tt = t.palettes[P], R = new Uint8Array(256);
219
+ for (let w = 0; w < M.length; w++) R[M[w]] = 1;
220
+ const et = [...R.keys()].filter((w) => R[w]), V = /* @__PURE__ */ new Map(), U = (w, k) => tt[w * 3 + k] & 63;
221
+ for (let w = 0; w < u; w++) {
222
+ const k = r + w, _ = n / 2 + s.panY + (k - b) / a, nt = _ + 1 / a;
223
+ for (let $ = 0; $ < p; $++) {
224
+ const B = c + $, D = e / 2 + s.panX + (B - y) / a, st = D + 1 / a;
225
+ if (a >= 1) {
226
+ const A = Math.floor(D + 0.5 / a), g = Math.floor(_ + 0.5 / a);
227
+ A >= 0 && A < e && g >= 0 && g < n && (S[k * e + B] = M[g * e + A]);
228
+ continue;
229
+ }
230
+ const I = Math.max(0, Math.floor(D)), q = Math.min(e - 1, Math.ceil(st) - 1), Y = Math.max(0, Math.floor(_)), H = Math.min(n - 1, Math.ceil(nt) - 1);
231
+ if (I > q || Y > H) continue;
232
+ const z = M[Y * e + I];
233
+ let T = !0, j = 0, G = 0, J = 0, E = 0;
234
+ for (let A = Y; A <= H; A++)
235
+ for (let g = I; g <= q; g++) {
236
+ const v = M[A * e + g];
237
+ v !== z && (T = !1), j += U(v, 0), G += U(v, 1), J += U(v, 2), E++;
238
+ }
239
+ if (T) {
240
+ S[k * e + B] = z;
241
+ continue;
242
+ }
243
+ const O = Math.round(j / E) << 12 | Math.round(G / E) << 6 | Math.round(J / E);
244
+ let L = V.get(O);
245
+ if (L === void 0) {
246
+ let A = 1 / 0;
247
+ L = z;
248
+ for (const g of et) {
249
+ const v = (U(g, 0) - (O >> 12)) ** 2 + (U(g, 1) - (O >> 6 & 63)) ** 2 + (U(g, 2) - (O & 63)) ** 2;
250
+ v < A && ([L, A] = [g, v]);
251
+ }
252
+ V.set(O, L);
253
+ }
254
+ S[k * e + B] = L;
255
+ }
256
+ }
257
+ return S;
258
+ });
259
+ return C("frame it", !1, t, { ...t, frames: x }, {}, f);
260
+ }
261
+ export {
262
+ Mt as bandColor,
263
+ Ct as chainVisibility,
264
+ ct as decodeFli,
265
+ dt as dropFramesStep,
266
+ Q as encodeFrames,
267
+ ft as flattenDitherStep,
268
+ wt as fliReport,
269
+ bt as frameStep,
270
+ it as holdStillStep,
271
+ ut as maxColorsStep,
272
+ xt as onePaletteStep,
273
+ At as shrinkFrames,
274
+ mt as snapColorsStep,
275
+ ht as sparsePalettesStep,
276
+ Z as visibilityOf
277
+ };
@@ -0,0 +1,235 @@
1
+ import { readdirSync as A, readFileSync as M } from "node:fs";
2
+ import { join as b } from "node:path";
3
+ import { fileURLToPath as v } from "node:url";
4
+ import { moveChars as B } from "@lewismoten/gamemaker-sandbox-engine/engine/character";
5
+ import { simKeyPress as C } from "@lewismoten/gamemaker-sandbox-engine/engine/keys";
6
+ import { blockBirthMonsters as K, moveAllMonsters as L } from "@lewismoten/gamemaker-sandbox-engine/engine/monsters";
7
+ import { updateCharDrawState as x } from "@lewismoten/gamemaker-sandbox-engine/engine/render";
8
+ import { initScroll as N, drawScroll as E, changeScroll as j } from "@lewismoten/gamemaker-sandbox-engine/engine/scroll";
9
+ import { startSceneInits as I, changeBlocks as O } from "@lewismoten/gamemaker-sandbox-engine/engine/scene";
10
+ import { getTouchBlocks as R } from "@lewismoten/gamemaker-sandbox-engine/engine/touch";
11
+ import { BACKBL as D, newCharInfo as P, createWorld as W, padBlocks as f, LASTMM as q, MLEN as F, bgBlock as G } from "@lewismoten/gamemaker-sandbox-engine/engine/world";
12
+ import { parseBlocks as k, BACKBL as y, MONBL as w, CHARBL as S } from "@lewismoten/gamemaker-sandbox-engine/formats/blocks";
13
+ import { parseCharacter as H } from "@lewismoten/gamemaker-sandbox-engine/formats/character";
14
+ import { parseMonsters as U } from "@lewismoten/gamemaker-sandbox-engine/formats/monster";
15
+ import { parseMap as _ } from "@lewismoten/gamemaker-sandbox-engine/formats/map";
16
+ import { parseGame as $, MAXSCENELINKS as m, NOSCENE as V } from "@lewismoten/gamemaker-sandbox-engine/formats/game";
17
+ import { parseSoundSet as X, LASTSND as z } from "@lewismoten/gamemaker-sandbox-engine/formats/sound";
18
+ import { vocMs as J } from "./clips.js";
19
+ const Q = 18.2;
20
+ function Y(n) {
21
+ const t = /* @__PURE__ */ new Map();
22
+ for (const e of A(n)) t.set(e.toLowerCase(), new Uint8Array(M(b(n, e))));
23
+ return t;
24
+ }
25
+ class p {
26
+ constructor(t) {
27
+ this.a = t;
28
+ }
29
+ a;
30
+ next = () => {
31
+ this.a = this.a + 1831565813 >>> 0;
32
+ let t = this.a;
33
+ return t = Math.imul(t ^ t >>> 15, t | 1), t ^= t + Math.imul(t ^ t >>> 7, t | 61), ((t ^ t >>> 14) >>> 0) / 4294967296;
34
+ };
35
+ }
36
+ class Z {
37
+ constructor(t, e = 1) {
38
+ this.files = t, this.seed = e, this.game = $(t.get([...t.keys()].find((o) => o.endsWith(".gam")))), this.ci.hitpts = -1, this.ci.lives = -1, this.ci.score = 0, this.ci.inv.fill(D + 1), this.ci.meter.fill(-1), this.enter(this.game.scenes[0].links[0].toScene, 0, 0);
39
+ }
40
+ files;
41
+ seed;
42
+ game;
43
+ ci = P();
44
+ w;
45
+ scene = 0;
46
+ prev = 0;
47
+ link = 0;
48
+ /** Scenes entered, in order (a death enters the same scene again). */
49
+ trail = [];
50
+ /** Game ticks since play entered the current scene (a segment's clock). */
51
+ ticks = 0;
52
+ /** Keys per scene entered (a scene entered again after a death keeps counting on). */
53
+ segments = [];
54
+ /** Ticks each finished segment ran. */
55
+ segmentTicks = [];
56
+ lastKeyTick = 0;
57
+ held = /* @__PURE__ */ new Set();
58
+ pending = [];
59
+ doScroll = !1;
60
+ /** TimerCounter: it runs on across scenes. */
61
+ clock = 1;
62
+ rand = new p(0);
63
+ ended;
64
+ deaths = 0;
65
+ /** The inventory slot the drop picker answers with once (set it before pressing the drop key; -1, the default, is Esc). */
66
+ dropSlot = -1;
67
+ /**
68
+ * The .VOC clip the Sound Blaster is playing (PlaySbVocFile: a new one cuts it off), with
69
+ * the game clock tick it ends on; a scene's exit or intro picture cuts it too (ShowGraphic).
70
+ */
71
+ clip;
72
+ file(t, e) {
73
+ const o = this.game.scenes[t].files[e];
74
+ return o ? this.files.get(o) : void 0;
75
+ }
76
+ /** loadscene + StartSceneInits, as playScene does them. */
77
+ enter(t, e, o, a = !1) {
78
+ this.scene = t, this.prev = e, this.link = o, this.trail.push(t), a || (this.segments.length && this.segmentTicks.push(this.ticks), this.segments.push({ scene: t, keys: [] }), this.ticks = 0, this.lastKeyTick = 0);
79
+ const r = (c) => this.file(t, c);
80
+ this.rand = new p(this.seed), (this.game.scenes[e]?.files[10] || this.game.scenes[t].files[9]) && (this.clip = void 0);
81
+ const l = r(7) && X(r(7)), h = (c) => {
82
+ if (!l || c < 0 || c >= z) return !1;
83
+ const d = l.digital[c], g = d && d[0] !== " " ? this.files.get(`${d.toLowerCase()}.voc`) : void 0;
84
+ return g && (this.clip = { name: d, until: this.clock + Math.ceil(J(g) / 1e3 * Q) }), !0;
85
+ }, i = this.w, s = W(_(r(1)), { play: h }, this.rand.next);
86
+ s.bg = f(k(r(2), y), y), s.mblk = f(r(4) ? k(r(4), w) : void 0, w), s.cblk = r(6) || !i ? f(k(r(6), S), S) : i.cblk, s.mon = r(3) ? U(r(3)) : [], s.noMons = !r(3) || !r(4), s.chr = r(5) || !i ? H(r(5)) : i.chr, s.ci = this.ci, s.clock = this.clock, this.w = s, I(s, this.game.scenes[e].links[o].to), s.gclk = s.clock, s.joyOldTime = s.clock, N(s, this.game.scenes[t].scroll), this.doScroll = !1, this.held.clear(), this.pending = [];
87
+ }
88
+ press(t) {
89
+ this.held.has(t) || (this.held.add(t), this.event(t));
90
+ }
91
+ release(t) {
92
+ this.held.has(t) && (this.held.delete(t), this.event(t | 128));
93
+ }
94
+ releaseAll() {
95
+ for (const t of [...this.held]) this.release(t);
96
+ }
97
+ isHeld(t) {
98
+ return this.held.has(t);
99
+ }
100
+ event(t) {
101
+ this.ended || (this.pending.push(t), this.record(t));
102
+ }
103
+ /** Writes key `k` into the segment (without playing it in this sim): the drop picker's own keys, which the real engine reads. */
104
+ record(t) {
105
+ if (this.ended) return;
106
+ const e = this.ticks + 1;
107
+ this.segments[this.segments.length - 1].keys.push({ delta: e - this.lastKeyTick, key: t }), this.lastKeyTick = e;
108
+ }
109
+ /** One game tick; returns 'link' (into another scene), 'dead' or 'end'. */
110
+ async tick() {
111
+ if (this.ended) return "end";
112
+ const t = this.w;
113
+ this.ticks++, this.clock++;
114
+ for (let i = 0; i < 8; i++) this.doScroll && E(t);
115
+ t.clock = this.clock, t.noMons || (K(t), L(t));
116
+ const e = t.chr;
117
+ if (this.pending.length && (!e.sequences[e.cseq].ground || e.cframe === 0)) for (const i of this.pending.splice(0)) C(t, i);
118
+ let o = !1, a = 0;
119
+ do
120
+ await B(t, async () => {
121
+ const i = this.dropSlot;
122
+ if (this.dropSlot = -1, i >= 0) {
123
+ const s = [...new Array(i % 5).fill(77), ...i >= 5 ? [80] : [], 28];
124
+ for (const c of s)
125
+ this.record(c), this.record(c | 128);
126
+ }
127
+ return i;
128
+ }) && (o = !0);
129
+ while (t.chr.cframe === -1 && !o && a++ < 50);
130
+ O(t), x(t), this.doScroll = j(t) !== 0, t.monsolid = 0, t.mmSolid = q;
131
+ const r = R(t), l = this.game.scenes[this.scene].links;
132
+ let h = m;
133
+ for (let i = 0; i < m && h === m; i++) {
134
+ const s = l[i];
135
+ if (!(!s || s.toScene === V))
136
+ for (let c = 0; c < 6; c++) s.from.x === r.blks[c][0] && s.from.y === r.blks[c][1] && (h = i);
137
+ }
138
+ if (o)
139
+ return this.deaths++, this.ci.lives > 0 ? (this.ci.lives--, this.ci.hitpts = -1, this.enter(this.scene, this.prev, this.link, !0)) : this.ended = "lost", "dead";
140
+ if (h !== m) {
141
+ const i = l[h].toScene;
142
+ return i === 1 ? (this.ended = "won", "end") : (this.enter(i, this.scene, h), "link");
143
+ }
144
+ }
145
+ /** Game ticks until the clip playing now ends (0 when none is). */
146
+ get clipTicksLeft() {
147
+ return this.clip ? Math.max(0, this.clip.until - this.clock) : 0;
148
+ }
149
+ /**
150
+ * Stands still until the clip playing now has played to the end, so the bot's next sound
151
+ * (a jump, a pickup) doesn't cut it off: a level's start clip, say. Returns what a tick
152
+ * returned if play left the scene meanwhile.
153
+ */
154
+ async waitForClip() {
155
+ for (this.releaseAll(); this.clipTicksLeft > 0; ) {
156
+ const t = await this.tick();
157
+ if (t) return t;
158
+ }
159
+ }
160
+ /** The character's block and the pixels past it. */
161
+ get pos() {
162
+ const t = this.w.chr;
163
+ return { x: t.x[0], y: t.y[0], sx: t.x[1], sy: t.y[1] };
164
+ }
165
+ meter(t) {
166
+ return this.ci.meter[t];
167
+ }
168
+ /** Money: Game-Maker's counter 3, which shows meter - 1. */
169
+ get money() {
170
+ return this.ci.meter[3] - 1;
171
+ }
172
+ /** The block number at (x, y) as it is now (a block taken or pushed is a new block number). */
173
+ blockAt(t, e) {
174
+ return this.w.map[e * F + t];
175
+ }
176
+ /** The block's attributes as shown now (timed blocks change by themselves). */
177
+ shown(t, e) {
178
+ return G(this.w, this.w.blkmap[this.blockAt(t, e)] ?? 0);
179
+ }
180
+ solidAt(t, e) {
181
+ return this.shown(t, e).solid !== 0;
182
+ }
183
+ snapshot() {
184
+ return {
185
+ w: T(this.w),
186
+ rand: this.rand.a,
187
+ scene: this.scene,
188
+ prev: this.prev,
189
+ link: this.link,
190
+ ticks: this.ticks,
191
+ clock: this.clock,
192
+ lastKeyTick: this.lastKeyTick,
193
+ held: [...this.held],
194
+ pending: [...this.pending],
195
+ doScroll: this.doScroll,
196
+ ended: this.ended,
197
+ deaths: this.deaths,
198
+ trail: [...this.trail],
199
+ segments: this.segments.map((t) => ({ scene: t.scene, keys: t.keys.slice() })),
200
+ segmentTicks: [...this.segmentTicks],
201
+ clip: this.clip && { ...this.clip }
202
+ };
203
+ }
204
+ restore(t) {
205
+ this.rand = new p(t.rand);
206
+ const e = T(t.w);
207
+ e.rand = this.rand.next, this.w = e, this.ci = e.ci, this.scene = t.scene, this.prev = t.prev, this.link = t.link, this.ticks = t.ticks, this.clock = t.clock, this.lastKeyTick = t.lastKeyTick, this.held = new Set(t.held), this.pending = [...t.pending], this.doScroll = t.doScroll, this.ended = t.ended, this.deaths = t.deaths, this.trail = [...t.trail], this.segments = t.segments.map((o) => ({ scene: o.scene, keys: o.keys.slice() })), this.segmentTicks = [...t.segmentTicks], this.clip = t.clip && { ...t.clip };
208
+ }
209
+ }
210
+ function T(n) {
211
+ const { sound: t, rand: e, joystick: o, bg: a, mblk: r, cblk: l, mon: h, palette: i, chr: s, ...c } = n;
212
+ return { ...u(c), sound: t, rand: e, joystick: o, bg: a, mblk: r, cblk: l, mon: h, palette: i, chr: { ...s, x: [...s.x], y: [...s.y] } };
213
+ }
214
+ function u(n) {
215
+ if (n === null || typeof n != "object") return n;
216
+ if (ArrayBuffer.isView(n)) return n.slice();
217
+ if (Array.isArray(n)) return n.map(u);
218
+ const t = {};
219
+ for (const e in n) t[e] = u(n[e]);
220
+ return t;
221
+ }
222
+ function yt(n) {
223
+ const t = v(n);
224
+ return class extends Z {
225
+ constructor(e = Y(t), o = 1) {
226
+ super(e, o);
227
+ }
228
+ };
229
+ }
230
+ export {
231
+ Z as GameSim,
232
+ Q as TICKS_PER_SECOND,
233
+ yt as diskSim,
234
+ Y as readDisk
235
+ };
@@ -0,0 +1,142 @@
1
+ class d {
2
+ constructor(s, t) {
3
+ this.sim = s, this.scan = t.scan, this.origin = t.origin, this.meterShown = t.meter, this.traced = t.traced ?? [72, 75, 77, 80, 16, 18];
4
+ }
5
+ sim;
6
+ log = [];
7
+ scan;
8
+ origin;
9
+ meterShown;
10
+ traced;
11
+ snapshot() {
12
+ return { sim: this.sim.snapshot(), log: this.log.length };
13
+ }
14
+ restore(s) {
15
+ this.sim.restore(s.sim), this.log.length = s.log;
16
+ }
17
+ /** The hero's position in level pixels (left/top of its bottom block). */
18
+ get px() {
19
+ const s = this.sim.pos;
20
+ return { x: (s.x - this.origin) * 20 + s.sx, y: (s.y - this.origin) * 20 + s.sy };
21
+ }
22
+ /** Its block (level coordinates), rounded to the nearest. */
23
+ get at() {
24
+ const s = this.px;
25
+ return { x: Math.round(s.x / 20), y: Math.round(s.y / 20) };
26
+ }
27
+ say(s) {
28
+ const t = this.px;
29
+ this.log.push(`[${this.sim.scene}] ${s} @${(t.x / 20).toFixed(2)},${(t.y / 20).toFixed(2)} t${this.sim.ticks} hp ${this.sim.ci.hitpts}`);
30
+ }
31
+ async tick() {
32
+ const s = this.sim.ci.hitpts, t = await this.sim.tick() ?? void 0;
33
+ if (process.env.BOT_HURT && this.sim.ci.hitpts < s) {
34
+ const e = this.sim.w, h = e.mi.map((r, o) => ({ mi: r, n: e.mm[o].monnum })).filter(({ mi: r, n: o }) => o < 64 && Math.abs(r.curx[0] - this.sim.pos.x) <= 1 && Math.abs(r.cury[0] - this.sim.pos.y) <= 1), i = this.meterShown;
35
+ this.say(
36
+ ` hurt ${s - this.sim.ci.hitpts}: monsters ${h.map((r) => r.n).join(",")} block ${this.sim.blockAt(this.sim.pos.x + 1, this.sim.pos.y)} ${i.name} ${this.sim.meter(i.n) - 1}`
37
+ );
38
+ }
39
+ return process.env.BOT_TRACE && this.say(` keys ${this.traced.filter((e) => this.sim.isHeld(e)).join(",")} gy ${this.sim.ci.gravy}${t ? " " + t : ""}`), t;
40
+ }
41
+ async wait(s) {
42
+ for (let t = 0; t < s; t++) {
43
+ const e = await this.tick();
44
+ if (e) return e;
45
+ }
46
+ }
47
+ /** Waits until the hero stops moving up or down (standing, or holding on to something). */
48
+ async land(s = 80) {
49
+ let t = this.px.y, e = 0;
50
+ for (let h = 0; h < s; h++) {
51
+ const i = await this.tick();
52
+ if (i) return i;
53
+ const r = this.px.y;
54
+ if (e = r === t ? e + 1 : 0, t = r, e >= 3) return;
55
+ }
56
+ return "never landed";
57
+ }
58
+ /** Before each step of a walk: a game's chance to stop for something (keys still held). */
59
+ async beforeStep(s) {
60
+ }
61
+ /** Before a hop (and before a walk hops what blocks it, `dir` still held): a game's chance to wait. */
62
+ async beforeHop(s) {
63
+ }
64
+ /** Walks to level column `bx` (its left edge at bx * 20 px), hopping over whatever stops it. */
65
+ async walk(s, t = "", e = 900) {
66
+ const h = this.scan, i = s * 20, r = i > this.px.x ? "right" : "left", o = () => r === "right" ? this.px.x >= i : this.px.x <= i;
67
+ this.sim.press(h[r]);
68
+ let n = 0, a = this.px.x;
69
+ for (let l = 0; l < e && !o(); l++) {
70
+ const p = await this.beforeStep(r);
71
+ if (p) return this.sim.releaseAll(), p;
72
+ const m = await this.tick();
73
+ if (m) return this.sim.releaseAll(), m;
74
+ if (n = this.px.x === a ? n + 1 : 0, a = this.px.x, n >= 3) {
75
+ const x = await this.beforeHop(r);
76
+ if (x) return this.sim.releaseAll(), x;
77
+ this.sim.press(h.up);
78
+ const f = await this.wait(10);
79
+ if (this.sim.release(h.up), f) return this.sim.releaseAll(), f;
80
+ n = -12;
81
+ }
82
+ }
83
+ this.sim.release(h[r]);
84
+ const c = await this.land();
85
+ if (c) return c;
86
+ if (Math.abs(this.px.x - i) > 3) {
87
+ const l = this.px.x > i ? "left" : "right";
88
+ this.sim.press(h[l]);
89
+ for (let p = 0; p < 10 && Math.abs(this.px.x - i) > 2; p++) {
90
+ const m = await this.tick();
91
+ if (m) return this.sim.releaseAll(), m;
92
+ }
93
+ this.sim.release(h[l]);
94
+ }
95
+ return Math.abs(this.px.x - i) > 6 ? `walk to ${s} ${t} stopped at ${(this.px.x / 20).toFixed(2)}` : (t && this.say(`at ${t}`), this.land());
96
+ }
97
+ /**
98
+ * A hop: Up (or Q/E sideways) held `hold` ticks; then `steer` held while it falls, until it
99
+ * lands or has gone `steerTo` columns (level block x).
100
+ */
101
+ async hop(s, t = 12, e, h) {
102
+ const i = this.scan, r = s === "up" ? i.up : s === "left" ? i.q : i.e, o = await this.beforeHop();
103
+ if (o) return o;
104
+ this.sim.press(r);
105
+ let n = await this.wait(t);
106
+ if (this.sim.release(r), n) return this.sim.releaseAll(), n;
107
+ if (e) {
108
+ this.sim.press(i[e]);
109
+ const a = h !== void 0 ? h * 20 : void 0;
110
+ for (let c = 0; c < 60 && !(a !== void 0 && (e === "right" ? this.px.x >= a : this.px.x <= a)); c++)
111
+ if (n = await this.tick(), n) return this.sim.releaseAll(), n;
112
+ this.sim.release(i[e]);
113
+ }
114
+ return n = await this.land(), this.say(`hopped ${s}${e ? " then " + e : ""}`), n;
115
+ }
116
+ /** Holds a key for `ticks` (Down to climb down, Up to climb up, Space for the game's action). */
117
+ async hold(s, t) {
118
+ const e = this.scan[s];
119
+ this.sim.press(e);
120
+ const h = await this.wait(t);
121
+ return this.sim.release(e), h;
122
+ }
123
+ /** Climbs (or walks) along one axis until the hero reaches level column/row `to`. */
124
+ async dig(s, t, e = "", h = 400) {
125
+ const i = t * 20, r = () => s === "left" ? this.px.x <= i : s === "right" ? this.px.x >= i : s === "up" ? this.px.y <= i : this.px.y >= i;
126
+ return this.until(s, r, e || `dug ${s} to ${t}`, h);
127
+ }
128
+ /** Holds `key` until `check` says so (or `max` ticks: then it fails). */
129
+ async until(s, t, e, h = 300) {
130
+ const i = s === void 0 ? void 0 : this.scan[s];
131
+ i !== void 0 && this.sim.press(i);
132
+ for (let r = 0; r < h && !t(); r++) {
133
+ const o = await this.tick();
134
+ if (o) return this.sim.releaseAll(), o;
135
+ }
136
+ if (i !== void 0 && this.sim.release(i), !t()) return `never ${e}`;
137
+ this.say(e);
138
+ }
139
+ }
140
+ export {
141
+ d as HopBot
142
+ };