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
@@ -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.71.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,29 +75,15 @@
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
  },
119
83
  {
120
84
  "name": "Vitals",
121
85
  "type": "Node2D",
122
- "script": {
123
- "name": "Health",
124
- "props": {
125
- "max": 100,
126
- "invulnerableFor": 0.6
127
- }
128
- },
86
+ "script": { "name": "Health", "props": { "max": 100, "invulnerableFor": 0.6 } },
129
87
  "uid": "n_bk99wi475y8y1c1g"
130
88
  }
131
89
  ],
@@ -134,17 +92,10 @@
134
92
  {
135
93
  "name": "GoblinSpawner",
136
94
  "type": "Node2D",
137
- "props": {
138
- "position": [0, 0]
139
- },
95
+ "props": { "position": [0, 0] },
140
96
  "script": {
141
97
  "name": "Spawner",
142
- "props": {
143
- "prefab": "GoblinTemplate",
144
- "interval": 1.3,
145
- "max": 44,
146
- "total": 0
147
- }
98
+ "props": { "prefab": "GoblinTemplate", "interval": 1.3, "max": 44, "total": 0 }
148
99
  },
149
100
  "children": [
150
101
  {
@@ -154,18 +105,9 @@
154
105
  "props": {
155
106
  "position": [0, 0],
156
107
  "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
- }
108
+ "collider": { "shape": "circle", "radius": 15 }
168
109
  },
110
+ "script": { "name": "Health", "props": { "max": 3, "freeOnDeath": true } },
169
111
  "children": [
170
112
  {
171
113
  "name": "Skin",
@@ -176,16 +118,8 @@
176
118
  "scale": [0.4, 0.4],
177
119
  "anchor": [0.5, 0.58],
178
120
  "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
- }
121
+ "idle": { "frames": [0, 6], "fps": 9, "loop": true },
122
+ "move": { "frames": [7, 12], "fps": 12, "loop": true }
189
123
  }
190
124
  }
191
125
  },
@@ -194,31 +128,20 @@
194
128
  "type": "Node2D",
195
129
  "script": {
196
130
  "name": "Chase",
197
- "props": {
198
- "target": "/root/Player",
199
- "speed": 62,
200
- "moveParent": true
201
- }
131
+ "props": { "target": "/root/Player", "speed": 62, "moveParent": true }
202
132
  },
203
133
  "uid": "n_pyb54alv0xh0gvn4"
204
134
  },
205
135
  {
206
136
  "name": "Face",
207
137
  "type": "Node2D",
208
- "script": {
209
- "name": "EnemyFace"
210
- },
138
+ "script": { "name": "EnemyFace" },
211
139
  "uid": "n_uww78ofvpq4ecg8k"
212
140
  },
