incanto 0.1.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 (138) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +36 -0
  3. package/THIRD-PARTY-NOTICES.md +88 -0
  4. package/assets/audio/attacked.mp3 +0 -0
  5. package/assets/audio/explosion.mp3 +0 -0
  6. package/assets/audio/gold_loot.mp3 +0 -0
  7. package/assets/audio/heal.mp3 +0 -0
  8. package/assets/audio/hit_metal_bang.mp3 +0 -0
  9. package/assets/audio/ice_spear.mp3 +0 -0
  10. package/assets/audio/monster_died.mp3 +0 -0
  11. package/assets/audio/slash.mp3 +0 -0
  12. package/assets/audio/smite.mp3 +0 -0
  13. package/assets/audio/spells_cast.mp3 +0 -0
  14. package/assets/audio/ui_click.wav +0 -0
  15. package/assets/audio/walk.mp3 +0 -0
  16. package/assets/catalog.json +390 -0
  17. package/assets/characters/2dbasic.json +41 -0
  18. package/assets/characters/2dbasic.png +0 -0
  19. package/assets/characters/ghost.json +46 -0
  20. package/assets/characters/ghost.png +0 -0
  21. package/assets/characters/goblin.json +40 -0
  22. package/assets/characters/goblin.png +0 -0
  23. package/assets/characters/medieval-knight.json +41 -0
  24. package/assets/characters/medieval-knight.png +0 -0
  25. package/assets/effects/swoosh.png +0 -0
  26. package/assets/items/box.png +0 -0
  27. package/assets/items/buff_potion.png +0 -0
  28. package/assets/items/coin.png +0 -0
  29. package/assets/items/gem.png +0 -0
  30. package/assets/items/gold.png +0 -0
  31. package/assets/items/hp_potion.png +0 -0
  32. package/assets/items/locked_item_box.png +0 -0
  33. package/assets/items/map.png +0 -0
  34. package/assets/items/resurrection_potion.png +0 -0
  35. package/assets/items/super_box.png +0 -0
  36. package/assets/items/trap.png +0 -0
  37. package/assets/tiles/floor00.jpg +0 -0
  38. package/assets/tiles/minecraft-tiles.png +0 -0
  39. package/assets/tiles/wall00.jpg +0 -0
  40. package/assets/vegetation/ash_color.png +0 -0
  41. package/assets/vegetation/aspen_color.png +0 -0
  42. package/assets/vegetation/bark/birch_color_1k.jpg +0 -0
  43. package/assets/vegetation/bark/birch_normal_1k.jpg +0 -0
  44. package/assets/vegetation/bark/birch_roughness_1k.jpg +0 -0
  45. package/assets/vegetation/bark/oak_color_1k.jpg +0 -0
  46. package/assets/vegetation/bark/oak_normal_1k.jpg +0 -0
  47. package/assets/vegetation/bark/oak_roughness_1k.jpg +0 -0
  48. package/assets/vegetation/bark/pine_color_1k.jpg +0 -0
  49. package/assets/vegetation/bark/pine_normal_1k.jpg +0 -0
  50. package/assets/vegetation/bark/pine_roughness_1k.jpg +0 -0
  51. package/assets/vegetation/ground/dirt_color.jpg +0 -0
  52. package/assets/vegetation/ground/dirt_normal.jpg +0 -0
  53. package/assets/vegetation/ground/grass.jpg +0 -0
  54. package/assets/vegetation/oak_color.png +0 -0
  55. package/assets/vegetation/pine_color.png +0 -0
  56. package/bin/incanto-assets.mjs +107 -0
  57. package/bin/incanto-check.mjs +107 -0
  58. package/bin/incanto-editor.mjs +343 -0
  59. package/bin/incanto-env.mjs +144 -0
  60. package/bin/incanto-model.mjs +296 -0
  61. package/bin/incanto-play.mjs +219 -0
  62. package/bin/incanto-skills.mjs +71 -0
  63. package/dist/2d.d.ts +642 -0
  64. package/dist/2d.js +44 -0
  65. package/dist/3d.d.ts +1860 -0
  66. package/dist/3d.js +5 -0
  67. package/dist/agent8-DzU2fFyH.js +129 -0
  68. package/dist/audio-player-DqUR3XFs.d.ts +110 -0
  69. package/dist/behavior-BAQq7HGM.d.ts +851 -0
  70. package/dist/create-game-BdjpTHrW.js +1725 -0
  71. package/dist/create-game-CZHROKcT.js +527 -0
  72. package/dist/debug-draw-CZmOYjL2.js +13 -0
  73. package/dist/debug.d.ts +66 -0
  74. package/dist/debug.js +658 -0
  75. package/dist/duplicate-DP2WPYom.js +22 -0
  76. package/dist/env.d.ts +430 -0
  77. package/dist/env.js +3152 -0
  78. package/dist/errors-BMFaY68Q.d.ts +33 -0
  79. package/dist/errors-BpWbnbb_.js +13 -0
  80. package/dist/gameplay-Ccruc3Wd.js +1501 -0
  81. package/dist/gameplay.d.ts +543 -0
  82. package/dist/gameplay.js +2 -0
  83. package/dist/heightmap-CroQPEER.js +185 -0
  84. package/dist/index.d.ts +305 -0
  85. package/dist/index.js +62 -0
  86. package/dist/json-BLk7H2Qa.js +30 -0
  87. package/dist/loader-CGs_G-r0.js +919 -0
  88. package/dist/loader-Mo0KghCv.d.ts +41 -0
  89. package/dist/net.d.ts +427 -0
  90. package/dist/net.js +772 -0
  91. package/dist/noise-CGUMx44x.js +82 -0
  92. package/dist/particle-sim-CbN4YUuH.d.ts +63 -0
  93. package/dist/particle-sim-DYuSUxvK.js +1319 -0
  94. package/dist/physics-2d-KuMWPTf6.js +288 -0
  95. package/dist/physics-3d-Dl67vOLT.js +434 -0
  96. package/dist/react.d.ts +65 -0
  97. package/dist/react.js +209 -0
  98. package/dist/register-BuUV1_KB.js +561 -0
  99. package/dist/register-CNlYAS1_.js +10634 -0
  100. package/dist/register-DPEV9_9t.js +851 -0
  101. package/dist/register-Dasmnurl.js +374 -0
  102. package/dist/registry-BVJ2HbCn.js +132 -0
  103. package/dist/rng-DP-SR7eg.js +38 -0
  104. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  105. package/dist/schema-CcoWb32N.d.ts +104 -0
  106. package/dist/test.d.ts +158 -0
  107. package/dist/test.js +275 -0
  108. package/dist/touch-031PxtCR.js +208 -0
  109. package/dist/vite.d.ts +26 -0
  110. package/dist/vite.js +57 -0
  111. package/editor/assets/GameServer-C56iOUgF.js +1 -0
  112. package/editor/assets/agent8-Bp7QFI7v.js +1 -0
  113. package/editor/assets/index-DF3tMeKJ.css +1 -0
  114. package/editor/assets/index-Dl2pjA8e.js +7365 -0
  115. package/editor/assets/rapier-CEuLKeCu.js +1 -0
  116. package/editor/assets/rapier-DE6a0vmv.js +1 -0
  117. package/editor/index.html +169 -0
  118. package/package.json +97 -0
  119. package/schemas/scene.schema.json +4254 -0
  120. package/skills/README.md +9 -0
  121. package/skills/incanto-3d-character.md +229 -0
  122. package/skills/incanto-3d-models.md +151 -0
  123. package/skills/incanto-assets.md +118 -0
  124. package/skills/incanto-audio.md +309 -0
  125. package/skills/incanto-behaviors-and-scripts.md +169 -0
  126. package/skills/incanto-building-2d-games.md +242 -0
  127. package/skills/incanto-building-3d-games.md +245 -0
  128. package/skills/incanto-editor.md +163 -0
  129. package/skills/incanto-environment.md +743 -0
  130. package/skills/incanto-gameplay-behaviors.md +707 -0
  131. package/skills/incanto-multiplayer.md +264 -0
  132. package/skills/incanto-node-reference.md +797 -0
  133. package/skills/incanto-physics-and-input.md +164 -0
  134. package/skills/incanto-scene-json-authoring.md +325 -0
  135. package/skills/incanto-verifying-your-game.md +191 -0
  136. package/skills/incanto-web-integration.md +96 -0
  137. package/templates/agent8-server.js +84 -0
  138. package/templates/agent8-server.ts +138 -0
