cubeforge 0.1.9 → 0.2.1

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.
@@ -0,0 +1,909 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import React__default, { CSSProperties, ReactNode } from 'react';
4
+ import { Plugin, EntityId, System, ECSWorld, ScriptUpdateFn, NavGrid, WorldSnapshot, EventBus } from '@cubeforge/core';
5
+ export { AssetProgress, Component, ECSWorld, Ease, EntityId, GameTimer, NavGrid, Plugin, PreloadManifest, ScriptUpdateFn, TransformComponent, TweenHandle, Vec2Like, WorldSnapshot, arrive, createTag, createTimer, createTransform, definePlugin, findByTag, flee, patrol, preloadManifest, seek, tween, wander } from '@cubeforge/core';
6
+ import { InputManager, ActionBindings, InputContextName, PlayerInput, InputRecorderControls } from '@cubeforge/input';
7
+ export { ActionBindings, AxisBinding, InputContextName, InputManager, InputMap, InputRecorderControls, InputRecording, InputRecording as InputRecordingData, PlayerInput, createInputMap, createInputRecorder, createPlayerInput, globalInputContext } from '@cubeforge/input';
8
+ import { EngineState } from '@cubeforge/context';
9
+ export { EngineState, useCircleEnter, useCircleExit, useCircleStay, useCollisionEnter, useCollisionExit, useCollisionStay, useTriggerEnter, useTriggerExit, useTriggerStay } from '@cubeforge/context';
10
+ export { AISteering, AnimationClip, AnimationControllerResult, BindingControls, GameState as GameStateDefinition, GameStateMachineResult, HealthControls, HealthOptions, KinematicBodyControls, LevelTransitionControls, PathfindingControls, PlatformerControllerOptions, RestartControls, SaveControls, SaveOptions, TopDownMovementOptions, TransitionOptions, TransitionType, useAISteering, useAnimationController, useDamageZone, useDropThrough, useGameStateMachine, useHealth, useKinematicBody, useLevelTransition, usePathfinding, usePersistedBindings, usePlatformerController, useRestart, useSave, useTopDownMovement } from '@cubeforge/gameplay';
11
+ export { AudioGroup, SoundControls, duck, getGroupVolume, setGroupMute, setGroupVolume, setMasterVolume, stopGroup, useSound } from '@cubeforge/audio';
12
+ export { DevToolsHandle } from '@cubeforge/devtools';
13
+ export { BoxColliderComponent, CapsuleColliderComponent, CircleColliderComponent, RaycastHit, RigidBodyComponent, overlapBox, overlapCircle, raycast, raycastAll, sweepBox } from '@cubeforge/physics';
14
+ export { AnimationStateComponent, ParallaxLayerComponent, Particle, ParticlePoolComponent, SpriteComponent, SquashStretchComponent, TextComponent, TrailComponent, createSprite } from '@cubeforge/renderer';
15
+
16
+ interface GameControls {
17
+ pause(): void;
18
+ resume(): void;
19
+ reset(): void;
20
+ }
21
+ interface GameProps {
22
+ width?: number;
23
+ height?: number;
24
+ /** Pixels per second squared downward (default 980) */
25
+ gravity?: number;
26
+ /** Enable debug overlay: collider wireframes, FPS, entity count */
27
+ debug?: boolean;
28
+ /**
29
+ * Canvas scaling strategy (default 'none'):
30
+ * - 'none' — fixed pixel size, no scaling
31
+ * - 'contain' — CSS scale to fit parent while preserving aspect ratio
32
+ * - 'pixel' — nearest-neighbor pixel-art scaling via CSS
33
+ */
34
+ scale?: 'none' | 'contain' | 'pixel';
35
+ /** Called once the engine is ready — receives pause/resume/reset controls */
36
+ onReady?: (controls: GameControls) => void;
37
+ /** Enable time-travel debugging overlay (frame scrubber + entity inspector). */
38
+ devtools?: boolean;
39
+ /** Run the simulation in deterministic mode using a seeded RNG. */
40
+ deterministic?: boolean;
41
+ /** Seed for the deterministic RNG (default 0). Only used when deterministic=true. */
42
+ seed?: number;
43
+ /**
44
+ * When true, the game loop starts immediately and sprites swap from color → image as
45
+ * they load in the background. When false (default) the loop is held until every
46
+ * sprite that is part of the initial scene has finished loading, so the first frame
47
+ * shown is fully rendered with real assets.
48
+ */
49
+ asyncAssets?: boolean;
50
+ /** Custom plugins to register after core systems. Each plugin's systems run after Render. */
51
+ plugins?: Plugin[];
52
+ /**
53
+ * Custom render system constructor. Must implement the System interface and accept
54
+ * `(canvas: HTMLCanvasElement, entityIds: Map<string, EntityId>)`.
55
+ *
56
+ * Defaults to the built-in Canvas2D RenderSystem.
57
+ * Example: `import { WebGLRenderSystem } from '@cubeforge/webgl-renderer'`
58
+ */
59
+ renderer?: new (canvas: HTMLCanvasElement, entityIds: Map<string, EntityId>) => System;
60
+ style?: CSSProperties;
61
+ className?: string;
62
+ children?: React__default.ReactNode;
63
+ }
64
+ declare function Game({ width, height, gravity, debug, devtools, scale, deterministic, seed, asyncAssets, onReady, plugins, renderer: CustomRenderer, style, className, children, }: GameProps): react_jsx_runtime.JSX.Element;
65
+
66
+ interface WorldProps {
67
+ /** Gravitational acceleration in pixels/s² (default inherited from Game) */
68
+ gravity?: number;
69
+ /** Canvas background color */
70
+ background?: string;
71
+ children?: ReactNode;
72
+ }
73
+ declare function World({ gravity, background, children }: WorldProps): react_jsx_runtime.JSX.Element;
74
+
75
+ interface EntityProps {
76
+ /** Optional string ID for cross-entity lookups (e.g. camera follow) */
77
+ id?: string;
78
+ /** Tags for grouping / querying (e.g. ['enemy', 'damageable']) */
79
+ tags?: string[];
80
+ children?: ReactNode;
81
+ }
82
+ declare function Entity({ id, tags, children }: EntityProps): react_jsx_runtime.JSX.Element | null;
83
+
84
+ interface TransformProps {
85
+ x?: number;
86
+ y?: number;
87
+ rotation?: number;
88
+ scaleX?: number;
89
+ scaleY?: number;
90
+ }
91
+ declare function Transform({ x, y, rotation, scaleX, scaleY }: TransformProps): null;
92
+
93
+ /** Maps frame names to frameIndex numbers */
94
+ type SpriteAtlas = Record<string, number>;
95
+ /**
96
+ * Helper to build an atlas from a grid spritesheet.
97
+ * columns = number of frames per row.
98
+ * names = frame names in row-major order.
99
+ */
100
+ declare function createAtlas(names: string[], _columns: number): SpriteAtlas;
101
+
102
+ interface SpriteProps {
103
+ width: number;
104
+ height: number;
105
+ color?: string;
106
+ src?: string;
107
+ offsetX?: number;
108
+ offsetY?: number;
109
+ zIndex?: number;
110
+ visible?: boolean;
111
+ flipX?: boolean;
112
+ anchorX?: number;
113
+ anchorY?: number;
114
+ frameIndex?: number;
115
+ frameWidth?: number;
116
+ frameHeight?: number;
117
+ frameColumns?: number;
118
+ atlas?: SpriteAtlas;
119
+ frame?: string;
120
+ tileX?: boolean;
121
+ tileY?: boolean;
122
+ }
123
+ declare function Sprite({ width, height, color, src, offsetX, offsetY, zIndex, visible, flipX, anchorX, anchorY, frameIndex, frameWidth, frameHeight, frameColumns, atlas, frame, tileX, tileY, }: SpriteProps): null;
124
+
125
+ interface TextProps {
126
+ text: string;
127
+ fontSize?: number;
128
+ fontFamily?: string;
129
+ color?: string;
130
+ align?: CanvasTextAlign;
131
+ baseline?: CanvasTextBaseline;
132
+ zIndex?: number;
133
+ visible?: boolean;
134
+ maxWidth?: number;
135
+ offsetX?: number;
136
+ offsetY?: number;
137
+ }
138
+ declare function Text({ text, fontSize, fontFamily, color, align, baseline, zIndex, visible, maxWidth, offsetX, offsetY, }: TextProps): null;
139
+
140
+ interface RigidBodyProps {
141
+ mass?: number;
142
+ gravityScale?: number;
143
+ isStatic?: boolean;
144
+ bounce?: number;
145
+ friction?: number;
146
+ vx?: number;
147
+ vy?: number;
148
+ /** Prevent any horizontal movement — velocity.x is zeroed every frame */
149
+ lockX?: boolean;
150
+ /** Prevent any vertical movement — velocity.y is zeroed every frame (disables gravity) */
151
+ lockY?: boolean;
152
+ }
153
+ declare function RigidBody({ mass, gravityScale, isStatic, bounce, friction, vx, vy, lockX, lockY, }: RigidBodyProps): null;
154
+
155
+ interface BoxColliderProps {
156
+ width: number;
157
+ height: number;
158
+ offsetX?: number;
159
+ offsetY?: number;
160
+ isTrigger?: boolean;
161
+ layer?: string;
162
+ /** Which layers this collider interacts with. '*' = all (default). */
163
+ mask?: string | string[];
164
+ /**
165
+ * One-way platform: only blocks entities falling onto the top surface.
166
+ * Entities below pass through freely (useful for jump-through ledges).
167
+ */
168
+ oneWay?: boolean;
169
+ }
170
+ declare function BoxCollider({ width, height, offsetX, offsetY, isTrigger, layer, mask, oneWay, }: BoxColliderProps): null;
171
+
172
+ interface CircleColliderProps {
173
+ radius: number;
174
+ offsetX?: number;
175
+ offsetY?: number;
176
+ isTrigger?: boolean;
177
+ layer?: string;
178
+ /** Which layers this collider interacts with. '*' = all (default). */
179
+ mask?: string | string[];
180
+ }
181
+ declare function CircleCollider({ radius, offsetX, offsetY, isTrigger, layer, mask, }: CircleColliderProps): null;
182
+
183
+ interface CapsuleColliderProps {
184
+ width: number;
185
+ height: number;
186
+ offsetX?: number;
187
+ offsetY?: number;
188
+ isTrigger?: boolean;
189
+ layer?: string;
190
+ mask?: string | string[];
191
+ }
192
+ declare function CapsuleCollider({ width, height, offsetX, offsetY, isTrigger, layer, mask, }: CapsuleColliderProps): null;
193
+
194
+ interface ScriptProps {
195
+ /** Called once when the entity is mounted — use to attach extra components */
196
+ init?: (entityId: EntityId, world: ECSWorld) => void;
197
+ /** Called every frame */
198
+ update: ScriptUpdateFn | ((entityId: EntityId, world: ECSWorld, input: InputManager, dt: number) => void);
199
+ }
200
+ declare function Script({ init, update }: ScriptProps): null;
201
+
202
+ interface Camera2DProps {
203
+ /** String ID of entity to follow */
204
+ followEntity?: string;
205
+ /** Initial camera X position in world space (default 0 = world origin at screen center) */
206
+ x?: number;
207
+ /** Initial camera Y position in world space (default 0 = world origin at screen center) */
208
+ y?: number;
209
+ zoom?: number;
210
+ /** Lerp smoothing factor (0 = instant snap, 0.85 = smooth) */
211
+ smoothing?: number;
212
+ background?: string;
213
+ bounds?: {
214
+ x: number;
215
+ y: number;
216
+ width: number;
217
+ height: number;
218
+ };
219
+ deadZone?: {
220
+ w: number;
221
+ h: number;
222
+ };
223
+ /** World-space offset applied to the follow target (look-ahead, vertical bias, etc.) */
224
+ followOffsetX?: number;
225
+ followOffsetY?: number;
226
+ }
227
+ declare function Camera2D({ followEntity, x, y, zoom, smoothing, background, bounds, deadZone, followOffsetX, followOffsetY, }: Camera2DProps): null;
228
+
229
+ interface AnimationProps {
230
+ /** Frame indices to play (indexes into the sprite sheet) */
231
+ frames: number[];
232
+ /** Frames per second, default 12 */
233
+ fps?: number;
234
+ /** Whether to loop, default true */
235
+ loop?: boolean;
236
+ /** Whether currently playing, default true */
237
+ playing?: boolean;
238
+ /** Called once when a non-looping animation finishes playing */
239
+ onComplete?: () => void;
240
+ /**
241
+ * Callbacks fired when the animation advances to specific frame indices.
242
+ * Key = 0-based position in the `frames` array.
243
+ *
244
+ * @example
245
+ * frameEvents={{ 2: () => playFootstep(), 5: () => playFootstep() }}
246
+ */
247
+ frameEvents?: Record<number, () => void>;
248
+ }
249
+ declare function Animation({ frames, fps, loop, playing, onComplete, frameEvents }: AnimationProps): null;
250
+
251
+ interface SquashStretchProps {
252
+ /** How much to squash/stretch (default 0.2) */
253
+ intensity?: number;
254
+ /** How fast it returns to 1.0 — lerp speed (default 8) */
255
+ recovery?: number;
256
+ }
257
+ declare function SquashStretch({ intensity, recovery }: SquashStretchProps): null;
258
+
259
+ type ParticlePreset = 'explosion' | 'spark' | 'smoke' | 'coinPickup' | 'jumpDust';
260
+
261
+ interface ParticleEmitterProps {
262
+ active?: boolean;
263
+ /** Named preset — values can be overridden by explicit props */
264
+ preset?: ParticlePreset;
265
+ /** Particles per second, default 20 */
266
+ rate?: number;
267
+ /** Initial particle speed (pixels/s), default 80 */
268
+ speed?: number;
269
+ /** Angle spread in radians, default Math.PI */
270
+ spread?: number;
271
+ /** Base emit angle in radians (0=right, -PI/2=up), default -Math.PI/2 */
272
+ angle?: number;
273
+ /** Particle lifetime in seconds, default 0.8 */
274
+ particleLife?: number;
275
+ /** Particle size in pixels, default 4 */
276
+ particleSize?: number;
277
+ /** Particle color, default '#ffffff' */
278
+ color?: string;
279
+ /** Gravity applied to particles (pixels/s²), default 200 */
280
+ gravity?: number;
281
+ /** Maximum live particles, default 100 */
282
+ maxParticles?: number;
283
+ }
284
+ declare function ParticleEmitter({ active, preset, rate, speed, spread, angle, particleLife, particleSize, color, gravity, maxParticles, }: ParticleEmitterProps): null;
285
+
286
+ interface VirtualJoystickProps {
287
+ /** Diameter of the joystick base in pixels (default 120) */
288
+ size?: number;
289
+ /** Screen corner to anchor to (default 'left') */
290
+ position?: 'left' | 'right';
291
+ /** Extra CSS applied to the outer container */
292
+ style?: React__default.CSSProperties;
293
+ /** Show an action button (e.g. jump) alongside the joystick (default false) */
294
+ actionButton?: boolean;
295
+ /** Label shown on the action button (default 'A') */
296
+ actionLabel?: string;
297
+ /** Name of the virtual button to set (default 'action') */
298
+ actionName?: string;
299
+ }
300
+ /**
301
+ * On-screen virtual joystick for touch / mobile. Place it as a sibling of the
302
+ * `<Game>` canvas (inside a `position: relative` container).
303
+ *
304
+ * Read the joystick state with `useVirtualInput()`.
305
+ *
306
+ * @example
307
+ * <div style={{ position: 'relative' }}>
308
+ * <Game ...>...</Game>
309
+ * <VirtualJoystick position="left" actionButton />
310
+ * </div>
311
+ *
312
+ * // Inside an entity:
313
+ * function MobilePlayer() {
314
+ * const virt = useVirtualInput()
315
+ * // virt.axisX, virt.axisY, virt.button('action')
316
+ * }
317
+ */
318
+ declare function VirtualJoystick({ size, position, style, actionButton, actionLabel, actionName, }: VirtualJoystickProps): react_jsx_runtime.JSX.Element;
319
+
320
+ interface MovingPlatformProps {
321
+ /** Start position */
322
+ x1: number;
323
+ y1: number;
324
+ /** End position */
325
+ x2: number;
326
+ y2: number;
327
+ width?: number;
328
+ height?: number;
329
+ /** Seconds for a full round trip (default 3) */
330
+ duration?: number;
331
+ color?: string;
332
+ }
333
+ /**
334
+ * A static platform that oscillates between (x1,y1) and (x2,y2).
335
+ *
336
+ * @example
337
+ * <MovingPlatform x1={200} y1={350} x2={450} y2={350} width={120} duration={2.5} />
338
+ */
339
+ declare function MovingPlatform({ x1, y1, x2, y2, width, height, duration, color, }: MovingPlatformProps): React__default.ReactElement;
340
+
341
+ interface CheckpointProps {
342
+ x: number;
343
+ y: number;
344
+ width?: number;
345
+ height?: number;
346
+ color?: string;
347
+ /** Called once when a 'player'-tagged entity enters the checkpoint */
348
+ onActivate?: () => void;
349
+ }
350
+ /**
351
+ * A trigger zone that fires `onActivate` once when a player-tagged entity enters it.
352
+ *
353
+ * @example
354
+ * <Checkpoint x={800} y={450} onActivate={() => setCheckpoint(800)} />
355
+ */
356
+ declare function Checkpoint({ x, y, width, height, color, onActivate, }: CheckpointProps): React__default.ReactElement;
357
+
358
+ interface TiledProperty {
359
+ name: string;
360
+ type: string;
361
+ value: string | number | boolean;
362
+ }
363
+ interface TiledObject {
364
+ id: number;
365
+ name: string;
366
+ type: string;
367
+ x: number;
368
+ y: number;
369
+ width: number;
370
+ height: number;
371
+ properties?: TiledProperty[];
372
+ }
373
+ interface TiledLayer {
374
+ type: 'tilelayer' | 'objectgroup';
375
+ name: string;
376
+ visible: boolean;
377
+ opacity: number;
378
+ data?: number[];
379
+ objects?: TiledObject[];
380
+ properties?: TiledProperty[];
381
+ }
382
+ interface TilemapProps {
383
+ /** URL to the Tiled JSON file */
384
+ src: string;
385
+ /**
386
+ * Object layer spawner: called for each object in object layers.
387
+ * Return a React element or null.
388
+ */
389
+ onSpawnObject?: (obj: TiledObject, layer: TiledLayer) => React__default.ReactNode;
390
+ /**
391
+ * Layer filter: return false to skip rendering/processing a layer.
392
+ * Default: all layers rendered.
393
+ */
394
+ layerFilter?: (layer: TiledLayer) => boolean;
395
+ /** Z-index for tile sprites (default 0) */
396
+ zIndex?: number;
397
+ /**
398
+ * Name of the layer (or layers with property `collision: true`) that
399
+ * should create invisible solid colliders. Default: "collision".
400
+ */
401
+ collisionLayer?: string;
402
+ /**
403
+ * Name of the layer (or layers with property `trigger: true`) that
404
+ * should create trigger BoxColliders (no sprite). Default: "triggers".
405
+ */
406
+ triggerLayer?: string;
407
+ /**
408
+ * Called for every tile that has custom properties defined in the tileset.
409
+ * Receives the global tile ID, the property map, and the tile's world position.
410
+ */
411
+ onTileProperty?: (tileId: number, properties: Record<string, unknown>, x: number, y: number) => void;
412
+ /**
413
+ * If provided, collision-layer tiles automatically mark the corresponding
414
+ * NavGrid cells as non-walkable. The grid must already be created with the
415
+ * correct dimensions (map.width × map.height) and cellSize (map.tilewidth).
416
+ */
417
+ navGrid?: NavGrid;
418
+ /**
419
+ * Name of the object layer that contains spawn markers.
420
+ * Objects in this layer are forwarded to `onSpawnObject` just like any
421
+ * other object layer, but they are also identified as spawn points so you
422
+ * can filter them by `layer.name === spawnLayer` inside the callback.
423
+ */
424
+ spawnLayer?: string;
425
+ }
426
+ declare function Tilemap({ src, onSpawnObject, layerFilter, zIndex, collisionLayer, triggerLayer: triggerLayerName, onTileProperty, navGrid, }: TilemapProps): React__default.ReactElement | null;
427
+
428
+ interface ParallaxLayerProps {
429
+ /** Image URL to use as the background layer */
430
+ src: string;
431
+ /** Scroll speed relative to camera (0 = fixed, 1 = moves with camera, 0.3 = slow parallax). Default 0.5 */
432
+ speedX?: number;
433
+ /** Vertical scroll speed relative to camera. Default 0 */
434
+ speedY?: number;
435
+ /** Tile image horizontally. Default true */
436
+ repeatX?: boolean;
437
+ /** Tile image vertically. Default false */
438
+ repeatY?: boolean;
439
+ /** Render order — use negative values to render behind sprites. Default -10 */
440
+ zIndex?: number;
441
+ /** Manual horizontal offset in pixels. Default 0 */
442
+ offsetX?: number;
443
+ /** Manual vertical offset in pixels. Default 0 */
444
+ offsetY?: number;
445
+ }
446
+ /**
447
+ * A background layer that scrolls at a fraction of the camera speed to create depth.
448
+ *
449
+ * @example
450
+ * <ParallaxLayer src="/bg/sky.png" speedX={0.2} repeatX />
451
+ * <ParallaxLayer src="/bg/mountains.png" speedX={0.5} repeatX zIndex={-5} />
452
+ */
453
+ declare function ParallaxLayer({ src, speedX, speedY, repeatX, repeatY, zIndex, offsetX, offsetY, }: ParallaxLayerProps): React__default.ReactElement;
454
+
455
+ interface ScreenFlashHandle {
456
+ flash(color: string, duration: number): void;
457
+ }
458
+ declare const ScreenFlash: React.ForwardRefExoticComponent<React.RefAttributes<ScreenFlashHandle>>;
459
+
460
+ interface CameraZoneProps {
461
+ /** World-space center X of the trigger zone */
462
+ x: number;
463
+ /** World-space center Y of the trigger zone */
464
+ y: number;
465
+ /** Half-width of the trigger zone */
466
+ width: number;
467
+ /** Half-height of the trigger zone */
468
+ height: number;
469
+ /**
470
+ * When an entity with this tag enters the zone, the camera stops following
471
+ * its entity and locks to the fixed position (targetX, targetY).
472
+ */
473
+ watchTag?: string;
474
+ /** Fixed world-space X the camera moves to when activated (defaults to zone center) */
475
+ targetX?: number;
476
+ /** Fixed world-space Y the camera moves to when activated (defaults to zone center) */
477
+ targetY?: number;
478
+ children?: React__default.ReactNode;
479
+ }
480
+ /**
481
+ * Invisible trigger area. When the player (or another tagged entity) enters
482
+ * the zone, the camera follow entity is cleared and the camera locks to the
483
+ * zone's center (or a custom target). On exit, the camera resumes following.
484
+ *
485
+ * Zone detection runs inside the ScriptSystem tick — it respects pause and
486
+ * deterministic stepping.
487
+ *
488
+ * @example
489
+ * ```tsx
490
+ * <CameraZone x={500} y={300} width={200} height={150} watchTag="player" />
491
+ * ```
492
+ */
493
+ declare function CameraZone({ x, y, width, height, watchTag, targetX, targetY, children, }: CameraZoneProps): react_jsx_runtime.JSX.Element;
494
+
495
+ interface TrailProps {
496
+ /** Maximum number of trail points (default 20) */
497
+ length?: number;
498
+ /** CSS color string (default '#ffffff') */
499
+ color?: string;
500
+ /** Trail width in pixels (default 3) */
501
+ width?: number;
502
+ }
503
+ /**
504
+ * Renders a fading polyline that follows the entity's position.
505
+ *
506
+ * The trail is drawn in the render pass — it uses the entity's Transform
507
+ * history collected each frame.
508
+ *
509
+ * Must be used inside an `<Entity>` with a `<Transform>`.
510
+ *
511
+ * @example
512
+ * ```tsx
513
+ * <Entity id="bullet">
514
+ * <Transform x={x} y={y} />
515
+ * <Sprite width={6} height={6} color="#ff0" />
516
+ * <Trail length={15} color="#ff0" width={2} />
517
+ * </Entity>
518
+ * ```
519
+ */
520
+ declare function Trail({ length, color, width }: TrailProps): null;
521
+
522
+ interface AssetLoaderProps {
523
+ /** List of asset URLs to preload */
524
+ assets: string[];
525
+ /** Shown while assets are loading */
526
+ fallback?: React__default.ReactNode;
527
+ /** Called if any asset fails to load */
528
+ onError?: (err: Error) => void;
529
+ children: React__default.ReactNode;
530
+ }
531
+ /**
532
+ * Suspense-style asset loading boundary.
533
+ *
534
+ * Shows `fallback` until all assets in the list are loaded (or errored).
535
+ * Once loaded, renders `children`.
536
+ *
537
+ * @example
538
+ * ```tsx
539
+ * <AssetLoader
540
+ * assets={['/hero.png', '/tiles.png', '/jump.wav']}
541
+ * fallback={<div>Loading…</div>}
542
+ * >
543
+ * <GameScene />
544
+ * </AssetLoader>
545
+ * ```
546
+ */
547
+ declare function AssetLoader({ assets, fallback, onError, children }: AssetLoaderProps): react_jsx_runtime.JSX.Element;
548
+
549
+ declare function useGame(): EngineState;
550
+
551
+ interface CameraControls {
552
+ /**
553
+ * Trigger a screen-shake effect.
554
+ * @param intensity - Maximum pixel displacement per frame.
555
+ * @param duration - How long the shake lasts in seconds.
556
+ */
557
+ shake(intensity: number, duration: number): void;
558
+ /**
559
+ * Set the world-space offset applied to the camera's follow target.
560
+ * Useful for look-ahead: `setFollowOffset(facing * 80, 0)`.
561
+ */
562
+ setFollowOffset(x: number, y: number): void;
563
+ /**
564
+ * Instantly move the camera center to a world-space position.
565
+ * Bypasses smoothing — useful for instant scene cuts.
566
+ */
567
+ setPosition(x: number, y: number): void;
568
+ /**
569
+ * Programmatically set the camera zoom level.
570
+ */
571
+ setZoom(zoom: number): void;
572
+ }
573
+ /**
574
+ * Returns controls for the active Camera2D in the scene.
575
+ * Must be used inside `<Game>`.
576
+ *
577
+ * @example
578
+ * ```tsx
579
+ * function HUD() {
580
+ * const camera = useCamera()
581
+ * return (
582
+ * <button onClick={() => camera.shake(8, 0.4)}>Shake!</button>
583
+ * )
584
+ * }
585
+ * ```
586
+ */
587
+ declare function useCamera(): CameraControls;
588
+
589
+ interface SnapshotControls {
590
+ /**
591
+ * Capture a full serialisable snapshot of all ECS entity/component data.
592
+ * Safe to JSON-stringify and store externally (localStorage, server, etc.)
593
+ *
594
+ * @example
595
+ * ```ts
596
+ * const { save, restore } = useSnapshot()
597
+ * const checkpoint = save() // capture at checkpoint
598
+ * restore(checkpoint) // reset to that state
599
+ * ```
600
+ */
601
+ save(): WorldSnapshot;
602
+ /**
603
+ * Restore the world to a previously captured snapshot.
604
+ * All current entities are replaced with the snapshot's entities.
605
+ *
606
+ * Note: React component state is NOT rolled back — only ECS data is restored.
607
+ * For a full game reset, prefer remounting the `<Game>` key instead.
608
+ */
609
+ restore(snapshot: WorldSnapshot): void;
610
+ }
611
+ /**
612
+ * Returns save/restore controls for the ECS world snapshot system.
613
+ * Must be used inside `<Game>`.
614
+ *
615
+ * @example
616
+ * ```tsx
617
+ * function SaveButton() {
618
+ * const { save, restore } = useSnapshot()
619
+ * const [slot, setSlot] = useState<WorldSnapshot | null>(null)
620
+ * return (
621
+ * <>
622
+ * <button onClick={() => setSlot(save())}>Save</button>
623
+ * <button onClick={() => slot && restore(slot)}>Load</button>
624
+ * </>
625
+ * )
626
+ * }
627
+ * ```
628
+ */
629
+ declare function useSnapshot(): SnapshotControls;
630
+
631
+ declare function useEntity(): EntityId;
632
+
633
+ /**
634
+ * Returns a function that destroys the current entity on the next ECS update.
635
+ * Must be used inside `<Entity>`.
636
+ *
637
+ * Useful for collectibles, projectiles, or any entity that needs to destroy
638
+ * itself in response to a collision or trigger event.
639
+ *
640
+ * @example
641
+ * function CoinPickup() {
642
+ * const destroy = useDestroyEntity()
643
+ * useTriggerEnter(() => {
644
+ * collectCoin()
645
+ * destroy()
646
+ * }, { tag: 'player' })
647
+ * return null
648
+ * }
649
+ */
650
+ declare function useDestroyEntity(): () => void;
651
+
652
+ interface VirtualInputState {
653
+ /** Left–right axis in [−1, 1]. Positive = right. */
654
+ readonly axisX: number;
655
+ /** Up–down axis in [−1, 1]. Positive = down. */
656
+ readonly axisY: number;
657
+ /** Whether a named virtual button is currently pressed. */
658
+ button(name: string): boolean;
659
+ }
660
+ /**
661
+ * Returns the current state of the on-screen virtual joystick and buttons.
662
+ * Reads synchronously from the module-level store — safe to call in a Script
663
+ * update function every frame without triggering React re-renders.
664
+ *
665
+ * Combine with keyboard or gamepad input for multi-input support:
666
+ *
667
+ * @example
668
+ * function MobilePlayer() {
669
+ * const input = useInput()
670
+ * const virt = useVirtualInput()
671
+ *
672
+ * // Script update:
673
+ * const moveX = input.isHeld('ArrowRight') ? 1
674
+ * : input.isHeld('ArrowLeft') ? -1
675
+ * : virt.axisX
676
+ * const jump = input.isPressed('Space') || virt.button('action')
677
+ * }
678
+ */
679
+ declare function useVirtualInput(): VirtualInputState;
680
+
681
+ declare function useInput(): InputManager;
682
+
683
+ interface BoundInputMap {
684
+ /** True every frame any bound key is held. */
685
+ isActionDown(action: string): boolean;
686
+ /** True only on the first frame any bound key was pressed. */
687
+ isActionPressed(action: string): boolean;
688
+ /** True only on the frame any bound key was released. */
689
+ isActionReleased(action: string): boolean;
690
+ /**
691
+ * Returns -1..1 for axis bindings. For key bindings, 1 if down, 0 otherwise.
692
+ * @see {@link AxisBinding}
693
+ */
694
+ getAxis(action: string): number;
695
+ /**
696
+ * Alias for `getAxis`. Matches the `PlayerInput` naming convention — use this
697
+ * when writing code that needs to work with both `useInputMap` and `usePlayerInput`.
698
+ */
699
+ getActionAxis(action: string): number;
700
+ }
701
+ /**
702
+ * React hook that returns a pre-bound action map for use inside `<Game>`.
703
+ *
704
+ * @example
705
+ * ```tsx
706
+ * function MyScript() {
707
+ * const actions = useInputMap({
708
+ * left: ['ArrowLeft', 'KeyA'],
709
+ * right: ['ArrowRight', 'KeyD'],
710
+ * jump: ['Space', 'ArrowUp', 'KeyW'],
711
+ * })
712
+ * // use actions.isActionDown('left') in a Script update or game loop callback
713
+ * }
714
+ * ```
715
+ */
716
+ declare function useInputMap(bindings: ActionBindings): BoundInputMap;
717
+
718
+ declare function useEvents(): EventBus;
719
+ declare function useEvent<T>(event: string, handler: (data: T) => void): void;
720
+
721
+ interface CoordinateHelpers {
722
+ /**
723
+ * Convert a world-space position to canvas pixel coordinates.
724
+ * Takes current camera position and zoom into account.
725
+ */
726
+ worldToScreen(wx: number, wy: number): {
727
+ x: number;
728
+ y: number;
729
+ };
730
+ /**
731
+ * Convert canvas pixel coordinates to world-space position.
732
+ * Takes current camera position and zoom into account.
733
+ */
734
+ screenToWorld(sx: number, sy: number): {
735
+ x: number;
736
+ y: number;
737
+ };
738
+ }
739
+ /**
740
+ * Returns helpers for converting between world-space and screen (canvas pixel) coordinates.
741
+ *
742
+ * @example
743
+ * ```tsx
744
+ * function Minimap() {
745
+ * const { worldToScreen, screenToWorld } = useCoordinates()
746
+ * // worldToScreen(player.x, player.y) → canvas pixel position of player
747
+ * }
748
+ * ```
749
+ */
750
+ declare function useCoordinates(): CoordinateHelpers;
751
+
752
+ interface PreloadState {
753
+ /** 0–1 loading progress */
754
+ progress: number;
755
+ /** True when all assets have finished loading (or errored) */
756
+ loaded: boolean;
757
+ /** First error encountered, if any */
758
+ error: Error | null;
759
+ }
760
+ /**
761
+ * Preloads a list of asset URLs using the engine's AssetManager.
762
+ * Returns loading progress and a `loaded` flag.
763
+ *
764
+ * Works with images (any extension) — audio goes through the engine's audio API.
765
+ *
766
+ * @example
767
+ * ```tsx
768
+ * function Level() {
769
+ * const { progress, loaded } = usePreload(['/hero.png', '/tiles.png'])
770
+ * if (!loaded) return <LoadingBar progress={progress} />
771
+ * return <GameScene />
772
+ * }
773
+ * ```
774
+ */
775
+ declare function usePreload(assets: string[]): PreloadState;
776
+
777
+ interface InputContextControls {
778
+ push(ctx: InputContextName): void;
779
+ pop(ctx: InputContextName): void;
780
+ readonly active: InputContextName;
781
+ }
782
+ /**
783
+ * Access and manipulate the global input context stack.
784
+ *
785
+ * If you pass a `ctx` argument, that context is automatically pushed on mount
786
+ * and popped on unmount — ideal for pause menus, cutscenes, etc.
787
+ *
788
+ * @example
789
+ * ```tsx
790
+ * function PauseMenu() {
791
+ * useInputContext('pause') // auto-push/pop
792
+ * return <div>Paused</div>
793
+ * }
794
+ * ```
795
+ */
796
+ declare function useInputContext(ctx?: InputContextName): InputContextControls;
797
+
798
+ /**
799
+ * Returns a `PlayerInput` bound to the shared InputManager for the given player ID.
800
+ *
801
+ * @param playerId - Player number (1-based).
802
+ * @param bindings - Action bindings for this player.
803
+ *
804
+ * @example
805
+ * ```tsx
806
+ * function P1({ x, y }: Props) {
807
+ * const p1 = usePlayerInput(1, { jump: 'Space', left: 'ArrowLeft', right: 'ArrowRight' })
808
+ * return (
809
+ * <Entity id="p1">
810
+ * <Script update={(id, world, input, dt) => {
811
+ * if (p1.isActionPressed('jump')) rb.vy = -400
812
+ * }} />
813
+ * </Entity>
814
+ * )
815
+ * }
816
+ * ```
817
+ */
818
+ declare function usePlayerInput(playerId: number, bindings: ActionBindings): PlayerInput;
819
+
820
+ /**
821
+ * Returns an array of `PlayerInput` objects for local multiplayer.
822
+ * Each player gets their own action bindings.
823
+ *
824
+ * @param bindingsPerPlayer - Array of binding objects, one per player.
825
+ *
826
+ * @example
827
+ * ```tsx
828
+ * const [p1, p2] = useLocalMultiplayer([
829
+ * { jump: 'Space', left: 'ArrowLeft', right: 'ArrowRight' },
830
+ * { jump: 'KeyW', left: 'KeyA', right: 'KeyD' },
831
+ * ])
832
+ * ```
833
+ */
834
+ declare function useLocalMultiplayer(bindingsPerPlayer: ActionBindings[]): PlayerInput[];
835
+
836
+ /**
837
+ * Returns an `InputRecorderControls` instance that persists across renders.
838
+ *
839
+ * Use inside a `<Script update>` to drive recording/playback:
840
+ *
841
+ * @example
842
+ * ```tsx
843
+ * function DemoPlayer() {
844
+ * const recorder = useInputRecorder()
845
+ *
846
+ * return (
847
+ * <Script update={(id, world, input, dt) => {
848
+ * if (recorder.isRecording) {
849
+ * recorder.captureFrame([...downKeys])
850
+ * }
851
+ * if (recorder.isPlaying) {
852
+ * const frame = recorder.advancePlayback()
853
+ * // apply frame.pressedKeys to player controller
854
+ * }
855
+ * }} />
856
+ * )
857
+ * }
858
+ * ```
859
+ */
860
+ declare function useInputRecorder(): InputRecorderControls;
861
+
862
+ interface GamepadState {
863
+ /** Whether a gamepad is connected at this player index. */
864
+ connected: boolean;
865
+ /**
866
+ * Normalised axis values (typically −1 to +1).
867
+ * Index 0 = left stick X, 1 = left stick Y, 2 = right stick X, 3 = right stick Y
868
+ * (varies by browser / controller).
869
+ */
870
+ axes: readonly number[];
871
+ /**
872
+ * Button pressed states.
873
+ * Standard mapping: 0=A/Cross, 1=B/Circle, 2=X/Square, 3=Y/Triangle,
874
+ * 4=LB, 5=RB, 12=DPad-Up, 13=DPad-Down, 14=DPad-Left, 15=DPad-Right
875
+ */
876
+ buttons: readonly boolean[];
877
+ }
878
+ /**
879
+ * Polls the Gamepad API every frame and returns the current state.
880
+ *
881
+ * @param playerIndex - Which gamepad slot to read (0–3). Default 0.
882
+ *
883
+ * @example
884
+ * function MoveWithGamepad() {
885
+ * const gp = useGamepad()
886
+ * // gp.axes[0] = left stick horizontal
887
+ * // gp.buttons[0] = A button
888
+ * }
889
+ */
890
+ declare function useGamepad(playerIndex?: number): GamepadState;
891
+
892
+ interface PauseControls {
893
+ paused: boolean;
894
+ pause(): void;
895
+ resume(): void;
896
+ toggle(): void;
897
+ }
898
+ /**
899
+ * Controls the game loop pause state from inside a `<Game>` tree.
900
+ *
901
+ * @example
902
+ * function PauseButton() {
903
+ * const { paused, toggle } = usePause()
904
+ * return <button onClick={toggle}>{paused ? 'Resume' : 'Pause'}</button>
905
+ * }
906
+ */
907
+ declare function usePause(): PauseControls;
908
+
909
+ export { Animation, AssetLoader, type BoundInputMap, BoxCollider, Camera2D, type CameraControls, CameraZone, CapsuleCollider, Checkpoint, CircleCollider, type CoordinateHelpers, Entity, Game, type GameControls, type GamepadState, type InputContextControls, MovingPlatform, ParallaxLayer, ParticleEmitter, type ParticlePreset, type PauseControls, type PreloadState, RigidBody, ScreenFlash, type ScreenFlashHandle, Script, type SnapshotControls, Sprite, type SpriteAtlas, SquashStretch, Text, type TiledLayer, type TiledObject, Tilemap, Trail, Transform, type VirtualInputState, VirtualJoystick, type VirtualJoystickProps, World, createAtlas, useCamera, useCoordinates, useDestroyEntity, useEntity, useEvent, useEvents, useGame, useGamepad, useInput, useInputContext, useInputMap, useInputRecorder, useLocalMultiplayer, usePause, usePlayerInput, usePreload, useSnapshot, useVirtualInput };