incanto 0.69.0 → 0.70.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 (146) 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 +125 -44
  15. package/bin/incanto.mjs +3 -2
  16. package/dist/2d.d.ts +281 -34
  17. package/dist/2d.js +4 -4
  18. package/dist/3d.d.ts +157 -9
  19. package/dist/3d.js +7 -7
  20. package/dist/{agent8-CvsfVskX.js → agent8-CmNF01gA.js} +61 -8
  21. package/dist/{audio-player-C0j2k-yB.d.ts → audio-player-DaMxqfNE.d.ts} +2 -2
  22. package/dist/{behavior-rm8vVzu_.d.ts → behavior-DZExDn9o.d.ts} +744 -46
  23. package/dist/{create-game-CwxEDlCm.js → create-game-Bwvh6q8A.js} +142 -59
  24. package/dist/{create-game-CvJ1wSRP.js → create-game-C7ffQWW7.js} +97 -47
  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 +1065 -863
  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-D6Q5BxeE.js} +298 -45
  33. package/dist/{gameplay-D1KixWK8.js → gameplay-BfHkuzVb.js} +809 -211
  34. package/dist/gameplay.d.ts +187 -7
  35. package/dist/gameplay.js +1 -1
  36. package/dist/{heightmap-CRK0M4jT.js → heightmap-BYgD5Edk.js} +1 -1
  37. package/dist/index.d.ts +156 -13
  38. package/dist/index.js +10 -12
  39. package/dist/json-CfTjpvW8.js +67 -0
  40. package/dist/{loader-DadV2QCT.d.ts → loader-Cff09LMm.d.ts} +2 -2
  41. package/dist/net.d.ts +27 -3
  42. package/dist/net.js +2 -2
  43. package/dist/{noise-CGUMx44x.js → noise-D3nPpmFg.js} +1 -1
  44. package/dist/{physics-2d-CF5yyjbj.js → physics-2d-CE0Qvy3V.js} +136 -11
  45. package/dist/{physics-3d-DbYaEcZy.js → physics-3d-CpH-2gn5.js} +104 -23
  46. package/dist/{teardown-CMqFVxO9.js → picking-CQJ_PJKh.js} +103 -12
  47. package/dist/react.d.ts +2 -2
  48. package/dist/react.js +2 -2
  49. package/dist/{register-mmfugpaQ.js → register-6DYnKZcy.js} +546 -817
  50. package/dist/{register-DbVArsns.js → register-Bkk0wSDB.js} +332 -23
  51. package/dist/{replay-BEHLDcQV.js → replay-DjAkAzMq.js} +224 -14
  52. package/dist/{replay-BQfkX4x7.d.ts → replay-Dmw-PKQu.d.ts} +20 -3
  53. package/dist/{schema-B6ugCV1Q.d.ts → rng-Bb-IutXB.d.ts} +38 -21
  54. package/dist/{rng-DP-SR7eg.js → rng-CDOMybym.js} +22 -0
  55. package/dist/{loader-DZE7B07H.js → save-slots-BXVg148r.js} +4570 -2322
  56. package/dist/{split-screen-Cg76MCV5.d.ts → split-screen--k-XpBjr.d.ts} +36 -4
  57. package/dist/{split-screen-CauMwu6y.js → split-screen-PL78oVXP.js} +159 -26
  58. package/dist/{sprite-animation-CY-mrr1L.js → sprite-animation-CqR2o3SA.js} +39 -8
  59. package/dist/{src-CYEu-_S9.js → src-Cxfiv1Hg.js} +2 -17
  60. package/dist/test-iHYVUcDK.js +4036 -0
  61. package/dist/test.d.ts +531 -30
  62. package/dist/test.js +3 -3
  63. package/dist/touch-BnCyPA0G.js +519 -0
  64. package/dist/vite.d.ts +20 -37
  65. package/dist/vite.js +176 -6
  66. package/dist/{webgl-unavailable-Z5_g8x9H.js → webgl-unavailable-C8aDbGmR.js} +44 -0
  67. package/editor/assets/agent8-D0MS174y.js +1 -0
  68. package/editor/assets/{debug-CMULinBW.js → debug-BnXkKuYu.js} +2 -2
  69. package/editor/assets/index-CIu3uc3l.js +11046 -0
  70. package/editor/index.html +1 -1
  71. package/package.json +4 -14
  72. package/schemas/scene.schema.json +30 -3
  73. package/skills/incanto-3d-character.md +14 -1
  74. package/skills/incanto-3d-models.md +12 -0
  75. package/skills/incanto-assets.md +9 -1
  76. package/skills/incanto-audio.md +8 -2
  77. package/skills/incanto-behaviors-and-scripts.md +83 -6
  78. package/skills/incanto-building-2d-games.md +77 -8
  79. package/skills/incanto-building-3d-games.md +58 -4
  80. package/skills/incanto-editor.md +46 -7
  81. package/skills/incanto-environment.md +19 -1
  82. package/skills/incanto-game-feel.md +70 -0
  83. package/skills/incanto-gameplay-behaviors.md +109 -19
  84. package/skills/incanto-hud.md +128 -7
  85. package/skills/incanto-localization.md +13 -5
  86. package/skills/incanto-multiplayer.md +83 -3
  87. package/skills/incanto-node-reference.md +222 -58
  88. package/skills/incanto-performance.md +52 -0
  89. package/skills/incanto-physics-and-input.md +123 -24
  90. package/skills/incanto-playtesting.md +78 -2
  91. package/skills/incanto-save-slots.md +161 -6
  92. package/skills/incanto-scene-json-authoring.md +69 -12
  93. package/skills/incanto-verifying-your-game.md +196 -10
  94. package/skills/incanto-web-integration.md +28 -0
  95. package/skills/incanto-your-first-game.md +3 -1
  96. package/templates-app/beacon-isle-3d/generate-world.ts +77 -9
  97. package/templates-app/beacon-isle-3d/package.json +2 -2
  98. package/templates-app/beacon-isle-3d/src/behaviors.ts +22 -0
  99. package/templates-app/beacon-isle-3d/src/game.scene.json +103 -378
  100. package/templates-app/beacon-isle-3d/src/main.ts +11 -1
  101. package/templates-app/beacon-isle-3d/tsconfig.json +1 -1
  102. package/templates-app/beacon-isle-3d/verify.ts +3 -1
  103. package/templates-app/molehill-2d/.incanto/playtest/lost-seed1.json +4277 -0
  104. package/templates-app/molehill-2d/PROJECT/Context.md +58 -0
  105. package/templates-app/molehill-2d/PROJECT/Requirements.md +39 -0
  106. package/templates-app/molehill-2d/PROJECT/Status.md +27 -0
  107. package/templates-app/molehill-2d/PROJECT/Structure.md +48 -0
  108. package/templates-app/molehill-2d/docs/project-2d-rules.md +44 -0
  109. package/templates-app/molehill-2d/index.html +73 -0
  110. package/templates-app/molehill-2d/package.json +23 -0
  111. package/templates-app/molehill-2d/src/behaviors.ts +198 -0
  112. package/templates-app/molehill-2d/src/game.scene.json +1255 -0
  113. package/templates-app/molehill-2d/src/main.ts +41 -0
  114. package/templates-app/molehill-2d/tsconfig.json +13 -0
  115. package/templates-app/molehill-2d/verify.ts +247 -0
  116. package/templates-app/molehill-2d/vite.config.ts +12 -0
  117. package/templates-app/platformer-2d/package.json +2 -2
  118. package/templates-app/platformer-2d/src/game.scene.json +128 -600
  119. package/templates-app/platformer-2d/src/main.ts +11 -1
  120. package/templates-app/platformer-2d/tsconfig.json +1 -1
  121. package/templates-app/star-survivor/package.json +2 -2
  122. package/templates-app/star-survivor/src/game.scene.json +41 -195
  123. package/templates-app/star-survivor/src/main.ts +11 -1
  124. package/templates-app/star-survivor/tsconfig.json +1 -1
  125. package/templates-app/tps-3d/PROJECT/Context.md +1 -1
  126. package/templates-app/tps-3d/package.json +2 -2
  127. package/templates-app/tps-3d/src/behaviors.ts +19 -1
  128. package/templates-app/tps-3d/src/game.scene.json +78 -217
  129. package/templates-app/tps-3d/src/main.ts +13 -2
  130. package/templates-app/tps-3d/tsconfig.json +1 -1
  131. package/templates-app/village-quest-3d/.incanto/playtest/swapped-seed1.json +1735 -0
  132. package/templates-app/village-quest-3d/package.json +2 -2
  133. package/templates-app/village-quest-3d/src/behaviors.ts +21 -0
  134. package/templates-app/village-quest-3d/src/grove.scene.json +54 -221
  135. package/templates-app/village-quest-3d/src/main.ts +11 -1
  136. package/templates-app/village-quest-3d/src/village.scene.json +199 -838
  137. package/templates-app/village-quest-3d/tsconfig.json +1 -1
  138. package/templates-app/village-quest-3d/verify.ts +14 -1
  139. package/dist/duplicate-Dj4L-5AA.js +0 -22
  140. package/dist/json-CwwhxQgb.js +0 -36
  141. package/dist/registry-CF70EArN.js +0 -212
  142. package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
  143. package/dist/test-D6ISmuFE.js +0 -2383
  144. package/dist/touch-DEAmqGdf.js +0 -225
  145. package/editor/assets/agent8-BDvaZMQJ.js +0 -1
  146. package/editor/assets/index-CZIRr5EL.js +0 -10958
