cubeforge 0.1.2 → 0.1.4

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 (37) hide show
  1. package/dist/index.d.mts +619 -0
  2. package/dist/index.js +758 -917
  3. package/package.json +12 -12
  4. package/dist/components/Animation.d.ts +0 -14
  5. package/dist/components/BoxCollider.d.ts +0 -17
  6. package/dist/components/Camera2D.d.ts +0 -27
  7. package/dist/components/Checkpoint.d.ts +0 -18
  8. package/dist/components/CircleCollider.d.ts +0 -11
  9. package/dist/components/DevTools.d.ts +0 -15
  10. package/dist/components/Entity.d.ts +0 -10
  11. package/dist/components/Game.d.ts +0 -52
  12. package/dist/components/MovingPlatform.d.ts +0 -22
  13. package/dist/components/ParallaxLayer.d.ts +0 -28
  14. package/dist/components/ParticleEmitter.d.ts +0 -26
  15. package/dist/components/RigidBody.d.ts +0 -15
  16. package/dist/components/ScreenFlash.d.ts +0 -4
  17. package/dist/components/Script.d.ts +0 -11
  18. package/dist/components/Sprite.d.ts +0 -22
  19. package/dist/components/SquashStretch.d.ts +0 -8
  20. package/dist/components/Tilemap.d.ts +0 -58
  21. package/dist/components/Transform.d.ts +0 -9
  22. package/dist/components/World.d.ts +0 -10
  23. package/dist/components/particlePresets.d.ts +0 -13
  24. package/dist/components/spriteAtlas.d.ts +0 -8
  25. package/dist/context.d.ts +0 -22
  26. package/dist/hooks/useCamera.d.ts +0 -37
  27. package/dist/hooks/useContact.d.ts +0 -57
  28. package/dist/hooks/useEntity.d.ts +0 -2
  29. package/dist/hooks/useEvents.d.ts +0 -3
  30. package/dist/hooks/useGame.d.ts +0 -2
  31. package/dist/hooks/useInput.d.ts +0 -2
  32. package/dist/hooks/useInputMap.d.ts +0 -25
  33. package/dist/hooks/usePlatformerController.d.ts +0 -32
  34. package/dist/hooks/useSnapshot.d.ts +0 -42
  35. package/dist/hooks/useTopDownMovement.d.ts +0 -22
  36. package/dist/index.d.ts +0 -60
  37. package/dist/systems/debugSystem.d.ts +0 -10
