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
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
"jump": { "type": "button", "keys": ["Space"], "touch": "button" },
|
|
35
35
|
"sprint": { "type": "button", "keys": ["ShiftLeft", "ShiftRight"] },
|
|
36
36
|
"interact": { "type": "button", "keys": ["KeyE"], "touch": "button" },
|
|
37
|
-
"attack": { "type": "button", "keys": ["Mouse0", "KeyF"], "touch": "button" }
|
|
37
|
+
"attack": { "type": "button", "keys": ["Mouse0", "KeyF"], "touch": "button" },
|
|
38
|
+
"pause": { "type": "button", "keys": ["Escape"] },
|
|
39
|
+
"restart": { "type": "button", "keys": ["KeyR"] }
|
|
38
40
|
},
|
|
39
41
|
"assets": {
|
|
40
42
|
"characters/base": {
|
|
@@ -64,6 +66,10 @@
|
|
|
64
66
|
"anims/attack": {
|
|
65
67
|
"type": "animation",
|
|
66
68
|
"url": "https://agent8-games.verse8.io/assets/3d/animations/mixamorig/melee-attack.glb"
|
|
69
|
+
},
|
|
70
|
+
"anims/swim": {
|
|
71
|
+
"type": "animation",
|
|
72
|
+
"url": "https://agent8-games.verse8.io/assets/3d/animations/mixamorig/swim.glb"
|
|
67
73
|
}
|
|
68
74
|
},
|
|
69
75
|
"root": {
|
|
@@ -2458,7 +2464,8 @@
|
|
|
2458
2464
|
"walk": "$anims/walk",
|
|
2459
2465
|
"run": "$anims/run",
|
|
2460
2466
|
"fastRun": "$anims/runFast",
|
|
2461
|
-
"airborne": "$anims/jump"
|
|
2467
|
+
"airborne": "$anims/jump",
|
|
2468
|
+
"swim": "$anims/swim"
|
|
2462
2469
|
}
|
|
2463
2470
|
},
|
|
2464
2471
|
"script": { "name": "SwordStrike" },
|
|
@@ -2756,6 +2763,110 @@
|
|
|
2756
2763
|
"anchor": "bottomRight",
|
|
2757
2764
|
"shadow": true
|
|
2758
2765
|
}
|
|
2766
|
+
},
|
|
2767
|
+
{
|
|
2768
|
+
"name": "PauseMenu",
|
|
2769
|
+
"type": "UiPanel",
|
|
2770
|
+
"uid": "n_36ox1wee9cdz2cgj",
|
|
2771
|
+
"props": {
|
|
2772
|
+
"anchor": "center",
|
|
2773
|
+
"layout": "column",
|
|
2774
|
+
"gap": 12,
|
|
2775
|
+
"padding": 22,
|
|
2776
|
+
"visible": false,
|
|
2777
|
+
"background": "rgba(10,14,22,0.92)",
|
|
2778
|
+
"radius": 12
|
|
2779
|
+
},
|
|
2780
|
+
"children": [
|
|
2781
|
+
{
|
|
2782
|
+
"name": "PausedTitle",
|
|
2783
|
+
"type": "UiText",
|
|
2784
|
+
"uid": "n_kdecsevdetjk0q1i",
|
|
2785
|
+
"props": { "text": "PAUSED", "size": 26 }
|
|
2786
|
+
},
|
|
2787
|
+
{
|
|
2788
|
+
"name": "Resume",
|
|
2789
|
+
"type": "UiButton",
|
|
2790
|
+
"uid": "n_vaxmkxrqot30vd7u",
|
|
2791
|
+
"props": { "text": "RESUME", "size": 18 }
|
|
2792
|
+
},
|
|
2793
|
+
{
|
|
2794
|
+
"name": "PauseOptions",
|
|
2795
|
+
"type": "UiButton",
|
|
2796
|
+
"uid": "n_3nt1ztixpsvjtb86",
|
|
2797
|
+
"props": { "text": "OPTIONS", "size": 16 }
|
|
2798
|
+
},
|
|
2799
|
+
{
|
|
2800
|
+
"name": "Save",
|
|
2801
|
+
"type": "UiButton",
|
|
2802
|
+
"uid": "n_enx1clunsomchjx2",
|
|
2803
|
+
"props": { "text": "SAVE", "size": 16 }
|
|
2804
|
+
},
|
|
2805
|
+
{
|
|
2806
|
+
"name": "Continue",
|
|
2807
|
+
"type": "UiButton",
|
|
2808
|
+
"uid": "n_3hpo1n3ho12l7po2",
|
|
2809
|
+
"props": { "text": "CONTINUE", "size": 16 }
|
|
2810
|
+
},
|
|
2811
|
+
{
|
|
2812
|
+
"name": "Restart",
|
|
2813
|
+
"type": "UiButton",
|
|
2814
|
+
"uid": "n_0ytaknljuqmthwpl",
|
|
2815
|
+
"props": { "text": "START OVER", "size": 16 }
|
|
2816
|
+
}
|
|
2817
|
+
]
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
"name": "Options",
|
|
2821
|
+
"type": "UiPanel",
|
|
2822
|
+
"uid": "n_drf0bze03zae5h4l",
|
|
2823
|
+
"props": {
|
|
2824
|
+
"anchor": "center",
|
|
2825
|
+
"layout": "column",
|
|
2826
|
+
"gap": 10,
|
|
2827
|
+
"padding": 22,
|
|
2828
|
+
"visible": false,
|
|
2829
|
+
"background": "rgba(10,14,22,0.92)",
|
|
2830
|
+
"radius": 12
|
|
2831
|
+
},
|
|
2832
|
+
"children": [
|
|
2833
|
+
{
|
|
2834
|
+
"name": "OptionsTitle",
|
|
2835
|
+
"type": "UiText",
|
|
2836
|
+
"uid": "n_wn6vz16vfsnz4guj",
|
|
2837
|
+
"props": { "text": "OPTIONS", "size": 24 }
|
|
2838
|
+
},
|
|
2839
|
+
{
|
|
2840
|
+
"name": "Master",
|
|
2841
|
+
"type": "UiVolumeSlider",
|
|
2842
|
+
"uid": "n_aguq7s3aeapln07y",
|
|
2843
|
+
"props": { "label": "master", "bus": "master", "width": 260 }
|
|
2844
|
+
},
|
|
2845
|
+
{
|
|
2846
|
+
"name": "Sfx",
|
|
2847
|
+
"type": "UiVolumeSlider",
|
|
2848
|
+
"uid": "n_yyaguhkmv9z4u9a8",
|
|
2849
|
+
"props": { "label": "effects", "bus": "sfx", "width": 260 }
|
|
2850
|
+
},
|
|
2851
|
+
{
|
|
2852
|
+
"name": "Mute",
|
|
2853
|
+
"type": "UiMuteToggle",
|
|
2854
|
+
"uid": "n_3yj77bemd2sjm4yi",
|
|
2855
|
+
"props": { "label": "mute everything" }
|
|
2856
|
+
},
|
|
2857
|
+
{
|
|
2858
|
+
"name": "Quality",
|
|
2859
|
+
"type": "UiQualitySelect",
|
|
2860
|
+
"uid": "n_e3ehj0ln9p28ib59",
|
|
2861
|
+
"props": { "label": "quality" }
|
|
2862
|
+
},
|
|
2863
|
+
{
|
|
2864
|
+
"name": "Back",
|
|
2865
|
+
"type": "UiButton",
|
|
2866
|
+
"uid": "n_y95ob0mpwrvvhr78",
|
|
2867
|
+
"props": { "text": "BACK", "size": 16 }
|
|
2868
|
+
}
|
|
2869
|
+
]
|
|
2759
2870
|
}
|
|
2760
2871
|
],
|
|
2761
2872
|
"uid": "n_0q1r3a6rijr3hc1w"
|
|
@@ -2895,12 +3006,55 @@
|
|
|
2895
3006
|
}
|
|
2896
3007
|
}
|
|
2897
3008
|
]
|
|
3009
|
+
},
|
|
3010
|
+
{
|
|
3011
|
+
"name": "Flow",
|
|
3012
|
+
"type": "Node",
|
|
3013
|
+
"uid": "n_ibf6agaxzcxjhcmg",
|
|
3014
|
+
"script": { "name": "GameFlow", "props": { "saveSlots": "beacon-isle" } }
|
|
2898
3015
|
}
|
|
2899
3016
|
],
|
|
2900
3017
|
"uid": "n_nxftk1ep8wbi1bn9"
|
|
2901
3018
|
},
|
|
2902
3019
|
"connections": [
|
|
2903
3020
|
{ "signal": "interacted", "from": "Keeper", "to": "/root", "handler": "onTalk" },
|
|
2904
|
-
{ "signal": "died", "from": "Player", "to": "/root", "handler": "onPlayerDown" }
|
|
3021
|
+
{ "signal": "died", "from": "Player", "to": "/root", "handler": "onPlayerDown" },
|
|
3022
|
+
{
|
|
3023
|
+
"from": "/root/HUD/PauseMenu/Resume",
|
|
3024
|
+
"signal": "pressed",
|
|
3025
|
+
"to": "/root/Flow",
|
|
3026
|
+
"handler": "resume"
|
|
3027
|
+
},
|
|
3028
|
+
{
|
|
3029
|
+
"from": "/root/HUD/PauseMenu/PauseOptions",
|
|
3030
|
+
"signal": "pressed",
|
|
3031
|
+
"to": "/root/Flow",
|
|
3032
|
+
"handler": "screen",
|
|
3033
|
+
"args": ["/root/HUD/Options"]
|
|
3034
|
+
},
|
|
3035
|
+
{
|
|
3036
|
+
"from": "/root/HUD/PauseMenu/Restart",
|
|
3037
|
+
"signal": "pressed",
|
|
3038
|
+
"to": "/root/Flow",
|
|
3039
|
+
"handler": "restart"
|
|
3040
|
+
},
|
|
3041
|
+
{
|
|
3042
|
+
"from": "/root/HUD/Options/Back",
|
|
3043
|
+
"signal": "pressed",
|
|
3044
|
+
"to": "/root/Flow",
|
|
3045
|
+
"handler": "back"
|
|
3046
|
+
},
|
|
3047
|
+
{
|
|
3048
|
+
"from": "/root/HUD/PauseMenu/Save",
|
|
3049
|
+
"signal": "pressed",
|
|
3050
|
+
"to": "/root/Flow",
|
|
3051
|
+
"handler": "save"
|
|
3052
|
+
},
|
|
3053
|
+
{
|
|
3054
|
+
"from": "/root/HUD/PauseMenu/Continue",
|
|
3055
|
+
"signal": "pressed",
|
|
3056
|
+
"to": "/root/Flow",
|
|
3057
|
+
"handler": "continueFrom"
|
|
3058
|
+
}
|
|
2905
3059
|
]
|
|
2906
3060
|
}
|
|
@@ -25,3 +25,10 @@
|
|
|
25
25
|
`tags` entry on the node and a branch in `onMoleClicked`.
|
|
26
26
|
- Turn the field into a GRID of clickable cells and you have a match-3, a
|
|
27
27
|
minesweeper or a tower-defense build menu; the input path is identical.
|
|
28
|
+
|
|
29
|
+
## Pausing
|
|
30
|
+
|
|
31
|
+
ESC pauses and shows the pause menu; OPTIONS opens the settings over it and
|
|
32
|
+
BACK returns to the menu that asked. All of it is `GameFlow` (`pausePanelPath`,
|
|
33
|
+
`screen(path)`, `back()`) wired with `connections` — there is no TypeScript for
|
|
34
|
+
any screen in this game.
|
|
@@ -1175,8 +1175,106 @@
|
|
|
1175
1175
|
"type": "UiButton",
|
|
1176
1176
|
"uid": "n_u20dbq90sumk4806",
|
|
1177
1177
|
"props": { "anchor": "bottom", "text": "PLAY AGAIN", "visible": false }
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
"name": "PauseMenu",
|
|
1181
|
+
"type": "UiPanel",
|
|
1182
|
+
"uid": "n_z9nyxykbc17ausrw",
|
|
1183
|
+
"props": {
|
|
1184
|
+
"anchor": "center",
|
|
1185
|
+
"layout": "column",
|
|
1186
|
+
"gap": 12,
|
|
1187
|
+
"padding": 22,
|
|
1188
|
+
"visible": false,
|
|
1189
|
+
"background": "rgba(10,14,22,0.92)",
|
|
1190
|
+
"radius": 12
|
|
1191
|
+
},
|
|
1192
|
+
"children": [
|
|
1193
|
+
{
|
|
1194
|
+
"name": "PausedTitle",
|
|
1195
|
+
"type": "UiText",
|
|
1196
|
+
"uid": "n_f7eckdwb24bwqpo8",
|
|
1197
|
+
"props": { "text": "PAUSED", "size": 26 }
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
"name": "Resume",
|
|
1201
|
+
"type": "UiButton",
|
|
1202
|
+
"uid": "n_oz53g65a66krulb0",
|
|
1203
|
+
"props": { "text": "RESUME", "size": 18 }
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
"name": "PauseOptions",
|
|
1207
|
+
"type": "UiButton",
|
|
1208
|
+
"uid": "n_cn8ix0y56khi3i90",
|
|
1209
|
+
"props": { "text": "OPTIONS", "size": 16 }
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
"name": "Restart",
|
|
1213
|
+
"type": "UiButton",
|
|
1214
|
+
"uid": "n_cy1svsikf8hhtz3m",
|
|
1215
|
+
"props": { "text": "START OVER", "size": 16 }
|
|
1216
|
+
}
|
|
1217
|
+
]
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
"name": "Options",
|
|
1221
|
+
"type": "UiPanel",
|
|
1222
|
+
"uid": "n_2t2waf1lsux76rw9",
|
|
1223
|
+
"props": {
|
|
1224
|
+
"anchor": "center",
|
|
1225
|
+
"layout": "column",
|
|
1226
|
+
"gap": 10,
|
|
1227
|
+
"padding": 22,
|
|
1228
|
+
"visible": false,
|
|
1229
|
+
"background": "rgba(10,14,22,0.92)",
|
|
1230
|
+
"radius": 12
|
|
1231
|
+
},
|
|
1232
|
+
"children": [
|
|
1233
|
+
{
|
|
1234
|
+
"name": "OptionsTitle",
|
|
1235
|
+
"type": "UiText",
|
|
1236
|
+
"uid": "n_rp0oo781nder7a6m",
|
|
1237
|
+
"props": { "text": "OPTIONS", "size": 24 }
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
"name": "Master",
|
|
1241
|
+
"type": "UiVolumeSlider",
|
|
1242
|
+
"uid": "n_8sufldfevj26zdb2",
|
|
1243
|
+
"props": { "label": "master", "bus": "master", "width": 260 }
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
"name": "Sfx",
|
|
1247
|
+
"type": "UiVolumeSlider",
|
|
1248
|
+
"uid": "n_ahyngscv3eatp9mi",
|
|
1249
|
+
"props": { "label": "effects", "bus": "sfx", "width": 260 }
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
"name": "Mute",
|
|
1253
|
+
"type": "UiMuteToggle",
|
|
1254
|
+
"uid": "n_478neek47ba9eaa4",
|
|
1255
|
+
"props": { "label": "mute everything" }
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
"name": "Quality",
|
|
1259
|
+
"type": "UiQualitySelect",
|
|
1260
|
+
"uid": "n_f35z57tq3jsxrdz1",
|
|
1261
|
+
"props": { "label": "quality" }
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
"name": "Back",
|
|
1265
|
+
"type": "UiButton",
|
|
1266
|
+
"uid": "n_m5d8dwpml28xj9jz",
|
|
1267
|
+
"props": { "text": "BACK", "size": 16 }
|
|
1268
|
+
}
|
|
1269
|
+
]
|
|
1178
1270
|
}
|
|
1179
1271
|
]
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
"name": "Flow",
|
|
1275
|
+
"type": "Node",
|
|
1276
|
+
"uid": "n_08m0apva5yx370w6",
|
|
1277
|
+
"script": { "name": "GameFlow" }
|
|
1180
1278
|
}
|
|
1181
1279
|
]
|
|
1182
1280
|
},
|
|
@@ -1200,6 +1298,35 @@
|
|
|
1200
1298
|
{ "signal": "lost", "from": ".", "to": "Hud", "handler": "onLost" },
|
|
1201
1299
|
{ "signal": "won", "from": ".", "to": "Field", "handler": "stop" },
|
|
1202
1300
|
{ "signal": "lost", "from": ".", "to": "Field", "handler": "stop" },
|
|
1203
|
-
{ "signal": "pressed", "from": "Hud/Again", "to": "Hud", "handler": "onAgain" }
|
|
1204
|
-
|
|
1301
|
+
{ "signal": "pressed", "from": "Hud/Again", "to": "Hud", "handler": "onAgain" },
|
|
1302
|
+
{
|
|
1303
|
+
"from": "/root/Hud/PauseMenu/Resume",
|
|
1304
|
+
"signal": "pressed",
|
|
1305
|
+
"to": "/root/Flow",
|
|
1306
|
+
"handler": "resume"
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
"from": "/root/Hud/PauseMenu/PauseOptions",
|
|
1310
|
+
"signal": "pressed",
|
|
1311
|
+
"to": "/root/Flow",
|
|
1312
|
+
"handler": "screen",
|
|
1313
|
+
"args": ["/root/Hud/Options"]
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
"from": "/root/Hud/PauseMenu/Restart",
|
|
1317
|
+
"signal": "pressed",
|
|
1318
|
+
"to": "/root/Flow",
|
|
1319
|
+
"handler": "restart"
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
"from": "/root/Hud/Options/Back",
|
|
1323
|
+
"signal": "pressed",
|
|
1324
|
+
"to": "/root/Flow",
|
|
1325
|
+
"handler": "back"
|
|
1326
|
+
}
|
|
1327
|
+
],
|
|
1328
|
+
"input": {
|
|
1329
|
+
"pause": { "type": "button", "keys": ["Escape"] },
|
|
1330
|
+
"restart": { "type": "button", "keys": ["KeyR"] }
|
|
1331
|
+
}
|
|
1205
1332
|
}
|
|
@@ -65,3 +65,10 @@ A course past GroundD (x 3000+): `Trampoline` (`restitution 1`) with a dead `Bou
|
|
|
65
65
|
(`friction 0`) with a `Sled` at 260 px/s, and a `Hound` (`CharacterBody2D` + `Chase`
|
|
66
66
|
`ground: true`) under a `Perch`. `verify.ts` measures the rebound (287 of 300 px), the slide
|
|
67
67
|
(400 px of ice) and that the hound stays on the floor under a perched player.
|
|
68
|
+
|
|
69
|
+
## Pausing
|
|
70
|
+
|
|
71
|
+
ESC pauses and shows the pause menu; OPTIONS opens the settings over it and
|
|
72
|
+
BACK returns to the menu that asked. All of it is `GameFlow` (`pausePanelPath`,
|
|
73
|
+
`screen(path)`, `back()`) wired with `connections` — there is no TypeScript for
|
|
74
|
+
any screen in this game.
|
|
@@ -17,8 +17,9 @@ YOU MUST follow these rules EXACTLY when vibe-coding on this Incanto template
|
|
|
17
17
|
`createGame2D({ behaviors })` and attached with `"script": { "name": "Name" }`.
|
|
18
18
|
This game's custom behaviors are the platformer GAME FEEL the library leaves
|
|
19
19
|
open: `PlayerController` (coyote-time, jump-buffer, double-jump, variable jump
|
|
20
|
-
height, stomp,
|
|
21
|
-
|
|
20
|
+
height, stomp, checkpoint respawn, moving-platform carry — hearts, i-frames,
|
|
21
|
+
the knockback and the control lock are the Player's `Vitals` **Health**, which
|
|
22
|
+
the controller reads: `staggered`, `kickVelocity`, `takeLift()`), `GoblinSkin`, `FollowCam` (follow + screen-shake), `ParallaxLayer`,
|
|
22
23
|
`HudUpdater`.
|
|
23
24
|
4. 🔑 UIDS ARE GENERATED: omit `uid` and the loader assigns one (via the engine's
|
|
24
25
|
`newUid`). NEVER hand-invent readable uid strings.
|
|
@@ -23,6 +23,7 @@ import type { Node, UiBanner, UiText } from 'incanto';
|
|
|
23
23
|
import { Behavior } from 'incanto';
|
|
24
24
|
import type { AnimatedSprite2D, Camera2D, CharacterBody2D, Node2D } from 'incanto/2d';
|
|
25
25
|
import { Particles2D } from 'incanto/2d';
|
|
26
|
+
import type { Health } from 'incanto/gameplay';
|
|
26
27
|
|
|
27
28
|
// ---- tuning (px, y-down; scene gravity is [0, 1800]) --------------------------
|
|
28
29
|
const RUN_SPEED = 250;
|
|
@@ -35,12 +36,9 @@ const BUFFER = 0.12; // press jump slightly early and it still fires on landing
|
|
|
35
36
|
const JUMP_CUT = 0.45; // release jump while rising → cut the climb (variable height)
|
|
36
37
|
const MAX_JUMPS = 2; // ground jump + one air (double) jump
|
|
37
38
|
const STOMP_BOUNCE = 560; // upward pop after squashing a goblin
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const CONTROL_LOCK = 0.22; // seconds the player can't steer after a knockback
|
|
41
|
-
const INVULN = 1.1; // i-frames after a hit
|
|
39
|
+
// hearts, i-frames, the knockback and the control lock after a hit are the
|
|
40
|
+
// Player's `Vitals` Health (game.scene.json) — this controller reads them
|
|
42
41
|
const RESPAWN_INVULN = 1.4;
|
|
43
|
-
const MAX_HEARTS = 3;
|
|
44
42
|
const PLAYER_HW = 18;
|
|
45
43
|
const PLAYER_HH = 33;
|
|
46
44
|
const ENEMY_HW = 26;
|
|
@@ -71,14 +69,15 @@ interface Audio {
|
|
|
71
69
|
}
|
|
72
70
|
|
|
73
71
|
export class PlayerController extends Behavior {
|
|
74
|
-
|
|
72
|
+
/** Hearts = the Vitals' hit points; the HUD reads this. */
|
|
73
|
+
get hearts(): number {
|
|
74
|
+
return this.health.current;
|
|
75
|
+
}
|
|
75
76
|
|
|
76
77
|
private gravity = 1800;
|
|
77
78
|
private coyote = 0;
|
|
78
79
|
private buffer = 0;
|
|
79
80
|
private jumps = 0;
|
|
80
|
-
private invuln = 0;
|
|
81
|
-
private lock = 0;
|
|
82
81
|
private wasGrounded = true;
|
|
83
82
|
private respawn: [number, number] = [0, 0];
|
|
84
83
|
private platform: Node2D | null = null;
|
|
@@ -89,6 +88,9 @@ export class PlayerController extends Behavior {
|
|
|
89
88
|
private get body(): CharacterBody2D {
|
|
90
89
|
return this.node as unknown as CharacterBody2D;
|
|
91
90
|
}
|
|
91
|
+
private get health(): Health {
|
|
92
|
+
return this.getNode('Vitals').behavior as unknown as Health;
|
|
93
|
+
}
|
|
92
94
|
private get skin(): AnimatedSprite2D {
|
|
93
95
|
return this.getNode('Skin') as unknown as AnimatedSprite2D;
|
|
94
96
|
}
|
|
@@ -108,6 +110,9 @@ export class PlayerController extends Behavior {
|
|
|
108
110
|
this.getNode('/root').on('lost', () => {
|
|
109
111
|
this.over = true;
|
|
110
112
|
});
|
|
113
|
+
// a death is the Vitals' signal, whoever dealt it — a hit, a pit, or a
|
|
114
|
+
// probe calling `kill()` — so every route spends a life the same way
|
|
115
|
+
this.getNode('Vitals').on('died', () => this.die());
|
|
111
116
|
// moving-platform CARRY: a Feet sensor remembers the platform we stand on so we
|
|
112
117
|
// ride its motion (the kinematic controller doesn't inherit platform velocity).
|
|
113
118
|
const feet = this.getNode('Feet');
|
|
@@ -137,15 +142,19 @@ export class PlayerController extends Behavior {
|
|
|
137
142
|
|
|
138
143
|
this.coyote = Math.max(0, this.coyote - dt);
|
|
139
144
|
this.buffer = Math.max(0, this.buffer - dt);
|
|
140
|
-
this.invuln = Math.max(0, this.invuln - dt);
|
|
141
|
-
this.lock = Math.max(0, this.lock - dt);
|
|
142
145
|
|
|
143
146
|
const grounded = body.isOnFloor();
|
|
144
147
|
const dir = this.input.getVector('move');
|
|
145
148
|
let vx = body.velocity[0] ?? 0;
|
|
146
149
|
let vy = body.velocity[1] ?? 0;
|
|
147
150
|
|
|
148
|
-
|
|
151
|
+
// a hit's stagger takes the stick, and its kick IS the velocity while it
|
|
152
|
+
// lasts: this controller drives the body, so it reads the Health's kick
|
|
153
|
+
// (`applyKnockback: false` in the scene) instead of Health moving the body
|
|
154
|
+
const health = this.health;
|
|
155
|
+
if (!health.staggered) vx = dir.x * RUN_SPEED;
|
|
156
|
+
const kick = health.kickVelocity;
|
|
157
|
+
if (kick.length > 0) vx = kick[0] ?? vx;
|
|
149
158
|
|
|
150
159
|
if (grounded) {
|
|
151
160
|
this.jumps = 0;
|
|
@@ -177,6 +186,13 @@ export class PlayerController extends Behavior {
|
|
|
177
186
|
}
|
|
178
187
|
if (this.input.justReleased('jump') && vy < 0) vy *= JUMP_CUT;
|
|
179
188
|
|
|
189
|
+
// the hit's hop: the kick's vertical part comes out ONCE, then gravity owns it
|
|
190
|
+
const lift = health.takeLift();
|
|
191
|
+
if (lift !== 0) {
|
|
192
|
+
vy = lift;
|
|
193
|
+
this.platform = null;
|
|
194
|
+
}
|
|
195
|
+
|
|
180
196
|
body.velocity = [vx, vy];
|
|
181
197
|
|
|
182
198
|
// ride a moving platform: add its per-frame delta before resolving collisions
|
|
@@ -224,12 +240,12 @@ export class PlayerController extends Behavior {
|
|
|
224
240
|
body.velocity = [body.velocity[0] ?? 0, vy];
|
|
225
241
|
this.jumps = 1; // a stomp refreshes the air jump — chain bounces
|
|
226
242
|
this.shake(8);
|
|
227
|
-
} else if (this.
|
|
228
|
-
this.takeHit(
|
|
243
|
+
} else if (this.health.invulnerableRemaining <= 0) {
|
|
244
|
+
this.takeHit([ex, ey]);
|
|
229
245
|
}
|
|
230
246
|
}
|
|
231
247
|
|
|
232
|
-
if (this.
|
|
248
|
+
if (this.health.invulnerableRemaining <= 0) {
|
|
233
249
|
for (const h of tree.getNodesInGroup('hazard')) {
|
|
234
250
|
const hn = h as unknown as Node2D;
|
|
235
251
|
const size = ((hn as unknown as { collider?: { size?: number[] } }).collider?.size ?? [
|
|
@@ -247,7 +263,7 @@ export class PlayerController extends Behavior {
|
|
|
247
263
|
(size[1] ?? 40) / 2,
|
|
248
264
|
)
|
|
249
265
|
) {
|
|
250
|
-
this.takeHit(this.facing *
|
|
266
|
+
this.takeHit([px + this.facing * 10, py]); // a hazard hits from the front
|
|
251
267
|
break;
|
|
252
268
|
}
|
|
253
269
|
}
|
|
@@ -270,7 +286,7 @@ export class PlayerController extends Behavior {
|
|
|
270
286
|
(size[1] ?? 80) / 2,
|
|
271
287
|
)
|
|
272
288
|
) {
|
|
273
|
-
this.die()
|
|
289
|
+
this.health.kill(); // → `died` → `die()`
|
|
274
290
|
return;
|
|
275
291
|
}
|
|
276
292
|
}
|
|
@@ -300,23 +316,20 @@ export class PlayerController extends Behavior {
|
|
|
300
316
|
}
|
|
301
317
|
}
|
|
302
318
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
this.body.velocity = [dirX * KNOCK_X, -KNOCK_UP];
|
|
319
|
+
/** Hurt from a world point: the Vitals take the heart, the i-frames, the kick and the stagger. */
|
|
320
|
+
private takeHit(from: [number, number]): void {
|
|
321
|
+
const health = this.health;
|
|
322
|
+
health.damage(1, from); // the killing blow reaches `die()` through `died`
|
|
308
323
|
this.sfx('SfxHurt');
|
|
309
324
|
this.shake(10);
|
|
310
|
-
if (this.hearts <= 0) this.die();
|
|
311
325
|
}
|
|
312
326
|
|
|
313
327
|
private die(): void {
|
|
314
328
|
const sk = this.scoreKeeper;
|
|
315
329
|
sk.loseLife();
|
|
316
330
|
if (sk.lives > 0) {
|
|
317
|
-
this.
|
|
318
|
-
this.
|
|
319
|
-
this.lock = 0;
|
|
331
|
+
this.health.revive();
|
|
332
|
+
this.health.protect(RESPAWN_INVULN);
|
|
320
333
|
this.platform = null;
|
|
321
334
|
this.body.velocity = [0, 0];
|
|
322
335
|
this.body.position = [this.respawn[0], this.respawn[1]];
|
|
@@ -381,7 +394,8 @@ export class PlayerController extends Behavior {
|
|
|
381
394
|
this.playOnce(skin, clip);
|
|
382
395
|
|
|
383
396
|
// blink while invulnerable
|
|
384
|
-
|
|
397
|
+
const inv = this.health.invulnerableRemaining;
|
|
398
|
+
skin.opacity = inv > 0 && Math.floor(inv * 14) % 2 === 0 ? 0.35 : 1;
|
|
385
399
|
}
|
|
386
400
|
|
|
387
401
|
private lastClip = '';
|