incanto 0.7.1 → 0.8.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 (47) hide show
  1. package/bin/incanto-check.mjs +6 -4
  2. package/dist/2d.d.ts +91 -3
  3. package/dist/2d.js +4 -4
  4. package/dist/3d.d.ts +43 -4
  5. package/dist/3d.js +4 -4
  6. package/dist/{audio-player-C3bH_eZ5.d.ts → audio-player-Dyjg5k92.d.ts} +1 -1
  7. package/dist/audit-C6rMyict.js +58 -0
  8. package/dist/{behavior-q3cejJgs.d.ts → behavior-Bod1AyJS.d.ts} +31 -1
  9. package/dist/{create-game-rFulaS3s.js → create-game-66c4iYJE.js} +4 -5
  10. package/dist/{create-game-DuDkqnjl.js → create-game-BNDSbhy-.js} +4 -5
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/{errors-BMFaY68Q.d.ts → errors-1dXlIwoR.d.ts} +7 -1
  13. package/dist/{gameplay-CDFgSG6z.js → gameplay-DPMgZk9W.js} +108 -1
  14. package/dist/gameplay.d.ts +44 -3
  15. package/dist/gameplay.js +2 -2
  16. package/dist/index.d.ts +151 -9
  17. package/dist/index.js +189 -4
  18. package/dist/{loader-DnLMMRy8.d.ts → loader-BcUDfNHn.d.ts} +1 -1
  19. package/dist/net.d.ts +46 -2
  20. package/dist/net.js +54 -2
  21. package/dist/particle-sim-CyUU7HVU.js +281 -0
  22. package/dist/{physics-2d-CZM5Y90X.js → physics-2d-q2N362ph.js} +45 -4
  23. package/dist/{physics-3d-DT5e8izo.js → physics-3d-CvHr31d4.js} +1 -1
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +1 -1
  26. package/dist/{register-CpXcMiEk.js → register-Cbs7QxoV.js} +139 -4
  27. package/dist/{register-Cl3pAxX-.js → register-Cqjxrfq0.js} +254 -4
  28. package/dist/{register-C35HDZpm.js → register-CxmuI9FL.js} +1 -1
  29. package/dist/{particle-sim-m3CdTGSl.js → register-DRPIjrKG.js} +1178 -278
  30. package/dist/test.d.ts +4 -10
  31. package/dist/test.js +11 -16
  32. package/editor/assets/{agent8-N4bwVH7C.js → agent8-B8QnWCeP.js} +1 -1
  33. package/editor/assets/{index-CXFNanuR.js → index-CVhnNkb3.js} +81 -81
  34. package/editor/index.html +1 -1
  35. package/package.json +1 -1
  36. package/schemas/scene.schema.json +427 -0
  37. package/skills/incanto-3d-character.md +14 -0
  38. package/skills/incanto-audio.md +23 -0
  39. package/skills/incanto-building-2d-games.md +31 -0
  40. package/skills/incanto-building-3d-games.md +21 -0
  41. package/skills/incanto-gameplay-behaviors.md +28 -0
  42. package/skills/incanto-hud.md +24 -0
  43. package/skills/incanto-multiplayer.md +27 -0
  44. package/skills/incanto-node-reference.md +73 -0
  45. package/skills/incanto-physics-and-input.md +9 -0
  46. package/skills/incanto-verifying-your-game.md +31 -0
  47. package/dist/register-Dl_ixIJe.js +0 -834