@@ -0,0 +1,619 @@
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, EventBus, AssetManager, GameLoop, WorldSnapshot } from '@cubeforge/core';
5
+ export { Component, ECSWorld, Ease, EntityId, Plugin, ScriptUpdateFn, TransformComponent, TweenHandle, WorldSnapshot, createTag, createTransform, definePlugin, findByTag, tween } from '@cubeforge/core';
6
+ import { InputManager, ActionBindings } from '@cubeforge/input';
7
+ export { ActionBindings, InputManager, InputMap, createInputMap } from '@cubeforge/input';
8
+ import { Canvas2DRenderer, RenderSystem } from '@cubeforge/renderer';
9
+ export { AnimationStateComponent, ParallaxLayerComponent, Particle, ParticlePoolComponent, SpriteComponent, SquashStretchComponent, createSprite } from '@cubeforge/renderer';
10
+ import { PhysicsSystem } from '@cubeforge/physics';
11
+ export { BoxColliderComponent, CircleColliderComponent, RaycastHit, RigidBodyComponent, overlapBox, raycast } from '@cubeforge/physics';
12
+
13
+ interface GameControls {
14
+ pause(): void;
15
+ resume(): void;
16
+ reset(): void;
17
+ }
18
+ interface GameProps {
19
+ width?: number;
20
+ height?: number;
21
+ /** Pixels per second squared downward (default 980) */
22
+ gravity?: number;
23
+ /** Enable debug overlay: collider wireframes, FPS, entity count */
24
+ debug?: boolean;
25
+ /**
26
+ * Canvas scaling strategy (default 'none'):
27
+ * - 'none' — fixed pixel size, no scaling
28
+ * - 'contain' — CSS scale to fit parent while preserving aspect ratio
29
+ * - 'pixel' — nearest-neighbor pixel-art scaling via CSS
30
+ */
31
+ scale?: 'none' | 'contain' | 'pixel';
32
+ /** Called once the engine is ready — receives pause/resume/reset controls */
33
+ onReady?: (controls: GameControls) => void;
34
+ /** Enable time-travel debugging overlay (frame scrubber + entity inspector). */
35
+ devtools?: boolean;
36
+ /** Run the simulation in deterministic mode using a seeded RNG. */
37
+ deterministic?: boolean;
38
+ /** Seed for the deterministic RNG (default 0). Only used when deterministic=true. */
39
+ seed?: number;
40
+ /**
41
+ * When true, the game loop starts immediately and sprites swap from color → image as
42
+ * they load in the background. When false (default) the loop is held until every
43
+ * sprite that is part of the initial scene has finished loading, so the first frame
44
+ * shown is fully rendered with real assets.
45
+ */
46
+ asyncAssets?: boolean;
47
+ /** Custom plugins to register after core systems. Each plugin's systems run after Render. */
48
+ plugins?: Plugin[];
49
+ /**
50
+ * Custom render system constructor. Must implement the System interface and accept
51
+ * `(canvas: HTMLCanvasElement, entityIds: Map<string, EntityId>)`.
52
+ *
53
+ * Defaults to the built-in Canvas2D RenderSystem.
54
+ * Example: `import { WebGLRenderSystem } from '@cubeforge/webgl-renderer'`
55
+ */
56
+ renderer?: new (canvas: HTMLCanvasElement, entityIds: Map<string, EntityId>) => System;
57
+ style?: CSSProperties;
58
+ className?: string;
59
+ children?: React__default.ReactNode;
60
+ }
61
+ 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;
62
+
63
+ interface WorldProps {
64
+ /** Gravitational acceleration in pixels/s² (default inherited from Game) */
65
+ gravity?: number;
66
+ /** Canvas background color */
67
+ background?: string;
68
+ children?: ReactNode;
69
+ }
70
+ declare function World({ gravity, background, children }: WorldProps): react_jsx_runtime.JSX.Element;
71
+
72
+ interface EntityProps {
73
+ /** Optional string ID for cross-entity lookups (e.g. camera follow) */
74
+ id?: string;
75
+ /** Tags for grouping / querying (e.g. ['enemy', 'damageable']) */
76
+ tags?: string[];
77
+ children?: ReactNode;
78
+ }
79
+ declare function Entity({ id, tags, children }: EntityProps): react_jsx_runtime.JSX.Element | null;
80
+
81
+ interface TransformProps {
82
+ x?: number;
83
+ y?: number;
84
+ rotation?: number;
85
+ scaleX?: number;
86
+ scaleY?: number;
87
+ }
88
+ declare function Transform({ x, y, rotation, scaleX, scaleY }: TransformProps): null;
89
+
90
+ /** Maps frame names to frameIndex numbers */
91
+ type SpriteAtlas = Record<string, number>;
92
+ /**
93
+ * Helper to build an atlas from a grid spritesheet.
94
+ * columns = number of frames per row.
95
+ * names = frame names in row-major order.
96
+ */
97
+ declare function createAtlas(names: string[], _columns: number): SpriteAtlas;
98
+
99
+ interface SpriteProps {
100
+ width: number;
101
+ height: number;
102
+ color?: string;
103
+ src?: string;
104
+ offsetX?: number;
105
+ offsetY?: number;
106
+ zIndex?: number;
107
+ visible?: boolean;
108
+ flipX?: boolean;
109
+ anchorX?: number;
110
+ anchorY?: number;
111
+ frameIndex?: number;
112
+ frameWidth?: number;
113
+ frameHeight?: number;
114
+ frameColumns?: number;
115
+ atlas?: SpriteAtlas;
116
+ frame?: string;
117
+ }
118
+ declare function Sprite({ width, height, color, src, offsetX, offsetY, zIndex, visible, flipX, anchorX, anchorY, frameIndex, frameWidth, frameHeight, frameColumns, atlas, frame, }: SpriteProps): null;
119
+
120
+ interface RigidBodyProps {
121
+ mass?: number;
122
+ gravityScale?: number;
123
+ isStatic?: boolean;
124
+ bounce?: number;
125
+ friction?: number;
126
+ vx?: number;
127
+ vy?: number;
128
+ /** Prevent any horizontal movement — velocity.x is zeroed every frame */
129
+ lockX?: boolean;
130
+ /** Prevent any vertical movement — velocity.y is zeroed every frame (disables gravity) */
131
+ lockY?: boolean;
132
+ }
133
+ declare function RigidBody({ mass, gravityScale, isStatic, bounce, friction, vx, vy, lockX, lockY, }: RigidBodyProps): null;
134
+
135
+ interface BoxColliderProps {
136
+ width: number;
137
+ height: number;
138
+ offsetX?: number;
139
+ offsetY?: number;
140
+ isTrigger?: boolean;
141
+ layer?: string;
142
+ /** Which layers this collider interacts with. '*' = all (default). */
143
+ mask?: string | string[];
144
+ /**
145
+ * One-way platform: only blocks entities falling onto the top surface.
146
+ * Entities below pass through freely (useful for jump-through ledges).
147
+ */
148
+ oneWay?: boolean;
149
+ }
150
+ declare function BoxCollider({ width, height, offsetX, offsetY, isTrigger, layer, mask, oneWay, }: BoxColliderProps): null;
151
+
152
+ interface CircleColliderProps {
153
+ radius: number;
154
+ offsetX?: number;
155
+ offsetY?: number;
156
+ isTrigger?: boolean;
157
+ layer?: string;
158
+ /** Which layers this collider interacts with. '*' = all (default). */
159
+ mask?: string | string[];
160
+ }
161
+ declare function CircleCollider({ radius, offsetX, offsetY, isTrigger, layer, mask, }: CircleColliderProps): null;
162
+
163
+ interface ScriptProps {
164
+ /** Called once when the entity is mounted — use to attach extra components */
165
+ init?: (entityId: EntityId, world: ECSWorld) => void;
166
+ /** Called every frame */
167
+ update: ScriptUpdateFn | ((entityId: EntityId, world: ECSWorld, input: InputManager, dt: number) => void);
168
+ }
169
+ declare function Script({ init, update }: ScriptProps): null;
170
+
171
+ interface Camera2DProps {
172
+ /** String ID of entity to follow */
173
+ followEntity?: string;
174
+ /** Initial camera X position in world space (default 0 = world origin at screen center) */
175
+ x?: number;
176
+ /** Initial camera Y position in world space (default 0 = world origin at screen center) */
177
+ y?: number;
178
+ zoom?: number;
179
+ /** Lerp smoothing factor (0 = instant snap, 0.85 = smooth) */
180
+ smoothing?: number;
181
+ background?: string;
182
+ bounds?: {
183
+ x: number;
184
+ y: number;
185
+ width: number;
186
+ height: number;
187
+ };
188
+ deadZone?: {
189
+ w: number;
190
+ h: number;
191
+ };
192
+ /** World-space offset applied to the follow target (look-ahead, vertical bias, etc.) */
193
+ followOffsetX?: number;
194
+ followOffsetY?: number;
195
+ }
196
+ declare function Camera2D({ followEntity, x, y, zoom, smoothing, background, bounds, deadZone, followOffsetX, followOffsetY, }: Camera2DProps): null;
197
+
198
+ interface AnimationProps {
199
+ /** Frame indices to play (indexes into the sprite sheet) */
200
+ frames: number[];
201
+ /** Frames per second, default 12 */
202
+ fps?: number;
203
+ /** Whether to loop, default true */
204
+ loop?: boolean;
205
+ /** Whether currently playing, default true */
206
+ playing?: boolean;
207
+ /** Called once when a non-looping animation finishes playing */
208
+ onComplete?: () => void;
209
+ }
210
+ declare function Animation({ frames, fps, loop, playing, onComplete }: AnimationProps): null;
211
+
212
+ interface SquashStretchProps {
213
+ /** How much to squash/stretch (default 0.2) */
214
+ intensity?: number;
215
+ /** How fast it returns to 1.0 — lerp speed (default 8) */
216
+ recovery?: number;
217
+ }
218
+ declare function SquashStretch({ intensity, recovery }: SquashStretchProps): null;
219
+
220
+ type ParticlePreset = 'explosion' | 'spark' | 'smoke' | 'coinPickup' | 'jumpDust';
221
+
222
+ interface ParticleEmitterProps {
223
+ active?: boolean;
224
+ /** Named preset — values can be overridden by explicit props */
225
+ preset?: ParticlePreset;
226
+ /** Particles per second, default 20 */
227
+ rate?: number;
228
+ /** Initial particle speed (pixels/s), default 80 */
229
+ speed?: number;
230
+ /** Angle spread in radians, default Math.PI */
231
+ spread?: number;
232
+ /** Base emit angle in radians (0=right, -PI/2=up), default -Math.PI/2 */
233
+ angle?: number;
234
+ /** Particle lifetime in seconds, default 0.8 */
235
+ particleLife?: number;
236
+ /** Particle size in pixels, default 4 */
237
+ particleSize?: number;
238
+ /** Particle color, default '#ffffff' */
239
+ color?: string;
240
+ /** Gravity applied to particles (pixels/s²), default 200 */
241
+ gravity?: number;
242
+ /** Maximum live particles, default 100 */
243
+ maxParticles?: number;
244
+ }
245
+ declare function ParticleEmitter({ active, preset, rate, speed, spread, angle, particleLife, particleSize, color, gravity, maxParticles, }: ParticleEmitterProps): null;
246
+
247
+ interface MovingPlatformProps {
248
+ /** Start position */
249
+ x1: number;
250
+ y1: number;
251
+ /** End position */
252
+ x2: number;
253
+ y2: number;
254
+ width?: number;
255
+ height?: number;
256
+ /** Seconds for a full round trip (default 3) */
257
+ duration?: number;
258
+ color?: string;
259
+ }
260
+ /**
261
+ * A static platform that oscillates between (x1,y1) and (x2,y2).
262
+ *
263
+ * @example
264
+ * <MovingPlatform x1={200} y1={350} x2={450} y2={350} width={120} duration={2.5} />
265
+ */
266
+ declare function MovingPlatform({ x1, y1, x2, y2, width, height, duration, color, }: MovingPlatformProps): React__default.ReactElement;
267
+
268
+ interface CheckpointProps {
269
+ x: number;
270
+ y: number;
271
+ width?: number;
272
+ height?: number;
273
+ color?: string;
274
+ /** Called once when a 'player'-tagged entity enters the checkpoint */
275
+ onActivate?: () => void;
276
+ }
277
+ /**
278
+ * A trigger zone that fires `onActivate` once when a player-tagged entity enters it.
279
+ *
280
+ * @example
281
+ * <Checkpoint x={800} y={450} onActivate={() => setCheckpoint(800)} />
282
+ */
283
+ declare function Checkpoint({ x, y, width, height, color, onActivate, }: CheckpointProps): React__default.ReactElement;
284
+
285
+ interface TiledProperty {
286
+ name: string;
287
+ type: string;
288
+ value: string | number | boolean;
289
+ }
290
+ interface TiledObject {
291
+ id: number;
292
+ name: string;
293
+ type: string;
294
+ x: number;
295
+ y: number;
296
+ width: number;
297
+ height: number;
298
+ properties?: TiledProperty[];
299
+ }
300
+ interface TiledLayer {
301
+ type: 'tilelayer' | 'objectgroup';
302
+ name: string;
303
+ visible: boolean;
304
+ opacity: number;
305
+ data?: number[];
306
+ objects?: TiledObject[];
307
+ properties?: TiledProperty[];
308
+ }
309
+ interface TilemapProps {
310
+ /** URL to the Tiled JSON file */
311
+ src: string;
312
+ /**
313
+ * Object layer spawner: called for each object in object layers.
314
+ * Return a React element or null.
315
+ */
316
+ onSpawnObject?: (obj: TiledObject, layer: TiledLayer) => React__default.ReactNode;
317
+ /**
318
+ * Layer filter: return false to skip rendering/processing a layer.
319
+ * Default: all layers rendered.
320
+ */
321
+ layerFilter?: (layer: TiledLayer) => boolean;
322
+ /** Z-index for tile sprites (default 0) */
323
+ zIndex?: number;
324
+ /**
325
+ * Name of the layer (or layers with property `collision: true`) that
326
+ * should create invisible solid colliders. Default: "collision".
327
+ */
328
+ collisionLayer?: string;
329
+ /**
330
+ * Name of the layer (or layers with property `trigger: true`) that
331
+ * should create trigger BoxColliders (no sprite). Default: "triggers".
332
+ */
333
+ triggerLayer?: string;
334
+ /**
335
+ * Called for every tile that has custom properties defined in the tileset.
336
+ * Receives the global tile ID, the property map, and the tile's world position.
337
+ */
338
+ onTileProperty?: (tileId: number, properties: Record<string, unknown>, x: number, y: number) => void;
339
+ }
340
+ declare function Tilemap({ src, onSpawnObject, layerFilter, zIndex, collisionLayer, triggerLayer: triggerLayerName, onTileProperty, }: TilemapProps): React__default.ReactElement | null;
341
+
342
+ interface ParallaxLayerProps {
343
+ /** Image URL to use as the background layer */
344
+ src: string;
345
+ /** Scroll speed relative to camera (0 = fixed, 1 = moves with camera, 0.3 = slow parallax). Default 0.5 */
346
+ speedX?: number;
347
+ /** Vertical scroll speed relative to camera. Default 0 */
348
+ speedY?: number;
349
+ /** Tile image horizontally. Default true */
350
+ repeatX?: boolean;
351
+ /** Tile image vertically. Default false */
352
+ repeatY?: boolean;
353
+ /** Render order — use negative values to render behind sprites. Default -10 */
354
+ zIndex?: number;
355
+ /** Manual horizontal offset in pixels. Default 0 */
356
+ offsetX?: number;
357
+ /** Manual vertical offset in pixels. Default 0 */
358
+ offsetY?: number;
359
+ }
360
+ /**
361
+ * A background layer that scrolls at a fraction of the camera speed to create depth.
362
+ *
363
+ * @example
364
+ * <ParallaxLayer src="/bg/sky.png" speedX={0.2} repeatX />
365
+ * <ParallaxLayer src="/bg/mountains.png" speedX={0.5} repeatX zIndex={-5} />
366
+ */
367
+ declare function ParallaxLayer({ src, speedX, speedY, repeatX, repeatY, zIndex, offsetX, offsetY, }: ParallaxLayerProps): React__default.ReactElement;
368
+
369
+ interface ScreenFlashHandle {
370
+ flash(color: string, duration: number): void;
371
+ }
372
+ declare const ScreenFlash: React.ForwardRefExoticComponent<React.RefAttributes<ScreenFlashHandle>>;
373
+
374
+ interface EngineState {
375
+ ecs: ECSWorld;
376
+ input: InputManager;
377
+ /** Canvas2D renderer. Undefined when a custom WebGL renderer is used via the `renderer` Game prop. */
378
+ renderer?: Canvas2DRenderer;
379
+ /** The active render system. Undefined when a custom renderer is used. */
380
+ renderSystem?: RenderSystem;
381
+ physics: PhysicsSystem;
382
+ events: EventBus;
383
+ assets: AssetManager;
384
+ loop: GameLoop;
385
+ canvas: HTMLCanvasElement;
386
+ /** Maps string entity IDs (e.g. "player") to numeric ECS EntityIds */
387
+ entityIds: Map<string, EntityId>;
388
+ }
389
+
390
+ declare function useGame(): EngineState;
391
+
392
+ interface CameraControls {
393
+ /**
394
+ * Trigger a screen-shake effect.
395
+ * @param intensity - Maximum pixel displacement per frame.
396
+ * @param duration - How long the shake lasts in seconds.
397
+ */
398
+ shake(intensity: number, duration: number): void;
399
+ /**
400
+ * Set the world-space offset applied to the camera's follow target.
401
+ * Useful for look-ahead: `setFollowOffset(facing * 80, 0)`.
402
+ */
403
+ setFollowOffset(x: number, y: number): void;
404
+ /**
405
+ * Instantly move the camera center to a world-space position.
406
+ * Bypasses smoothing — useful for instant scene cuts.
407
+ */
408
+ setPosition(x: number, y: number): void;
409
+ /**
410
+ * Programmatically set the camera zoom level.
411
+ */
412
+ setZoom(zoom: number): void;
413
+ }
414
+ /**
415
+ * Returns controls for the active Camera2D in the scene.
416
+ * Must be used inside `<Game>`.
417
+ *
418
+ * @example
419
+ * ```tsx
420
+ * function HUD() {
421
+ * const camera = useCamera()
422
+ * return (
423
+ * <button onClick={() => camera.shake(8, 0.4)}>Shake!</button>
424
+ * )
425
+ * }
426
+ * ```
427
+ */
428
+ declare function useCamera(): CameraControls;
429
+
430
+ interface SnapshotControls {
431
+ /**
432
+ * Capture a full serialisable snapshot of all ECS entity/component data.
433
+ * Safe to JSON-stringify and store externally (localStorage, server, etc.)
434
+ *
435
+ * @example
436
+ * ```ts
437
+ * const { save, restore } = useSnapshot()
438
+ * const checkpoint = save() // capture at checkpoint
439
+ * restore(checkpoint) // reset to that state
440
+ * ```
441
+ */
442
+ save(): WorldSnapshot;
443
+ /**
444
+ * Restore the world to a previously captured snapshot.
445
+ * All current entities are replaced with the snapshot's entities.
446
+ *
447
+ * Note: React component state is NOT rolled back — only ECS data is restored.
448
+ * For a full game reset, prefer remounting the `<Game>` key instead.
449
+ */
450
+ restore(snapshot: WorldSnapshot): void;
451
+ }
452
+ /**
453
+ * Returns save/restore controls for the ECS world snapshot system.
454
+ * Must be used inside `<Game>`.
455
+ *
456
+ * @example
457
+ * ```tsx
458
+ * function SaveButton() {
459
+ * const { save, restore } = useSnapshot()
460
+ * const [slot, setSlot] = useState<WorldSnapshot | null>(null)
461
+ * return (
462
+ * <>
463
+ * <button onClick={() => setSlot(save())}>Save</button>
464
+ * <button onClick={() => slot && restore(slot)}>Load</button>
465
+ * </>
466
+ * )
467
+ * }
468
+ * ```
469
+ */
470
+ declare function useSnapshot(): SnapshotControls;
471
+
472
+ declare function useEntity(): EntityId;
473
+
474
+ declare function useInput(): InputManager;
475
+
476
+ interface BoundInputMap {
477
+ /** True every frame any bound key is held. */
478
+ isActionDown(action: string): boolean;
479
+ /** True only on the first frame any bound key was pressed. */
480
+ isActionPressed(action: string): boolean;
481
+ /** True only on the frame any bound key was released. */
482
+ isActionReleased(action: string): boolean;
483
+ }
484
+ /**
485
+ * React hook that returns a pre-bound action map for use inside `<Game>`.
486
+ *
487
+ * @example
488
+ * ```tsx
489
+ * function MyScript() {
490
+ * const actions = useInputMap({
491
+ * left: ['ArrowLeft', 'KeyA'],
492
+ * right: ['ArrowRight', 'KeyD'],
493
+ * jump: ['Space', 'ArrowUp', 'KeyW'],
494
+ * })
495
+ * // use actions.isActionDown('left') in a Script update or game loop callback
496
+ * }
497
+ * ```
498
+ */
499
+ declare function useInputMap(bindings: ActionBindings): BoundInputMap;
500
+
501
+ declare function useEvents(): EventBus;
502
+ declare function useEvent<T>(event: string, handler: (data: T) => void): void;
503
+
504
+ interface PlatformerControllerOptions {
505
+ /** Horizontal move speed in px/s (default 200) */
506
+ speed?: number;
507
+ /** Upward impulse on jump (default -500) */
508
+ jumpForce?: number;
509
+ /** Max consecutive jumps, e.g. 2 for double jump (default 1) */
510
+ maxJumps?: number;
511
+ /** Seconds after leaving ground the player can still jump (default 0.08) */
512
+ coyoteTime?: number;
513
+ /** Seconds to buffer a jump input before landing (default 0.08) */
514
+ jumpBuffer?: number;
515
+ }
516
+ /**
517
+ * Attaches platformer movement (WASD/Arrows + Space/Up to jump) to an entity.
518
+ * The entity must already have a RigidBody component.
519
+ *
520
+ * @example
521
+ * function Player() {
522
+ * const id = useEntity()
523
+ * usePlatformerController(id, { speed: 220, maxJumps: 2 })
524
+ * return (
525
+ * <Entity id="player">
526
+ * <Transform x={100} y={300} />
527
+ * <Sprite width={28} height={40} color="#4fc3f7" />
528
+ * <RigidBody />
529
+ * <BoxCollider width={26} height={40} />
530
+ * </Entity>
531
+ * )
532
+ * }
533
+ */
534
+ declare function usePlatformerController(entityId: EntityId, opts?: PlatformerControllerOptions): void;
535
+
536
+ interface TopDownMovementOptions {
537
+ /** Movement speed in px/s (default 200) */
538
+ speed?: number;
539
+ /** Normalize diagonal movement to avoid faster diagonal movement (default true) */
540
+ normalizeDiagonal?: boolean;
541
+ }
542
+ /**
543
+ * Attaches 4-directional top-down movement (WASD/Arrows) to an entity.
544
+ * The entity must have a RigidBody with gravityScale=0 for top-down games.
545
+ *
546
+ * @example
547
+ * <Entity id="player">
548
+ * <Transform x={100} y={100} />
549
+ * <Sprite width={24} height={24} color="#4fc3f7" />
550
+ * <RigidBody gravityScale={0} />
551
+ * <BoxCollider width={24} height={24} />
552
+ * </Entity>
553
+ * // In a Script or parent component:
554
+ * useTopDownMovement(playerId, { speed: 180 })
555
+ */
556
+ declare function useTopDownMovement(entityId: EntityId, opts?: TopDownMovementOptions): void;
557
+
558
+ interface ContactOpts {
559
+ /** Only fire if the other entity has this tag */
560
+ tag?: string;
561
+ /** Only fire if the other entity's BoxCollider is on this layer */
562
+ layer?: string;
563
+ }
564
+ /**
565
+ * Fires once when another entity's collider first overlaps this entity's trigger.
566
+ * Must be used inside an `<Entity>`.
567
+ *
568
+ * @example
569
+ * function CoinPickup() {
570
+ * useTriggerEnter((other) => collectCoin(), { tag: 'player' })
571
+ * return null
572
+ * }
573
+ */
574
+ declare function useTriggerEnter(handler: (other: EntityId) => void, opts?: ContactOpts): void;
575
+ /**
576
+ * Fires once when an overlapping entity's collider leaves this entity's trigger.
577
+ * Must be used inside an `<Entity>`.
578
+ */
579
+ declare function useTriggerExit(handler: (other: EntityId) => void, opts?: ContactOpts): void;
580
+ /**
581
+ * Fires once on the first frame two solid dynamic bodies touch.
582
+ * Must be used inside an `<Entity>`.
583
+ *
584
+ * @example
585
+ * function Enemy() {
586
+ * useCollisionEnter((other) => takeDamage(), { tag: 'player' })
587
+ * return null
588
+ * }
589
+ */
590
+ declare function useCollisionEnter(handler: (other: EntityId) => void, opts?: ContactOpts): void;
591
+ /**
592
+ * Fires once when two solid dynamic bodies separate.
593
+ * Must be used inside an `<Entity>`.
594
+ */
595
+ declare function useCollisionExit(handler: (other: EntityId) => void, opts?: ContactOpts): void;
596
+ /**
597
+ * Fires once when another entity's CircleCollider first overlaps this entity's CircleCollider.
598
+ * Also fires when a CircleCollider overlaps a BoxCollider.
599
+ * Must be used inside an `<Entity>`.
600
+ *
601
+ * @example
602
+ * function Asteroid() {
603
+ * useCircleEnter((other) => onHit(other), { tag: 'bullet' })
604
+ * return null
605
+ * }
606
+ */
607
+ declare function useCircleEnter(handler: (other: EntityId) => void, opts?: ContactOpts): void;
608
+ /**
609
+ * Fires once when two CircleCollider entities stop overlapping.
610
+ * Must be used inside an `<Entity>`.
611
+ */
612
+ declare function useCircleExit(handler: (other: EntityId) => void, opts?: ContactOpts): void;
613
+
614
+ interface DevToolsHandle {
615
+ buffer: WorldSnapshot[];
616
+ onFrame?: () => void;
617
+ }
618
+
619
+ export { Animation, type BoundInputMap, BoxCollider, Camera2D, type CameraControls, Checkpoint, CircleCollider, type DevToolsHandle, type EngineState, Entity, Game, type GameControls, MovingPlatform, ParallaxLayer, ParticleEmitter, type ParticlePreset, type PlatformerControllerOptions, RigidBody, ScreenFlash, type ScreenFlashHandle, Script, type SnapshotControls, Sprite, type SpriteAtlas, SquashStretch, type TiledLayer, type TiledObject, Tilemap, type TopDownMovementOptions, Transform, World, createAtlas, useCamera, useCircleEnter, useCircleExit, useCollisionEnter, useCollisionExit, useEntity, useEvent, useEvents, useGame, useInput, useInputMap, usePlatformerController, useSnapshot, useTopDownMovement, useTriggerEnter, useTriggerExit };