incanto 0.70.0 → 0.72.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 (74) hide show
  1. package/bin/incanto-editor.mjs +15 -9
  2. package/bin/incanto-feel.mjs +11 -1
  3. package/bin/incanto-play.mjs +11 -1
  4. package/bin/incanto-playtest.mjs +11 -1
  5. package/bin/incanto-verify.mjs +36 -98
  6. package/dist/2d.d.ts +18 -3
  7. package/dist/2d.js +3 -3
  8. package/dist/3d.d.ts +3 -3
  9. package/dist/3d.js +6 -6
  10. package/dist/{agent8-CmNF01gA.js → agent8-BqZ0_O1u.js} +1 -1
  11. package/dist/{audio-player-DaMxqfNE.d.ts → audio-player-ioZ7VKC6.d.ts} +1 -1
  12. package/dist/{behavior-DZExDn9o.d.ts → behavior-Bf9P9oB2.d.ts} +12 -1
  13. package/dist/{create-game-Bwvh6q8A.js → create-game-C9rWqqAC.js} +37 -13
  14. package/dist/{create-game-C7ffQWW7.js → create-game-CmFAtLZp.js} +8 -7
  15. package/dist/debug.d.ts +1 -1
  16. package/dist/editor.js +1083 -1045
  17. package/dist/{environment-presets-D6Q5BxeE.js → environment-presets-BeHoaGhy.js} +3 -3
  18. package/dist/{frame-report-Dlq13Gyj.js → frame-report-D-_7YF2G.js} +1 -1
  19. package/dist/{gameplay-BfHkuzVb.js → gameplay-BlQe-M07.js} +108 -6
  20. package/dist/gameplay.d.ts +68 -4
  21. package/dist/gameplay.js +2 -2
  22. package/dist/index.d.ts +27 -4
  23. package/dist/index.js +4 -4
  24. package/dist/{loader-Cff09LMm.d.ts → loader-CBfQzB6S.d.ts} +10 -3
  25. package/dist/net.d.ts +2 -2
  26. package/dist/net.js +2 -2
  27. package/dist/{physics-2d-CE0Qvy3V.js → physics-2d-DrIgTlLk.js} +2 -2
  28. package/dist/{physics-3d-CpH-2gn5.js → physics-3d-BzOCw1VS.js} +3 -3
  29. package/dist/{picking-CQJ_PJKh.js → picking-C9McVJrO.js} +3 -3
  30. package/dist/react.d.ts +1 -1
  31. package/dist/react.js +1 -1
  32. package/dist/{register-6DYnKZcy.js → register-5IYfooAN.js} +49 -15
  33. package/dist/{register-Bkk0wSDB.js → register-C8HuRkOf.js} +100 -8
  34. package/dist/{replay-Dmw-PKQu.d.ts → replay-CBEChqXq.d.ts} +2 -2
  35. package/dist/{replay-DjAkAzMq.js → replay-datiAE-b.js} +15 -8
  36. package/dist/{save-slots-BXVg148r.js → save-slots-CEuJPUle.js} +102 -4
  37. package/dist/{split-screen-PL78oVXP.js → split-screen-Bm5DTElG.js} +3 -3
  38. package/dist/{split-screen--k-XpBjr.d.ts → split-screen-DkqLng6q.d.ts} +2 -2
  39. package/dist/{src-Cxfiv1Hg.js → src-DRSsRleD.js} +1 -1
  40. package/dist/{test-iHYVUcDK.js → test-Djuq3Vhq.js} +252 -24
  41. package/dist/test.d.ts +113 -5
  42. package/dist/test.js +3 -3
  43. package/dist/vite.d.ts +8 -1
  44. package/dist/vite.js +23 -6
  45. package/editor/assets/{agent8-D0MS174y.js → agent8-CMKz2cax.js} +1 -1
  46. package/editor/assets/{debug-BnXkKuYu.js → debug-DLrQm-az.js} +1 -1
  47. package/editor/assets/{index-CIu3uc3l.js → index-BEKuxYcE.js} +94 -94
  48. package/editor/index.html +1 -1
  49. package/package.json +1 -1
  50. package/schemas/scene.schema.json +4 -0
  51. package/skills/incanto-behaviors-and-scripts.md +30 -9
  52. package/skills/incanto-building-2d-games.md +26 -2
  53. package/skills/incanto-editor.md +4 -0
  54. package/skills/incanto-gameplay-behaviors.md +63 -1
  55. package/skills/incanto-hud.md +14 -0
  56. package/skills/incanto-node-reference.md +18 -2
  57. package/skills/incanto-playtesting.md +34 -0
  58. package/skills/incanto-save-slots.md +56 -1
  59. package/skills/incanto-scene-json-authoring.md +30 -0
  60. package/skills/incanto-verifying-your-game.md +11 -0
  61. package/templates-app/beacon-isle-3d/package.json +1 -1
  62. package/templates-app/beacon-isle-3d/src/game.scene.json +396 -386
  63. package/templates-app/molehill-2d/package.json +1 -1
  64. package/templates-app/molehill-2d/src/game.scene.json +10 -60
  65. package/templates-app/platformer-2d/package.json +1 -1
  66. package/templates-app/platformer-2d/src/game.scene.json +268 -504
  67. package/templates-app/star-survivor/package.json +1 -1
  68. package/templates-app/star-survivor/src/game.scene.json +23 -33
  69. package/templates-app/tps-3d/package.json +1 -1
  70. package/templates-app/tps-3d/src/game.scene.json +90 -105
  71. package/templates-app/village-quest-3d/package.json +1 -1
  72. package/templates-app/village-quest-3d/src/behaviors.ts +21 -4
  73. package/templates-app/village-quest-3d/src/grove.scene.json +99 -118
  74. package/templates-app/village-quest-3d/src/village.scene.json +449 -245
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
- "incanto": "^0.70.0",
14
+ "incanto": "^0.72.0",
15
15
  "three": "^0.184.0"
