incanto 0.69.0 → 0.71.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 (147) hide show
  1. package/bin/_behaviors-loader.mjs +22 -0
  2. package/bin/_read-json.mjs +28 -0
  3. package/bin/incanto-assets.mjs +5 -4
  4. package/bin/incanto-check.mjs +58 -31
  5. package/bin/incanto-editor.mjs +128 -5
  6. package/bin/incanto-env.mjs +3 -2
  7. package/bin/incanto-feel.mjs +24 -13
  8. package/bin/incanto-frame.mjs +8 -1
  9. package/bin/incanto-multiplay.mjs +11 -9
  10. package/bin/incanto-new.mjs +105 -10
  11. package/bin/incanto-play.mjs +158 -16
  12. package/bin/incanto-playtest.mjs +66 -23
  13. package/bin/incanto-skills.mjs +14 -2
  14. package/bin/incanto-verify.mjs +121 -102
  15. package/bin/incanto.mjs +3 -2
  16. package/dist/2d.d.ts +296 -34
  17. package/dist/2d.js +4 -4
  18. package/dist/3d.d.ts +157 -9
  19. package/dist/3d.js +8 -8
  20. package/dist/{agent8-CvsfVskX.js → agent8-Cfmd3ar_.js} +61 -8
  21. package/dist/{audio-player-C0j2k-yB.d.ts → audio-player-L8yccdMP.d.ts} +2 -2
  22. package/dist/{behavior-rm8vVzu_.d.ts → behavior-Dcz0fr1S.d.ts} +755 -46
  23. package/dist/{create-game-CwxEDlCm.js → create-game-CH4kt78v.js} +143 -60
  24. package/dist/{create-game-CvJ1wSRP.js → create-game-LCHBLfA4.js} +98 -48
  25. package/dist/debug.d.ts +1 -1
  26. package/dist/debug.js +2 -3
  27. package/dist/diagnostics-Cu85N3tL.d.ts +12 -0
  28. package/dist/{editor-switch-CnIOiyNJ.d.ts → editor-switch-CFU9mCec.d.ts} +22 -13
  29. package/dist/editor.js +1088 -882
  30. package/dist/env.d.ts +1 -1
  31. package/dist/env.js +5 -3
  32. package/dist/{environment-presets-BO-7XoFV.js → environment-presets-99BzP_L-.js} +298 -45
  33. package/dist/{frame-report-Dlq13Gyj.js → frame-report-D-_7YF2G.js} +1 -1
  34. package/dist/{gameplay-D1KixWK8.js → gameplay-CNULJvwh.js} +914 -214
  35. package/dist/gameplay.d.ts +254 -10
  36. package/dist/gameplay.js +2 -2
  37. package/dist/{heightmap-CRK0M4jT.js → heightmap-BYgD5Edk.js} +1 -1
  38. package/dist/index.d.ts +179 -13
  39. package/dist/index.js +10 -12
  40. package/dist/json-CfTjpvW8.js +67 -0
  41. package/dist/{loader-DadV2QCT.d.ts → loader-8-IHvbKD.d.ts} +2 -2
  42. package/dist/net.d.ts +27 -3
  43. package/dist/net.js +2 -2
  44. package/dist/{noise-CGUMx44x.js → noise-D3nPpmFg.js} +1 -1
  45. package/dist/{physics-2d-CF5yyjbj.js → physics-2d-FhrXUc6B.js} +136 -11
  46. package/dist/{physics-3d-DbYaEcZy.js → physics-3d-CFEGxBXZ.js} +104 -23
  47. package/dist/{teardown-CMqFVxO9.js → picking-DVo7fI13.js} +104 -13
  48. package/dist/react.d.ts +2 -2
  49. package/dist/react.js +2 -2
  50. package/dist/{register-DbVArsns.js → register-BYQCBySi.js} +429 -28
  51. package/dist/{register-mmfugpaQ.js → register-DuVdzxbq.js} +594 -831
  52. package/dist/{replay-BEHLDcQV.js → replay-DEvp3kyV.js} +224 -14
  53. package/dist/{replay-BQfkX4x7.d.ts → replay-F7IZHdFR.d.ts} +20 -3
  54. package/dist/{schema-B6ugCV1Q.d.ts → rng-Bb-IutXB.d.ts} +38 -21
  55. package/dist/{rng-DP-SR7eg.js → rng-CDOMybym.js} +22 -0
  56. package/dist/{loader-DZE7B07H.js → save-slots-Bvuh2p_r.js} +4619 -2318
  57. package/dist/{split-screen-Cg76MCV5.d.ts → split-screen-CIYf1zSf.d.ts} +36 -4
  58. package/dist/{split-screen-CauMwu6y.js → split-screen-eULetcg-.js} +159 -26
  59. package/dist/{sprite-animation-CY-mrr1L.js → sprite-animation-CqR2o3SA.js} +39 -8
  60. package/dist/{src-CYEu-_S9.js → src-_jk0qLsS.js} +2 -17
  61. package/dist/test-CmN54kWv.js +4264 -0
  62. package/dist/test.d.ts +639 -30
  63. package/dist/test.js +3 -3
  64. package/dist/touch-BnCyPA0G.js +519 -0
  65. package/dist/vite.d.ts +20 -37
  66. package/dist/vite.js +177 -7
  67. package/dist/{webgl-unavailable-Z5_g8x9H.js → webgl-unavailable-C8aDbGmR.js} +44 -0
  68. package/editor/assets/agent8-yLIEYHbd.js +1 -0
  69. package/editor/assets/{debug-CMULinBW.js → debug-u31w_yhq.js} +2 -2
  70. package/editor/assets/index-DK9xMGpW.js +11046 -0
  71. package/editor/index.html +1 -1
  72. package/package.json +4 -14
  73. package/schemas/scene.schema.json +34 -3
  74. package/skills/incanto-3d-character.md +14 -1
  75. package/skills/incanto-3d-models.md +12 -0
  76. package/skills/incanto-assets.md +9 -1
  77. package/skills/incanto-audio.md +8 -2
  78. package/skills/incanto-behaviors-and-scripts.md +105 -7
  79. package/skills/incanto-building-2d-games.md +97 -10
  80. package/skills/incanto-building-3d-games.md +58 -4
  81. package/skills/incanto-editor.md +46 -7
  82. package/skills/incanto-environment.md +19 -1
  83. package/skills/incanto-game-feel.md +70 -0
  84. package/skills/incanto-gameplay-behaviors.md +172 -20
  85. package/skills/incanto-hud.md +142 -7
  86. package/skills/incanto-localization.md +13 -5
  87. package/skills/incanto-multiplayer.md +83 -3
  88. package/skills/incanto-node-reference.md +239 -59
  89. package/skills/incanto-performance.md +52 -0
  90. package/skills/incanto-physics-and-input.md +123 -24
  91. package/skills/incanto-playtesting.md +112 -2
  92. package/skills/incanto-save-slots.md +217 -7
  93. package/skills/incanto-scene-json-authoring.md +69 -12
  94. package/skills/incanto-verifying-your-game.md +207 -10
  95. package/skills/incanto-web-integration.md +28 -0
  96. package/skills/incanto-your-first-game.md +3 -1
  97. package/templates-app/beacon-isle-3d/generate-world.ts +77 -9
  98. package/templates-app/beacon-isle-3d/package.json +2 -2
  99. package/templates-app/beacon-isle-3d/src/behaviors.ts +22 -0
  100. package/templates-app/beacon-isle-3d/src/game.scene.json +143 -600
  101. package/templates-app/beacon-isle-3d/src/main.ts +11 -1
  102. package/templates-app/beacon-isle-3d/tsconfig.json +1 -1
  103. package/templates-app/beacon-isle-3d/verify.ts +3 -1
  104. package/templates-app/molehill-2d/.incanto/playtest/lost-seed1.json +4277 -0
  105. package/templates-app/molehill-2d/PROJECT/Context.md +58 -0
  106. package/templates-app/molehill-2d/PROJECT/Requirements.md +39 -0
  107. package/templates-app/molehill-2d/PROJECT/Status.md +27 -0
  108. package/templates-app/molehill-2d/PROJECT/Structure.md +48 -0
  109. package/templates-app/molehill-2d/docs/project-2d-rules.md +44 -0
  110. package/templates-app/molehill-2d/index.html +73 -0
  111. package/templates-app/molehill-2d/package.json +23 -0
  112. package/templates-app/molehill-2d/src/behaviors.ts +198 -0
  113. package/templates-app/molehill-2d/src/game.scene.json +1205 -0
  114. package/templates-app/molehill-2d/src/main.ts +41 -0
  115. package/templates-app/molehill-2d/tsconfig.json +13 -0
  116. package/templates-app/molehill-2d/verify.ts +247 -0
  117. package/templates-app/molehill-2d/vite.config.ts +12 -0
  118. package/templates-app/platformer-2d/package.json +2 -2
  119. package/templates-app/platformer-2d/src/game.scene.json +178 -986
  120. package/templates-app/platformer-2d/src/main.ts +11 -1
  121. package/templates-app/platformer-2d/tsconfig.json +1 -1
  122. package/templates-app/star-survivor/package.json +2 -2
  123. package/templates-app/star-survivor/src/game.scene.json +43 -216
  124. package/templates-app/star-survivor/src/main.ts +11 -1
  125. package/templates-app/star-survivor/tsconfig.json +1 -1
  126. package/templates-app/tps-3d/PROJECT/Context.md +1 -1
  127. package/templates-app/tps-3d/package.json +2 -2
  128. package/templates-app/tps-3d/src/behaviors.ts +19 -1
  129. package/templates-app/tps-3d/src/game.scene.json +66 -263
  130. package/templates-app/tps-3d/src/main.ts +13 -2
  131. package/templates-app/tps-3d/tsconfig.json +1 -1
  132. package/templates-app/village-quest-3d/.incanto/playtest/swapped-seed1.json +1735 -0
  133. package/templates-app/village-quest-3d/package.json +2 -2
  134. package/templates-app/village-quest-3d/src/behaviors.ts +42 -4
  135. package/templates-app/village-quest-3d/src/grove.scene.json +68 -306
  136. package/templates-app/village-quest-3d/src/main.ts +11 -1
  137. package/templates-app/village-quest-3d/src/village.scene.json +202 -863
  138. package/templates-app/village-quest-3d/tsconfig.json +1 -1
  139. package/templates-app/village-quest-3d/verify.ts +14 -1
  140. package/dist/duplicate-Dj4L-5AA.js +0 -22
  141. package/dist/json-CwwhxQgb.js +0 -36
  142. package/dist/registry-CF70EArN.js +0 -212
  143. package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
  144. package/dist/test-D6ISmuFE.js +0 -2383
  145. package/dist/touch-DEAmqGdf.js +0 -225
  146. package/editor/assets/agent8-BDvaZMQJ.js +0 -1
  147. package/editor/assets/index-CZIRr5EL.js +0 -10958
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Molehill — a whack-a-mole you play with the MOUSE and nothing else.
3
+ *
4
+ * No character body, no keyboard, no gravity. Nine holes, a mole that pops out
5
+ * of one of them, three misses before they win. The engine's `Clickable` turns
6
+ * each mole into a node that reports being clicked; the built-in `ScoreKeeper`
7
+ * holds the score and the misses; the wiring between them is `game.scene.json`.
8
+ *
9
+ * It is the starter for a whole family the other templates do not cover —
10
+ * match-3, tower defense, cards, point-and-click, RTS, board games, idle.
11
+ */
12
+ import { createGame2D, showBootFailure } from 'incanto/2d';
13
+ import { Molehill, Scoreboard } from './behaviors';
14
+ import sceneJson from './game.scene.json';
15
+
16
+ /** Dev-only surfaces: the ☰ debug overlay, and the editor behind it. */
17
+ const DEBUG = import.meta.env.VITE_INCANTO_DEBUG === '1';
18
+
19
+ const canvas = document.querySelector('canvas') as HTMLCanvasElement;
20
+
21
+ try {
22
+ const game = await createGame2D({
23
+ canvas,
24
+ scene: sceneJson,
25
+ behaviors: { Molehill, Scoreboard },
26
+ // The cursor is the input. `lockOnClick` would HIDE it, which is exactly
27
+ // wrong for a game you play by pointing — the pointer is attached for its
28
+ // position, and nothing else.
29
+ pointer: { lockOnClick: false },
30
+ debug: DEBUG,
31
+ // Gated on the same flag: `editor` DEFAULTS to `debug`, so a bare object
32
+ // here turns it on in a production build — and this file also hangs the
33
+ // game handle off `globalThis`, so `game.openEditor()` would be one line
34
+ // away in a shipped build. Every other starter gates it; this one did not.
35
+ editor: DEBUG && { library: true },
36
+ });
37
+ document.getElementById('loading')?.remove();
38
+ (globalThis as { game?: unknown }).game = game;
39
+ } catch (error) {
40
+ showBootFailure(error);
41
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "skipLibCheck": true,
9
+ "resolveJsonModule": true,
10
+ "types": ["vite/client", "node"]
11
+ },
12
+ "include": ["src", "verify.ts", "vite.config.ts"]
13
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Headless proof that Molehill actually PLAYS — driven by the MOUSE, which is
3
+ * the only input it has.
4
+ *
5
+ * Every other starter's harness holds `move` and presses `jump`. This one uses
6
+ * `runScript`'s `click` step: it installs a geometric picker (there is no
7
+ * renderer headless, so there is no raycast), points the cursor at a node and
8
+ * spreads the press and the release across the two frames `Clickable` needs.
9
+ *
10
+ * BOARD — nine holes, and a mole is up within the first second.
11
+ * WHACK — clicking the mole that is up scores; the score climbs.
12
+ * MISS — standing by while a mole's time runs out costs a life.
13
+ * LOSE — three misses and the ScoreKeeper says `lost`.
14
+ * WIN — twelve whacks and it says `won`, and the field stops.
15
+ * SILENCE — a click on a hole with no mole in it scores nothing.
16
+ *
17
+ * Run with `bun verify.ts` (or `bun run verify`).
18
+ */
19
+ import { registerBehavior } from 'incanto';
20
+ import { auditScene, registerAllNodes, runScript } from 'incanto/test';
21
+ import { Molehill, Scoreboard } from './src/behaviors';
22
+ import sceneJson from './src/game.scene.json';
23
+
24
+ const behaviors = { Molehill, Scoreboard };
25
+
26
+ const ok = (label: string, cond: boolean): void => {
27
+ if (!cond) {
28
+ console.error(`FAIL: ${label}`);
29
+ process.exitCode = 1;
30
+ } else {
31
+ console.log(`pass: ${label}`);
32
+ }
33
+ };
34
+
35
+ // ---- AUDIT ---------------------------------------------------------------------------
36
+ {
37
+ registerAllNodes();
38
+ for (const [name, ctor] of Object.entries(behaviors)) {
39
+ registerBehavior(name, ctor as never, { replace: true });
40
+ }
41
+ const warnings = auditScene(sceneJson as unknown as Parameters<typeof auditScene>[0]);
42
+ ok(`auditScene is clean (${warnings.length} warnings)`, warnings.length === 0);
43
+ for (const w of warnings) console.error(` warn: ${w}`);
44
+ }
45
+
46
+ interface Field {
47
+ holeCount: number;
48
+ }
49
+ interface Keeper {
50
+ score: number;
51
+ lives: number;
52
+ }
53
+ interface Mole {
54
+ visible: boolean;
55
+ getPath(): string;
56
+ }
57
+
58
+ /** Whichever mole is up right now, by path — what a player is aiming at. */
59
+ const moleUp = (ctx: { getNode(p: string): unknown }): string | null => {
60
+ const field = ctx.getNode('Field') as unknown as { children: Array<{ name: string }> };
61
+ for (const hole of field.children) {
62
+ const mole = ctx.getNode(`Field/${hole.name}/Mole`) as unknown as Mole;
63
+ if (mole.visible) return `Field/${hole.name}/Mole`;
64
+ }
65
+ return null;
66
+ };
67
+
68
+ // ---- BOARD + WHACK -------------------------------------------------------------------
69
+ {
70
+ let holes = 0;
71
+ let upEarly: string | null = null;
72
+ let scored = 0;
73
+ const result = await runScript(sceneJson, {
74
+ durationMs: 6000,
75
+ seed: 3,
76
+ behaviors,
77
+ steps: [
78
+ {
79
+ atMs: 800,
80
+ label: 'nine holes, and a mole is up',
81
+ do: (ctx) => {
82
+ holes = (ctx.getNode('Field').behavior as unknown as Field).holeCount;
83
+ upEarly = moleUp(ctx);
84
+ },
85
+ },
86
+ // Sixteen chances at whichever mole is up — enough to outrun the ramp.
87
+ // `click` takes a FUNCTION because a whack-a-mole cannot name its target
88
+ // when the script is written: it clicks whatever is up right now, and
89
+ // `null` on a frame with nothing up is a quiet no-op.
90
+ ...Array.from({ length: 16 }, (_, i) => ({
91
+ atMs: 1000 + i * 300,
92
+ click: (ctx: Parameters<typeof moleUp>[0]) => moleUp(ctx),
93
+ })),
94
+ {
95
+ atMs: 5900,
96
+ label: 'clicking the mole that is up scores',
97
+ do: (ctx) => {
98
+ scored = (ctx.getNode('.').behavior as unknown as Keeper).score;
99
+ },
100
+ },
101
+ ],
102
+ });
103
+ ok(`the field has nine holes (${holes})`, holes === 9);
104
+ ok(`a mole is up within the first second (${upEarly ?? 'none'})`, upEarly !== null);
105
+ ok(`whacking scores (${scored})`, scored > 0);
106
+ ok('nothing threw', result.ok);
107
+ if (!result.ok) console.error(result.describe());
108
+ }
109
+
110
+ // ---- MISS + LOSE ---------------------------------------------------------------------
111
+ {
112
+ let lives = MISSES();
113
+ let lost = false;
114
+ await runScript(sceneJson, {
115
+ durationMs: 12000,
116
+ seed: 5,
117
+ behaviors,
118
+ steps: [
119
+ {
120
+ atMs: 100,
121
+ label: 'watch, and never click',
122
+ do: (ctx) => {
123
+ ctx.getNode('.').on('lost', () => {
124
+ lost = true;
125
+ });
126
+ },
127
+ },
128
+ {
129
+ atMs: 11500,
130
+ do: (ctx) => {
131
+ lives = (ctx.getNode('.').behavior as unknown as Keeper).lives;
132
+ },
133
+ },
134
+ ],
135
+ });
136
+ ok(`missing costs lives (${lives} left)`, lives < MISSES());
137
+ ok('three misses lose the game', lost);
138
+ }
139
+
140
+ function MISSES(): number {
141
+ return 3;
142
+ }
143
+
144
+ // ---- WIN -----------------------------------------------------------------------------
145
+ {
146
+ let won = false;
147
+ let stopped = false;
148
+ await runScript(sceneJson, {
149
+ durationMs: 14000,
150
+ seed: 3,
151
+ behaviors,
152
+ steps: [
153
+ {
154
+ atMs: 100,
155
+ do: (ctx) => {
156
+ ctx.getNode('.').on('won', () => {
157
+ won = true;
158
+ });
159
+ },
160
+ },
161
+ ...Array.from({ length: 44 }, (_, i) => ({
162
+ atMs: 400 + i * 280,
163
+ click: (ctx: Parameters<typeof moleUp>[0]) => moleUp(ctx),
164
+ })),
165
+ {
166
+ atMs: 13500,
167
+ label: 'the field stops once the game is over',
168
+ do: (ctx) => {
169
+ // `won` is wired to `Field.stop` in the scene, so no mole should be
170
+ // up after the win — a board that keeps popping after the banner is
171
+ // the classic "the game ended and nobody told the level" bug.
172
+ stopped = won && moleUp(ctx) === null;
173
+ },
174
+ },
175
+ ],
176
+ });
177
+ ok('twelve whacks win the game', won);
178
+ ok('the field stops when the game is won', stopped);
179
+ }
180
+
181
+ // ---- SILENCE -------------------------------------------------------------------------
182
+ {
183
+ let scored = -1;
184
+ await runScript(sceneJson, {
185
+ durationMs: 2000,
186
+ seed: 9,
187
+ behaviors,
188
+ steps: [
189
+ // A hole is not a mole: the click lands on nothing pickable and scores 0.
190
+ { atMs: 300, click: [160, 180 + 40] },
191
+ {
192
+ atMs: 1500,
193
+ do: (ctx) => {
194
+ scored = (ctx.getNode('.').behavior as unknown as Keeper).score;
195
+ },
196
+ },
197
+ ],
198
+ });
199
+ ok(`a click on bare ground scores nothing (${scored})`, scored === 0);
200
+ }
201
+
202
+ // ---- PLAY AGAIN ----------------------------------------------------------------------
203
+ {
204
+ // The second time through is its own game: the score, the misses and the
205
+ // field all have to come back, and a button that only works once is the
206
+ // classic version of this bug.
207
+ let afterLoss = { score: -1, lives: -1, running: false };
208
+ let secondScore = -1;
209
+ await runScript(sceneJson, {
210
+ durationMs: 24000,
211
+ seed: 5,
212
+ behaviors,
213
+ steps: [
214
+ // Lose by watching: three moles get away.
215
+ {
216
+ atMs: 11500,
217
+ label: 'press PLAY AGAIN after losing',
218
+ do: (ctx) => {
219
+ const keeper = ctx.getNode('.').behavior as unknown as Keeper;
220
+ afterLoss = { score: keeper.score, lives: keeper.lives, running: false };
221
+ (ctx.getNode('Hud/Again') as unknown as { visible: boolean; press(): void }).press();
222
+ },
223
+ },
224
+ {
225
+ atMs: 12200,
226
+ label: 'the board is running again',
227
+ do: (ctx) => {
228
+ afterLoss.running = moleUp(ctx) !== null;
229
+ },
230
+ },
231
+ // …and it can still be scored in.
232
+ ...Array.from({ length: 20 }, (_, i) => ({
233
+ atMs: 12500 + i * 300,
234
+ click: (ctx: Parameters<typeof moleUp>[0]) => moleUp(ctx),
235
+ })),
236
+ {
237
+ atMs: 19000,
238
+ do: (ctx) => {
239
+ secondScore = (ctx.getNode('.').behavior as unknown as Keeper).score;
240
+ },
241
+ },
242
+ ],
243
+ });
244
+ ok(`the game is lost before PLAY AGAIN (lives ${afterLoss.lives})`, afterLoss.lives === 0);
245
+ ok('PLAY AGAIN starts the moles again', afterLoss.running);
246
+ ok(`the second round can be scored in (${secondScore})`, secondScore > 0);
247
+ }
@@ -0,0 +1,12 @@
1
+ import { incantoLibrary, incantoScenes } from 'incanto/vite';
2
+ import { defineConfig } from 'vite';
3
+
4
+ export default defineConfig({
5
+ base: './',
6
+ // Dev-server only, and both are why the editor is useful here:
7
+ // - incantoScenes(): validates every *.scene.json the moment you save it,
8
+ // AND serves this project's scenes, so the editor (☰ debug ▸ edit this
9
+ // scene ▸ scenes) can open, create and save any scene in the project.
10
+ // - incantoLibrary(): the agent8 asset catalog behind the 📚 buttons.
11
+ plugins: [incantoScenes(), incantoLibrary()],
12
+ });
@@ -6,12 +6,12 @@
6
6
  "dev": "vite",
7
7
  "build": "vite build",
8
8
  "typecheck": "tsc --noEmit",
9
- "check": "incanto-check src",
9
+ "check": "incanto-check src --behaviors src/behaviors.ts",
10
10
  "verify": "bun verify.ts"
11
11
  },
12
12
  "dependencies": {
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
- "incanto": "^0.69.0",
14
+ "incanto": "^0.71.0",
15
15
  "three": "^0.184.0"
16
16
  },
17
17
  "devDependencies": {