@waica/cli 0.3.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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/dist/cli.js +184 -0
  4. package/dist/editor/assets/abap-08VXUWAP.js +1 -0
  5. package/dist/editor/assets/apex-BWPQTe0t.js +1 -0
  6. package/dist/editor/assets/azcli-Bc_sGQ0U.js +1 -0
  7. package/dist/editor/assets/bat-i0X4ZdIN.js +1 -0
  8. package/dist/editor/assets/bicep-B5-_aFwp.js +2 -0
  9. package/dist/editor/assets/cameligo-DMUM7wLl.js +1 -0
  10. package/dist/editor/assets/clojure-Cm7r79vr.js +1 -0
  11. package/dist/editor/assets/codicon-DCmgc-ay.ttf +0 -0
  12. package/dist/editor/assets/coffee-Ba7i2nA0.js +1 -0
  13. package/dist/editor/assets/cpp-C7h46wYY.js +1 -0
  14. package/dist/editor/assets/csharp-BKxtCVv1.js +1 -0
  15. package/dist/editor/assets/csp-bTuwJoIa.js +1 -0
  16. package/dist/editor/assets/css-DIMkf-bt.js +3 -0
  17. package/dist/editor/assets/css.worker-Fwwr_HdT.js +88 -0
  18. package/dist/editor/assets/cssMode-CQE-WdwL.js +4 -0
  19. package/dist/editor/assets/cypher-CVaqCwHa.js +1 -0
  20. package/dist/editor/assets/dart-onAF5SnQ.js +1 -0
  21. package/dist/editor/assets/dockerfile-DZFCIeNp.js +1 -0
  22. package/dist/editor/assets/ecl-D05T4iGw.js +1 -0
  23. package/dist/editor/assets/editor.worker-DsBDrWS5.js +12 -0
  24. package/dist/editor/assets/elixir-6RTg0lbw.js +1 -0
  25. package/dist/editor/assets/flow9-C5_-GSwl.js +1 -0
  26. package/dist/editor/assets/freemarker2-CYgdVabn.js +3 -0
  27. package/dist/editor/assets/fsharp-C8Ef5oNN.js +1 -0
  28. package/dist/editor/assets/go-C-y9NEjX.js +1 -0
  29. package/dist/editor/assets/graphql-fmXr3nnJ.js +1 -0
  30. package/dist/editor/assets/handlebars-DdeORCJD.js +1 -0
  31. package/dist/editor/assets/hcl-CpzslTdj.js +1 -0
  32. package/dist/editor/assets/html-DTIAGV84.js +1 -0
  33. package/dist/editor/assets/html.worker-B3Ztmmz2.js +495 -0
  34. package/dist/editor/assets/htmlMode-CRV3iBih.js +4 -0
  35. package/dist/editor/assets/index-Btzc6zxr.css +1 -0
  36. package/dist/editor/assets/index-Dhkqk31v.js +11016 -0
  37. package/dist/editor/assets/ini-sBoK_t0W.js +1 -0
  38. package/dist/editor/assets/java-BEtHBSE6.js +1 -0
  39. package/dist/editor/assets/javascript-dxBW86cl.js +1 -0
  40. package/dist/editor/assets/json.worker-PWQxM7ZO.js +51 -0
  41. package/dist/editor/assets/jsonMode-DFA5-FFK.js +10 -0
  42. package/dist/editor/assets/julia-Bri6UV-V.js +1 -0
  43. package/dist/editor/assets/kotlin-B0qVs-E9.js +1 -0
  44. package/dist/editor/assets/less-B9JPFI3C.js +2 -0
  45. package/dist/editor/assets/lexon-CfSJPG6W.js +1 -0
  46. package/dist/editor/assets/liquid-CScWaXYV.js +1 -0
  47. package/dist/editor/assets/lua-CsQS60Ue.js +1 -0
  48. package/dist/editor/assets/m3-D-oSqn_W.js +1 -0
  49. package/dist/editor/assets/markdown-Cimd5fb3.js +1 -0
  50. package/dist/editor/assets/mdx-Bvp84SqA.js +1 -0
  51. package/dist/editor/assets/mips-CIPQ_RoX.js +1 -0
  52. package/dist/editor/assets/msdax-DauUninz.js +1 -0
  53. package/dist/editor/assets/mysql-SOo6toE5.js +1 -0
  54. package/dist/editor/assets/objective-c-FvmIjYaQ.js +1 -0
  55. package/dist/editor/assets/pascal-DrH0SRf2.js +1 -0
  56. package/dist/editor/assets/pascaligo-D-ptJ9y-.js +1 -0
  57. package/dist/editor/assets/perl-oz_6vUea.js +1 -0
  58. package/dist/editor/assets/pgsql-DTj74zXo.js +1 -0
  59. package/dist/editor/assets/php-nr791fC2.js +1 -0
  60. package/dist/editor/assets/pla-CopQ2nXW.js +1 -0
  61. package/dist/editor/assets/postiats-43DmfD33.js +1 -0
  62. package/dist/editor/assets/powerquery-D3hlyOfw.js +1 -0
  63. package/dist/editor/assets/powershell-DmHpPYUd.js +1 -0
  64. package/dist/editor/assets/protobuf-C531GsRP.js +2 -0
  65. package/dist/editor/assets/pug-Z5eAx3Zn.js +1 -0
  66. package/dist/editor/assets/python-CG-OtvFa.js +1 -0
  67. package/dist/editor/assets/qsharp-DkqhCAOL.js +1 -0
  68. package/dist/editor/assets/r-BwWrilGY.js +1 -0
  69. package/dist/editor/assets/razor-r-iUEZPj.js +1 -0
  70. package/dist/editor/assets/redis-ClamHrr6.js +1 -0
  71. package/dist/editor/assets/redshift-DT7zqm-g.js +1 -0
  72. package/dist/editor/assets/restructuredtext-BYgofb2h.js +1 -0
  73. package/dist/editor/assets/ruby-DezsRK8O.js +1 -0
  74. package/dist/editor/assets/rust-DdL9SqIa.js +1 -0
  75. package/dist/editor/assets/sb-CcwsVR0C.js +1 -0
  76. package/dist/editor/assets/scala-DHpiXF5c.js +1 -0
  77. package/dist/editor/assets/scheme-BeGwcela.js +1 -0
  78. package/dist/editor/assets/scss-gp-XZpBa.js +3 -0
  79. package/dist/editor/assets/shell-CC2rA5mh.js +1 -0
  80. package/dist/editor/assets/solidity-BEEn4gHE.js +1 -0
  81. package/dist/editor/assets/sophia-CRfGWb83.js +1 -0
  82. package/dist/editor/assets/sparql-D_Lu-MrJ.js +1 -0
  83. package/dist/editor/assets/sql-NEE52Syq.js +1 -0
  84. package/dist/editor/assets/st-DbInun42.js +1 -0
  85. package/dist/editor/assets/swift-Bxkupp3x.js +1 -0
  86. package/dist/editor/assets/systemverilog-Bz4Y3fRF.js +1 -0
  87. package/dist/editor/assets/tcl-DISqw1ZD.js +1 -0
  88. package/dist/editor/assets/ts.worker-0mEyHVru.js +51339 -0
  89. package/dist/editor/assets/tsMode-Bth1rJwM.js +11 -0
  90. package/dist/editor/assets/twig-De2hgUGE.js +1 -0
  91. package/dist/editor/assets/typescript-tNRuHsoe.js +1 -0
  92. package/dist/editor/assets/typespec-B8J7ngcE.js +1 -0
  93. package/dist/editor/assets/vb-DV3o63ZY.js +1 -0
  94. package/dist/editor/assets/wgsl-DpFanUEy.js +298 -0
  95. package/dist/editor/assets/xml-BCx9e6CK.js +1 -0
  96. package/dist/editor/assets/yaml-Bb-peGP_.js +1 -0
  97. package/dist/editor/index.html +14 -0
  98. package/dist/mcp/archetypes.js +103 -0
  99. package/dist/mcp/cli.js +6 -0
  100. package/dist/mcp/create-project.js +151 -0
  101. package/dist/mcp/introspection.js +201 -0
  102. package/dist/mcp/node_modules/@waica/archetype-platformer/assets/waica-coin.png +0 -0
  103. package/dist/mcp/node_modules/@waica/archetype-platformer/assets/waica-dog.png +0 -0
  104. package/dist/mcp/node_modules/@waica/archetype-platformer/assets/waica-slime.png +0 -0
  105. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/art.d.ts +10 -0
  106. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/art.js +13 -0
  107. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/bundle.d.ts +3 -0
  108. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/bundle.js +10 -0
  109. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/controls.d.ts +5 -0
  110. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/controls.js +12 -0
  111. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/index.d.ts +38 -0
  112. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/index.js +34 -0
  113. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/manifest.d.ts +15 -0
  114. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/manifest.js +26 -0
  115. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/prefabs.d.ts +7 -0
  116. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/prefabs.js +97 -0
  117. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry-data.d.ts +5 -0
  118. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry-data.js +45 -0
  119. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry.d.ts +6 -0
  120. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry.js +15 -0
  121. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/scene-default.d.ts +40 -0
  122. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/scene-default.js +93 -0
  123. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/ui.d.ts +10 -0
  124. package/dist/mcp/node_modules/@waica/archetype-platformer/dist/ui.js +24 -0
  125. package/dist/mcp/node_modules/@waica/archetype-platformer/package.json +30 -0
  126. package/dist/mcp/node_modules/@waica/behaviors/dist/chaser.d.ts +59 -0
  127. package/dist/mcp/node_modules/@waica/behaviors/dist/chaser.js +127 -0
  128. package/dist/mcp/node_modules/@waica/behaviors/dist/collectible.d.ts +25 -0
  129. package/dist/mcp/node_modules/@waica/behaviors/dist/collectible.js +27 -0
  130. package/dist/mcp/node_modules/@waica/behaviors/dist/hazard.d.ts +29 -0
  131. package/dist/mcp/node_modules/@waica/behaviors/dist/hazard.js +42 -0
  132. package/dist/mcp/node_modules/@waica/behaviors/dist/index.d.ts +9 -0
  133. package/dist/mcp/node_modules/@waica/behaviors/dist/index.js +9 -0
  134. package/dist/mcp/node_modules/@waica/behaviors/dist/lifetime.d.ts +16 -0
  135. package/dist/mcp/node_modules/@waica/behaviors/dist/lifetime.js +19 -0
  136. package/dist/mcp/node_modules/@waica/behaviors/dist/npc.d.ts +4 -0
  137. package/dist/mcp/node_modules/@waica/behaviors/dist/npc.js +14 -0
  138. package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.d.ts +43 -0
  139. package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.js +68 -0
  140. package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.d.ts +124 -0
  141. package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.js +146 -0
  142. package/dist/mcp/node_modules/@waica/behaviors/dist/player-identity.d.ts +3 -0
  143. package/dist/mcp/node_modules/@waica/behaviors/dist/player-identity.js +5 -0
  144. package/dist/mcp/node_modules/@waica/behaviors/dist/player-states.d.ts +18 -0
  145. package/dist/mcp/node_modules/@waica/behaviors/dist/player-states.js +95 -0
  146. package/dist/mcp/node_modules/@waica/behaviors/dist/respawnable.d.ts +23 -0
  147. package/dist/mcp/node_modules/@waica/behaviors/dist/respawnable.js +27 -0
  148. package/dist/mcp/node_modules/@waica/behaviors/package.json +24 -0
  149. package/dist/mcp/node_modules/@waica/engine/dist/aabb.d.ts +2 -0
  150. package/dist/mcp/node_modules/@waica/engine/dist/aabb.js +4 -0
  151. package/dist/mcp/node_modules/@waica/engine/dist/animation/clip-player.d.ts +20 -0
  152. package/dist/mcp/node_modules/@waica/engine/dist/animation/clip-player.js +24 -0
  153. package/dist/mcp/node_modules/@waica/engine/dist/animation/contract.d.ts +21 -0
  154. package/dist/mcp/node_modules/@waica/engine/dist/animation/contract.js +23 -0
  155. package/dist/mcp/node_modules/@waica/engine/dist/animation/sheet.d.ts +65 -0
  156. package/dist/mcp/node_modules/@waica/engine/dist/animation/sheet.js +44 -0
  157. package/dist/mcp/node_modules/@waica/engine/dist/archetype.d.ts +37 -0
  158. package/dist/mcp/node_modules/@waica/engine/dist/archetype.js +1 -0
  159. package/dist/mcp/node_modules/@waica/engine/dist/camera.d.ts +71 -0
  160. package/dist/mcp/node_modules/@waica/engine/dist/camera.js +59 -0
  161. package/dist/mcp/node_modules/@waica/engine/dist/collision-shape.d.ts +26 -0
  162. package/dist/mcp/node_modules/@waica/engine/dist/collision-shape.js +136 -0
  163. package/dist/mcp/node_modules/@waica/engine/dist/component-registry.d.ts +16 -0
  164. package/dist/mcp/node_modules/@waica/engine/dist/component-registry.js +44 -0
  165. package/dist/mcp/node_modules/@waica/engine/dist/component.d.ts +58 -0
  166. package/dist/mcp/node_modules/@waica/engine/dist/component.js +11 -0
  167. package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.d.ts +80 -0
  168. package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.js +210 -0
  169. package/dist/mcp/node_modules/@waica/engine/dist/components/dynamic-body.d.ts +68 -0
  170. package/dist/mcp/node_modules/@waica/engine/dist/components/dynamic-body.js +189 -0
  171. package/dist/mcp/node_modules/@waica/engine/dist/components/hitbox.d.ts +25 -0
  172. package/dist/mcp/node_modules/@waica/engine/dist/components/hitbox.js +21 -0
  173. package/dist/mcp/node_modules/@waica/engine/dist/components/solid.d.ts +32 -0
  174. package/dist/mcp/node_modules/@waica/engine/dist/components/solid.js +45 -0
  175. package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.d.ts +51 -0
  176. package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.js +112 -0
  177. package/dist/mcp/node_modules/@waica/engine/dist/entity.d.ts +22 -0
  178. package/dist/mcp/node_modules/@waica/engine/dist/entity.js +52 -0
  179. package/dist/mcp/node_modules/@waica/engine/dist/events.d.ts +8 -0
  180. package/dist/mcp/node_modules/@waica/engine/dist/events.js +20 -0
  181. package/dist/mcp/node_modules/@waica/engine/dist/game.d.ts +95 -0
  182. package/dist/mcp/node_modules/@waica/engine/dist/game.js +263 -0
  183. package/dist/mcp/node_modules/@waica/engine/dist/index.d.ts +39 -0
  184. package/dist/mcp/node_modules/@waica/engine/dist/index.js +26 -0
  185. package/dist/mcp/node_modules/@waica/engine/dist/input.d.ts +40 -0
  186. package/dist/mcp/node_modules/@waica/engine/dist/input.js +84 -0
  187. package/dist/mcp/node_modules/@waica/engine/dist/scene.d.ts +80 -0
  188. package/dist/mcp/node_modules/@waica/engine/dist/scene.js +93 -0
  189. package/dist/mcp/node_modules/@waica/engine/dist/solid-axis.d.ts +21 -0
  190. package/dist/mcp/node_modules/@waica/engine/dist/solid-axis.js +77 -0
  191. package/dist/mcp/node_modules/@waica/engine/dist/state/hooks.d.ts +90 -0
  192. package/dist/mcp/node_modules/@waica/engine/dist/state/hooks.js +81 -0
  193. package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.d.ts +83 -0
  194. package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.js +173 -0
  195. package/dist/mcp/node_modules/@waica/engine/dist/stats.d.ts +27 -0
  196. package/dist/mcp/node_modules/@waica/engine/dist/stats.js +54 -0
  197. package/dist/mcp/node_modules/@waica/engine/dist/ui.d.ts +47 -0
  198. package/dist/mcp/node_modules/@waica/engine/dist/ui.js +175 -0
  199. package/dist/mcp/node_modules/@waica/engine/package.json +25 -0
  200. package/dist/mcp/package-resolver.js +240 -0
  201. package/dist/mcp/project-path.js +86 -0
  202. package/dist/mcp/scaffolds.js +153 -0
  203. package/dist/mcp/server.js +217 -0
  204. package/dist/mcp/stdio.js +13 -0
  205. package/dist/mcp/template/README.md +22 -0
  206. package/dist/mcp/template/_gitignore +5 -0
  207. package/dist/mcp/template/index.html +25 -0
  208. package/dist/mcp/template/package.json.tpl +20 -0
  209. package/dist/mcp/template/public/waica.params.json +1 -0
  210. package/dist/mcp/template/src/art/waica-coin.png +0 -0
  211. package/dist/mcp/template/src/art/waica-dog.png +0 -0
  212. package/dist/mcp/template/src/art/waica-slime.png +0 -0
  213. package/dist/mcp/template/src/characters/player.character.json +133 -0
  214. package/dist/mcp/template/src/characters/slime.character.json +61 -0
  215. package/dist/mcp/template/src/controls.json +8 -0
  216. package/dist/mcp/template/src/game.json +10 -0
  217. package/dist/mcp/template/src/main.ts +105 -0
  218. package/dist/mcp/template/src/objects/coin.object.json +41 -0
  219. package/dist/mcp/template/src/scenes/main.scene.json +217 -0
  220. package/dist/mcp/template/src/stats.json +7 -0
  221. package/dist/mcp/template/src/tiles/block.tile.json +21 -0
  222. package/dist/mcp/template/src/tiles/decor.tile.json +14 -0
  223. package/dist/mcp/template/src/tiles/platform.tile.json +21 -0
  224. package/dist/mcp/template/src/ui/coin-counter.html +12 -0
  225. package/dist/mcp/template/tsconfig.json +15 -0
  226. package/dist/mcp/template/vite.config.ts +3 -0
  227. package/dist/mcp/validation.js +323 -0
  228. package/dist/mcp/workspace-loader.js +14 -0
  229. package/dist/mcp/workspace-runtime.js +109 -0
  230. package/dist/server.js +210 -0
  231. package/package.json +38 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Resolves which clip to play: the requested one if it exists, otherwise