16
16
  },
17
17
  "devDependencies": {
@@ -78,15 +78,13 @@
78
78
  "idle": { "frames": [0, 5], "fps": 10, "loop": true },
79
79
  "move": { "frames": [6, 11], "fps": 14, "loop": true }
80
80
  }
81
- }
81
+ },
82
+ "uid": "n_yll7rl1d6l5iq4c0"
82
83
  },
83
84
  {
84
85
  "name": "Vitals",
85
86
  "type": "Node2D",
86
- "script": {
87
- "name": "Health",
88
- "props": { "max": 100, "invulnerableFor": 0.6 }
89
- },
87
+ "script": { "name": "Health", "props": { "max": 100, "invulnerableFor": 0.6 } },
90
88
  "uid": "n_bk99wi475y8y1c1g"
91
89
  }
92
90
  ],
@@ -124,7 +122,8 @@
124
122
  "idle": { "frames": [0, 6], "fps": 9, "loop": true },
125
123
  "move": { "frames": [7, 12], "fps": 12, "loop": true }
126
124
  }
127
- }
125
+ },
126
+ "uid": "n_9062s2tszonwgz8t"
128
127
  },
129
128
  {
130
129
  "name": "AI",
@@ -159,10 +158,7 @@
159
158
  {
160
159
  "name": "Drop",
161
160
  "type": "Node2D",
162
- "script": {
163
- "name": "EnemyDrop",
164
- "props": { "xp": 1, "color": "#ff8a5b" }
165
- },
161
+ "script": { "name": "EnemyDrop", "props": { "xp": 1, "color": "#ff8a5b" } },
166
162
  "uid": "n_ik9ixyv3h463m3bj"
167
163
  }
168
164
  ],
@@ -204,7 +200,8 @@
204
200
  "idle": { "frames": [0, 5], "fps": 9, "loop": true },
205
201
  "move": { "frames": [6, 11], "fps": 12, "loop": true }
206
202
  }
207
- }
203
+ },
204
+ "uid": "n_l6q192h44anj9bhr"
208
205
  },
209
206
  {
210
207
  "name": "AI",
@@ -239,10 +236,7 @@
239
236
  {
240
237
  "name": "Drop",
241
238
  "type": "Node2D",
242
- "script": {
243
- "name": "EnemyDrop",
244
- "props": { "xp": 2, "color": "#8fd0ff" }
245
- },
239
+ "script": { "name": "EnemyDrop", "props": { "xp": 2, "color": "#8fd0ff" } },
246
240
  "uid": "n_ldwl2f8oqqnmv0uc"
247
241
  }
248
242
  ],