@@ -44,8 +44,18 @@ scene.assets.goblin.url = goblinUrl;
44
44
  scene.assets.coin.url = coinUrl;
45
45
  scene.assets.gem.url = gemUrl;
46
46
 
47
+ /** Dev-only surfaces: the ☰ debug overlay, and the editor behind it. */
48
+ const DEBUG = import.meta.env.VITE_INCANTO_DEBUG === '1';
49
+
47
50
  const game = await createGame2D({
48
- debug: import.meta.env.VITE_INCANTO_DEBUG === '1',
51
+ debug: DEBUG,
52
+ // The editor's 📚 buttons, served by `incantoLibrary()` in vite.config.ts.
53
+ // BOTH halves are the opt-in, and this half was missing from every shipped
54
+ // template: the plugin alone left a vite config whose own comment promised
55
+ // "the agent8 asset catalog behind the 📚 buttons" and no button anywhere.
56
+ // Gated on the same flag, because `editor` DEFAULTS to `debug` and an object
57
+ // here would turn the editor on in a production build.
58
+ editor: DEBUG && { library: true },
49
59
  canvas,
50
60
  scene,
51
61
  behaviors,
@@ -9,5 +9,5 @@
9
9
  "resolveJsonModule": true,
10
10
  "types": ["vite/client", "node"]
11
11
  },
12
- "include": ["src", "verify.ts"]
12
+ "include": ["src", "verify.ts", "vite.config.ts"]
13
13
  }