3
+ * it follows the fallback chain, and as a last resort the first available
4
+ * clip.
5
+ */
6
+ export function resolveClip(contract, available, wanted) {
7
+ const set = new Set(available);
8
+ const seen = new Set();
9
+ let current = wanted;
10
+ while (current && !seen.has(current)) {
11
+ if (set.has(current))
12
+ return current;
13
+ seen.add(current);
14
+ current = contract.fallbacks[current];
15
+ }
16
+ const [first] = set;
17
+ return first;
18
+ }
19
+ /** Which contract clips are missing — what the editor will show as gaps. */
20
+ export function missingClips(contract, available) {
21
+ const set = new Set(available);
22
+ return contract.required.filter((clip) => !set.has(clip));
23
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Spritesheet slicing geometry shared by the runtime (UV math) and the
3
+ * editor (grid overlay + previews). A sheet is a cols×rows grid of cells;
4
+ * the optional params handle sheets whose frames don't fill the image:
5
+ * a margin before the first cell, gaps between cells, or an explicit
6
+ * cell size. All values are in source-image pixels and may be fractional
7
+ * (downscaled exports often land on half pixels).
8
+ */
9
+ export interface SheetGridParams {
10
+ /** Top-left corner of the first cell. */
11
+ gridOffsetX?: number;
12
+ gridOffsetY?: number;
13
+ /** Gap between adjacent cells. */
14
+ spacingX?: number;
15
+ spacingY?: number;
16
+ /** Explicit cell size; unset/0 = split what the offset leaves evenly. */
17
+ cellWidth?: number;
18
+ cellHeight?: number;
19
+ }
20
+ /** One cell's pixel rect within the sheet image. */
21
+ export interface SheetCell {
22
+ x: number;
23
+ y: number;
24
+ width: number;
25
+ height: number;
26
+ }
27
+ /**
28
+ * One spritesheet: its image plus how it slices into frames — a uniform
29
+ * cols×rows grid, or explicit per-frame `cells` (auto-detected or hand-drawn)
30
+ * for packed sheets whose frames don't sit on a grid. When `cells` is present
31
+ * it wins over the grid.
32
+ */
33
+ export interface SheetDef extends SheetGridParams {
34
+ texture: string;
35
+ cols: number;
36
+ rows: number;
37
+ cells?: SheetCell[];
38
+ }
39
+ /** How many frames a sheet yields: its cell count, else the grid, at least 1. */
40
+ export declare function sheetFrameCount(sheet: {
41
+ cols: number;
42
+ rows: number;
43
+ cells?: SheetCell[];
44
+ }): number;
45
+ /**
46
+ * Maps a global frame index to its sheet and the frame within it. Sheets
47
+ * are numbered consecutively: sheet 0 owns frames 0..n0-1, sheet 1 the next
48
+ * n1, and so on. Out-of-range indices clamp to the nearest valid frame.
49
+ */
50
+ export declare function locateFrame(sheets: readonly {
51
+ cols: number;
52
+ rows: number;
53
+ cells?: SheetCell[];
54
+ }[], frame: number): {
55
+ sheet: number;
56
+ frame: number;
57
+ };
58
+ /**
59
+ * The pixel rect of `frame` (row-major index) in a sheet image. Explicit
60
+ * `cells` win when present (out-of-range indices clamp); otherwise the grid:
61
+ * with no params, the image divided evenly into cols×rows.
62
+ */
63
+ export declare function sheetCell(imageWidth: number, imageHeight: number, cols: number, rows: number, frame: number, params?: SheetGridParams & {
64
+ cells?: SheetCell[];
65
+ }): SheetCell;
@@ -0,0 +1,44 @@
1
+ /** How many frames a sheet yields: its cell count, else the grid, at least 1. */
2
+ export function sheetFrameCount(sheet) {
3
+ if (sheet.cells?.length)
4
+ return sheet.cells.length;
5
+ return Math.max(1, Math.floor(sheet.cols)) * Math.max(1, Math.floor(sheet.rows));
6
+ }
7
+ /**
8
+ * Maps a global frame index to its sheet and the frame within it. Sheets
9
+ * are numbered consecutively: sheet 0 owns frames 0..n0-1, sheet 1 the next
10
+ * n1, and so on. Out-of-range indices clamp to the nearest valid frame.
11
+ */
12
+ export function locateFrame(sheets, frame) {
13
+ let rest = Math.max(0, Math.floor(frame));
14
+ for (let i = 0; i < sheets.length; i++) {
15
+ const count = sheetFrameCount(sheets[i]);
16
+ if (rest < count || i === sheets.length - 1)
17
+ return { sheet: i, frame: Math.min(rest, count - 1) };
18
+ rest -= count;
19
+ }
20
+ return { sheet: 0, frame: 0 };
21
+ }
22
+ const positive = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
23
+ /**
24
+ * The pixel rect of `frame` (row-major index) in a sheet image. Explicit
25
+ * `cells` win when present (out-of-range indices clamp); otherwise the grid:
26
+ * with no params, the image divided evenly into cols×rows.
27
+ */
28
+ export function sheetCell(imageWidth, imageHeight, cols, rows, frame, params = {}) {
29
+ if (params.cells?.length) {
30
+ const index = Math.min(Math.max(0, Math.floor(frame)), params.cells.length - 1);
31
+ return params.cells[index];
32
+ }
33
+ const c = Math.max(1, Math.floor(cols));
34
+ const r = Math.max(1, Math.floor(rows));
35
+ const ox = positive(params.gridOffsetX);
36
+ const oy = positive(params.gridOffsetY);
37
+ const sx = positive(params.spacingX);
38
+ const sy = positive(params.spacingY);
39
+ const width = positive(params.cellWidth) || Math.max(1, (imageWidth - ox - sx * (c - 1)) / c);
40
+ const height = positive(params.cellHeight) || Math.max(1, (imageHeight - oy - sy * (r - 1)) / r);
41
+ const col = frame % c;
42
+ const row = Math.floor(frame / c);
43
+ return { x: ox + col * (width + sx), y: oy + row * (height + sy), width, height };
44
+ }
@@ -0,0 +1,37 @@
1
+ import type { InputBindings } from './input.js';
2
+ import type { PrefabJson, SceneEntityJson, SceneJson, SceneRegistry } from './scene.js';
3
+ import type { ArchetypeBundle } from './state/hooks.js';
4
+ /** One entity template exposed in an archetype's editor palette. */
5
+ export interface EntityTemplate {
6
+ label: string;
7
+ icon: string;
8
+ category: PrefabJson['type'];
9
+ /** Builds the JSON for a new instance (no position; the editor sets it). */
10
+ make: () => SceneEntityJson;
11
+ }
12
+ /** One stock-art file shipped by an archetype package. */
13
+ export interface ArchetypeArt {
14
+ /** File name under the archetype's assets/ and a demo project's src/art/. */
15
+ file: string;
16
+ /** Registry URI resolved by the archetype at runtime. */
17
+ uri: string;
18
+ }
19
+ /** The conventional contract exported by every archetype package. */
20
+ export interface ArchetypeManifest {
21
+ id: string;
22
+ label: string;
23
+ scene: SceneJson;
24
+ blankScene: SceneJson;
25
+ registry: SceneRegistry;
26
+ palette: EntityTemplate[];
27
+ prefabs: Record<string, PrefabJson>;
28
+ art: ArchetypeArt[];
29
+ entityIcons: Readonly<Record<string, string>>;
30
+ bindings: Readonly<InputBindings>;
31
+ actionLabels: Readonly<Record<string, string>>;
32
+ bundle: ArchetypeBundle;
33
+ }
34
+ /** Browser manifest enriched with URLs produced by an asset-aware bundler. */
35
+ export interface BrowserArchetypeManifest extends ArchetypeManifest {
36
+ artUrls: Readonly<Record<string, string>>;
37
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The scene camera: a built-in, singular part of every scene — not a
3
+ * component you attach to an entity. The scene JSON carries its framing
4
+ * (position, zoom), an optional follow target and world limits; the Game
5
+ * drives the real THREE camera from it while simulating.
6
+ */
7
+ /** World-space rectangle the camera view may never leave. */
8
+ export interface CameraLimitsJson {
9
+ minX: number;
10
+ maxX: number;
11
+ minY: number;
12
+ maxY: number;
13
+ }
14
+ export interface SceneCameraJson {
15
+ /** Where the camera starts (and stays, without a follow target). */
16
+ position?: [number, number];
17
+ /** Visible world height in units — the camera's zoom. */
18
+ zoom?: number;
19
+ /** Entity name to follow; absent or empty = fixed camera. */
20
+ follow?: string;
21
+ deadzoneWidth?: number;
22
+ deadzoneHeight?: number;
23
+ lookahead?: number;
24
+ smoothing?: number;
25
+ limits?: CameraLimitsJson;
26
+ }
27
+ export interface ResolvedSceneCamera {
28
+ position: [number, number];
29
+ zoom: number;
30
+ follow: string;
31
+ deadzoneWidth: number;
32
+ deadzoneHeight: number;
33
+ lookahead: number;
34
+ smoothing: number;
35
+ limits: CameraLimitsJson | null;
36
+ }
37
+ export declare const CAMERA_DEFAULTS: {
38
+ readonly position: [number, number];
39
+ readonly zoom: 12;
40
+ readonly follow: '';
41
+ readonly deadzoneWidth: 2;
42
+ readonly deadzoneHeight: 2.5;
43
+ readonly lookahead: 1.5;
44
+ readonly smoothing: 6;
45
+ };
46
+ /** Fills a scene's camera block with the engine defaults. */
47
+ export declare function resolveSceneCamera(json?: SceneCameraJson): ResolvedSceneCamera;
48
+ export interface CameraStepInput {
49
+ /** Current camera center. */
50
+ x: number;
51
+ y: number;
52
+ /** Half the visible world extents (from zoom and aspect). */
53
+ halfW: number;
54
+ halfH: number;
55
+ /** Followed entity's position, if the target exists. */
56
+ target: {
57
+ x: number;
58
+ y: number;
59
+ } | null;
60
+ /** Followed entity's horizontal velocity, for lookahead. */
61
+ vx: number;
62
+ dt: number;
63
+ }
64
+ /**
65
+ * One simulation step of the camera: deadzone-follow with lookahead and
66
+ * exponential smoothing, then limits. Pure — returns the next center.
67
+ */
68
+ export declare function stepSceneCamera(cam: ResolvedSceneCamera, input: CameraStepInput): {
69
+ x: number;
70
+ y: number;
71
+ };
@@ -0,0 +1,59 @@
1
+ import * as THREE from 'three';
2
+ export const CAMERA_DEFAULTS = {
3
+ position: [0, 0],
4
+ zoom: 12,
5
+ follow: '',
6
+ deadzoneWidth: 2,
7
+ deadzoneHeight: 2.5,
8
+ lookahead: 1.5,
9
+ smoothing: 6,
10
+ };
11
+ /** Fills a scene's camera block with the engine defaults. */
12
+ export function resolveSceneCamera(json) {
13
+ return {
14
+ position: json?.position ?? CAMERA_DEFAULTS.position,
15
+ zoom: json?.zoom ?? CAMERA_DEFAULTS.zoom,
16
+ follow: json?.follow ?? CAMERA_DEFAULTS.follow,
17
+ deadzoneWidth: json?.deadzoneWidth ?? CAMERA_DEFAULTS.deadzoneWidth,
18
+ deadzoneHeight: json?.deadzoneHeight ?? CAMERA_DEFAULTS.deadzoneHeight,
19
+ lookahead: json?.lookahead ?? CAMERA_DEFAULTS.lookahead,
20
+ smoothing: json?.smoothing ?? CAMERA_DEFAULTS.smoothing,
21
+ limits: json?.limits ?? null,
22
+ };
23
+ }
24
+ /** Clamps a camera center so the view stays inside the limits on one axis. */
25
+ function clampAxis(center, halfView, min, max) {
26
+ // Limits narrower than the view: center the view on them.
27
+ if (max - min <= halfView * 2)
28
+ return (min + max) / 2;
29
+ return Math.min(Math.max(center, min + halfView), max - halfView);
30
+ }
31
+ /**
32
+ * One simulation step of the camera: deadzone-follow with lookahead and
33
+ * exponential smoothing, then limits. Pure — returns the next center.
34
+ */
35
+ export function stepSceneCamera(cam, input) {
36
+ let x = input.x;
37
+ let y = input.y;
38
+ if (input.target) {
39
+ let wantX = x;
40
+ let wantY = y;
41
+ const dx = input.target.x - x;
42
+ const dy = input.target.y - y;
43
+ const halfDzW = cam.deadzoneWidth / 2;
44
+ const halfDzH = cam.deadzoneHeight / 2;
45
+ if (Math.abs(dx) > halfDzW)
46
+ wantX = input.target.x - Math.sign(dx) * halfDzW;
47
+ if (Math.abs(dy) > halfDzH)
48
+ wantY = input.target.y - Math.sign(dy) * halfDzH;
49
+ if (Math.abs(input.vx) > 1)
50
+ wantX += Math.sign(input.vx) * cam.lookahead;
51
+ x = THREE.MathUtils.damp(x, wantX, cam.smoothing, input.dt);
52
+ y = THREE.MathUtils.damp(y, wantY, cam.smoothing, input.dt);
53
+ }
54
+ if (cam.limits) {
55
+ x = clampAxis(x, input.halfW, cam.limits.minX, cam.limits.maxX);
56
+ y = clampAxis(y, input.halfH, cam.limits.minY, cam.limits.maxY);
57
+ }
58
+ return { x, y };
59
+ }
@@ -0,0 +1,26 @@
1
+ export type CollisionShape = 'rectangle' | 'circle' | 'polygon';
2
+ export type CollisionPoint = [number, number];
3
+ export interface CollisionBody {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ shape?: CollisionShape;
9
+ /** Polygon vertices normalized against width/height, centered on the entity. */
10
+ points?: unknown;
11
+ }
12
+ export interface CollisionBounds {
13
+ left: number;
14
+ right: number;
15
+ top: number;
16
+ bottom: number;
17
+ }
18
+ export declare const COLLISION_SHAPES: readonly CollisionShape[];
19
+ export declare const DEFAULT_COLLISION_POLYGON: ReadonlyArray<Readonly<CollisionPoint>>;
20
+ /** Valid serialized points, or a fresh default triangle. */
21
+ export declare function resolveCollisionPoints(value: unknown): CollisionPoint[];
22
+ /** World-space outline used by collision tests and editor guides. */
23
+ export declare function collisionVertices(body: CollisionBody): CollisionPoint[];
24
+ export declare function collisionBounds(body: CollisionBody): CollisionBounds;
25
+ /** Overlap between rectangle, ellipse/circle, and freeform simple polygons. */
26
+ export declare function collisionOverlap(a: CollisionBody, b: CollisionBody): boolean;
@@ -0,0 +1,136 @@
1
+ export const COLLISION_SHAPES = [
2
+ 'rectangle',
3
+ 'circle',
4
+ 'polygon',
5
+ ];
6
+ export const DEFAULT_COLLISION_POLYGON = [
7
+ [-0.5, -0.5],
8
+ [0.5, -0.5],
9
+ [0, 0.5],
10
+ ];
11
+ const CIRCLE_SEGMENTS = 32;
12
+ const EPSILON = 1e-9;
13
+ /** Valid serialized points, or a fresh default triangle. */
14
+ export function resolveCollisionPoints(value) {
15
+ if (Array.isArray(value) && value.length >= 3) {
16
+ const points = [];
17
+ for (const point of value) {
18
+ if (!Array.isArray(point) ||
19
+ point.length < 2 ||
20
+ typeof point[0] !== 'number' ||
21
+ typeof point[1] !== 'number' ||
22
+ !Number.isFinite(point[0]) ||
23
+ !Number.isFinite(point[1])) {
24
+ return DEFAULT_COLLISION_POLYGON.map(([x, y]) => [x, y]);
25
+ }
26
+ points.push([point[0], point[1]]);
27
+ }
28
+ return points;
29
+ }
30
+ return DEFAULT_COLLISION_POLYGON.map(([x, y]) => [x, y]);
31
+ }
32
+ /** World-space outline used by collision tests and editor guides. */
33
+ export function collisionVertices(body) {
34
+ const width = Math.abs(body.width);
35
+ const height = Math.abs(body.height);
36
+ const normalized = body.shape === 'circle'
37
+ ? Array.from({ length: CIRCLE_SEGMENTS }, (_, index) => {
38
+ const angle = (index / CIRCLE_SEGMENTS) * Math.PI * 2;
39
+ return [Math.cos(angle) * 0.5, Math.sin(angle) * 0.5];
40
+ })
41
+ : body.shape === 'polygon'
42
+ ? resolveCollisionPoints(body.points)
43
+ : [
44
+ [-0.5, -0.5],
45
+ [0.5, -0.5],
46
+ [0.5, 0.5],
47
+ [-0.5, 0.5],
48
+ ];
49
+ return normalized.map(([x, y]) => [body.x + x * width, body.y + y * height]);
50
+ }
51
+ export function collisionBounds(body) {
52
+ const vertices = collisionVertices(body);
53
+ let left = Infinity;
54
+ let right = -Infinity;
55
+ let top = -Infinity;
56
+ let bottom = Infinity;
57
+ for (const [x, y] of vertices) {
58
+ left = Math.min(left, x);
59
+ right = Math.max(right, x);
60
+ top = Math.max(top, y);
61
+ bottom = Math.min(bottom, y);
62
+ }
63
+ return { left, right, top, bottom };
64
+ }
65
+ /** Overlap between rectangle, ellipse/circle, and freeform simple polygons. */
66
+ export function collisionOverlap(a, b) {
67
+ const boundsA = collisionBounds(a);
68
+ const boundsB = collisionBounds(b);
69
+ if (boundsA.right <= boundsB.left ||
70
+ boundsA.left >= boundsB.right ||
71
+ boundsA.top <= boundsB.bottom ||
72
+ boundsA.bottom >= boundsB.top) {
73
+ return false;
74
+ }
75
+ const verticesA = collisionVertices(a);
76
+ const verticesB = collisionVertices(b);
77
+ for (let ai = 0; ai < verticesA.length; ai++) {
78
+ const a1 = verticesA[ai];
79
+ const a2 = verticesA[(ai + 1) % verticesA.length];
80
+ for (let bi = 0; bi < verticesB.length; bi++) {
81
+ const b1 = verticesB[bi];
82
+ const b2 = verticesB[(bi + 1) % verticesB.length];
83
+ if (segmentsCross(a1, a2, b1, b2))
84
+ return true;
85
+ }
86
+ }
87
+ return hasInteriorPoint(verticesA, verticesB) || hasInteriorPoint(verticesB, verticesA);
88
+ }
89
+ function hasInteriorPoint(points, polygon) {
90
+ for (let index = 0; index < points.length; index++) {
91
+ const point = points[index];
92
+ if (pointInPolygon(point, polygon))
93
+ return true;
94
+ const next = points[(index + 1) % points.length];
95
+ if (pointInPolygon([(point[0] + next[0]) / 2, (point[1] + next[1]) / 2], polygon)) {
96
+ return true;
97
+ }
98
+ }
99
+ const center = [
100
+ points.reduce((sum, [x]) => sum + x, 0) / points.length,
101
+ points.reduce((sum, [, y]) => sum + y, 0) / points.length,
102
+ ];
103
+ return pointInPolygon(center, polygon);
104
+ }
105
+ function segmentsCross(a1, a2, b1, b2) {
106
+ const ab1 = cross(a1, a2, b1);
107
+ const ab2 = cross(a1, a2, b2);
108
+ const ba1 = cross(b1, b2, a1);
109
+ const ba2 = cross(b1, b2, a2);
110
+ return ab1 * ab2 < -EPSILON && ba1 * ba2 < -EPSILON;
111
+ }
112
+ function cross(a, b, point) {
113
+ return (b[0] - a[0]) * (point[1] - a[1]) - (b[1] - a[1]) * (point[0] - a[0]);
114
+ }
115
+ function pointInPolygon(point, polygon) {
116
+ let inside = false;
117
+ for (let index = 0, previous = polygon.length - 1; index < polygon.length; previous = index++) {
118
+ const a = polygon[index];
119
+ const b = polygon[previous];
120
+ if (pointOnSegment(point, a, b))
121
+ return false;
122
+ const crosses = (a[1] > point[1]) !== (b[1] > point[1]) &&
123
+ point[0] < ((b[0] - a[0]) * (point[1] - a[1])) / (b[1] - a[1]) + a[0];
124
+ if (crosses)
125
+ inside = !inside;
126
+ }
127
+ return inside;
128
+ }
129
+ function pointOnSegment(point, a, b) {
130
+ if (Math.abs(cross(a, b, point)) > EPSILON)
131
+ return false;
132
+ return (point[0] >= Math.min(a[0], b[0]) - EPSILON &&
133
+ point[0] <= Math.max(a[0], b[0]) + EPSILON &&
134
+ point[1] >= Math.min(a[1], b[1]) - EPSILON &&
135
+ point[1] <= Math.max(a[1], b[1]) + EPSILON);
136
+ }
@@ -0,0 +1,16 @@
1
+ import { type ComponentClass } from './component.js';
2
+ import type { SceneRegistry } from './scene.js';
3
+ /** The namespace returned by importing one project code module. */
4
+ export type ComponentModule = Readonly<Record<string, unknown>>;
5
+ /**
6
+ * Finds exported Component subclasses in project modules. Classes are keyed by
7
+ * their stable componentName, not by the export name, so minification and
8
+ * default exports do not change scene JSON.
9
+ */
10
+ export declare function collectModuleComponents(modules: Iterable<ComponentModule>, warn?: (message: string) => void): Record<string, ComponentClass>;
11
+ /**
12
+ * Adds project-owned component classes to a registry. Project code is the
13
+ * extension layer, so it deliberately wins a stable-name collision while
14
+ * making that shadowing visible to the host.
15
+ */
16
+ export declare function mergeRegistryComponents(registry: SceneRegistry, project: Readonly<Record<string, ComponentClass>>, warn?: (message: string) => void): SceneRegistry;
@@ -0,0 +1,44 @@
1
+ import { Component } from './component.js';
2
+ /**
3
+ * Finds exported Component subclasses in project modules. Classes are keyed by
4
+ * their stable componentName, not by the export name, so minification and
5
+ * default exports do not change scene JSON.
6
+ */
7
+ export function collectModuleComponents(modules, warn = console.warn) {
8
+ const components = {};
9
+ for (const module of modules) {
10
+ for (const value of Object.values(module)) {
11
+ if (typeof value !== 'function')
12
+ continue;
13
+ const Class = value;
14
+ if (!(Class.prototype instanceof Component))
15
+ continue;
16
+ // Without its own componentName a class inherits the base's, so nothing
17
+ // could reference it from scene JSON. Silently skipping it looks like
18
+ // the editor lost the file: say so instead.
19
+ if (typeof Class.componentName !== 'string' || Class.componentName === 'Component') {
20
+ warn(`[waica] component class "${Class.name || '(anonymous)'}" declares no ` +
21
+ `static componentName — scenes cannot reference it`);
22
+ continue;
23
+ }
24
+ components[Class.componentName] = Class;
25
+ }
26
+ }
27
+ return components;
28
+ }
29
+ /**
30
+ * Adds project-owned component classes to a registry. Project code is the
31
+ * extension layer, so it deliberately wins a stable-name collision while
32
+ * making that shadowing visible to the host.
33
+ */
34
+ export function mergeRegistryComponents(registry, project, warn = console.warn) {
35
+ for (const name of Object.keys(project)) {
36
+ if (registry.components[name]) {
37
+ warn(`[waica] project component "${name}" shadows registry component "${name}"`);
38
+ }
39
+ }
40
+ return {
41
+ ...registry,
42
+ components: { ...registry.components, ...project },
43
+ };
44
+ }
@@ -0,0 +1,58 @@
1
+ import type { Entity } from './entity.js';
2
+ import type { Game } from './game.js';
3
+ import type { Solid } from './components/solid.js';
4
+ /** Metadata for a parameter editable from the inspector. */
5
+ export interface ParamSpec {
6
+ label?: string;
7
+ min?: number;
8
+ max?: number;
9
+ step?: number;
10
+ /** Allowed values for a string param; rendered as a dropdown. */
11
+ options?: string[];
12
+ }
13
+ export interface ComponentClass<T extends Component = Component> {
14
+ new (): T;
15
+ /**
16
+ * Stable component name (survives minification).
17
+ * Used for `waica.params.json` overrides and the inspector.
18
+ */
19
+ componentName: string;
20
+ /** Friendly name the inspector shows instead of componentName. */
21
+ displayName?: string;
22
+ /** Which properties the inspector exposes, with their ranges. */
23
+ params?: Record<string, ParamSpec>;
24
+ }
25
+ /** Cardinal unit normal pointing away from the contacted Solid surface. */
26
+ export interface ContactNormal {
27
+ readonly x: -1 | 0 | 1;
28
+ readonly y: -1 | 0 | 1;
29
+ }
30
+ /** A physical DynamicBody contact, deliberately separate from Hitbox triggers. */
31
+ export interface SolidContact {
32
+ /** Entity that owns the contacted Solid. */
33
+ readonly entity: Entity;
34
+ readonly solid: Solid;
35
+ readonly axis: 'x' | 'y';
36
+ readonly normal: ContactNormal;
37
+ }
38
+ /**
39
+ * A pluggable piece of an entity. User behaviors and engine ones are the
40
+ * same thing: Component subclasses with public props.
41
+ */
42
+ export declare abstract class Component {
43
+ static componentName: string;
44
+ static displayName?: string;
45
+ static params?: Record<string, ParamSpec>;
46
+ entity: Entity;
47
+ game: Game;
48
+ /** Runs once the component is mounted on its entity. */
49
+ onReady?(): void;
50
+ /** Runs once per frame. */
51
+ onUpdate?(dt: number): void;
52
+ /** Runs when this entity's Hitbox overlaps another one's. */
53
+ onCollide?(other: Entity): void;
54
+ /** Runs when this entity's DynamicBody physically contacts a Solid. */
55
+ onContact?(contact: SolidContact): void;
56
+ /** Runs when the entity is destroyed or the component removed. */
57
+ onDestroy?(): void;
58
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A pluggable piece of an entity. User behaviors and engine ones are the
3
+ * same thing: Component subclasses with public props.
4
+ */
5
+ export class Component {
6
+ static componentName = 'Component';
7
+ static displayName;
8
+ static params;
9
+ entity;
10
+ game;
11
+ }
@@ -0,0 +1,80 @@
1
+ import { Component } from '../component.js';
2
+ import { type ClipDef } from '../animation/clip-player.js';
3
+ import { type SheetCell, type SheetDef } from '../animation/sheet.js';
4
+ /**
5
+ * Sprite animated from one or more spritesheets. The main sheet is the
6
+ * top-level texture/cols/rows (or explicit cells); extraSheets append after
7
+ * it, and clip frames index the sheets consecutively (sheet 0 owns 0..n0-1,
8
+ * sheet 1 the next n1, …). Each instance clones its textures to animate UVs
9
+ * independently. On sheets with explicit cells the frames vary in pixel size,
10
+ * so the quad rescales per frame, anchored bottom-center — width/height size
11
+ * the sheet's largest frame and smaller ones keep their feet planted.
12
+ */
13
+ export declare class AnimatedSprite extends Component {
14
+ static componentName: string;
15
+ static params: {
16
+ offsetX: {
17
+ label: string;
18
+ };
19
+ offsetY: {
20
+ label: string;
21
+ };
22
+ layer: {
23
+ label: string;
24
+ min: number;
25
+ max: number;
26
+ step: number;
27
+ };
28
+ };
29
+ /** Spritesheet URL. */
30
+ texture: string;
31
+ /** Sheet grid dimensions. */
32
+ cols: number;
33
+ rows: number;
34
+ gridOffsetX: number;
35
+ gridOffsetY: number;
36
+ spacingX: number;
37
+ spacingY: number;
38
+ cellWidth: number;
39
+ cellHeight: number;
40
+ /** Explicit frame rects for the main sheet (packed sheets); wins over the grid. */
41
+ cells: SheetCell[];
42
+ /** Additional sheets after the main one; clip frames continue across them. */
43
+ extraSheets: SheetDef[];
44
+ private _width;
45
+ private _height;
46
+ get width(): number;
47
+ set width(value: number);
48
+ get height(): number;
49
+ set height(value: number);
50
+ private _offsetX;
51
+ private _offsetY;
52
+ get offsetX(): number;
53
+ set offsetX(value: number);
54
+ get offsetY(): number;
55
+ set offsetY(value: number);
56
+ pixelArt: boolean;
57
+ private _layer;
58
+ get layer(): number;
59
+ set layer(value: number);
60
+ clips: Record<string, ClipDef>;
61
+ initialClip?: string;
62
+ /** Clip currently playing. */
63
+ current?: string;
64
+ private readonly player;
65
+ private sheets;
66
+ private texs;
67
+ private mesh?;
68
+ private frame;
69
+ private frameScaleX;
70
+ private frameScaleY;
71
+ onReady(): void;
72
+ /** Switches clip; repeating the same name doesn't restart the animation. */
73
+ play(name: string): void;
74
+ onUpdate(dt: number): void;
75
+ onDestroy(): void;
76
+ private showFrame;
77
+ /** Repositions/rescales the quad from size, offsets and the frame scale. */
78
+ private syncQuad;
79
+ private applyFrame;
80
+ }