@@ -277,49 +271,45 @@
277
271
  {
278
272
  "name": "SfxShoot",
279
273
  "type": "AudioPlayer",
280
- "props": { "preset": "shoot", "bus": "sfx", "volume": 0.28 }
274
+ "props": { "preset": "shoot", "bus": "sfx", "volume": 0.28 },
275
+ "uid": "n_d0ejl6328op4o5zo"
281
276
  },
282
277
  {
283
278
  "name": "SfxXp",
284
279
  "type": "AudioPlayer",
285
- "props": { "preset": "coin", "bus": "sfx", "volume": 0.4 }
280
+ "props": { "preset": "coin", "bus": "sfx", "volume": 0.4 },
281
+ "uid": "n_v82hfmgahhqd7qyw"
286
282
  },
287
283
  {
288
284
  "name": "SfxKill",
289
285
  "type": "AudioPlayer",
290
- "props": { "preset": "hit", "bus": "sfx", "volume": 0.3 }
286
+ "props": { "preset": "hit", "bus": "sfx", "volume": 0.3 },
287
+ "uid": "n_7cwqf3w8q2avgnll"
291
288
  },
292
289
  {
293
290
  "name": "SfxHurt",
294
291
  "type": "AudioPlayer",
295
- "props": { "preset": "hurt", "bus": "sfx", "volume": 0.55 }
292
+ "props": { "preset": "hurt", "bus": "sfx", "volume": 0.55 },
293
+ "uid": "n_xgtwcz56c0kmh2mr"
296
294
  },
297
295
  {
298
296
  "name": "SfxWin",
299
297
  "type": "AudioPlayer",
300
- "props": { "preset": "win", "bus": "sfx", "volume": 0.8 }
298
+ "props": { "preset": "win", "bus": "sfx", "volume": 0.8 },
299
+ "uid": "n_ylglu2j9xvu80el3"
301
300
  },
302
301
  {
303
302
  "name": "SfxLose",
304
303
  "type": "AudioPlayer",
305
- "props": { "preset": "lose", "bus": "sfx", "volume": 0.8 }
304
+ "props": { "preset": "lose", "bus": "sfx", "volume": 0.8 },
305
+ "uid": "n_9c48ixvudwh3egqc"
306
306
  }
307
307
  ],
308
308
  "uid": "n_tlqu945vs98khzn8"
309
309
  },
310
310
  "connections": [
311
- {
312
- "signal": "died",
313
- "from": "Player/Vitals",
314
- "to": "/root",
315
- "handler": "loseLife"
316
- },
317
- {
318
- "signal": "damaged",
319
- "from": "Player/Vitals",
320
- "to": "SfxHurt",
321
- "handler": "play"
322
- },
311
+ { "signal": "died", "from": "Player/Vitals", "to": "/root", "handler": "loseLife" },
312
+ { "signal": "damaged", "from": "Player/Vitals", "to": "SfxHurt", "handler": "play" },
323
313
  { "signal": "won", "from": "/root", "to": "SfxWin", "handler": "play" },
324
314
  { "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" }
325
315
  ]
@@ -13,7 +13,7 @@
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.70.0",
16
+ "incanto": "^0.72.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -93,9 +93,11 @@
93
93
  "size": [48, 0.1, 48],
94
94
  "material": { "color": "#3a4253", "roughness": 1 },
95
95
  "receiveShadow": true
96
- }
96
+ },
97
+ "uid": "n_fhrx2w1swdg4aouk"
97
98
  }
98
- ]
99
+ ],
100
+ "uid": "n_a9g237qshrv6q3zr"
99
101
  },
100
102
  {
101
103
  "name": "Wall1",
@@ -113,9 +115,11 @@
113
115
  "size": [49, 3, 0.5],
114
116
  "material": { "color": "#4a5468", "roughness": 0.9 },
115
117
  "receiveShadow": true
116
- }
118
+ },
119
+ "uid": "n_7zgr4tm46gypk29z"
117
120
  }