@@ -140,6 +140,33 @@ const manager = await NetworkManager.create(engine, { transport: local.createCli
140
140
  engine.updated.connect((dt) => void local.tick(Math.min(dt, 0.05) * 1000));
141
141
  ```
142
142
 
143
+ ### Split-screen preview in one call (`createSplitScreen`)
144
+
145
+ Testing multiplayer alone means playing BOTH sides. `createSplitScreen` wires
146
+ the whole N-panel harness — one LocalGameServer, one engine + NetworkManager
147
+ per player — so you only supply the per-panel renderer/input:
148
+
149
+ ```ts
150
+ import { createSplitScreen, registerNodesNet } from 'incanto/net';
151
+
152
+ const canvases = [document.getElementById('p1'), document.getElementById('p2')];
153
+ const { players, server, dispose } = await createSplitScreen({
154
+ scene: gameJson, // shared scene (each panel gets its own copy)
155
+ server: Server, // your v2 Server class (optional)
156
+ scenes: { 'remote-player': remoteJson }, // NetworkSpawner scenes, every panel
157
+ accounts: ['p1', 'p2'], // one panel per account (default)
158
+ setup: ({ engine }, i) => { // finish wiring each panel
159
+ new Renderer2D({ canvas: canvases[i], engine });
160
+ engine.input.attachKeyboard(i === 0 ? window : canvases[i]); // split inputs!
161
+ engine.start();
162
+ },
163
+ });
164
+ ```
165
+
166
+ The first panel's clock pumps `server.tick` (so `$roomTick` runs) — don't add
167
+ your own. `dispose()` tears every panel down. Going live is unchanged: ONE
168
+ client per browser with `createAgent8Server()` as the transport.
169
+
143
170
  It runs the SAME class body the cloud runs: the v2 globals (`$sender`/`$global`/
144
171
  `$room`/`$lock`) are injected per call, a FRESH `Server` instance runs per request
145
172
  (so `this.*` never persists), and calls are serialized (no global leaks across
@@ -241,6 +241,7 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
241
241
  | `sprintAction` | `"sprint"` | string |
242
242
  | `skinPath` | `"../Skin"` | string |
243
243
  | `skinYawOffset` | `0` | number |
244
+ | `animations` | `{}` | object |
244
245
 
245
246
  Signals: `movementStateChanged(state)`
246
247
 
@@ -711,6 +712,25 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
711
712
  | `textureBase` | `"https://agent8-games.verse8.io/assets/3D/default/textures/terrain"` | string |
712
713
  | `basins` | `[]` | array |
713
714
 
715
+ ## `TileMap2D` — `incanto/2d`
716
+
717
+ | Prop | Default | Kind |
718
+ |---|---|---|
719
+ | `position` | `[0,0]` | array |
720
+ | `rotation` | `0` | number |
721
+ | `static` | `false` | boolean |
722
+ | `scale` | `[1,1]` | array |
723
+ | `renderOrder` | `0` | number |
724
+ | `orderGroup` | `"default"` | one of: `background` `terrain` `default` `characters` `effects` `overlay` |
725
+ | `visible` | `true` | boolean |
726
+ | `texture` | `""` | string |
727
+ | `tileSize` | `32` | number |
728
+ | `columns` | `0` | number |
729
+ | `cells` | `[]` | array |
730
+ | `legend` | `{}` | object |
731
+ | `solid` | `[]` | array |
732
+ | `opacity` | `1` | number |
733
+
714
734
  ## `Timer` — `incanto`
715
735
 
716
736
  | Prop | Default | Kind |
@@ -721,6 +741,25 @@ Signals: `triggerEnter(other)` · `triggerExit(other)`
721
741
 
722
742
  Signals: `timeout`
723
743
 
744
+ ## `Trail3D` — `incanto/3d`
745
+
746
+ | Prop | Default | Kind |
747
+ |---|---|---|
748
+ | `position` | `[0,0,0]` | array |
749
+ | `rotation` | `[0,0,0]` | array |
750
+ | `static` | `false` | boolean |
751
+ | `scale` | `[1,1,1]` | array |
752
+ | `visible` | `true` | boolean |
753
+ | `renderOrder` | `0` | number |
754
+ | `orderGroup` | `"default"` | one of: `background` `terrain` `default` `characters` `effects` `overlay` |
755
+ | `width` | `0.3` | number |
756
+ | `color` | `"#ffffff"` | string |
757
+ | `opacity` | `0.8` | number |
758
+ | `seconds` | `0.6` | number |
759
+ | `emitting` | `true` | boolean |
760
+ | `additive` | `false` | boolean |
761
+ | `minDistance` | `0.05` | number |
762
+
724
763
  ## `Tree3D` — `incanto/3d`
725
764
 
726
765
  | Prop | Default | Kind |
@@ -780,6 +819,31 @@ Signals: `bannerShown`
780
819
  | `background` | `"rgba(0,0,0,0.5)"` | string |
781
820
  | `label` | `""` | string |
782
821
 
822
+ ## `UiButton` — `incanto`
823
+
824
+ | Prop | Default | Kind |
825
+ |---|---|---|
826
+ | `anchor` | `"topLeft"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
827
+ | `visible` | `true` | boolean |
828
+ | `text` | `"OK"` | string |
829
+ | `size` | `16` | number |
830
+ | `color` | `"#ffffff"` | string |
831
+ | `background` | `"rgba(255,255,255,0.14)"` | string |
832
+ | `disabled` | `false` | boolean |
833
+
834
+ Signals: `pressed`
835
+
836
+ ## `UiDialogue` — `incanto`
837
+
838
+ | Prop | Default | Kind |
839
+ |---|---|---|
840
+ | `anchor` | `"bottom"` | one of: `topLeft` `top` `topRight` `left` `center` `right` `bottomLeft` `bottom` `bottomRight` |
841
+ | `visible` | `true` | boolean |
842
+ | `charsPerSecond` | `40` | number |
843
+ | `width` | `520` | number |
844
+
845
+ Signals: `lineShown` · `choiceMade` · `dialogueFinished`
846
+
783
847
  ## `UiText` — `incanto`
784
848
 
785
849
  | Prop | Default | Kind |
@@ -951,6 +1015,15 @@ Signals: `arrived`
951
1015
  | `frequency` | `1` | number |
952
1016
  | `mode` | `"position"` | one of: `position` `rotation` `scale` |
953
1017
 
1018
+ ### `PathFollow`
1019
+
1020
+ | Prop | Default | Kind |
1021
+ |---|---|---|
1022
+ | `speed` | `120` | number |
1023
+ | `loop` | `false` | boolean |
1024
+
1025
+ Signals: `waypointReached` · `arrived`
1026
+
954
1027
  ### `Patrol`
955
1028
 
956
1029
  | Prop | Default | Kind |
@@ -133,6 +133,15 @@ Injected state combines with key state (vectors clamped to unit length).
133
133
  - Reference: [examples/2d-phaser-sprite-character-gravity](https://github.com/rareboe/Incanto/tree/main/examples/2d-phaser-sprite-character-gravity) — gravity, jump, attack lockout, custom
134
134
  `Player` node type. Verified in Chromium end-to-end.
135
135
 
136
+ ## Platformer staples (2D)
137
+
138
+ - **One-way platforms**: `"collider": { "shape": "rect", "size": [300, 20],
139
+ "oneWay": true }` on a StaticBody2D — characters jump up THROUGH it and
140
+ land ON it (solid only when falling from above). The Mario ledge.
141
+ - **Moving platforms carry riders automatically**: a CharacterBody2D
142
+ standing on ANY body follows that body's movement (elevators, patrol
143
+ platforms — just animate the platform's `position`; the character rides).
144
+
136
145
  ## Joints (Joint2D / Joint3D)
137
146
 
138
147
  Link two bodies: put the joint node as a CHILD of body A, point `target` at
@@ -198,3 +198,34 @@ depth-test off, so it shows through walls). Extent-less nodes get a small
198
198
  marker cube at their position. That is the fastest answer to "which thing
199
199
  on screen IS this node?" — and the reverse of it: walk the tree clicking
200
200
  rows until the orange box lands on the thing you're hunting.
201
+
202
+ ## Semantic warnings (audit)
203
+
204
+ `incanto-check` now WARNS about scenes that load fine but play wrong: no
205
+ `current` camera, physics bodies without colliders, animated nodes frozen
206
+ inside a `static` subtree, HUD widgets outside a HudLayer. Programmatic:
207
+ `auditScene(json)` from `incanto` or `incanto/test` returns the warnings.
208
+
209
+ ## Deterministic replay (record once, regression-test forever)
210
+
211
+ The engine is fully deterministic under a seed + injected clock, so a
212
+ recording of (tick timestamps + input events) replays BIT-identically:
213
+
214
+ ```ts
215
+ import { Engine, loadScene, replay, startRecording } from 'incanto';
216
+
217
+ // while playing (dev build):
218
+ const rec = startRecording(engine);
219
+ // ... play the level ...
220
+ save.set('replays/level1', rec.stop());
221
+
222
+ // in a headless test:
223
+ const engine = new Engine({ seed: SAME_SEED });
224
+ engine.setScene(loadScene(sceneJson));
225
+ replay(engine, recording, { onTick(i) { /* mid-run asserts */ } });
226
+ expect(player.position).toEqual(expected); // exact, not approximate
227
+ ```
228
+
229
+ Rules that make it hold (the engine's own rules anyway): use `engine.rng`
230
+ never Math.random, dt/`engine.time` never Date.now. Gamepads replay through
231
+ the ACTIONS they were bound to, not raw pad state.