package/dist/2d.d.ts ADDED
@@ -0,0 +1,642 @@
1
+ import { s as JsonObject } from "./schema-CcoWb32N.js";
2
+ import { C as RendererStats, S as GameStats, T as Node, b as Scheduler, l as PropSchema, n as BehaviorCtor, v as Engine, w as Scene$1 } from "./behavior-BAQq7HGM.js";
3
+ import { t as LoadSceneOptions } from "./loader-Mo0KghCv.js";
4
+ import { t as ParticleSim } from "./particle-sim-CbN4YUuH.js";
5
+ import { Group, Mesh, Object3D, Scene, Texture } from "three";
6
+ import * as RapierNs from "@dimforge/rapier2d-compat";
7
+
8
+ //#region src/2d/assets.d.ts
9
+ type AssetStatus = "loading" | "ready" | "error";
10
+ interface TextureLoadCallbacks {
11
+ onLoad?: () => void;
12
+ onError?: (error: unknown) => void;
13
+ }
14
+ interface SheetInfo {
15
+ texture: Texture;
16
+ frameWidth: number;
17
+ frameHeight: number;
18
+ }
19
+ /**
20
+ * String-keyed asset registry for the 2D layer (Phaser's loader model).
21
+ *
22
+ * Scene JSON declares assets under stable keys; node props reference them as
23
+ * `"$key"`. The texture loader is injectable so tests run headless.
24
+ */
25
+ declare class AssetStore2D {
26
+ private readonly entries;
27
+ private readonly loader;
28
+ constructor(loader?: (url: string, callbacks?: TextureLoadCallbacks) => Texture);
29
+ /** Load (or extend with) a scene's `assets` declarations. Hard-validates each entry. */
30
+ load(assets: Record<string, JsonObject>): void;
31
+ /** Load status of a declared asset KEY (no '$'), or undefined if unknown. */
32
+ status(key: string): AssetStatus | undefined;
33
+ getTexture(ref: string): Texture;
34
+ getSheet(ref: string): SheetInfo;
35
+ private resolve;
36
+ }
37
+ //#endregion
38
+ //#region src/2d/nodes/node-2d.d.ts
39
+ /**
40
+ * Base of every 2D node. Convention (Phaser/Godot prior):
41
+ * **1 unit = 1 px, +y DOWN, (0,0) top-left, positive rotation = clockwise.**
42
+ * Internally mapped onto three's y-up space by negating y and the z-spin.
43
+ */
44
+ declare class Node2D extends Node {
45
+ static override readonly typeName: string;
46
+ static readonly props: PropSchema;
47
+ /** Legacy alias: old 2D scenes (and `zIndex`-trained agents) keep loading —
48
+ * `zIndex` resolves to `renderOrder` at load. The schema exposes only
49
+ * `renderOrder` so 2D and 3D share ONE render-order name. */
50
+ static readonly propAliases: Record<string, string>;
51
+ /** Pixels, y-down. */
52
+ position: number[];
53
+ /** Degrees, clockwise. */
54
+ rotation: number;
55
+ scale: number[];
56
+ /** Draw order among 2D drawables (higher = on top); matches 3D `renderOrder`. */
57
+ renderOrder: number;
58
+ visible: boolean;
59
+ private _object2D;
60
+ /** @internal The backing three object (lazily created). */
61
+ _ensureObject2D(): Object3D;
62
+ /** @internal Override point. */
63
+ protected _createObject2D(): Object3D;
64
+ /** @internal Push JSON props onto the backing object. Called every frame. */
65
+ _syncObject2D(_assets: AssetStore2D | null): void;
66
+ override free(): void;
67
+ }
68
+ //#endregion
69
+ //#region src/2d/nodes/bodies-2d.d.ts
70
+ /**
71
+ * Shared base for physics-backed 2D nodes. Colliders are NODE PROPS:
72
+ * `{shape:'rect', size:[w,h]}` · `{shape:'circle', radius}` ·
73
+ * `{shape:'capsule', radius, height}` — never child shape nodes.
74
+ */
75
+ declare class PhysicsBody2D extends Node2D {
76
+ static override readonly props: PropSchema;
77
+ /** The unified collision model: every collider participant can emit these. */
78
+ static override readonly signals: readonly string[];
79
+ collider: JsonObject;
80
+ /** Loader hook: bad collider shapes fail at LOAD, not at physics start. */
81
+ static validateJson(node: Node): void;
82
+ /** @internal Set by Physics2D when the body is created. */
83
+ _physics: Physics2D | null;
84
+ }
85
+ /** Immovable collider (ground, walls, platforms). */
86
+ declare class StaticBody2D extends PhysicsBody2D {
87
+ static override readonly typeName: string;
88
+ }
89
+ /**
90
+ * Sensor volume. Emits `triggerEnter(other)` / `triggerExit(other)` — the
91
+ * unified collision model (solid bodies emit the same signals on contact).
92
+ */
93
+ declare class Area2D extends PhysicsBody2D {
94
+ static override readonly typeName: string;
95
+ }
96
+ /** Dynamic simulated body. */
97
+ declare class RigidBody2D extends PhysicsBody2D {
98
+ static override readonly typeName: string;
99
+ static override readonly props: PropSchema;
100
+ mass: number;
101
+ gravityScale: number;
102
+ fixedRotation: boolean;
103
+ friction: number;
104
+ restitution: number;
105
+ /** px/s, y-down. Read back every step; write to launch. */
106
+ linearVelocity: number[];
107
+ }
108
+ /**
109
+ * Kinematic body on Rapier's character controller. Gravity is NOT applied
110
+ * automatically (Godot semantics) — integrate `velocity` yourself, then call
111
+ * `moveAndSlide()` from `fixedUpdate`.
112
+ */
113
+ declare class CharacterBody2D extends PhysicsBody2D {
114
+ static override readonly typeName: string;
115
+ static override readonly props: PropSchema;
116
+ /** px/s, y-down (up = -y). */
117
+ velocity: number[];
118
+ /** Keep contact on slopes/steps while not moving upward. */
119
+ snapToGround: boolean;
120
+ /** Max climbable slope angle. */
121
+ slopeLimitDeg: number;
122
+ /** @internal Updated by Physics2D.moveAndSlide. */
123
+ _grounded: boolean;
124
+ moveAndSlide(): void;
125
+ isOnFloor(): boolean;
126
+ }
127
+ //#endregion
128
+ //#region src/2d/physics/physics-2d.d.ts
129
+ type Rapier = typeof RapierNs;
130
+ interface Physics2DOptions {
131
+ /** px/s², y-down. Default: scene `physics.gravity`, else [0, 980]. */
132
+ gravity?: [number, number];
133
+ }
134
+ /**
135
+ * Enable 2D physics for an engine. Dynamically imports Rapier (compat build,
136
+ * wasm inlined) so games without physics never pay its bundle cost.
137
+ */
138
+ declare function enablePhysics2D(engine: Engine, opts?: Physics2DOptions): Promise<Physics2D>;
139
+ /**
140
+ * Per-engine 2D physics world (y-down pixel space, consistent with Node2D).
141
+ * Each fixedUpdate: sync tree → step world → write back → drain trigger events.
142
+ */
143
+ declare class Physics2D {
144
+ private readonly R;
145
+ private readonly engine;
146
+ /** Render collider outlines in the GAME view (renderers pick this up). */
147
+ debugDraw: boolean;
148
+ readonly dimension = "2d";
149
+ private readonly unregisterDebug;
150
+ private readonly warnedNoCollider;
151
+ private readonly entries;
152
+ private readonly byColliderHandle;
153
+ private readonly world;
154
+ private readonly events;
155
+ private readonly kcc;
156
+ private readonly disconnect;
157
+ private lastDt;
158
+ private readonly optsGravity;
159
+ private lastScene;
160
+ constructor(R: Rapier, engine: Engine, opts?: Physics2DOptions);
161
+ /** @internal Driven by engine.fixedUpdated. */
162
+ step(dt: number): void;
163
+ /** @internal Called by CharacterBody2D.moveAndSlide (during tree fixedUpdate). */
164
+ moveAndSlide(node: CharacterBody2D): void;
165
+ /** Rapier debug segments, scaled back to pixels. Null while debugDraw is off. */
166
+ debugLines(): Float32Array | null;
167
+ dispose(): void;
168
+ private syncBodies;
169
+ private ensureEntry;
170
+ }
171
+ //#endregion
172
+ //#region src/2d/create-game.d.ts
173
+ /** Minimal keyboard event source (window, or anything shaped like it). */
174
+ interface KeyboardTarget {
175
+ addEventListener: (t: string, cb: (e: KeyboardEvent) => void) => void;
176
+ removeEventListener: (t: string, cb: (e: KeyboardEvent) => void) => void;
177
+ }
178
+ interface CreateGame2DOptions {
179
+ canvas: HTMLCanvasElement;
180
+ /** Scene JSON (cloned internally — the same object can boot many games). */
181
+ scene: unknown;
182
+ /** Behavior classes to register (hot-replace tolerant). */
183
+ behaviors?: Record<string, BehaviorCtor>;
184
+ /**
185
+ * Auto-register the built-in `incanto/gameplay` behaviors (Health, Pickup,
186
+ * ScoreKeeper, …) — default true (batteries-included). They register BEFORE
187
+ * `behaviors`, so a same-named user behavior always wins. Set false to ship
188
+ * none of them.
189
+ */
190
+ gameplay?: boolean;
191
+ /**
192
+ * Keyboard source (default: window when available). `false` disables.
193
+ * Bound keys preventDefault; editable elements are ignored (see InputMap).
194
+ */
195
+ keyboard?: KeyboardTarget | false;
196
+ /** 'auto' (default): enable Rapier iff the tree has physics bodies. */
197
+ physics?: "auto" | boolean;
198
+ /**
199
+ * On-screen touch controls for the scene's `"touch"` input declarations:
200
+ * 'auto' (default) shows them on coarse-pointer devices, true forces them,
201
+ * false disables. They overlay `touchContainer` (default: the canvas's
202
+ * parent element — give it position: relative).
203
+ */
204
+ touch?: "auto" | boolean;
205
+ touchContainer?: HTMLElement;
206
+ /** @internal Test seam — replaces `document` for the touch overlay. */
207
+ _touchDoc?: {
208
+ createElement(tag: string): HTMLElement;
209
+ };
210
+ /**
211
+ * Mount the runtime debug overlay (`incanto/debug` — explorer/inspector/
212
+ * logs). Default: false (off), and there is NO URL/query toggle — a deployed
213
+ * build must never be switchable on by end users. Opt in via `debug`, gated
214
+ * to development, e.g. `debug: import.meta.env.VITE_INCANTO_DEBUG === '1'`.
215
+ */
216
+ debug?: boolean;
217
+ /** @internal Test seam — replaces `document` for the debug overlay. */
218
+ _debugDoc?: {
219
+ createElement(tag: string): HTMLElement;
220
+ };
221
+ seed?: number;
222
+ fixedHz?: number;
223
+ /** Renderer extras (pixelRatio, antialias, custom asset store). */
224
+ pixelRatio?: number;
225
+ antialias?: boolean;
226
+ resolveScene?: LoadSceneOptions["resolveScene"];
227
+ /** @internal Test seam — replaces the Renderer2D construction. */
228
+ _rendererFactory?: (engine: Engine, canvas: HTMLCanvasElement) => GameRenderer;
229
+ /** @internal Test seam — replaces the rAF scheduler. */
230
+ _scheduler?: Scheduler;
231
+ }
232
+ /** What createGame needs from a renderer (stats is optional for test stubs). */
233
+ interface GameRenderer {
234
+ dispose(): void;
235
+ stats?(): RendererStats;
236
+ }
237
+ interface Game2D {
238
+ engine: Engine;
239
+ scene: Scene$1;
240
+ renderer: GameRenderer;
241
+ physics: Physics2D | null;
242
+ /** Engine + renderer perf counters in one read (fps/nodes/triangles/…). */
243
+ stats(): GameStats;
244
+ /** Tear the whole game down (renderer, loop, scene, listeners). */
245
+ dispose(): void;
246
+ }
247
+ /**
248
+ * The one-call boot every game was hand-rolling: register → load (engine
249
+ * attached, so onReady can use this.rng/this.log) → physics (auto-detected) →
250
+ * input → renderer → start. The returned `dispose()` is the one-call
251
+ * teardown for SPA unmounts. Audio unlocks on the first user gesture.
252
+ */
253
+ declare function createGame2D(opts: CreateGame2DOptions): Promise<Game2D>;
254
+ //#endregion
255
+ //#region src/2d/library-sprite.d.ts
256
+ interface SpriteFromLibraryResult {
257
+ /** Scene `assets{}` entry — put it under your chosen key. */
258
+ asset: JsonObject;
259
+ /** AnimatedSprite2D props (sheet/autoplay/animations) — ready to paste. */
260
+ props: JsonObject;
261
+ }
262
+ /**
263
+ * Turn a library sprite-animation JSON into a scene-ready spritesheet asset
264
+ * declaration + AnimatedSprite2D props. `autoplay` defaults to the first
265
+ * LOOPING animation (idle-like), falling back to the first one.
266
+ */
267
+ declare function spriteFromLibraryMeta(meta: unknown, opts: {
268
+ url: string;
269
+ assetKey: string;
270
+ autoplay?: string;
271
+ }): SpriteFromLibraryResult;
272
+ //#endregion
273
+ //#region src/2d/nodes/sprite-2d.d.ts
274
+ interface ResolvedSpriteTexture {
275
+ texture: Texture;
276
+ width: number;
277
+ height: number;
278
+ }
279
+ /**
280
+ * A textured quad. The drawable is an inner mesh child of the node's backing
281
+ * object, so node children never inherit the texture-size scaling.
282
+ */
283
+ declare class Sprite2D extends Node2D {
284
+ static override readonly typeName: string;
285
+ static override readonly props: PropSchema;
286
+ /** `'$assetKey'` texture reference. Empty = hidden. */
287
+ texture: string;
288
+ /** [0,0] = top-left on the origin … [1,1] = bottom-right on the origin. */
289
+ anchor: number[];
290
+ flipX: boolean;
291
+ flipY: boolean;
292
+ tint: string;
293
+ opacity: number;
294
+ private quadMesh;
295
+ /** @internal The drawable quad (lazily created, attached under the backing object). */
296
+ _quad(): Mesh;
297
+ protected override _createObject2D(): Object3D;
298
+ /** Override point: AnimatedSprite2D substitutes its frame window here. */
299
+ protected resolveTexture(assets: AssetStore2D | null): ResolvedSpriteTexture | null;
300
+ override _syncObject2D(assets: AssetStore2D | null): void;
301
+ }
302
+ //#endregion
303
+ //#region src/2d/nodes/animated-sprite-2d.d.ts
304
+ interface AnimationDef {
305
+ /**
306
+ * Two numbers = inclusive [start, end] frame RANGE.
307
+ * Any other length = explicit frame list.
308
+ */
309
+ frames: number[];
310
+ fps: number;
311
+ loop?: boolean;
312
+ }
313
+ /**
314
+ * Spritesheet animation: pure-JSON animation map, frame selection via a UV
315
+ * window on the node's own texture clone. Frames advance in `update(dt)` —
316
+ * deterministic and headless-testable.
317
+ */
318
+ declare class AnimatedSprite2D extends Sprite2D {
319
+ static override readonly typeName: string;
320
+ static override readonly signals: readonly string[];
321
+ static override readonly props: PropSchema;
322
+ /** `'$assetKey'` of a spritesheet asset. */
323
+ sheet: string;
324
+ animations: Record<string, AnimationDef>;
325
+ autoplay: string;
326
+ playing: boolean;
327
+ /** Absolute frame index within the sheet. */
328
+ currentFrame: number;
329
+ currentAnimation: string;
330
+ private frameList;
331
+ private frameIndex;
332
+ private frameTime;
333
+ private ownTexture;
334
+ private loadedSheetRef;
335
+ play(name: string): void;
336
+ stop(): void;
337
+ override onReady(): void;
338
+ override update(dt: number): void;
339
+ protected override resolveTexture(assets: AssetStore2D | null): ResolvedSpriteTexture | null;
340
+ }
341
+ //#endregion
342
+ //#region src/2d/nodes/camera-2d.d.ts
343
+ /**
344
+ * 2D camera: its `position` is the view CENTER. `follow` tracks a node path
345
+ * with exponential smoothing; `limits [minX,minY,maxX,maxY]` clamp the view
346
+ * rect inside a world region (applied by the renderer via `clampedCenter`).
347
+ */
348
+ declare class Camera2D extends Node2D {
349
+ static override readonly typeName: string;
350
+ static override readonly props: PropSchema;
351
+ /** NodePath of a Node2D to track. */
352
+ follow: string;
353
+ /** 0 = snap; 0.85–0.95 = smooth chase (per-frame retention at 60fps). */
354
+ smoothing: number;
355
+ zoom: number;
356
+ /** [minX, minY, maxX, maxY] world px; empty = unlimited. */
357
+ limits: number[];
358
+ current: boolean;
359
+ /** `zoom` clamped to a positive floor — 0/negative zoom must never produce NaN views. */
360
+ get effectiveZoom(): number;
361
+ override update(dt: number): void;
362
+ /** View center after clamping the (vw×vh)/zoom view rect inside `limits`. */
363
+ clampedCenter(vw: number, vh: number): {
364
+ x: number;
365
+ y: number;
366
+ };
367
+ }
368
+ //#endregion
369
+ //#region src/2d/nodes/character-controller-2d.d.ts
370
+ /**
371
+ * Preset controller: drives its PARENT CharacterBody2D from the engine
372
+ * InputMap — JSON-only games get a playable character with zero TypeScript.
373
+ *
374
+ * - `platformer`: x movement + gravity (from the scene `physics.gravity[1]`,
375
+ * default 980) + jump (`jumpHeight` px → impulse √(2·g·h))
376
+ * - `topDown`: full-axis movement, no gravity
377
+ */
378
+ declare class CharacterController2D extends Node {
379
+ static override readonly typeName: string;
380
+ static readonly props: PropSchema;
381
+ mode: string;
382
+ maxSpeed: number;
383
+ /** Pixels (platformer mode). */
384
+ jumpHeight: number;
385
+ moveAction: string;
386
+ jumpAction: string;
387
+ override onReady(): void;
388
+ override fixedUpdate(dt: number): void;
389
+ }
390
+ //#endregion
391
+ //#region src/2d/nodes/color-rect-2d.d.ts
392
+ /**
393
+ * A solid-colored rectangle — no texture, no asset file. The prototyping
394
+ * workhorse: paddles, walls, platforms, flashes, fade overlays. Swap in a
395
+ * Sprite2D when art arrives.
396
+ */
397
+ declare class ColorRect2D extends Node2D {
398
+ static override readonly typeName: string;
399
+ static override readonly props: PropSchema;
400
+ /** [width, height] in world px. */
401
+ size: number[];
402
+ color: string;
403
+ opacity: number;
404
+ /** [0,0] = top-left on the origin … [1,1] = bottom-right on the origin. */
405
+ anchor: number[];
406
+ private quadMesh;
407
+ /** @internal The drawable quad (lazily created under the backing object). */
408
+ _quad(): Mesh;
409
+ override _syncObject2D(assets: AssetStore2D | null): void;
410
+ }
411
+ //#endregion
412
+ //#region src/2d/nodes/label.d.ts
413
+ /**
414
+ * CanvasTexture-backed text (zero deps, WebGL-safe). Re-rasterizes only when
415
+ * text props change. Headless-safe: without a DOM it simply stays hidden —
416
+ * all prop/serialization logic still works.
417
+ */
418
+ declare class Label extends Node2D {
419
+ static override readonly typeName: string;
420
+ static override readonly props: PropSchema;
421
+ text: string;
422
+ fontSize: number;
423
+ color: string;
424
+ font: string;
425
+ /** 'left' | 'center' | 'right' — anchor of the text block on the node origin. */
426
+ align: string;
427
+ private quadMesh;
428
+ private canvas;
429
+ private lastKey;
430
+ /** @internal */
431
+ _quad(): Mesh;
432
+ override _syncObject2D(assets: AssetStore2D | null): void;
433
+ private rasterize;
434
+ }
435
+ //#endregion
436
+ //#region src/2d/nodes/particles-2d.d.ts
437
+ /**
438
+ * GPU-instanced particle emitter with predefined looks: set `preset` to
439
+ * `'fire' | 'smoke' | 'sparks' | 'fireworks' | 'explosion' | 'flash' |
440
+ * 'lightning' | 'rain' | 'snow' | 'magic'` and every prop snaps to that
441
+ * look's baseline — any prop you write in the scene overrides it. `rate: 0`
442
+ * + `burst` makes a one-shot that emits `finished` (queueFree it there).
443
+ * Simulation is deterministic under the engine seed.
444
+ */
445
+ declare class Particles2D extends Node2D {
446
+ static override readonly typeName: string;
447
+ static override readonly signals: readonly string[];
448
+ static override readonly props: PropSchema;
449
+ preset: string;
450
+ emitting: boolean;
451
+ rate: number;
452
+ burst: number;
453
+ lifetime: number[];
454
+ speed: number[];
455
+ directionDeg: number;
456
+ spreadDeg: number;
457
+ gravity: number[];
458
+ drag: number;
459
+ sizeStart: number;
460
+ sizeEnd: number;
461
+ colorStart: string;
462
+ colorEnd: string;
463
+ /** Per-particle base colours sampled by the particle's stable seed (multi-
464
+ * colour confetti). Empty → the colorStart→colorEnd ramp is used unchanged. */
465
+ paletteColors: string[];
466
+ alphaStart: number;
467
+ alphaEnd: number;
468
+ /** Per-particle alpha twinkle frequency in Hz (0 = off) — a seed-phased sine
469
+ * over the particle's age, so each sparkle glitters on its own rhythm. */
470
+ shimmer: number;
471
+ blend: string;
472
+ maxParticles: number;
473
+ /** Loader hook: unknown presets and bad blends fail at LOAD. */
474
+ static validateJson(node: Node): void;
475
+ private sim;
476
+ private bursted;
477
+ private finishedEmitted;
478
+ /** @internal Lazily applies the preset + builds the deterministic sim. */
479
+ _ensureSim(): ParticleSim;
480
+ override update(dt: number): void;
481
+ /** Restart a one-shot (re-burst + allow finished to fire again). */
482
+ replay(): void;
483
+ private mesh;
484
+ /** Parsed `paletteColors`, reused; rebuilt only when the hex list changes. */
485
+ private paletteCache;
486
+ private paletteCacheKey;
487
+ /** Parse `paletteColors` into a reused `Color[]`, rebuilt only on change;
488
+ * null when empty so the caller uses the colorStart→colorEnd ramp. */
489
+ private refreshPalette;
490
+ override _syncObject2D(assets: AssetStore2D | null): void;
491
+ }
492
+ //#endregion
493
+ //#region src/2d/nodes/ui-layer.d.ts
494
+ declare const ANCHORS: readonly ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
495
+ type UIAnchor = (typeof ANCHORS)[number];
496
+ /**
497
+ * Screen-space container (Godot CanvasLayer): 2D descendants render in a
498
+ * separate pass that ignores the world camera — HUDs, scores, menus.
499
+ *
500
+ * `anchor` pins the layer's origin to a screen corner/edge/center, so HUD
501
+ * coordinates stay tiny offsets that survive any canvas size: a Label at
502
+ * [-16, 16] under a 'top-right' layer hugs the top-right corner everywhere.
503
+ */
504
+ declare class UILayer extends Node {
505
+ static override readonly typeName: string;
506
+ static readonly props: PropSchema;
507
+ anchor: string;
508
+ private group;
509
+ /** @internal The three group all descendants mount under (lazily created). */
510
+ _ensureGroup(): Group;
511
+ /** Loader hook: a misspelled anchor fails at LOAD, listing the valid set. */
512
+ static validateJson(node: Node): void;
513
+ /** @internal Anchor origin in y-down ui px for a given ui size. */
514
+ _anchorOrigin(width: number, height: number): {
515
+ x: number;
516
+ y: number;
517
+ };
518
+ }
519
+ //#endregion
520
+ //#region src/2d/register.d.ts
521
+ /**
522
+ * Register the 2D node taxonomy (and the core nodes). Call once in your game
523
+ * entry before loading a 2D scene. Explicit — never an import side effect.
524
+ */
525
+ declare function registerNodes2D(): void;
526
+ //#endregion
527
+ //#region src/2d/picking.d.ts
528
+ /**
529
+ * Pure view-transform helpers shared by Renderer2D's picking API and editor
530
+ * overlays. World space is y-down pixels (the engine's 2D convention); screen
531
+ * space is CSS pixels on the canvas.
532
+ */
533
+ interface View2D {
534
+ /** World-space view center. */
535
+ cx: number;
536
+ cy: number;
537
+ zoom: number;
538
+ /** Canvas CSS size. */
539
+ w: number;
540
+ h: number;
541
+ }
542
+ //#endregion
543
+ //#region src/2d/renderer.d.ts
544
+ interface Renderer2DOptions {
545
+ canvas: HTMLCanvasElement;
546
+ engine: Engine;
547
+ /** Bring your own store (custom loader) — defaults to a TextureLoader-backed one. */
548
+ assets?: AssetStore2D;
549
+ /**
550
+ * Backbuffer scale. DEFAULTS TO 1 — Phaser parity: the browser upscales on
551
+ * hiDPI screens, which reads as soft antialiased edges. Pass
552
+ * `window.devicePixelRatio` for crisp retina rendering instead.
553
+ */
554
+ pixelRatio?: number;
555
+ /** MSAA on the canvas. Defaults to TRUE (Phaser parity). */
556
+ antialias?: boolean;
557
+ }
558
+ /**
559
+ * 2D presentation layer: world pass through the active Camera2D
560
+ * (center/zoom/limits), then a screen-space UI pass for UILayer subtrees.
561
+ * 1 unit = 1 px, (0,0) top-left, +y down.
562
+ */
563
+ declare class Renderer2D {
564
+ /**
565
+ * When set, the world pass frames THIS view instead of the scene's active
566
+ * Camera2D — tooling (the scene editor) pans/zooms freely without touching
567
+ * scene data. `null` restores normal camera behavior.
568
+ */
569
+ viewOverride: {
570
+ cx: number;
571
+ cy: number;
572
+ zoom: number;
573
+ } | null;
574
+ private readonly webgl;
575
+ private readonly worldScene;
576
+ private readonly uiScene;
577
+ private readonly worldCam;
578
+ private readonly uiCam;
579
+ private readonly engine;
580
+ private readonly assets;
581
+ private readonly loadedAssetScenes;
582
+ private readonly disconnect;
583
+ private readonly canvas;
584
+ constructor(opts: Renderer2DOptions);
585
+ private readonly debugLines;
586
+ private syncDebugLines;
587
+ private render;
588
+ private lastViewport;
589
+ private lastView;
590
+ /** The world view used by the LAST render (center/zoom/canvas size). */
591
+ view(): View2D;
592
+ /**
593
+ * GPU counters for the LAST rendered frame (world + UI passes combined),
594
+ * straight from three's per-frame-maintained `renderer.info`.
595
+ */
596
+ stats(): RendererStats;
597
+ worldFromScreen(sx: number, sy: number): {
598
+ x: number;
599
+ y: number;
600
+ };
601
+ screenFromWorld(wx: number, wy: number): {
602
+ x: number;
603
+ y: number;
604
+ };
605
+ /**
606
+ * The topmost world-pass node under a canvas pixel (UI pass excluded).
607
+ * Hits resolve through `userData.incantoNode` and prefer higher renderOrder.
608
+ */
609
+ pick(sx: number, sy: number): Node | null;
610
+ /**
611
+ * Canvas-pixel AABB of a node's rendered world-pass objects, or null when it
612
+ * has no renderable extent (plain Nodes, empty containers).
613
+ */
614
+ boundsOf(node: Node): {
615
+ x: number;
616
+ y: number;
617
+ w: number;
618
+ h: number;
619
+ } | null;
620
+ dispose(): void;
621
+ }
622
+ //#endregion
623
+ //#region src/2d/sync.d.ts
624
+ interface Sync2DResult {
625
+ activeCamera: Camera2D | null;
626
+ }
627
+ interface UiSize {
628
+ width: number;
629
+ height: number;
630
+ }
631
+ /**
632
+ * Mirror an Incanto node tree onto two three scenes:
633
+ * - `world`: camera-space drawables
634
+ * - `ui`: everything under a `UILayer` (screen space, ignores the camera);
635
+ * each layer mounts through its own group, positioned by its `anchor`
636
+ * against `uiSize` (origin when no size is known — headless).
637
+ *
638
+ * Same dirty-push contract as the 3D sync; pure scene-graph math, headless-testable.
639
+ */
640
+ declare function syncTree2D(root: Node, world: Scene, ui: Scene, assets: AssetStore2D | null, uiSize?: UiSize): Sync2DResult;
641
+ //#endregion
642
+ export { AnimatedSprite2D, type AnimationDef, Area2D, type AssetStatus, AssetStore2D, Camera2D, CharacterBody2D, CharacterController2D, ColorRect2D, type CreateGame2DOptions, type Game2D, Label, Node2D, Particles2D, Physics2D, type Physics2DOptions, PhysicsBody2D, Renderer2D, type Renderer2DOptions, type ResolvedSpriteTexture, RigidBody2D, type SheetInfo, Sprite2D, type SpriteFromLibraryResult, StaticBody2D, type Sync2DResult, type TextureLoadCallbacks, type UIAnchor, UILayer, createGame2D, enablePhysics2D, registerNodes2D, spriteFromLibraryMeta, syncTree2D };
package/dist/2d.js ADDED
@@ -0,0 +1,44 @@
1
+ import { t as IncantoError } from "./errors-BpWbnbb_.js";
2
+ import { a as AssetStore2D, i as syncTree2D, r as Renderer2D, t as createGame2D } from "./create-game-CZHROKcT.js";
3
+ import { a as ColorRect2D, c as AnimatedSprite2D, d as CharacterBody2D, f as PhysicsBody2D, h as Node2D, i as Label, l as Sprite2D, m as StaticBody2D, n as UILayer, o as CharacterController2D, p as RigidBody2D, r as Particles2D, s as Camera2D, t as registerNodes2D, u as Area2D } from "./register-DPEV9_9t.js";
4
+ import { n as enablePhysics2D, t as Physics2D } from "./physics-2d-KuMWPTf6.js";
5
+ //#region src/2d/library-sprite.ts
6
+ /**
7
+ * Turn a library sprite-animation JSON into a scene-ready spritesheet asset
8
+ * declaration + AnimatedSprite2D props. `autoplay` defaults to the first
9
+ * LOOPING animation (idle-like), falling back to the first one.
10
+ */
11
+ function spriteFromLibraryMeta(meta, opts) {
12
+ const m = meta;
13
+ if (typeof m?.frame?.width !== "number" || typeof m.frame.height !== "number" || !m.animations || Object.keys(m.animations).length === 0) throw new IncantoError("BAD_FORMAT", `Library sprite metadata needs {frame: {width, height}, animations: {<name>: {start, end, frameRate, repeat}}} — got ${JSON.stringify(meta).slice(0, 120)}.`);
14
+ const animations = {};
15
+ let firstName = null;
16
+ let firstLooping = null;
17
+ for (const [name, anim] of Object.entries(m.animations)) {
18
+ if (typeof anim.start !== "number" || typeof anim.end !== "number" || typeof anim.frameRate !== "number") throw new IncantoError("BAD_FORMAT", `Library animation '${name}' needs numeric start/end/frameRate, got ${JSON.stringify(anim)}.`);
19
+ const loop = anim.repeat === -1;
20
+ animations[name] = {
21
+ frames: [anim.start, anim.end],
22
+ fps: anim.frameRate,
23
+ loop
24
+ };
25
+ firstName ??= name;
26
+ if (loop && firstLooping === null) firstLooping = name;
27
+ }
28
+ const autoplay = opts.autoplay ?? firstLooping ?? firstName;
29
+ return {
30
+ asset: {
31
+ type: "spritesheet",
32
+ url: opts.url,
33
+ frameWidth: m.frame.width,
34
+ frameHeight: m.frame.height
35
+ },
36
+ props: {
37
+ sheet: `$${opts.assetKey}`,
38
+ autoplay,
39
+ animations
40
+ }
41
+ };
42
+ }
43
+ //#endregion
44
+ export { AnimatedSprite2D, Area2D, AssetStore2D, Camera2D, CharacterBody2D, CharacterController2D, ColorRect2D, Label, Node2D, Particles2D, Physics2D, PhysicsBody2D, Renderer2D, RigidBody2D, Sprite2D, StaticBody2D, UILayer, createGame2D, enablePhysics2D, registerNodes2D, spriteFromLibraryMeta, syncTree2D };