118
- ]
121
+ ],
122
+ "uid": "n_dva41ykintj4ckoz"
119
123
  },
120
124
  {
121
125
  "name": "Wall2",
@@ -133,9 +137,11 @@
133
137
  "size": [49, 3, 0.5],
134
138
  "material": { "color": "#4a5468", "roughness": 0.9 },
135
139
  "receiveShadow": true
136
- }
140
+ },
141
+ "uid": "n_m70mrkagrg1igwdk"
137
142
  }
138
- ]
143
+ ],
144
+ "uid": "n_9gan2h8arfads6nj"
139
145
  },
140
146
  {
141
147
  "name": "Wall3",
@@ -153,9 +159,11 @@
153
159
  "size": [0.5, 3, 48],
154
160
  "material": { "color": "#4a5468", "roughness": 0.9 },
155
161
  "receiveShadow": true
156
- }
162
+ },
163
+ "uid": "n_i5eorlm18c6o3x10"
157
164
  }
158
- ]
165
+ ],
166
+ "uid": "n_pibc3l5f1peblsvr"
159
167
  },
160
168
  {
161
169
  "name": "Wall4",
@@ -173,9 +181,11 @@
173
181
  "size": [0.5, 3, 48],
174
182
  "material": { "color": "#4a5468", "roughness": 0.9 },
175
183
  "receiveShadow": true
176
- }
184
+ },
185
+ "uid": "n_w7s4c7cv69pfnbk7"
177
186
  }
178
- ]
187
+ ],
188
+ "uid": "n_4lh2uesc09jprpaf"
179
189
  },
180
190
  {
181
191
  "name": "Cover1",
@@ -194,9 +204,11 @@
194
204
  "material": { "color": "#5b6678", "roughness": 0.85, "metalness": 0.1 },
195
205
  "castShadow": true,
196
206
  "receiveShadow": true
197
- }
207
+ },
208
+ "uid": "n_hsy2c0ukmioj0611"
198
209
  }
199
- ]
210
+ ],
211
+ "uid": "n_wrzlr0jqbyxh7m11"
200
212
  },
201
213
  {
202
214
  "name": "Cover2",
@@ -215,9 +227,11 @@
215
227
  "material": { "color": "#5b6678", "roughness": 0.85, "metalness": 0.1 },
216
228
  "castShadow": true,
217
229
  "receiveShadow": true
218
- }
230
+ },
231
+ "uid": "n_jap0t9ofeggpl03j"
219
232
  }
220
- ]
233
+ ],
234
+ "uid": "n_6ailumev172o037a"
221
235
  },
222
236
  {
223
237
  "name": "Pillar1",
@@ -235,9 +249,11 @@
235
249
  "size": [1.1, 4, 1.1],
236
250
  "material": { "color": "#454f61", "roughness": 0.95 },
237
251
  "castShadow": true
238
- }
252
+ },
253
+ "uid": "n_0pm93he7baqzdk5d"
239
254
  }
240
- ]
255
+ ],
256
+ "uid": "n_xeh6r9631usk2i6w"
241
257
  },
242
258
  {
243
259
  "name": "Pillar2",
@@ -255,9 +271,11 @@
255
271
  "size": [1.1, 4, 1.1],
256
272
  "material": { "color": "#454f61", "roughness": 0.95 },
257
273
  "castShadow": true
258
- }
274
+ },
275
+ "uid": "n_mkfowx2du427b74v"
259
276
  }
260
- ]
277
+ ],
278
+ "uid": "n_7ksm42z50jbh13np"
261
279
  },
262
280
  {
263
281
  "name": "Ramp",
@@ -276,9 +294,11 @@
276
294
  "material": { "color": "#5b6678", "roughness": 0.85 },
277
295
  "castShadow": true,
278
296
  "receiveShadow": true
279
- }
297
+ },
298
+ "uid": "n_fgwyll0y26mldnum"
280
299
  }
281
- ]
300
+ ],
301
+ "uid": "n_3jp7mtbnbx0rhv29"
282
302
  },