213
141
  {
214
142
  "name": "Hit",
215
143
  "type": "Area2D",
216
- "props": {
217
- "collider": {
218
- "shape": "circle",
219
- "radius": 18
220
- }
221
- },
144
+ "props": { "collider": { "shape": "circle", "radius": 18 } },
222
145
  "script": {
223
146
  "name": "DamageOnContact",
224
147
  "props": {
@@ -233,13 +156,7 @@
233
156
  {
234
157
  "name": "Drop",
235
158
  "type": "Node2D",
236
- "script": {
237
- "name": "EnemyDrop",
238
- "props": {
239
- "xp": 1,
240
- "color": "#ff8a5b"
241
- }
242
- },
159
+ "script": { "name": "EnemyDrop", "props": { "xp": 1, "color": "#ff8a5b" } },
243
160
  "uid": "n_ik9ixyv3h463m3bj"
244
161
  }
245
162
  ],
@@ -251,17 +168,10 @@
251
168
  {
252
169
  "name": "GhostSpawner",
253
170
  "type": "Node2D",
254
- "props": {
255
- "position": [0, 0]
256
- },
171
+ "props": { "position": [0, 0] },
257
172
  "script": {
258
173
  "name": "Spawner",
259
- "props": {
260
- "prefab": "GhostTemplate",
261
- "interval": 2.8,
262
- "max": 22,
263
- "total": 0
264
- }
174
+ "props": { "prefab": "GhostTemplate", "interval": 2.8, "max": 22, "total": 0 }
265
175
  },
266
176
  "children": [
267
177
  {
@@ -271,18 +181,9 @@
271
181
  "props": {
272
182
  "position": [0, 0],
273
183
  "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
- }
184
+ "collider": { "shape": "circle", "radius": 14 }
285
185
  },
186
+ "script": { "name": "Health", "props": { "max": 5, "freeOnDeath": true } },
286
187
  "children": [
287
188
  {
288
189
  "name": "Skin",
@@ -294,16 +195,8 @@
294
195
  "anchor": [0.5, 0.6],
295
196
  "opacity": 0.92,
296
197
  "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
- }
198
+ "idle": { "frames": [0, 5], "fps": 9, "loop": true },
199
+ "move": { "frames": [6, 11], "fps": 12, "loop": true }
307
200
  }
308
201
  }
309
202
  },
@@ -312,31 +205,20 @@
312
205
  "type": "Node2D",
313
206
  "script": {
314
207
  "name": "Chase",
315
- "props": {
316
- "target": "/root/Player",
317
- "speed": 98,
318
- "moveParent": true
319
- }
208
+ "props": { "target": "/root/Player", "speed": 98, "moveParent": true }
320
209
  },
321
210
  "uid": "n_runap60ko5ppsjjz"
322
211
  },
323
212
  {
324
213
  "name": "Face",
325
214
  "type": "Node2D",
326
- "script": {
327
- "name": "EnemyFace"
328
- },
215
+ "script": { "name": "EnemyFace" },
329
216
  "uid": "n_wkgtjkvevrhj9z9q"
330
217
  },
331
218
  {
332
219
  "name": "Hit",
333
220
  "type": "Area2D",
334
- "props": {
335
- "collider": {
336
- "shape": "circle",
337
- "radius": 16
338
- }
339
- },
221
+ "props": { "collider": { "shape": "circle", "radius": 16 } },
340
222
  "script": {
341
223
  "name": "DamageOnContact",
342
224
  "props": {
@@ -351,13 +233,7 @@
351
233
  {
352
234
  "name": "Drop",
353
235
  "type": "Node2D",
354
- "script": {
355
- "name": "EnemyDrop",
356
- "props": {
357
- "xp": 2,
358
- "color": "#8fd0ff"
359
- }
360
- },
236
+ "script": { "name": "EnemyDrop", "props": { "xp": 2, "color": "#8fd0ff" } },
361
237
  "uid": "n_ldwl2f8oqqnmv0uc"
362
238
  }
363
239
  ],
@@ -382,99 +258,50 @@
382
258
  {
383
259
  "name": "Camera",
384
260
  "type": "Camera2D",
385
- "props": {
386
- "current": true
387
- },
261
+ "props": { "current": true },
388
262
  "script": {
389
263
  "name": "FollowCamera",
390
- "props": {
391
- "target": "/root/Player",
392
- "smoothing": 0.86
393
- }
264
+ "props": { "target": "/root/Player", "smoothing": 0.86 }
394
265
  },
395
266
  "uid": "n_2c36byg8q2ob5w7l"
396
267
  },
397
268
  {
398
269
  "name": "SfxShoot",
399
270
  "type": "AudioPlayer",
400
- "props": {
401
- "preset": "shoot",
402
- "bus": "sfx",
403
- "volume": 0.28
404
- }
271
+ "props": { "preset": "shoot", "bus": "sfx", "volume": 0.28 }
405
272
  },
406
273
  {
407
274
  "name": "SfxXp",
408
275
  "type": "AudioPlayer",
409
- "props": {
410
- "preset": "coin",
411
- "bus": "sfx",
412
- "volume": 0.4
413
- }
276
+ "props": { "preset": "coin", "bus": "sfx", "volume": 0.4 }
414
277
  },
415
278
  {
416
279
  "name": "SfxKill",
417
280
  "type": "AudioPlayer",
418
- "props": {
419
- "preset": "hit",
420
- "bus": "sfx",
421
- "volume": 0.3
422
- }
281
+ "props": { "preset": "hit", "bus": "sfx", "volume": 0.3 }
423
282
  },
424
283
  {
425
284
  "name": "SfxHurt",
426
285
  "type": "AudioPlayer",
427
- "props": {
428
- "preset": "hurt",
429
- "bus": "sfx",
430
- "volume": 0.55
431
- }
286
+ "props": { "preset": "hurt", "bus": "sfx", "volume": 0.55 }
432
287
  },
433
288
  {
434
289
  "name": "SfxWin",
435
290
  "type": "AudioPlayer",
436
- "props": {
437
- "preset": "win",
438
- "bus": "sfx",
439
- "volume": 0.8
440
- }
291
+ "props": { "preset": "win", "bus": "sfx", "volume": 0.8 }
441
292
  },
442
293
  {
443
294
  "name": "SfxLose",
444
295
  "type": "AudioPlayer",
445
- "props": {
446
- "preset": "lose",
447
- "bus": "sfx",
448
- "volume": 0.8
449
- }
296
+ "props": { "preset": "lose", "bus": "sfx", "volume": 0.8 }
450
297
  }
451
298
  ],
452
299
  "uid": "n_tlqu945vs98khzn8"
453
300
  },
454
301
  "connections": [
455
- {
456
- "signal": "died",
457
- "from": "Player/Vitals",
458
- "to": "/root",
459
- "handler": "loseLife"
460
- },
461
- {
462
- "signal": "damaged",
463
- "from": "Player/Vitals",
464
- "to": "SfxHurt",
465
- "handler": "play"
466
- },
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
- }
302
+ { "signal": "died", "from": "Player/Vitals", "to": "/root", "handler": "loseLife" },
303
+ { "signal": "damaged", "from": "Player/Vitals", "to": "SfxHurt", "handler": "play" },
304
+ { "signal": "won", "from": "/root", "to": "SfxWin", "handler": "play" },
305
+ { "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" }
479
306
  ]
480
307
  }
@@ -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.71.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
  }