@@ -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.70.0",
15
15
  "three": "^0.184.0"
16
16
  },
17
17
  "devDependencies": {
@@ -3,16 +3,9 @@
3
3
  "type": "scene",
4
4
  "dimension": "2d",
5
5
  "name": "HollowHorde",
6
- "environment": {
7
- "background": "#0d1022"
8
- },
9
- "viewport": {
10
- "design": [960, 540],
11
- "fit": "expand"
12
- },
13
- "physics": {
14
- "gravity": [0, 0]
15
- },
6
+ "environment": { "background": "#0d1022" },
7
+ "viewport": { "design": [960, 540], "fit": "expand" },
8
+ "physics": { "gravity": [0, 0] },
16
9
  "input": {
17
10
  "move": {
18
11
  "type": "vector2",
@@ -54,28 +47,15 @@
54
47
  "root": {
55
48
  "name": "Game",
56
49
  "type": "Node",
57
- "script": {
58
- "name": "ScoreKeeper",
59
- "props": {
60
- "scoreToWin": 1,
61
- "lives": 1
62
- }
63
- },
50
+ "script": { "name": "ScoreKeeper", "props": { "scoreToWin": 1, "lives": 1 } },
64
51
  "children": [
65
52
  {
66
53
  "name": "Floor",
67
54
  "type": "Node2D",
68
- "props": {
69
- "position": [0, 0]
70
- },
55
+ "props": { "position": [0, 0] },
71
56
  "script": {
72
57
  "name": "GridFloor",
73
- "props": {
74
- "color": "#12152a",
75
- "line": "#1d244a",
76
- "cell": 200,
77
- "lines": 26
78
- }
58
+ "props": { "color": "#12152a", "line": "#1d244a", "cell": 200, "lines": 26 }
79
59
  },
80
60
  "uid": "n_gyoazhctbyg011uf"
81
61
  },
@@ -83,16 +63,8 @@
83
63
  "name": "Player",
84
64
  "type": "CharacterBody2D",
85
65
  "groups": ["player"],
86
- "props": {
87
- "position": [0, 0],
88
- "collider": {
89
- "shape": "circle",
90
- "radius": 15
91
- }
92
- },
93
- "script": {
94
- "name": "Survivor"
95
- },
66
+ "props": { "position": [0, 0], "collider": { "shape": "circle", "radius": 15 } },
67
+ "script": { "name": "Survivor" },
96
68
  "children": [
97
69
  {
98
70
  "name": "Skin",
@@ -103,16 +75,8 @@
103
75
  "scale": [0.46, 0.46],
104
76
  "anchor": [0.5, 0.56],
105
77
  "animations": {
106
- "idle": {
107
- "frames": [0, 5],
108
- "fps": 10,
109
- "loop": true
110
- },
111
- "move": {
112
- "frames": [6, 11],
113
- "fps": 14,
114
- "loop": true
115
- }
78
+ "idle": { "frames": [0, 5], "fps": 10, "loop": true },
79
+ "move": { "frames": [6, 11], "fps": 14, "loop": true }
116
80
  }
117
81
  }
118
82
  },
@@ -121,10 +85,7 @@
121
85
  "type": "Node2D",
122
86
  "script": {
123
87
  "name": "Health",
124
- "props": {
125
- "max": 100,
126
- "invulnerableFor": 0.6
127
- }
88
+ "props": { "max": 100, "invulnerableFor": 0.6 }
128
89
  },
129
90
  "uid": "n_bk99wi475y8y1c1g"
130
91
  }
@@ -134,17 +95,10 @@
134
95
  {
135
96
  "name": "GoblinSpawner",
136
97
  "type": "Node2D",
137
- "props": {
138
- "position": [0, 0]
139
- },
98
+ "props": { "position": [0, 0] },
140
99
  "script": {
141
100
  "name": "Spawner",
142
- "props": {
143
- "prefab": "GoblinTemplate",
144
- "interval": 1.3,
145
- "max": 44,
146
- "total": 0
147
- }
101
+ "props": { "prefab": "GoblinTemplate", "interval": 1.3, "max": 44, "total": 0 }
148
102
  },
149
103
  "children": [
150
104
  {
@@ -154,18 +108,9 @@
154
108
  "props": {
155
109
  "position": [0, 0],
156
110
  "visible": false,
157
- "collider": {
158
- "shape": "circle",
159
- "radius": 15
160
- }
161
- },
162
- "script": {
163
- "name": "Health",
164
- "props": {
165
- "max": 3,
166
- "freeOnDeath": true
167
- }
111
+ "collider": { "shape": "circle", "radius": 15 }
168
112
  },
113
+ "script": { "name": "Health", "props": { "max": 3, "freeOnDeath": true } },
169
114
  "children": [
170
115
  {
171
116
  "name": "Skin",
@@ -176,16 +121,8 @@
176
121
  "scale": [0.4, 0.4],
177
122
  "anchor": [0.5, 0.58],
178
123
  "animations": {
179
- "idle": {
180
- "frames": [0, 6],
181
- "fps": 9,
182
- "loop": true
183
- },
184
- "move": {
185
- "frames": [7, 12],
186
- "fps": 12,
187
- "loop": true
188
- }
124
+ "idle": { "frames": [0, 6], "fps": 9, "loop": true },
125
+ "move": { "frames": [7, 12], "fps": 12, "loop": true }
189
126
  }
190
127
  }
191
128
  },
@@ -194,31 +131,20 @@
194
131
  "type": "Node2D",
195
132
  "script": {
196
133
  "name": "Chase",
197
- "props": {
198
- "target": "/root/Player",
199
- "speed": 62,
200
- "moveParent": true
201
- }
134
+ "props": { "target": "/root/Player", "speed": 62, "moveParent": true }
202
135
  },
203
136
  "uid": "n_pyb54alv0xh0gvn4"
204
137
  },
205
138
  {
206
139
  "name": "Face",
207
140
  "type": "Node2D",
208
- "script": {
209
- "name": "EnemyFace"
210
- },
141
+ "script": { "name": "EnemyFace" },
211
142
  "uid": "n_uww78ofvpq4ecg8k"
212
143
  },
213
144
  {
214
145
  "name": "Hit",
215
146
  "type": "Area2D",
216
- "props": {
217
- "collider": {
218
- "shape": "circle",
219
- "radius": 18
220
- }
221
- },
147
+ "props": { "collider": { "shape": "circle", "radius": 18 } },
222
148
  "script": {
223
149
  "name": "DamageOnContact",
224
150
  "props": {
@@ -235,10 +161,7 @@
235
161
  "type": "Node2D",
236
162
  "script": {
237
163
  "name": "EnemyDrop",
238
- "props": {
239
- "xp": 1,
240
- "color": "#ff8a5b"
241
- }
164
+ "props": { "xp": 1, "color": "#ff8a5b" }
242
165
  },
243
166
  "uid": "n_ik9ixyv3h463m3bj"
244
167
  }
@@ -251,17 +174,10 @@
251
174
  {
252
175
  "name": "GhostSpawner",
253
176
  "type": "Node2D",
254
- "props": {
255
- "position": [0, 0]
256
- },
177
+ "props": { "position": [0, 0] },
257
178
  "script": {
258
179
  "name": "Spawner",
259
- "props": {
260
- "prefab": "GhostTemplate",
261
- "interval": 2.8,
262
- "max": 22,
263
- "total": 0
264
- }
180
+ "props": { "prefab": "GhostTemplate", "interval": 2.8, "max": 22, "total": 0 }
265
181
  },
266
182
  "children": [
267
183
  {
@@ -271,18 +187,9 @@
271
187
  "props": {
272
188
  "position": [0, 0],
273
189
  "visible": false,
274
- "collider": {
275
- "shape": "circle",
276
- "radius": 14
277
- }
278
- },
279
- "script": {
280
- "name": "Health",
281
- "props": {
282
- "max": 5,
283
- "freeOnDeath": true
284
- }
190
+ "collider": { "shape": "circle", "radius": 14 }
285
191
  },
192
+ "script": { "name": "Health", "props": { "max": 5, "freeOnDeath": true } },
286
193
  "children": [
287
194
  {
288
195
  "name": "Skin",
@@ -294,16 +201,8 @@
294
201
  "anchor": [0.5, 0.6],
295
202
  "opacity": 0.92,
296
203
  "animations": {
297
- "idle": {
298
- "frames": [0, 5],
299
- "fps": 9,
300
- "loop": true
301
- },
302
- "move": {
303
- "frames": [6, 11],
304
- "fps": 12,
305
- "loop": true
306
- }
204
+ "idle": { "frames": [0, 5], "fps": 9, "loop": true },
205
+ "move": { "frames": [6, 11], "fps": 12, "loop": true }
307
206
  }
308
207
  }
309
208
  },
@@ -312,31 +211,20 @@
312
211
  "type": "Node2D",
313
212
  "script": {
314
213
  "name": "Chase",
315
- "props": {
316
- "target": "/root/Player",
317
- "speed": 98,
318
- "moveParent": true
319
- }
214
+ "props": { "target": "/root/Player", "speed": 98, "moveParent": true }
320
215
  },
321
216
  "uid": "n_runap60ko5ppsjjz"
322
217
  },
323
218
  {
324
219
  "name": "Face",
325
220
  "type": "Node2D",
326
- "script": {
327
- "name": "EnemyFace"
328
- },
221
+ "script": { "name": "EnemyFace" },
329
222
  "uid": "n_wkgtjkvevrhj9z9q"
330
223
  },
331
224
  {
332
225
  "name": "Hit",
333
226
  "type": "Area2D",
334
- "props": {
335
- "collider": {
336
- "shape": "circle",
337
- "radius": 16
338
- }
339
- },
227
+ "props": { "collider": { "shape": "circle", "radius": 16 } },
340
228
  "script": {
341
229
  "name": "DamageOnContact",
342
230
  "props": {
@@ -353,10 +241,7 @@
353
241
  "type": "Node2D",
354
242
  "script": {
355
243
  "name": "EnemyDrop",
356
- "props": {
357
- "xp": 2,
358
- "color": "#8fd0ff"
359
- }
244
+ "props": { "xp": 2, "color": "#8fd0ff" }
360
245
  },
361
246
  "uid": "n_ldwl2f8oqqnmv0uc"
362
247
  }
@@ -382,71 +267,42 @@
382
267
  {
383
268
  "name": "Camera",
384
269
  "type": "Camera2D",
385
- "props": {
386
- "current": true
387
- },
270
+ "props": { "current": true },
388
271
  "script": {
389
272
  "name": "FollowCamera",
390
- "props": {
391
- "target": "/root/Player",
392
- "smoothing": 0.86
393
- }
273
+ "props": { "target": "/root/Player", "smoothing": 0.86 }
394
274
  },
395
275
  "uid": "n_2c36byg8q2ob5w7l"
396
276
  },
397
277
  {
398
278
  "name": "SfxShoot",
399
279
  "type": "AudioPlayer",
400
- "props": {
401
- "preset": "shoot",
402
- "bus": "sfx",
403
- "volume": 0.28
404
- }
280
+ "props": { "preset": "shoot", "bus": "sfx", "volume": 0.28 }
405
281
  },
406
282
  {
407
283
  "name": "SfxXp",
408
284
  "type": "AudioPlayer",
409
- "props": {
410
- "preset": "coin",
411
- "bus": "sfx",
412
- "volume": 0.4
413
- }
285
+ "props": { "preset": "coin", "bus": "sfx", "volume": 0.4 }
414
286
  },
415
287
  {
416
288
  "name": "SfxKill",
417
289
  "type": "AudioPlayer",
418
- "props": {
419
- "preset": "hit",
420
- "bus": "sfx",
421
- "volume": 0.3
422
- }
290
+ "props": { "preset": "hit", "bus": "sfx", "volume": 0.3 }
423
291
  },
424
292
  {
425
293
  "name": "SfxHurt",
426
294
  "type": "AudioPlayer",
427
- "props": {
428
- "preset": "hurt",
429
- "bus": "sfx",
430
- "volume": 0.55
431
- }
295
+ "props": { "preset": "hurt", "bus": "sfx", "volume": 0.55 }
432
296
  },
433
297
  {
434
298
  "name": "SfxWin",
435
299
  "type": "AudioPlayer",
436
- "props": {
437
- "preset": "win",
438
- "bus": "sfx",
439
- "volume": 0.8
440
- }
300
+ "props": { "preset": "win", "bus": "sfx", "volume": 0.8 }
441
301
  },
442
302
  {
443
303
  "name": "SfxLose",
444
304
  "type": "AudioPlayer",
445
- "props": {
446
- "preset": "lose",
447
- "bus": "sfx",
448
- "volume": 0.8
449
- }
305
+ "props": { "preset": "lose", "bus": "sfx", "volume": 0.8 }
450
306
  }
451
307
  ],
452
308
  "uid": "n_tlqu945vs98khzn8"
@@ -464,17 +320,7 @@
464
320
  "to": "SfxHurt",
465
321
  "handler": "play"
466
322
  },
467
- {
468
- "signal": "won",
469
- "from": "/root",
470
- "to": "SfxWin",
471
- "handler": "play"
472
- },
473
- {
474
- "signal": "lost",
475
- "from": "/root",
476
- "to": "SfxLose",
477
- "handler": "play"
478
- }
323
+ { "signal": "won", "from": "/root", "to": "SfxWin", "handler": "play" },
324
+ { "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" }
479
325
  ]
480
326
  }
@@ -31,8 +31,18 @@ scene.assets.knight.url = knightUrl;
31
31
  scene.assets.goblin.url = goblinUrl;
32
32
  scene.assets.ghost.url = ghostUrl;
33
33
 
34
+ /** Dev-only surfaces: the ☰ debug overlay, and the editor behind it. */
35
+ const DEBUG = import.meta.env.VITE_INCANTO_DEBUG === '1';
36
+
34
37
  const game = await createGame2D({
35
- debug: import.meta.env.VITE_INCANTO_DEBUG === '1',
38
+ debug: DEBUG,
39
+ // The editor's 📚 buttons, served by `incantoLibrary()` in vite.config.ts.
40
+ // BOTH halves are the opt-in, and this half was missing from every shipped
41
+ // template: the plugin alone left a vite config whose own comment promised
42
+ // "the agent8 asset catalog behind the 📚 buttons" and no button anywhere.
43
+ // Gated on the same flag, because `editor` DEFAULTS to `debug` and an object
44
+ // here would turn the editor on in a production build.
45
+ editor: DEBUG && { library: true },
36
46
  canvas,
37
47
  scene,
38
48
  behaviors,
@@ -9,5 +9,5 @@
9
9
  "resolveJsonModule": true,
10
10
  "types": ["vite/client", "node"]
11
11
  },
12
- "include": ["src", "verify.ts"]
12
+ "include": ["src", "verify.ts", "vite.config.ts"]
13
13
  }
@@ -5,7 +5,7 @@
5
5
  "Vanguard" — a 3D THIRD-PERSON shooter. The camera rides over your soldier's
6
6
  shoulder (you SEE your character); the crosshair is where the hitscan rifle
7
7
  fires. Move with WASD, look with the mouse (click locks the pointer), Shift to
8
- sprint, Space to jump, click / F to fire, R to reload. Survive and clear three
8
+ sprint, Space to jump, click / F to fire, R to reload, Enter to restart. Survive and clear three
9
9
  escalating waves of chasing enemies to **SECURE THE AREA**; let them drain your
10
10
  HP to 0 and the **MISSION FAILS**.
11
11
 
@@ -6,14 +6,14 @@
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
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.69.0",
16
+ "incanto": "^0.70.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -20,6 +20,7 @@ import type { Node } from 'incanto';
20
20
  import { Behavior } from 'incanto';
21
21
  import type { CharacterController3D, ModelInstance3D, Node3D } from 'incanto/3d';
22
22
  import { MeshInstance3D, Particles3D } from 'incanto/3d';
23
+ import { restartScene } from 'incanto/gameplay';
23
24
 
24
25
  // CharacterController3D movement state → mixamo clip (retargets onto the GLB).
25
26
  const STATE_CLIPS: Record<string, string> = {
@@ -373,6 +374,20 @@ export class HudUpdater extends Behavior {
373
374
 
374
375
  override update(): void {
375
376
  this.refresh();
377
+ /*
378
+ * Enter restarts, once the mission is over.
379
+ *
380
+ * The scene has declared a `restart` action since this template shipped and
381
+ * nothing read it — so `MISSION FAILED` was a dead end you left with F5.
382
+ * It was bound to `KeyR` as well, the reload key, which is the tell: a
383
+ * binding nobody reads is also a binding nobody notices colliding.
384
+ */
385
+ const engine = this.node.tree?.engine;
386
+ // Read EVERY frame, act only when the mission is over: an action polled
387
+ // behind a condition is invisible to `unreadActions()` until a run reaches
388
+ // that screen, and this template's own playtest reported it dead.
389
+ const pressed = engine ? engine.input.justPressed('restart') : false;
390
+ if (this.over && pressed && engine) restartScene(engine);
376
391
  }
377
392
 
378
393
  private refresh(): void {
@@ -399,7 +414,10 @@ export class HudUpdater extends Behavior {
399
414
  if (!hasDom) return;
400
415
  const b = document.getElementById('banner');
401
416
  if (b) {
402
- b.textContent = text;
417
+ // A game-over screen has to say how to leave it, or the key that works is
418
+ // one only the input map knows about.
419
+ b.textContent = `${text}\nPress Enter to restart`;
420
+ b.style.whiteSpace = 'pre-line';
403
421
  b.style.color = color;
404
422
  b.style.opacity = '1';
405
423
  }