283
303
  {
284
304
  "name": "Sun",
@@ -289,14 +309,17 @@
289
309
  "castShadow": true,
290
310
  "shadowArea": 58,
291
311
  "shadowMapSize": 2048
292
- }
312
+ },
313
+ "uid": "n_23zij3jcjcb72y70"
293
314
  },
294
315
  {
295
316
  "name": "FillLight",
296
317
  "type": "DirectionalLight3D",
297
- "props": { "position": [-38, 36, -28], "intensity": 0.4, "color": "#b9d4ff" }
318
+ "props": { "position": [-38, 36, -28], "intensity": 0.4, "color": "#b9d4ff" },
319
+ "uid": "n_28u31t86eb8xnyjc"
298
320
  }
299
- ]
321
+ ],
322
+ "uid": "n_9yavlr08c9026im9"
300
323
  },
301
324
  {
302
325
  "name": "Player",
@@ -336,7 +359,8 @@
336
359
  "animation": "$anims/idle",
337
360
  "castShadow": true,
338
361
  "position": [0, -0.85, 0]
339
- }
362
+ },
363
+ "uid": "n_sirva2bpp1i01f1u"
340
364
  },
341
365
  {
342
366
  "name": "Muzzle",
@@ -352,7 +376,8 @@
352
376
  "spatial": true,
353
377
  "refDistance": 3,
354
378
  "maxDistance": 50
355
- }
379
+ },
380
+ "uid": "n_r6cpq75vhtxu4q18"
356
381
  },
357
382
  {
358
383
  "name": "KillSfx",
@@ -363,19 +388,23 @@
363
388
  "spatial": true,
364
389
  "refDistance": 4,
365
390
  "maxDistance": 60
366
- }
391
+ },
392
+ "uid": "n_fw07t0wpfd7wwtrb"
367
393
  },
368
394
  {
369
395
  "name": "EmptySfx",
370
396
  "type": "AudioPlayer",
371
- "props": { "preset": "blip", "volume": 0.4, "pitch": 0.7 }
397
+ "props": { "preset": "blip", "volume": 0.4, "pitch": 0.7 },
398
+ "uid": "n_0vtkipp7h0so4mx2"
372
399
  },
373
400
  {
374
401
  "name": "Flash",
375
402
  "type": "OmniLight3D",
376
- "props": { "color": "#ffd27f", "intensity": 0, "range": 7 }
403
+ "props": { "color": "#ffd27f", "intensity": 0, "range": 7 },
404
+ "uid": "n_s3033w4bwtacojs1"
377
405
  }
378
- ]
406
+ ],
407
+ "uid": "n_jstha9c29p0dlioy"
379
408
  }
380
409
  ],
381
410
  "uid": "n_nx7dsn65lts2arru"
@@ -383,12 +412,14 @@
383
412
  {
384
413
  "name": "Camera",
385
414
  "type": "Camera3D",
386
- "props": { "position": [0, 2.6, 23], "fov": 70, "current": true, "far": 400 }
415
+ "props": { "position": [0, 2.6, 23], "fov": 70, "current": true, "far": 400 },
416
+ "uid": "n_fldx3xcpdr27y1a3"
387
417
  },
388
418
  {
389
419
  "name": "HurtSfx",
390
420
  "type": "AudioPlayer",
391
- "props": { "preset": "hurt", "volume": 0.6 }
421
+ "props": { "preset": "hurt", "volume": 0.6 },
422
+ "uid": "n_oz5kawq52q4wmece"
392
423
  },
393
424
  {
394
425
  "name": "Spawner",
@@ -398,24 +429,9 @@
398
429
  "name": "WaveSpawner",
399
430
  "props": {
400
431
  "waves": [
401
- {
402
- "prefab": "EnemyTemplate",
403
- "count": 4,
404
- "interval": 1.1,
405
- "delayBefore": 2
406
- },
407
- {
408
- "prefab": "EnemyTemplate",
409
- "count": 6,
410
- "interval": 0.9,
411
- "delayBefore": 3
412
- },
413
- {
414
- "prefab": "EnemyTemplate",
415
- "count": 8,
416
- "interval": 0.7,
417
- "delayBefore": 3
418
- }
432
+ { "prefab": "EnemyTemplate", "count": 4, "interval": 1.1, "delayBefore": 2 },
433
+ { "prefab": "EnemyTemplate", "count": 6, "interval": 0.9, "delayBefore": 3 },
434
+ { "prefab": "EnemyTemplate", "count": 8, "interval": 0.7, "delayBefore": 3 }
419
435
  ]
420
436
  }
421
437
  },
