incanto 0.74.0 → 0.76.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.
- package/bin/incanto-multiplay.mjs +52 -4
- package/dist/2d.d.ts +83 -7
- package/dist/2d.js +4 -3
- package/dist/3d.d.ts +861 -167
- package/dist/3d.js +6 -6
- package/dist/{agent8-CHTT4unP.js → agent8-B-bh3n0X.js} +1 -1
- package/dist/{audio-player-BOrk4eQx.d.ts → audio-player-BRo2uvG6.d.ts} +23 -2
- package/dist/{behavior-J1cmVah0.d.ts → behavior-B_245qRy.d.ts} +15 -1
- package/dist/{create-game-ssvGvP7W.js → create-game-BMIPKrQW.js} +84 -8
- package/dist/{create-game-BAiA-FjP.js → create-game-Dai5vfVu.js} +80 -11
- package/dist/debug.d.ts +1 -1
- package/dist/{editor-switch-CFU9mCec.d.ts → editor-switch-Bzt0GzVp.d.ts} +13 -3
- package/dist/editor.js +1600 -1529
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-BkqlWewf.js → environment-presets-e_9YCnGu.js} +2361 -104
- package/dist/{gameplay-Bxe1sMVT.js → gameplay-Bfff_beb.js} +10919 -6798
- package/dist/gameplay.d.ts +573 -31
- package/dist/gameplay.js +3 -2
- package/dist/index.d.ts +150 -6
- package/dist/index.js +6 -6
- package/dist/{loader-BAeWMYfF.d.ts → loader-CbkVdXL8.d.ts} +2 -2
- package/dist/net.d.ts +2 -2
- package/dist/net.js +2 -2
- package/dist/{physics-2d-C-SfvjzH.js → physics-2d-Tuf5LAyq.js} +250 -31
- package/dist/{physics-3d-rOzeg890.js → physics-3d-C5MOujfQ.js} +673 -51
- package/dist/{picking-BMV34Pjl.js → picking-DIwE48sU.js} +10 -7
- package/dist/{save-slots-CEUGeLuB.js → pose-ByFB_J3O.js} +217 -11
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-DRQyZKGU.js → register-7E3P1gz6.js} +191 -430
- package/dist/{register-CjociOtt.js → register-B768Qedj.js} +596 -2
- package/dist/{replay-B6BbgGM_.js → replay-DrqQ6dZj.js} +133 -18
- package/dist/{replay-wZK5Ok5c.d.ts → replay-W5nCw_cU.d.ts} +21 -2
- package/dist/{rng-Bb-IutXB.d.ts → rng-BsXZg3D6.d.ts} +7 -0
- package/dist/{split-screen-BxZ5rBe7.js → split-screen-B-dIcQsQ.js} +10 -5
- package/dist/{split-screen-D6sx8z3Z.d.ts → split-screen-CiLJf2-C.d.ts} +3 -3
- package/dist/{sprite-animation-Bz3DHD_4.js → sprite-animation-SQa5gIu2.js} +8 -6
- package/dist/{src-CqtvYtSN.js → src-D823V07k.js} +1 -1
- package/dist/{test-CEl0y2Sw.js → test-CVbxnXlv.js} +487 -82
- package/dist/test.d.ts +90 -12
- package/dist/test.js +2 -2
- package/dist/vite.js +34 -5
- package/editor/assets/{agent8-BlzturGi.js → agent8-DA-I5qhv.js} +1 -1
- package/editor/assets/{debug-BMUi8usj.js → debug-99tEPoGm.js} +1 -1
- package/editor/assets/index-Db74LZY-.js +11579 -0
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +1639 -280
- package/skills/incanto-3d-character.md +383 -20
- package/skills/incanto-audio.md +24 -2
- package/skills/incanto-building-2d-games.md +21 -2
- package/skills/incanto-building-3d-games.md +38 -3
- package/skills/incanto-editor.md +29 -8
- package/skills/incanto-environment.md +257 -14
- package/skills/incanto-game-feel.md +60 -1
- package/skills/incanto-gameplay-behaviors.md +668 -20
- package/skills/incanto-hud.md +116 -0
- package/skills/incanto-multiplayer.md +141 -2
- package/skills/incanto-node-reference.md +398 -16
- package/skills/incanto-performance.md +32 -0
- package/skills/incanto-physics-and-input.md +241 -13
- package/skills/incanto-playtesting.md +2 -2
- package/skills/incanto-save-slots.md +61 -2
- package/skills/incanto-scene-json-authoring.md +13 -0
- package/skills/incanto-verifying-your-game.md +86 -3
- package/templates-app/beacon-isle-3d/PROJECT/Status.md +12 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +157 -3
- package/templates-app/molehill-2d/PROJECT/Status.md +7 -0
- package/templates-app/molehill-2d/package.json +1 -1
- package/templates-app/molehill-2d/src/game.scene.json +129 -2
- package/templates-app/platformer-2d/PROJECT/Status.md +7 -0
- package/templates-app/platformer-2d/docs/project-2d-rules.md +3 -2
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/behaviors.ts +40 -26
- package/templates-app/platformer-2d/src/game.scene.json +144 -2
- package/templates-app/platformer-2d/verify.ts +18 -1
- package/templates-app/star-survivor/PROJECT/Status.md +7 -0
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/behaviors.ts +9 -8
- package/templates-app/star-survivor/src/game.scene.json +142 -4
- package/templates-app/star-survivor/verify.ts +13 -0
- package/templates-app/tps-3d/PROJECT/Status.md +6 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +71 -10
- package/templates-app/village-quest-3d/PROJECT/Status.md +7 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/behaviors.ts +8 -16
- package/templates-app/village-quest-3d/src/grove.scene.json +121 -5
- package/templates-app/village-quest-3d/src/village.scene.json +126 -2
- package/templates-app/village-quest-3d/verify.ts +52 -2
- package/editor/assets/index-CrUCQoaB.js +0 -11046
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"touch": "joystick"
|
|
19
19
|
},
|
|
20
|
-
"jump": { "type": "button", "keys": ["Space", "KeyW", "ArrowUp"], "touch": "button" }
|
|
20
|
+
"jump": { "type": "button", "keys": ["Space", "KeyW", "ArrowUp"], "touch": "button" },
|
|
21
|
+
"pause": { "type": "button", "keys": ["Escape"] },
|
|
22
|
+
"restart": { "type": "button", "keys": ["KeyR"] }
|
|
21
23
|
},
|
|
22
24
|
"assets": {
|
|
23
25
|
"knight": {
|
|
@@ -1235,6 +1237,23 @@
|
|
|
1235
1237
|
},
|
|
1236
1238
|
"uid": "n_odjl6nsgedmaiijc"
|
|
1237
1239
|
},
|
|
1240
|
+
{
|
|
1241
|
+
"name": "Vitals",
|
|
1242
|
+
"type": "Node",
|
|
1243
|
+
"script": {
|
|
1244
|
+
"name": "Health",
|
|
1245
|
+
"props": {
|
|
1246
|
+
"max": 3,
|
|
1247
|
+
"invulnerableFor": 1.1,
|
|
1248
|
+
"knockback": 420,
|
|
1249
|
+
"knockbackSeconds": 0.22,
|
|
1250
|
+
"knockUp": 360,
|
|
1251
|
+
"staggerSeconds": 0.22,
|
|
1252
|
+
"applyKnockback": false
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
"uid": "n_636k806hf3tc9wxe"
|
|
1256
|
+
},
|
|
1238
1257
|
{
|
|
1239
1258
|
"name": "Feet",
|
|
1240
1259
|
"type": "Area2D",
|
|
@@ -1334,6 +1353,98 @@
|
|
|
1334
1353
|
"color": "#f4f4f4"
|
|
1335
1354
|
},
|
|
1336
1355
|
"uid": "n_xzbq736pxmlog2ze"
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
"name": "PauseMenu",
|
|
1359
|
+
"type": "UiPanel",
|
|
1360
|
+
"uid": "n_18nwr09ab7ql2z4r",
|
|
1361
|
+
"props": {
|
|
1362
|
+
"anchor": "center",
|
|
1363
|
+
"layout": "column",
|
|
1364
|
+
"gap": 12,
|
|
1365
|
+
"padding": 22,
|
|
1366
|
+
"visible": false,
|
|
1367
|
+
"background": "rgba(10,14,22,0.92)",
|
|
1368
|
+
"radius": 12
|
|
1369
|
+
},
|
|
1370
|
+
"children": [
|
|
1371
|
+
{
|
|
1372
|
+
"name": "PausedTitle",
|
|
1373
|
+
"type": "UiText",
|
|
1374
|
+
"uid": "n_zbj0pg98lnk8rv82",
|
|
1375
|
+
"props": { "text": "PAUSED", "size": 26 }
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
"name": "Resume",
|
|
1379
|
+
"type": "UiButton",
|
|
1380
|
+
"uid": "n_pn2pcwtmdaxii0ir",
|
|
1381
|
+
"props": { "text": "RESUME", "size": 18 }
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
"name": "PauseOptions",
|
|
1385
|
+
"type": "UiButton",
|
|
1386
|
+
"uid": "n_rlol3m8e7ev1akf3",
|
|
1387
|
+
"props": { "text": "OPTIONS", "size": 16 }
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"name": "Restart",
|
|
1391
|
+
"type": "UiButton",
|
|
1392
|
+
"uid": "n_08baib5wy10vg0ar",
|
|
1393
|
+
"props": { "text": "START OVER", "size": 16 }
|
|
1394
|
+
}
|
|
1395
|
+
]
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
"name": "Options",
|
|
1399
|
+
"type": "UiPanel",
|
|
1400
|
+
"uid": "n_rnlo15z2gqd6hma0",
|
|
1401
|
+
"props": {
|
|
1402
|
+
"anchor": "center",
|
|
1403
|
+
"layout": "column",
|
|
1404
|
+
"gap": 10,
|
|
1405
|
+
"padding": 22,
|
|
1406
|
+
"visible": false,
|
|
1407
|
+
"background": "rgba(10,14,22,0.92)",
|
|
1408
|
+
"radius": 12
|
|
1409
|
+
},
|
|
1410
|
+
"children": [
|
|
1411
|
+
{
|
|
1412
|
+
"name": "OptionsTitle",
|
|
1413
|
+
"type": "UiText",
|
|
1414
|
+
"uid": "n_dybfe2jy973oxrw8",
|
|
1415
|
+
"props": { "text": "OPTIONS", "size": 24 }
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
"name": "Master",
|
|
1419
|
+
"type": "UiVolumeSlider",
|
|
1420
|
+
"uid": "n_5rd1metkz071rwyl",
|
|
1421
|
+
"props": { "label": "master", "bus": "master", "width": 260 }
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
"name": "Sfx",
|
|
1425
|
+
"type": "UiVolumeSlider",
|
|
1426
|
+
"uid": "n_3jz8z3sx850i1vlh",
|
|
1427
|
+
"props": { "label": "effects", "bus": "sfx", "width": 260 }
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
"name": "Mute",
|
|
1431
|
+
"type": "UiMuteToggle",
|
|
1432
|
+
"uid": "n_9jn0ncey479106t8",
|
|
1433
|
+
"props": { "label": "mute everything" }
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
"name": "Quality",
|
|
1437
|
+
"type": "UiQualitySelect",
|
|
1438
|
+
"uid": "n_w0dd38u2gqui7oli",
|
|
1439
|
+
"props": { "label": "quality" }
|
|
1440
|
+
},
|
|
1441
|
+
{
|
|
1442
|
+
"name": "Back",
|
|
1443
|
+
"type": "UiButton",
|
|
1444
|
+
"uid": "n_huasc2fyi2sc1ejj",
|
|
1445
|
+
"props": { "text": "BACK", "size": 16 }
|
|
1446
|
+
}
|
|
1447
|
+
]
|
|
1337
1448
|
}
|
|
1338
1449
|
],
|
|
1339
1450
|
"uid": "n_ng718r3uiw1madux"
|
|
@@ -1476,6 +1587,12 @@
|
|
|
1476
1587
|
"props": { "size": [160, 20], "color": "#9aa3bd", "anchor": [0.5, 0.5] }
|
|
1477
1588
|
}
|
|
1478
1589
|
]
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
"name": "Flow",
|
|
1593
|
+
"type": "Node",
|
|
1594
|
+
"uid": "n_i26ub26uzhmxyulv",
|
|
1595
|
+
"script": { "name": "GameFlow" }
|
|
1479
1596
|
}
|
|
1480
1597
|
],
|
|
1481
1598
|
"uid": "n_d9t51gs0vhpnp2jj"
|
|
@@ -1510,6 +1627,31 @@
|
|
|
1510
1627
|
{ "signal": "collected", "from": "Gem1", "to": "SfxCheckpoint", "handler": "play" },
|
|
1511
1628
|
{ "signal": "collected", "from": "Gem2", "to": "SfxCheckpoint", "handler": "play" },
|
|
1512
1629
|
{ "signal": "won", "from": "/root", "to": "SfxWin", "handler": "play" },
|
|
1513
|
-
{ "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" }
|
|
1630
|
+
{ "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" },
|
|
1631
|
+
{
|
|
1632
|
+
"from": "/root/HUD/PauseMenu/Resume",
|
|
1633
|
+
"signal": "pressed",
|
|
1634
|
+
"to": "/root/Flow",
|
|
1635
|
+
"handler": "resume"
|
|
1636
|
+
},
|
|
1637
|
+
{
|
|
1638
|
+
"from": "/root/HUD/PauseMenu/PauseOptions",
|
|
1639
|
+
"signal": "pressed",
|
|
1640
|
+
"to": "/root/Flow",
|
|
1641
|
+
"handler": "screen",
|
|
1642
|
+
"args": ["/root/HUD/Options"]
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
"from": "/root/HUD/PauseMenu/Restart",
|
|
1646
|
+
"signal": "pressed",
|
|
1647
|
+
"to": "/root/Flow",
|
|
1648
|
+
"handler": "restart"
|
|
1649
|
+
},
|
|
1650
|
+
{
|
|
1651
|
+
"from": "/root/HUD/Options/Back",
|
|
1652
|
+
"signal": "pressed",
|
|
1653
|
+
"to": "/root/Flow",
|
|
1654
|
+
"handler": "back"
|
|
1655
|
+
}
|
|
1514
1656
|
]
|
|
1515
1657
|
}
|
|
@@ -124,6 +124,8 @@ interface Player {
|
|
|
124
124
|
let enemiesBefore = 0;
|
|
125
125
|
let enemiesAfterStomp = 0;
|
|
126
126
|
let heartsAfterHit = 3;
|
|
127
|
+
let hitKnock = 0; // px the knight was kicked AWAY from Gob2 while holding TOWARDS it
|
|
128
|
+
let hitLift = 0; // px it rose (knockUp)
|
|
127
129
|
|
|
128
130
|
const result = await runScript(sceneJson, {
|
|
129
131
|
durationMs: 4000,
|
|
@@ -165,10 +167,21 @@ interface Player {
|
|
|
165
167
|
do: (ctx) => {
|
|
166
168
|
const player = ctx.getNode('Player') as unknown as Body;
|
|
167
169
|
const gob = ctx.getNode('Gob2') as unknown as Body;
|
|
168
|
-
|
|
170
|
+
// just LEFT of the goblin, overlapping it, and holding RIGHT into it:
|
|
171
|
+
// the hit's kick must win the stick (stagger) and carry the knight left and up
|
|
172
|
+
player.position = [(gob.position[0] ?? 0) - 12, gob.position[1] ?? 0];
|
|
169
173
|
player.velocity = [0, 0];
|
|
174
|
+
ctx.engine.input.setActionVector('move', 1, 0);
|
|
170
175
|
for (let i = 0; i < 3; i++) ctx.engine.step();
|
|
171
176
|
heartsAfterHit = (ctx.getNode('Player').behavior as unknown as Player).hearts;
|
|
177
|
+
const x0 = player.position[0] ?? 0;
|
|
178
|
+
const y0 = player.position[1] ?? 0;
|
|
179
|
+
for (let i = 0; i < 12; i++) {
|
|
180
|
+
ctx.engine.step();
|
|
181
|
+
hitKnock = Math.max(hitKnock, x0 - (player.position[0] ?? 0));
|
|
182
|
+
hitLift = Math.max(hitLift, y0 - (player.position[1] ?? 0));
|
|
183
|
+
}
|
|
184
|
+
ctx.engine.input.setActionVector('move', 0, 0);
|
|
172
185
|
},
|
|
173
186
|
},
|
|
174
187
|
],
|
|
@@ -184,6 +197,10 @@ interface Player {
|
|
|
184
197
|
scores.some((s) => s >= 50),
|
|
185
198
|
);
|
|
186
199
|
ok('a side hit cost a heart', heartsAfterHit < 3);
|
|
200
|
+
ok(
|
|
201
|
+
`…and kicked the knight ${hitKnock.toFixed(0)} px AWAY against a held stick, ${hitLift.toFixed(0)} px up (Health.knockback/knockUp/staggerSeconds)`,
|
|
202
|
+
hitKnock > 20 && hitLift > 10,
|
|
203
|
+
);
|
|
187
204
|
ok('runScript reported no failures (coin/stomp/hit path)', result.ok);
|
|
188
205
|
}
|
|
189
206
|
|
|
@@ -42,3 +42,10 @@
|
|
|
42
42
|
- Spawning leans on the built-in `Spawner` + `Director` scatter (the engine's clone
|
|
43
43
|
path is internal). Add enemy types by copying a template + spawner into the
|
|
44
44
|
`Director.spawners` list.
|
|
45
|
+
|
|
46
|
+
## Pausing
|
|
47
|
+
|
|
48
|
+
ESC pauses and shows the pause menu; OPTIONS opens the settings over it and
|
|
49
|
+
BACK returns to the menu that asked. All of it is `GameFlow` (`pausePanelPath`,
|
|
50
|
+
`screen(path)`, `back()`) wired with `connections` — there is no TypeScript for
|
|
51
|
+
any screen in this game.
|
|
@@ -36,7 +36,7 @@ interface HealthLike {
|
|
|
36
36
|
current: number;
|
|
37
37
|
max: number;
|
|
38
38
|
isDead: boolean;
|
|
39
|
-
damage(n: number): void;
|
|
39
|
+
damage(n: number, from?: readonly number[]): void;
|
|
40
40
|
heal(n: number): void;
|
|
41
41
|
}
|
|
42
42
|
interface ScoreLike {
|
|
@@ -50,7 +50,6 @@ const BOLT_SPEED = 520;
|
|
|
50
50
|
const BOLT_LIFE = 0.9;
|
|
51
51
|
const PICKUP_RADIUS = 30;
|
|
52
52
|
const SURVIVE_SECONDS = 90;
|
|
53
|
-
const HIT_KNOCK = 18; // px an enemy is shoved back when a bolt connects (hit feedback)
|
|
54
53
|
|
|
55
54
|
interface Bolt {
|
|
56
55
|
node: Node2D;
|
|
@@ -238,13 +237,15 @@ export class Survivor extends Behavior {
|
|
|
238
237
|
if (dist2(nx, ny, en.position[0] ?? 0, en.position[1] ?? 0) < 26 * 26) {
|
|
239
238
|
b.hit.add(e);
|
|
240
239
|
// HIT FEEDBACK: damage (→ the enemy's EnemyFace flashes on 'damaged'),
|
|
241
|
-
//
|
|
242
|
-
|
|
240
|
+
// and pop an impact spark at the contact. The shove back along the
|
|
241
|
+
// bolt is the enemy's `Health.knockback` now — the hit's source is a
|
|
242
|
+
// point one px behind it, opposite the bolt's travel, so the kick
|
|
243
|
+
// lands along the bolt exactly as the hand-written one did.
|
|
243
244
|
const il = Math.hypot(b.vx, b.vy) || 1;
|
|
244
|
-
|
|
245
|
-
(en.position[0] ?? 0)
|
|
246
|
-
(en.position[1] ?? 0)
|
|
247
|
-
];
|
|
245
|
+
(e as unknown as { behavior?: HealthLike }).behavior?.damage?.(this.damage, [
|
|
246
|
+
(en.position[0] ?? 0) - b.vx / il,
|
|
247
|
+
(en.position[1] ?? 0) - b.vy / il,
|
|
248
|
+
]);
|
|
248
249
|
this.spark(nx, ny);
|
|
249
250
|
if (b.pierce <= 0) {
|
|
250
251
|
dead = true;
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"right": ["KeyD", "ArrowRight"]
|
|
17
17
|
},
|
|
18
18
|
"touch": "joystick"
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
|
+
"pause": { "type": "button", "keys": ["Escape"] },
|
|
21
|
+
"restart": { "type": "button", "keys": ["KeyR"] }
|
|
20
22
|
},
|
|
21
23
|
"assets": {
|
|
22
24
|
"knight": {
|
|
@@ -108,7 +110,10 @@
|
|
|
108
110
|
"visible": false,
|
|
109
111
|
"collider": { "shape": "circle", "radius": 15 }
|
|
110
112
|
},
|
|
111
|
-
"script": {
|
|
113
|
+
"script": {
|
|
114
|
+
"name": "Health",
|
|
115
|
+
"props": { "max": 3, "freeOnDeath": true, "knockback": 240, "staggerSeconds": 0.1 }
|
|
116
|
+
},
|
|
112
117
|
"children": [
|
|
113
118
|
{
|
|
114
119
|
"name": "Skin",
|
|
@@ -185,7 +190,10 @@
|
|
|
185
190
|
"visible": false,
|
|
186
191
|
"collider": { "shape": "circle", "radius": 14 }
|
|
187
192
|
},
|
|
188
|
-
"script": {
|
|
193
|
+
"script": {
|
|
194
|
+
"name": "Health",
|
|
195
|
+
"props": { "max": 5, "freeOnDeath": true, "knockback": 240, "staggerSeconds": 0.1 }
|
|
196
|
+
},
|
|
189
197
|
"children": [
|
|
190
198
|
{
|
|
191
199
|
"name": "Skin",
|
|
@@ -303,6 +311,111 @@
|
|
|
303
311
|
"type": "AudioPlayer",
|
|
304
312
|
"props": { "preset": "lose", "bus": "sfx", "volume": 0.8 },
|
|
305
313
|
"uid": "n_9c48ixvudwh3egqc"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"name": "Flow",
|
|
317
|
+
"type": "Node",
|
|
318
|
+
"uid": "n_k1b5tdq7qfgute05",
|
|
319
|
+
"script": { "name": "GameFlow" }
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"name": "Hud",
|
|
323
|
+
"type": "HudLayer",
|
|
324
|
+
"uid": "n_t21l3ly0b78tuyhf",
|
|
325
|
+
"children": [
|
|
326
|
+
{
|
|
327
|
+
"name": "PauseMenu",
|
|
328
|
+
"type": "UiPanel",
|
|
329
|
+
"uid": "n_4zudk60s8o480dzn",
|
|
330
|
+
"props": {
|
|
331
|
+
"anchor": "center",
|
|
332
|
+
"layout": "column",
|
|
333
|
+
"gap": 12,
|
|
334
|
+
"padding": 22,
|
|
335
|
+
"visible": false,
|
|
336
|
+
"background": "rgba(10,14,22,0.92)",
|
|
337
|
+
"radius": 12
|
|
338
|
+
},
|
|
339
|
+
"children": [
|
|
340
|
+
{
|
|
341
|
+
"name": "PausedTitle",
|
|
342
|
+
"type": "UiText",
|
|
343
|
+
"uid": "n_jkw1i6abam6b2ich",
|
|
344
|
+
"props": { "text": "PAUSED", "size": 26 }
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"name": "Resume",
|
|
348
|
+
"type": "UiButton",
|
|
349
|
+
"uid": "n_4xe73y1h6dfx33xx",
|
|
350
|
+
"props": { "text": "RESUME", "size": 18 }
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"name": "PauseOptions",
|
|
354
|
+
"type": "UiButton",
|
|
355
|
+
"uid": "n_vld87yos9y2n1ktz",
|
|
356
|
+
"props": { "text": "OPTIONS", "size": 16 }
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"name": "Restart",
|
|
360
|
+
"type": "UiButton",
|
|
361
|
+
"uid": "n_vwt3fbpsozqfdm80",
|
|
362
|
+
"props": { "text": "START OVER", "size": 16 }
|
|
363
|
+
}
|
|
364
|
+
]
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"name": "Options",
|
|
368
|
+
"type": "UiPanel",
|
|
369
|
+
"uid": "n_u1q9tf8qi4kd95xc",
|
|
370
|
+
"props": {
|
|
371
|
+
"anchor": "center",
|
|
372
|
+
"layout": "column",
|
|
373
|
+
"gap": 10,
|
|
374
|
+
"padding": 22,
|
|
375
|
+
"visible": false,
|
|
376
|
+
"background": "rgba(10,14,22,0.92)",
|
|
377
|
+
"radius": 12
|
|
378
|
+
},
|
|
379
|
+
"children": [
|
|
380
|
+
{
|
|
381
|
+
"name": "OptionsTitle",
|
|
382
|
+
"type": "UiText",
|
|
383
|
+
"uid": "n_9m4wm8glz3ah8rr3",
|
|
384
|
+
"props": { "text": "OPTIONS", "size": 24 }
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"name": "Master",
|
|
388
|
+
"type": "UiVolumeSlider",
|
|
389
|
+
"uid": "n_0xk7mn0j15d0kaq2",
|
|
390
|
+
"props": { "label": "master", "bus": "master", "width": 260 }
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"name": "Sfx",
|
|
394
|
+
"type": "UiVolumeSlider",
|
|
395
|
+
"uid": "n_fdvxcfvz0c2axgm9",
|
|
396
|
+
"props": { "label": "effects", "bus": "sfx", "width": 260 }
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"name": "Mute",
|
|
400
|
+
"type": "UiMuteToggle",
|
|
401
|
+
"uid": "n_f8cpyypniznhbja3",
|
|
402
|
+
"props": { "label": "mute everything" }
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"name": "Quality",
|
|
406
|
+
"type": "UiQualitySelect",
|
|
407
|
+
"uid": "n_d97o1nvn1ujgjfwe",
|
|
408
|
+
"props": { "label": "quality" }
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"name": "Back",
|
|
412
|
+
"type": "UiButton",
|
|
413
|
+
"uid": "n_0c24jyr9ddbkk5vb",
|
|
414
|
+
"props": { "text": "BACK", "size": 16 }
|
|
415
|
+
}
|
|
416
|
+
]
|
|
417
|
+
}
|
|
418
|
+
]
|
|
306
419
|
}
|
|
307
420
|
],
|
|
308
421
|
"uid": "n_tlqu945vs98khzn8"
|
|
@@ -311,6 +424,31 @@
|
|
|
311
424
|
{ "signal": "died", "from": "Player/Vitals", "to": "/root", "handler": "loseLife" },
|
|
312
425
|
{ "signal": "damaged", "from": "Player/Vitals", "to": "SfxHurt", "handler": "play" },
|
|
313
426
|
{ "signal": "won", "from": "/root", "to": "SfxWin", "handler": "play" },
|
|
314
|
-
{ "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" }
|
|
427
|
+
{ "signal": "lost", "from": "/root", "to": "SfxLose", "handler": "play" },
|
|
428
|
+
{
|
|
429
|
+
"from": "/root/Hud/PauseMenu/Resume",
|
|
430
|
+
"signal": "pressed",
|
|
431
|
+
"to": "/root/Flow",
|
|
432
|
+
"handler": "resume"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"from": "/root/Hud/PauseMenu/PauseOptions",
|
|
436
|
+
"signal": "pressed",
|
|
437
|
+
"to": "/root/Flow",
|
|
438
|
+
"handler": "screen",
|
|
439
|
+
"args": ["/root/Hud/Options"]
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
"from": "/root/Hud/PauseMenu/Restart",
|
|
443
|
+
"signal": "pressed",
|
|
444
|
+
"to": "/root/Flow",
|
|
445
|
+
"handler": "restart"
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
"from": "/root/Hud/Options/Back",
|
|
449
|
+
"signal": "pressed",
|
|
450
|
+
"to": "/root/Flow",
|
|
451
|
+
"handler": "back"
|
|
452
|
+
}
|
|
315
453
|
]
|
|
316
454
|
}
|
|
@@ -90,6 +90,7 @@ interface HealthB {
|
|
|
90
90
|
{
|
|
91
91
|
let kills = 0;
|
|
92
92
|
let xpShards = 0;
|
|
93
|
+
let retreat = 0; // px a bolt shoved the goblin back from its closest approach (Health.knockback)
|
|
93
94
|
const result = await runScript(sceneJson, {
|
|
94
95
|
durationMs: 5000,
|
|
95
96
|
seed: 2,
|
|
@@ -107,9 +108,20 @@ interface HealthB {
|
|
|
107
108
|
};
|
|
108
109
|
const enemy = spawner.spawn() as unknown as Vec | null;
|
|
109
110
|
if (enemy) enemy.position = [(player.position[0] ?? 0) + 200, player.position[1] ?? 0];
|
|
111
|
+
let nearest = Number.POSITIVE_INFINITY;
|
|
110
112
|
for (let i = 0; i < 260; i++) {
|
|
111
113
|
if ((player.behavior as unknown as SurvivorB).kills >= 1) break;
|
|
112
114
|
ctx.engine.step();
|
|
115
|
+
// a chaser only ever closes in; every step it is FURTHER than its
|
|
116
|
+
// closest approach so far is a hit that moved it
|
|
117
|
+
if (enemy && (enemy as unknown as { tree: unknown }).tree) {
|
|
118
|
+
const d = Math.hypot(
|
|
119
|
+
(enemy.position[0] ?? 0) - (player.position[0] ?? 0),
|
|
120
|
+
(enemy.position[1] ?? 0) - (player.position[1] ?? 0),
|
|
121
|
+
);
|
|
122
|
+
nearest = Math.min(nearest, d);
|
|
123
|
+
retreat = Math.max(retreat, d - nearest);
|
|
124
|
+
}
|
|
113
125
|
}
|
|
114
126
|
kills = (player.behavior as unknown as SurvivorB).kills;
|
|
115
127
|
xpShards = ctx.scene.tree.getNodesInGroup('xp').length;
|
|
@@ -118,6 +130,7 @@ interface HealthB {
|
|
|
118
130
|
],
|
|
119
131
|
});
|
|
120
132
|
ok('the auto-attack killed an enemy', kills >= 1);
|
|
133
|
+
ok(`a bolt SHOVED the goblin back ${retreat.toFixed(1)} px (Health.knockback)`, retreat >= 10);
|
|
121
134
|
ok('a dead enemy dropped an XP shard', xpShards >= 1);
|
|
122
135
|
ok('runScript reported no failures (attack path)', result.ok);
|
|
123
136
|
}
|
|
@@ -78,3 +78,9 @@ camera to 0.45 m (4.2 in the open); standing still on the lift for two seconds,
|
|
|
78
78
|
and Cliff plateaus, the Lift and a double jump reach over the 3 m walls (24 playtests fell
|
|
79
79
|
3 times before it, none after). `verify.ts` drops the player over Wall1 and asserts the catch.
|
|
80
80
|
|
|
81
|
+
## Pausing
|
|
82
|
+
|
|
83
|
+
ESC pauses and shows the pause menu; OPTIONS opens the settings over it and
|
|
84
|
+
BACK returns to the menu that asked. All of it is `GameFlow` (`pausePanelPath`,
|
|
85
|
+
`screen(path)`, `back()`) wired with `connections` — there is no TypeScript for
|
|
86
|
+
any screen in this game.
|
|
@@ -809,22 +809,58 @@
|
|
|
809
809
|
"uid": "n_ffjlx3r053emjjhc"
|
|
810
810
|
},
|
|
811
811
|
{
|
|
812
|
-
"name": "
|
|
812
|
+
"name": "Resume",
|
|
813
|
+
"type": "UiButton",
|
|
814
|
+
"uid": "n_ibf6agaxzcxjhcmg",
|
|
815
|
+
"props": { "text": "RESUME", "size": 18 }
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
"name": "PauseOptions",
|
|
819
|
+
"type": "UiButton",
|
|
820
|
+
"uid": "n_kdecsevdetjk0q1i",
|
|
821
|
+
"props": { "text": "OPTIONS", "size": 16 }
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
"name": "Restart",
|
|
825
|
+
"type": "UiButton",
|
|
826
|
+
"uid": "n_vaxmkxrqot30vd7u",
|
|
827
|
+
"props": { "text": "START OVER", "size": 16 }
|
|
828
|
+
}
|
|
829
|
+
],
|
|
830
|
+
"uid": "n_8bcregfz1ajwi03l"
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
"name": "Options",
|
|
834
|
+
"type": "UiPanel",
|
|
835
|
+
"uid": "n_36ox1wee9cdz2cgj",
|
|
836
|
+
"props": {
|
|
837
|
+
"anchor": "center",
|
|
838
|
+
"layout": "column",
|
|
839
|
+
"gap": 10,
|
|
840
|
+
"padding": 22,
|
|
841
|
+
"visible": false,
|
|
842
|
+
"background": "rgba(10,14,22,0.92)",
|
|
843
|
+
"radius": 12
|
|
844
|
+
},
|
|
845
|
+
"children": [
|
|
846
|
+
{
|
|
847
|
+
"name": "OptionsTitle",
|
|
813
848
|
"type": "UiText",
|
|
814
|
-
"
|
|
815
|
-
|
|
816
|
-
"size": 13,
|
|
817
|
-
"color": "#c9d2dd"
|
|
818
|
-
},
|
|
819
|
-
"uid": "n_atxlw44pqdn146zj"
|
|
849
|
+
"uid": "n_3nt1ztixpsvjtb86",
|
|
850
|
+
"props": { "text": "OPTIONS", "size": 24 }
|
|
820
851
|
},
|
|
821
852
|
{ "name": "Volume", "type": "UiVolumeSlider", "uid": "n_5p4124fbnjxx8s0c" },
|
|
822
853
|
{ "name": "Mute", "type": "UiMuteToggle", "uid": "n_e1lyn3mvsy3vj2qr" },
|
|
823
854
|
{ "name": "Quality", "type": "UiQualitySelect", "uid": "n_35r1h71i75yft3ex" },
|
|
824
855
|
{ "name": "RenderScale", "type": "UiRenderScaleSelect", "uid": "n_emg840df89fqsriw" },
|
|
825
|
-
{ "name": "FrameCap", "type": "UiFrameCapSelect", "uid": "n_mgqwgghn0bx900ip" }
|
|
826
|
-
|
|
827
|
-
|
|
856
|
+
{ "name": "FrameCap", "type": "UiFrameCapSelect", "uid": "n_mgqwgghn0bx900ip" },
|
|
857
|
+
{
|
|
858
|
+
"name": "Back",
|
|
859
|
+
"type": "UiButton",
|
|
860
|
+
"uid": "n_0ytaknljuqmthwpl",
|
|
861
|
+
"props": { "text": "BACK", "size": 16 }
|
|
862
|
+
}
|
|
863
|
+
]
|
|
828
864
|
}
|
|
829
865
|
],
|
|
830
866
|
"uid": "n_dimifr19c9et8cr4"
|
|
@@ -843,6 +879,31 @@
|
|
|
843
879
|
"to": "Arena/Mine",
|
|
844
880
|
"handler": "startTimer",
|
|
845
881
|
"filter": { "group": "player" }
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
"from": "/root/HUD/PauseMenu/Resume",
|
|
885
|
+
"signal": "pressed",
|
|
886
|
+
"to": "/root/Flow",
|
|
887
|
+
"handler": "resume"
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
"from": "/root/HUD/PauseMenu/PauseOptions",
|
|
891
|
+
"signal": "pressed",
|
|
892
|
+
"to": "/root/Flow",
|
|
893
|
+
"handler": "screen",
|
|
894
|
+
"args": ["/root/HUD/Options"]
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
"from": "/root/HUD/PauseMenu/Restart",
|
|
898
|
+
"signal": "pressed",
|
|
899
|
+
"to": "/root/Flow",
|
|
900
|
+
"handler": "restart"
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
"from": "/root/HUD/Options/Back",
|
|
904
|
+
"signal": "pressed",
|
|
905
|
+
"to": "/root/Flow",
|
|
906
|
+
"handler": "back"
|
|
846
907
|
}
|
|
847
908
|
]
|
|
848
909
|
}
|
|
@@ -24,3 +24,10 @@
|
|
|
24
24
|
## Known limitations / ideas
|
|
25
25
|
|
|
26
26
|
- A hurt/attack clip for the wolves once one lands in the animation catalog.
|
|
27
|
+
|
|
28
|
+
## Pausing
|
|
29
|
+
|
|
30
|
+
ESC pauses and shows the pause menu; OPTIONS opens the settings over it and
|
|
31
|
+
BACK returns to the menu that asked. All of it is `GameFlow` (`pausePanelPath`,
|
|
32
|
+
`screen(path)`, `back()`) wired with `connections` — there is no TypeScript for
|
|
33
|
+
any screen in this game.
|