@@ -444,7 +460,8 @@
444
460
  "emissiveIntensity": 0.4
445
461
  },
446
462
  "castShadow": true
447
- }
463
+ },
464
+ "uid": "n_cn4tjqgi2tk2t724"
448
465
  },
449
466
  {
450
467
  "name": "Eye",
@@ -453,12 +470,9 @@
453
470
  "mesh": "sphere",
454
471
  "size": [0.12, 0.12, 0.12],
455
472
  "position": [0, 0.55, 0.32],
456
- "material": {
457
- "color": "#ffec5c",
458
- "emissive": "#ffd000",
459
- "emissiveIntensity": 2
460
- }
461
- }
473
+ "material": { "color": "#ffec5c", "emissive": "#ffd000", "emissiveIntensity": 2 }
474
+ },
475
+ "uid": "n_xjsfyhxuxp0wkse1"
462
476
  },
463
477
  {
464
478
  "name": "AI",
@@ -502,7 +516,8 @@
502
516
  "maxDistance": 35,
503
517
  "rolloff": "inverse",
504
518
  "pitch": 0.6
505
- }
519
+ },
520
+ "uid": "n_jznb5aprs78r1qs0"
506
521
  }
507
522
  ],
508
523
  "uid": "n_17sotvbult7pgelw"
@@ -520,7 +535,8 @@
520
535
  "area": [44, 44],
521
536
  "seed": 17,
522
537
  "height": 6
523
- }
538
+ },
539
+ "uid": "n_ttfovkru8078togk"
524
540
  },
525
541
  {
526
542
  "name": "Hud",
@@ -540,7 +556,8 @@
540
556
  {
541
557
  "name": "PausedTitle",
542
558
  "type": "UiText",
543
- "props": { "text": "Paused", "size": 24 }
559
+ "props": { "text": "Paused", "size": 24 },
560
+ "uid": "n_ffjlx3r053emjjhc"
544
561
  },
545
562
  {
546
563
  "name": "ResumeHint",
@@ -549,59 +566,27 @@
549
566
  "text": "Esc to resume · Enter to restart",
550
567
  "size": 13,
551
568
  "color": "#c9d2dd"
552
- }
553
- },
554
- {
555
- "name": "Volume",
556
- "type": "UiVolumeSlider",
557
- "uid": "n_5p4124fbnjxx8s0c"
558
- },
559
- {
560
- "name": "Mute",
561
- "type": "UiMuteToggle",
562
- "uid": "n_e1lyn3mvsy3vj2qr"
563
- },
564
- {
565
- "name": "Quality",
566
- "type": "UiQualitySelect",
567
- "uid": "n_35r1h71i75yft3ex"
568
- },
569
- {
570
- "name": "RenderScale",
571
- "type": "UiRenderScaleSelect",
572
- "uid": "n_emg840df89fqsriw"
569
+ },
570
+ "uid": "n_atxlw44pqdn146zj"
573
571
  },
574
- {
575
- "name": "FrameCap",
576
- "type": "UiFrameCapSelect",
577
- "uid": "n_mgqwgghn0bx900ip"
578
- }
579
- ]
572
+ { "name": "Volume", "type": "UiVolumeSlider", "uid": "n_5p4124fbnjxx8s0c" },
573
+ { "name": "Mute", "type": "UiMuteToggle", "uid": "n_e1lyn3mvsy3vj2qr" },
574
+ { "name": "Quality", "type": "UiQualitySelect", "uid": "n_35r1h71i75yft3ex" },
575
+ { "name": "RenderScale", "type": "UiRenderScaleSelect", "uid": "n_emg840df89fqsriw" },
576
+ { "name": "FrameCap", "type": "UiFrameCapSelect", "uid": "n_mgqwgghn0bx900ip" }
577
+ ],
578
+ "uid": "n_8bcregfz1ajwi03l"
580
579
  }
581
- ]
580
+ ],
581
+ "uid": "n_dimifr19c9et8cr4"
582
582
  }
583
583
  ],
584
584
  "uid": "n_h46qaejxdqadqc1c"
585
585
  },
586
586
  "connections": [
587
587
  { "signal": "died", "from": "Player", "to": "/root", "handler": "loseLife" },
588
- {
589
- "signal": "lifeLost",
590
- "from": "/root",
591
- "to": "Player",
592
- "handler": "reviveFull"
593
- },
594
- {
595
- "signal": "damaged",
596
- "from": "Player",
597
- "to": "HurtSfx",
598
- "handler": "play"
599
- },
600
- {
601
- "signal": "allCleared",
602
- "from": "Spawner",
603
- "to": "Hud",
604
- "handler": "onAllCleared"
605
- }
588
+ { "signal": "lifeLost", "from": "/root", "to": "Player", "handler": "reviveFull" },
589
+ { "signal": "damaged", "from": "Player", "to": "HurtSfx", "handler": "play" },
590
+ { "signal": "allCleared", "from": "Spawner", "to": "Hud", "handler": "onAllCleared" }
606
591
  ]
607
592
  }
@@ -13,7 +13,7 @@
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.70.0",
16
+ "incanto": "^0.72.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -23,7 +23,7 @@ import type {
23
23
  UiText,
24
24
  Voice,
25
25
  } from 'incanto';
26
- import { Behavior, findPath, gridFromRows } from 'incanto';
26
+ import { Behavior, findPath, gridFromRows, Timer } from 'incanto';
27
27
  import { mergeSolidRects, parseCells } from 'incanto/2d';
28
28
  import type { Camera3D, CharacterController3D, Node3D, Trail3D } from 'incanto/3d';
29
29
  import { MeshInstance3D, Particles3D, StaticBody3D } from 'incanto/3d';
@@ -594,7 +594,18 @@ export class SwordStrike extends Behavior {
594
594
  /** Strike bookkeeping: hits per wolf (module-scope — survives behavior instances). */
595
595
  const wolfHits = new WeakMap<Node, number>();
596
596
 
597
- /** Red hurt-flash on every mesh of the wolf, reverted a beat later. */
597
+ /**
598
+ * Red hurt-flash on every mesh of the wolf, reverted a beat later.
599
+ *
600
+ * The beat is a `Timer`, not a `setTimeout` — which is what this was, and what
601
+ * `incanto-behaviors-and-scripts.md` calls "the canonical serializable game
602
+ * clock — NEVER setTimeout in game logic". Three things a wall-clock timeout
603
+ * gets wrong here: it keeps running while the game is paused (`timeScale: 0`),
604
+ * it ignores `timeScale` entirely, and in a headless run — where a minute of
605
+ * play takes a second — it fires long after the frame that asked for it, so a
606
+ * seeded playtest stops being reproducible. The node is a child of the wolf,
607
+ * so a wolf that dies mid-flash takes its timer with it.
608
+ */
598
609
  function flashWolf(wolf: Node): void {
599
610
  const meshes: { material: Record<string, unknown> }[] = [];
600
611
  const walk = (n: Node): void => {
@@ -606,11 +617,17 @@ function flashWolf(wolf: Node): void {
606
617
  walk(wolf);
607
618
  const originals = meshes.map((m) => m.material);
608
619
  for (const m of meshes) m.material = { ...m.material, color: '#ff5544' };
609
- setTimeout(() => {
620
+ const beat = new Timer('Unflash');
621
+ beat.waitTime = 0.13;
622
+ beat.oneShot = true;
623
+ beat.autostart = true;
624
+ beat.on('timeout', () => {
610
625
  meshes.forEach((m, i) => {
611
626
  if (wolf.tree) m.material = originals[i] as Record<string, unknown>;
612
627
  });
613
- }, 130);
628
+ beat.queueFree();
629
+ });
630
+ wolf.addChild(beat);
614
631
  }
615
632
 
616
633
  export const BEHAVIORS = { VillageDirector, GroveDirector, SwordStrike };