cubeforge 0.3.15 → 0.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ import * as React from 'react';
3
3
  import React__default, { CSSProperties, ReactNode, ReactElement } from 'react';
4
4
  import { Plugin, EntityId, ECSWorld, ScriptUpdateFn, NavGrid, WorldSnapshot, EventBus } from '@cubeforge/core';
5
5
  export { AssetProgress, Component, ECSWorld, Ease, EntityId, GameTimer, HotReloadablePlugin, MergedRect, NavGrid, Plugin, PreloadManifest, ScriptUpdateFn, TimelineEntry, TransformComponent, TweenHandle, TweenTimeline, Vec2Like, WorldSnapshot, arrive, createTag, createTimeline, createTimer, createTransform, definePlugin, findByTag, flee, hotReloadPlugin, mergeTileColliders, patrol, preloadManifest, seek, tween, wander } from '@cubeforge/core';
6
- import { Sampling, BlendMode, PostProcessEffect } from '@cubeforge/renderer';
7
- export { AnimationStateComponent, BlendMode, MagFilterValue, NineSliceComponent, ParallaxLayerComponent, Particle, ParticlePoolComponent, PostProcessEffect, PostProcessStack, RenderLayer, RenderLayerManager, RenderSystem, Sampling, SpriteComponent, SquashStretchComponent, TextComponent, TextureFilter, TextureFilterValue, TrailComponent, chromaticAberrationEffect, createNineSlice, createPostProcessStack, createRenderLayerManager, createSprite, defaultLayers, scanlineEffect, vignetteEffect } from '@cubeforge/renderer';
6
+ import { Sampling, BlendMode, AnimatorStateDefinition, AnimatorParamValue, PostProcessEffect } from '@cubeforge/renderer';
7
+ export { AnimationClipDefinition, AnimationStateComponent, AnimatorComponent, AnimatorCondition, AnimatorParamValue, AnimatorStateDefinition, AnimatorTransition, BlendMode, MagFilterValue, NineSliceComponent, ParallaxLayerComponent, Particle, ParticlePoolComponent, PostProcessEffect, PostProcessStack, RenderLayer, RenderLayerManager, RenderSystem, Sampling, SpriteComponent, SquashStretchComponent, TextComponent, TextureFilter, TextureFilterValue, TrailComponent, chromaticAberrationEffect, createNineSlice, createPostProcessStack, createRenderLayerManager, createSprite, defaultLayers, scanlineEffect, vignetteEffect } from '@cubeforge/renderer';
8
8
  import { ColliderShape } from '@cubeforge/physics';
9
9
  export { BoxColliderComponent, CapsuleColliderComponent, CircleColliderComponent, ColliderShape, CompoundColliderComponent, RaycastHit, RigidBodyComponent, createCompoundCollider, overlapBox, overlapCircle, raycast, raycastAll, sweepBox } from '@cubeforge/physics';
10
10
  import { InputManager, ActionBindings, InputContextName, PlayerInput, InputRecorderControls } from '@cubeforge/input';
@@ -376,6 +376,14 @@ declare function defineAnimations<S extends string>(clips: Record<S, AnimationCl
376
376
  */
377
377
  declare function AnimatedSprite<S extends string>(props: AnimatedSpriteProps<S>): ReactElement;
378
378
 
379
+ interface AnimatorProps {
380
+ initial: string;
381
+ states: Record<string, AnimatorStateDefinition>;
382
+ params?: Record<string, AnimatorParamValue>;
383
+ playing?: boolean;
384
+ }
385
+ declare function Animator({ initial, states, params, playing }: AnimatorProps): null;
386
+
379
387
  interface SquashStretchProps {
380
388
  /** How much to squash/stretch (default 0.2) */
381
389
  intensity?: number;
@@ -1122,6 +1130,10 @@ declare function useProfiler(): ProfilerData;
1122
1130
  */
1123
1131
  declare function usePostProcess(effect: PostProcessEffect): void;
1124
1132
 
1133
+ declare function playClip(world: ECSWorld, entityId: EntityId, clipName: string): void;
1134
+ declare function setAnimationState(world: ECSWorld, entityId: EntityId, stateName: string): void;
1135
+ declare function setAnimatorParam(world: ECSWorld, entityId: EntityId, name: string, value: AnimatorParamValue): void;
1136
+
1125
1137
  /**
1126
1138
  * Define a reusable entity prefab with default props.
1127
1139
  *
@@ -1147,4 +1159,4 @@ declare function usePostProcess(effect: PostProcessEffect): void;
1147
1159
  */
1148
1160
  declare function definePrefab<D extends Record<string, unknown>>(name: string, defaults: D, render: (props: D) => ReactElement): React__default.FC<Partial<D>>;
1149
1161
 
1150
- export { AnimatedSprite, type AnimatedSpriteProps, Animation, type AnimationSet, AssetLoader, type BoundInputMap, BoxCollider, Camera2D, type CameraControls, CameraZone, CapsuleCollider, Checkpoint, CircleCollider, CompoundCollider, type CoordinateHelpers, Entity, Game, type GameControls, type GamepadState, type InputContextControls, MovingPlatform, NineSlice, ParallaxLayer, ParticleEmitter, type ParticlePreset, type PauseControls, type PreloadState, type ProfilerData, 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, defineAnimations, definePrefab, useCamera, useCoordinates, useDestroyEntity, useEntity, useEvent, useEvents, useGame, useGamepad, useInput, useInputContext, useInputMap, useInputRecorder, useLocalMultiplayer, usePause, usePlayerInput, usePostProcess, usePreload, useProfiler, useSnapshot, useVirtualInput };
1162
+ export { AnimatedSprite, type AnimatedSpriteProps, Animation, type AnimationSet, Animator, AssetLoader, type BoundInputMap, BoxCollider, Camera2D, type CameraControls, CameraZone, CapsuleCollider, Checkpoint, CircleCollider, CompoundCollider, type CoordinateHelpers, Entity, Game, type GameControls, type GamepadState, type InputContextControls, MovingPlatform, NineSlice, ParallaxLayer, ParticleEmitter, type ParticlePreset, type PauseControls, type PreloadState, type ProfilerData, 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, defineAnimations, definePrefab, playClip, setAnimationState, setAnimatorParam, useCamera, useCoordinates, useDestroyEntity, useEntity, useEvent, useEvents, useGame, useGamepad, useInput, useInputContext, useInputMap, useInputRecorder, useLocalMultiplayer, usePause, usePlayerInput, usePostProcess, usePreload, useProfiler, useSnapshot, useVirtualInput };
package/dist/index.js CHANGED
@@ -2126,9 +2126,47 @@ var RenderSystem = class {
2126
2126
  camY = cam.y;
2127
2127
  zoom = cam.zoom;
2128
2128
  }
2129
+ for (const id of world.query("Animator", "AnimationState")) {
2130
+ const animator = world.getComponent(id, "Animator");
2131
+ const anim = world.getComponent(id, "AnimationState");
2132
+ if (!animator.playing) continue;
2133
+ if (!animator.states[animator.currentState]) {
2134
+ animator.currentState = animator.initialState;
2135
+ animator._entered = false;
2136
+ }
2137
+ const stateDef = animator.states[animator.currentState];
2138
+ if (!stateDef) continue;
2139
+ if (!animator._entered) {
2140
+ anim.currentClip = stateDef.clip;
2141
+ animator._entered = true;
2142
+ stateDef.onEnter?.();
2143
+ }
2144
+ if (stateDef.transitions && stateDef.transitions.length > 0) {
2145
+ const sorted = [...stateDef.transitions].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2146
+ for (const trans of sorted) {
2147
+ if (trans.exitTime != null && anim.frames.length > 0) {
2148
+ const progress = anim.currentIndex / anim.frames.length;
2149
+ if (progress < trans.exitTime) continue;
2150
+ }
2151
+ if (evaluateConditions(trans.when, animator.params)) {
2152
+ stateDef.onExit?.();
2153
+ animator.currentState = trans.to;
2154
+ animator._entered = false;
2155
+ break;
2156
+ }
2157
+ }
2158
+ }
2159
+ }
2129
2160
  for (const id of world.query("AnimationState", "Sprite")) {
2130
2161
  const anim = world.getComponent(id, "AnimationState");
2131
2162
  const sprite = world.getComponent(id, "Sprite");
2163
+ if (anim.clips && anim.currentClip && anim._resolvedClip !== anim.currentClip) {
2164
+ const clip = anim.clips[anim.currentClip];
2165
+ if (clip) {
2166
+ resolveClip(anim, clip);
2167
+ anim._resolvedClip = anim.currentClip;
2168
+ }
2169
+ }
2132
2170
  if (!anim.playing || anim.frames.length === 0) continue;
2133
2171
  anim.timer += dt;
2134
2172
  const frameDuration = 1 / anim.fps;
@@ -2136,8 +2174,24 @@ var RenderSystem = class {
2136
2174
  anim.timer -= frameDuration;
2137
2175
  anim.currentIndex++;
2138
2176
  if (anim.currentIndex >= anim.frames.length) {
2139
- anim.currentIndex = anim.loop ? 0 : anim.frames.length - 1;
2177
+ if (anim.loop) {
2178
+ anim.currentIndex = 0;
2179
+ } else {
2180
+ anim.currentIndex = anim.frames.length - 1;
2181
+ anim.playing = false;
2182
+ if (anim.onComplete && !anim._completed) {
2183
+ anim._completed = true;
2184
+ anim.onComplete();
2185
+ }
2186
+ if (anim.clips && anim.currentClip) {
2187
+ const currentClipDef = anim.clips[anim.currentClip];
2188
+ if (currentClipDef?.next && anim.clips[currentClipDef.next]) {
2189
+ anim.currentClip = currentClipDef.next;
2190
+ }
2191
+ }
2192
+ }
2140
2193
  }
2194
+ anim.frameEvents?.[anim.currentIndex]?.();
2141
2195
  }
2142
2196
  sprite.frameIndex = anim.frames[anim.currentIndex];
2143
2197
  }
@@ -2552,31 +2606,44 @@ var RenderSystem = class {
2552
2606
  this.lastTimestamp = now;
2553
2607
  }
2554
2608
  };
2555
-
2556
- // ../../packages/renderer/src/canvas2d.ts
2557
- var Canvas2DRenderer = class {
2558
- constructor(canvas) {
2559
- this.canvas = canvas;
2560
- const ctx = canvas.getContext("2d");
2561
- if (!ctx) throw new Error("Could not get 2D context from canvas");
2562
- this.ctx = ctx;
2563
- }
2564
- ctx;
2565
- clear(color) {
2566
- if (color) {
2567
- this.ctx.fillStyle = color;
2568
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
2569
- } else {
2570
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
2609
+ function resolveClip(anim, clip) {
2610
+ anim.frames = clip.frames;
2611
+ anim.fps = clip.fps ?? 12;
2612
+ anim.loop = clip.loop ?? true;
2613
+ anim.onComplete = clip.onComplete;
2614
+ anim.frameEvents = clip.frameEvents;
2615
+ anim.currentIndex = 0;
2616
+ anim.timer = 0;
2617
+ anim._completed = false;
2618
+ anim.playing = true;
2619
+ }
2620
+ function evaluateConditions(conditions, params) {
2621
+ for (const cond of conditions) {
2622
+ const val = params[cond.param];
2623
+ if (val === void 0) return false;
2624
+ switch (cond.op) {
2625
+ case "==":
2626
+ if (val !== cond.value) return false;
2627
+ break;
2628
+ case "!=":
2629
+ if (val === cond.value) return false;
2630
+ break;
2631
+ case ">":
2632
+ if (val <= cond.value) return false;
2633
+ break;
2634
+ case ">=":
2635
+ if (val < cond.value) return false;
2636
+ break;
2637
+ case "<":
2638
+ if (val >= cond.value) return false;
2639
+ break;
2640
+ case "<=":
2641
+ if (val > cond.value) return false;
2642
+ break;
2571
2643
  }
2572
2644
  }
2573
- get width() {
2574
- return this.canvas.width;
2575
- }
2576
- get height() {
2577
- return this.canvas.height;
2578
- }
2579
- };
2645
+ return true;
2646
+ }
2580
2647
 
2581
2648
  // ../../packages/renderer/src/postProcess.ts
2582
2649
  function createPostProcessStack() {
@@ -2644,6 +2711,31 @@ function chromaticAberrationEffect(offset = 2) {
2644
2711
  };
2645
2712
  }
2646
2713
 
2714
+ // ../../packages/renderer/src/canvas2d.ts
2715
+ var Canvas2DRenderer = class {
2716
+ constructor(canvas) {
2717
+ this.canvas = canvas;
2718
+ const ctx = canvas.getContext("2d");
2719
+ if (!ctx) throw new Error("Could not get 2D context from canvas");
2720
+ this.ctx = ctx;
2721
+ }
2722
+ ctx;
2723
+ clear(color) {
2724
+ if (color) {
2725
+ this.ctx.fillStyle = color;
2726
+ this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
2727
+ } else {
2728
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
2729
+ }
2730
+ }
2731
+ get width() {
2732
+ return this.canvas.width;
2733
+ }
2734
+ get height() {
2735
+ return this.canvas.height;
2736
+ }
2737
+ };
2738
+
2647
2739
  // ../../packages/physics/src/components/rigidbody.ts
2648
2740
  function createRigidBody(opts) {
2649
2741
  return {
@@ -5335,7 +5427,7 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete,
5335
5427
  }
5336
5428
 
5337
5429
  // src/components/AnimatedSprite.tsx
5338
- import { useMemo } from "react";
5430
+ import { useEffect as useEffect18, useContext as useContext16 } from "react";
5339
5431
  import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
5340
5432
  function defineAnimations(clips) {
5341
5433
  return clips;
@@ -5366,72 +5458,130 @@ function AnimatedSprite(props) {
5366
5458
  sampling,
5367
5459
  blendMode
5368
5460
  } = props;
5369
- const animProps = useResolvedAnimation(props);
5461
+ const spriteEl = /* @__PURE__ */ jsx5(
5462
+ Sprite,
5463
+ {
5464
+ width,
5465
+ height,
5466
+ src,
5467
+ color,
5468
+ offsetX,
5469
+ offsetY,
5470
+ zIndex,
5471
+ visible,
5472
+ flipX,
5473
+ flipY,
5474
+ anchorX,
5475
+ anchorY,
5476
+ frameWidth,
5477
+ frameHeight,
5478
+ frameColumns,
5479
+ atlas,
5480
+ frame,
5481
+ tileX,
5482
+ tileY,
5483
+ tileSizeX,
5484
+ tileSizeY,
5485
+ sampling,
5486
+ blendMode
5487
+ }
5488
+ );
5489
+ if (props.animations) {
5490
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
5491
+ spriteEl,
5492
+ /* @__PURE__ */ jsx5(
5493
+ MultiClipAnimation,
5494
+ {
5495
+ animations: props.animations,
5496
+ current: props.current
5497
+ }
5498
+ )
5499
+ ] });
5500
+ }
5370
5501
  return /* @__PURE__ */ jsxs3(Fragment3, { children: [
5502
+ spriteEl,
5371
5503
  /* @__PURE__ */ jsx5(
5372
- Sprite,
5504
+ Animation,
5373
5505
  {
5374
- width,
5375
- height,
5376
- src,
5377
- color,
5378
- offsetX,
5379
- offsetY,
5380
- zIndex,
5381
- visible,
5382
- flipX,
5383
- flipY,
5384
- anchorX,
5385
- anchorY,
5386
- frameWidth,
5387
- frameHeight,
5388
- frameColumns,
5389
- atlas,
5390
- frame,
5391
- tileX,
5392
- tileY,
5393
- tileSizeX,
5394
- tileSizeY,
5395
- sampling,
5396
- blendMode
5506
+ frames: props.frames,
5507
+ fps: props.fps,
5508
+ loop: props.loop,
5509
+ playing: props.playing,
5510
+ onComplete: props.onComplete,
5511
+ frameEvents: props.frameEvents
5397
5512
  }
5398
- ),
5399
- /* @__PURE__ */ jsx5(Animation, { ...animProps })
5513
+ )
5400
5514
  ] });
5401
5515
  }
5402
- function useResolvedAnimation(props) {
5403
- const clip = props.animations ? props.animations[props.current] ?? Object.values(props.animations)[0] : null;
5404
- const frames = useMemo(
5405
- () => clip ? clip.frames : props.frames,
5406
- // eslint-disable-next-line react-hooks/exhaustive-deps
5407
- [clip ? props.current : props.frames]
5408
- );
5409
- if (clip) {
5410
- return {
5411
- frames,
5412
- fps: clip.fps,
5413
- loop: clip.loop,
5516
+ function MultiClipAnimation({ animations, current }) {
5517
+ const engine = useContext16(EngineContext);
5518
+ const entityId = useContext16(EntityContext);
5519
+ useEffect18(() => {
5520
+ const clip = animations[current] ?? Object.values(animations)[0];
5521
+ const state = {
5522
+ type: "AnimationState",
5523
+ clips: animations,
5524
+ currentClip: current,
5525
+ _resolvedClip: current,
5526
+ frames: clip.frames,
5527
+ fps: clip.fps ?? 12,
5528
+ loop: clip.loop ?? true,
5414
5529
  playing: true,
5415
- onComplete: clip.onComplete,
5416
- frameEvents: void 0
5530
+ currentIndex: 0,
5531
+ timer: 0,
5532
+ _completed: false,
5533
+ onComplete: clip.onComplete
5417
5534
  };
5418
- }
5419
- return {
5420
- frames,
5421
- fps: props.fps,
5422
- loop: props.loop,
5423
- playing: props.playing,
5424
- onComplete: props.onComplete,
5425
- frameEvents: props.frameEvents
5426
- };
5535
+ engine.ecs.addComponent(entityId, state);
5536
+ return () => engine.ecs.removeComponent(entityId, "AnimationState");
5537
+ }, []);
5538
+ useEffect18(() => {
5539
+ const anim = engine.ecs.getComponent(entityId, "AnimationState");
5540
+ if (!anim) return;
5541
+ anim.clips = animations;
5542
+ anim.currentClip = current;
5543
+ }, [current, animations, engine, entityId]);
5544
+ return null;
5545
+ }
5546
+
5547
+ // src/components/Animator.tsx
5548
+ import { useEffect as useEffect19, useContext as useContext17 } from "react";
5549
+ function Animator({ initial, states, params = {}, playing = true }) {
5550
+ const engine = useContext17(EngineContext);
5551
+ const entityId = useContext17(EntityContext);
5552
+ useEffect19(() => {
5553
+ const comp = {
5554
+ type: "Animator",
5555
+ initialState: initial,
5556
+ currentState: initial,
5557
+ states,
5558
+ params: { ...params },
5559
+ playing,
5560
+ _entered: false
5561
+ };
5562
+ engine.ecs.addComponent(entityId, comp);
5563
+ return () => engine.ecs.removeComponent(entityId, "Animator");
5564
+ }, []);
5565
+ useEffect19(() => {
5566
+ const comp = engine.ecs.getComponent(entityId, "Animator");
5567
+ if (!comp) return;
5568
+ Object.assign(comp.params, params);
5569
+ comp.playing = playing;
5570
+ }, [params, playing, engine, entityId]);
5571
+ useEffect19(() => {
5572
+ const comp = engine.ecs.getComponent(entityId, "Animator");
5573
+ if (!comp) return;
5574
+ comp.states = states;
5575
+ }, [states, engine, entityId]);
5576
+ return null;
5427
5577
  }
5428
5578
 
5429
5579
  // src/components/SquashStretch.tsx
5430
- import { useEffect as useEffect18, useContext as useContext16 } from "react";
5580
+ import { useEffect as useEffect20, useContext as useContext18 } from "react";
5431
5581
  function SquashStretch({ intensity = 0.2, recovery = 8 }) {
5432
- const engine = useContext16(EngineContext);
5433
- const entityId = useContext16(EntityContext);
5434
- useEffect18(() => {
5582
+ const engine = useContext18(EngineContext);
5583
+ const entityId = useContext18(EntityContext);
5584
+ useEffect20(() => {
5435
5585
  engine.ecs.addComponent(entityId, {
5436
5586
  type: "SquashStretch",
5437
5587
  intensity,
@@ -5445,7 +5595,7 @@ function SquashStretch({ intensity = 0.2, recovery = 8 }) {
5445
5595
  }
5446
5596
 
5447
5597
  // src/components/ParticleEmitter.tsx
5448
- import { useEffect as useEffect19, useContext as useContext17 } from "react";
5598
+ import { useEffect as useEffect21, useContext as useContext19 } from "react";
5449
5599
 
5450
5600
  // src/components/particlePresets.ts
5451
5601
  var PARTICLE_PRESETS = {
@@ -5535,9 +5685,9 @@ function ParticleEmitter({
5535
5685
  const resolvedColor = color ?? presetConfig.color ?? "#ffffff";
5536
5686
  const resolvedGravity = gravity ?? presetConfig.gravity ?? 200;
5537
5687
  const resolvedMaxParticles = maxParticles ?? presetConfig.maxParticles ?? 100;
5538
- const engine = useContext17(EngineContext);
5539
- const entityId = useContext17(EntityContext);
5540
- useEffect19(() => {
5688
+ const engine = useContext19(EngineContext);
5689
+ const entityId = useContext19(EntityContext);
5690
+ useEffect21(() => {
5541
5691
  engine.ecs.addComponent(entityId, {
5542
5692
  type: "ParticlePool",
5543
5693
  particles: [],
@@ -5560,7 +5710,7 @@ function ParticleEmitter({
5560
5710
  });
5561
5711
  return () => engine.ecs.removeComponent(entityId, "ParticlePool");
5562
5712
  }, []);
5563
- useEffect19(() => {
5713
+ useEffect21(() => {
5564
5714
  const pool = engine.ecs.getComponent(entityId, "ParticlePool");
5565
5715
  if (!pool) return;
5566
5716
  pool.active = active;
@@ -5794,7 +5944,7 @@ function Checkpoint({
5794
5944
  }
5795
5945
 
5796
5946
  // src/components/Tilemap.tsx
5797
- import { useEffect as useEffect20, useState as useState5, useContext as useContext18 } from "react";
5947
+ import { useEffect as useEffect22, useState as useState5, useContext as useContext20 } from "react";
5798
5948
  import { Fragment as Fragment5, jsx as jsx9 } from "react/jsx-runtime";
5799
5949
  var animatedTiles = /* @__PURE__ */ new Map();
5800
5950
  function getProperty(props, name) {
@@ -5820,9 +5970,9 @@ function Tilemap({
5820
5970
  navGrid,
5821
5971
  mergeColliders = true
5822
5972
  }) {
5823
- const engine = useContext18(EngineContext);
5973
+ const engine = useContext20(EngineContext);
5824
5974
  const [spawnedNodes, setSpawnedNodes] = useState5([]);
5825
- useEffect20(() => {
5975
+ useEffect22(() => {
5826
5976
  if (!engine) return;
5827
5977
  const createdEntities = [];
5828
5978
  async function load() {
@@ -6035,7 +6185,7 @@ function Tilemap({
6035
6185
  }
6036
6186
 
6037
6187
  // src/components/ParallaxLayer.tsx
6038
- import { useEffect as useEffect21, useContext as useContext19 } from "react";
6188
+ import { useEffect as useEffect23, useContext as useContext21 } from "react";
6039
6189
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
6040
6190
  function ParallaxLayerInner({
6041
6191
  src,
@@ -6047,9 +6197,9 @@ function ParallaxLayerInner({
6047
6197
  offsetX,
6048
6198
  offsetY
6049
6199
  }) {
6050
- const engine = useContext19(EngineContext);
6051
- const entityId = useContext19(EntityContext);
6052
- useEffect21(() => {
6200
+ const engine = useContext21(EngineContext);
6201
+ const entityId = useContext21(EntityContext);
6202
+ useEffect23(() => {
6053
6203
  engine.ecs.addComponent(entityId, {
6054
6204
  type: "ParallaxLayer",
6055
6205
  src,
@@ -6065,7 +6215,7 @@ function ParallaxLayerInner({
6065
6215
  });
6066
6216
  return () => engine.ecs.removeComponent(entityId, "ParallaxLayer");
6067
6217
  }, []);
6068
- useEffect21(() => {
6218
+ useEffect23(() => {
6069
6219
  const layer = engine.ecs.getComponent(entityId, "ParallaxLayer");
6070
6220
  if (!layer) return;
6071
6221
  layer.src = src;
@@ -6147,7 +6297,7 @@ var ScreenFlash = forwardRef((_, ref) => {
6147
6297
  ScreenFlash.displayName = "ScreenFlash";
6148
6298
 
6149
6299
  // src/components/CameraZone.tsx
6150
- import { useEffect as useEffect22, useContext as useContext20, useRef as useRef8 } from "react";
6300
+ import { useEffect as useEffect24, useContext as useContext22, useRef as useRef8 } from "react";
6151
6301
  import { Fragment as Fragment6, jsx as jsx12 } from "react/jsx-runtime";
6152
6302
  function CameraZone({
6153
6303
  x,
@@ -6159,10 +6309,10 @@ function CameraZone({
6159
6309
  targetY,
6160
6310
  children
6161
6311
  }) {
6162
- const engine = useContext20(EngineContext);
6312
+ const engine = useContext22(EngineContext);
6163
6313
  const prevFollowRef = useRef8(void 0);
6164
6314
  const activeRef = useRef8(false);
6165
- useEffect22(() => {
6315
+ useEffect24(() => {
6166
6316
  const eid = engine.ecs.createEntity();
6167
6317
  engine.ecs.addComponent(eid, createScript(() => {
6168
6318
  const cam = engine.ecs.queryOne("Camera2D");
@@ -6209,11 +6359,11 @@ function CameraZone({
6209
6359
  }
6210
6360
 
6211
6361
  // src/components/Trail.tsx
6212
- import { useEffect as useEffect23, useContext as useContext21 } from "react";
6362
+ import { useEffect as useEffect25, useContext as useContext23 } from "react";
6213
6363
  function Trail({ length = 20, color = "#ffffff", width = 3 }) {
6214
- const engine = useContext21(EngineContext);
6215
- const entityId = useContext21(EntityContext);
6216
- useEffect23(() => {
6364
+ const engine = useContext23(EngineContext);
6365
+ const entityId = useContext23(EntityContext);
6366
+ useEffect25(() => {
6217
6367
  engine.ecs.addComponent(entityId, createTrail({ length, color, width }));
6218
6368
  return () => engine.ecs.removeComponent(entityId, "Trail");
6219
6369
  }, []);
@@ -6221,7 +6371,7 @@ function Trail({ length = 20, color = "#ffffff", width = 3 }) {
6221
6371
  }
6222
6372
 
6223
6373
  // src/components/NineSlice.tsx
6224
- import { useEffect as useEffect24, useContext as useContext22 } from "react";
6374
+ import { useEffect as useEffect26, useContext as useContext24 } from "react";
6225
6375
  function NineSlice({
6226
6376
  src,
6227
6377
  width,
@@ -6232,9 +6382,9 @@ function NineSlice({
6232
6382
  borderLeft = 8,
6233
6383
  zIndex = 0
6234
6384
  }) {
6235
- const engine = useContext22(EngineContext);
6236
- const entityId = useContext22(EntityContext);
6237
- useEffect24(() => {
6385
+ const engine = useContext24(EngineContext);
6386
+ const entityId = useContext24(EntityContext);
6387
+ useEffect26(() => {
6238
6388
  engine.ecs.addComponent(
6239
6389
  entityId,
6240
6390
  createNineSlice(src, width, height, {
@@ -6251,14 +6401,14 @@ function NineSlice({
6251
6401
  }
6252
6402
 
6253
6403
  // src/components/AssetLoader.tsx
6254
- import { useEffect as useEffect26 } from "react";
6404
+ import { useEffect as useEffect28 } from "react";
6255
6405
 
6256
6406
  // src/hooks/usePreload.ts
6257
- import { useState as useState6, useEffect as useEffect25, useContext as useContext23 } from "react";
6407
+ import { useState as useState6, useEffect as useEffect27, useContext as useContext25 } from "react";
6258
6408
  function usePreload(assets) {
6259
- const engine = useContext23(EngineContext);
6409
+ const engine = useContext25(EngineContext);
6260
6410
  const [state, setState] = useState6({ progress: assets.length === 0 ? 1 : 0, loaded: assets.length === 0, error: null });
6261
- useEffect25(() => {
6411
+ useEffect27(() => {
6262
6412
  if (assets.length === 0) {
6263
6413
  setState({ progress: 1, loaded: true, error: null });
6264
6414
  return;
@@ -6289,7 +6439,7 @@ function usePreload(assets) {
6289
6439
  import { Fragment as Fragment7, jsx as jsx13 } from "react/jsx-runtime";
6290
6440
  function AssetLoader({ assets, fallback = null, onError, children }) {
6291
6441
  const { loaded, error } = usePreload(assets);
6292
- useEffect26(() => {
6442
+ useEffect28(() => {
6293
6443
  if (error && onError) onError(error);
6294
6444
  }, [error, onError]);
6295
6445
  if (!loaded) {
@@ -6299,18 +6449,18 @@ function AssetLoader({ assets, fallback = null, onError, children }) {
6299
6449
  }
6300
6450
 
6301
6451
  // src/hooks/useGame.ts
6302
- import { useContext as useContext24 } from "react";
6452
+ import { useContext as useContext26 } from "react";
6303
6453
  function useGame() {
6304
- const engine = useContext24(EngineContext);
6454
+ const engine = useContext26(EngineContext);
6305
6455
  if (!engine) throw new Error("useGame must be used inside <Game>");
6306
6456
  return engine;
6307
6457
  }
6308
6458
 
6309
6459
  // src/hooks/useCamera.ts
6310
- import { useMemo as useMemo2 } from "react";
6460
+ import { useMemo } from "react";
6311
6461
  function useCamera() {
6312
6462
  const engine = useGame();
6313
- return useMemo2(() => ({
6463
+ return useMemo(() => ({
6314
6464
  shake(intensity, duration) {
6315
6465
  const cams = engine.ecs.query("Camera2D");
6316
6466
  if (cams.length === 0) return;
@@ -6348,28 +6498,28 @@ function useCamera() {
6348
6498
  }
6349
6499
 
6350
6500
  // src/hooks/useSnapshot.ts
6351
- import { useMemo as useMemo3 } from "react";
6501
+ import { useMemo as useMemo2 } from "react";
6352
6502
  function useSnapshot() {
6353
6503
  const engine = useGame();
6354
- return useMemo3(() => ({
6504
+ return useMemo2(() => ({
6355
6505
  save: () => engine.ecs.getSnapshot(),
6356
6506
  restore: (snapshot) => engine.ecs.restoreSnapshot(snapshot)
6357
6507
  }), [engine]);
6358
6508
  }
6359
6509
 
6360
6510
  // src/hooks/useEntity.ts
6361
- import { useContext as useContext25 } from "react";
6511
+ import { useContext as useContext27 } from "react";
6362
6512
  function useEntity() {
6363
- const id = useContext25(EntityContext);
6513
+ const id = useContext27(EntityContext);
6364
6514
  if (id === null) throw new Error("useEntity must be used inside <Entity>");
6365
6515
  return id;
6366
6516
  }
6367
6517
 
6368
6518
  // src/hooks/useDestroyEntity.ts
6369
- import { useCallback as useCallback2, useContext as useContext26 } from "react";
6519
+ import { useCallback as useCallback2, useContext as useContext28 } from "react";
6370
6520
  function useDestroyEntity() {
6371
- const engine = useContext26(EngineContext);
6372
- const entityId = useContext26(EntityContext);
6521
+ const engine = useContext28(EngineContext);
6522
+ const entityId = useContext28(EntityContext);
6373
6523
  if (!engine) throw new Error("useDestroyEntity must be used inside <Game>");
6374
6524
  if (entityId === null) throw new Error("useDestroyEntity must be used inside <Entity>");
6375
6525
  return useCallback2(() => {
@@ -6380,19 +6530,19 @@ function useDestroyEntity() {
6380
6530
  }
6381
6531
 
6382
6532
  // src/hooks/useInput.ts
6383
- import { useContext as useContext27 } from "react";
6533
+ import { useContext as useContext29 } from "react";
6384
6534
  function useInput() {
6385
- const engine = useContext27(EngineContext);
6535
+ const engine = useContext29(EngineContext);
6386
6536
  if (!engine) throw new Error("useInput must be used inside <Game>");
6387
6537
  return engine.input;
6388
6538
  }
6389
6539
 
6390
6540
  // src/hooks/useInputMap.ts
6391
- import { useMemo as useMemo4 } from "react";
6541
+ import { useMemo as useMemo3 } from "react";
6392
6542
  function useInputMap(bindings) {
6393
6543
  const input = useInput();
6394
- const map = useMemo4(() => createInputMap(bindings), [JSON.stringify(bindings)]);
6395
- return useMemo4(() => ({
6544
+ const map = useMemo3(() => createInputMap(bindings), [JSON.stringify(bindings)]);
6545
+ return useMemo3(() => ({
6396
6546
  isActionDown: (action) => map.isActionDown(input, action),
6397
6547
  isActionPressed: (action) => map.isActionPressed(input, action),
6398
6548
  isActionReleased: (action) => map.isActionReleased(input, action),
@@ -6402,9 +6552,9 @@ function useInputMap(bindings) {
6402
6552
  }
6403
6553
 
6404
6554
  // src/hooks/useEvents.ts
6405
- import { useContext as useContext28, useEffect as useEffect27, useRef as useRef9 } from "react";
6555
+ import { useContext as useContext30, useEffect as useEffect29, useRef as useRef9 } from "react";
6406
6556
  function useEvents() {
6407
- const engine = useContext28(EngineContext);
6557
+ const engine = useContext30(EngineContext);
6408
6558
  if (!engine) throw new Error("useEvents must be used inside <Game>");
6409
6559
  return engine.events;
6410
6560
  }
@@ -6412,15 +6562,15 @@ function useEvent(event, handler) {
6412
6562
  const events = useEvents();
6413
6563
  const handlerRef = useRef9(handler);
6414
6564
  handlerRef.current = handler;
6415
- useEffect27(() => {
6565
+ useEffect29(() => {
6416
6566
  return events.on(event, (data) => handlerRef.current(data));
6417
6567
  }, [events, event]);
6418
6568
  }
6419
6569
 
6420
6570
  // src/hooks/useCoordinates.ts
6421
- import { useCallback as useCallback3, useContext as useContext29 } from "react";
6571
+ import { useCallback as useCallback3, useContext as useContext31 } from "react";
6422
6572
  function useCoordinates() {
6423
- const engine = useContext29(EngineContext);
6573
+ const engine = useContext31(EngineContext);
6424
6574
  const worldToScreen = useCallback3((wx, wy) => {
6425
6575
  const canvas = engine.canvas;
6426
6576
  const camId = engine.ecs.queryOne("Camera2D");
@@ -6447,14 +6597,14 @@ function useCoordinates() {
6447
6597
  }
6448
6598
 
6449
6599
  // src/hooks/useInputContext.ts
6450
- import { useEffect as useEffect28, useMemo as useMemo5 } from "react";
6600
+ import { useEffect as useEffect30, useMemo as useMemo4 } from "react";
6451
6601
  function useInputContext(ctx) {
6452
- useEffect28(() => {
6602
+ useEffect30(() => {
6453
6603
  if (!ctx) return;
6454
6604
  globalInputContext.push(ctx);
6455
6605
  return () => globalInputContext.pop(ctx);
6456
6606
  }, [ctx]);
6457
- return useMemo5(() => ({
6607
+ return useMemo4(() => ({
6458
6608
  push: (c) => globalInputContext.push(c),
6459
6609
  pop: (c) => globalInputContext.pop(c),
6460
6610
  get active() {
@@ -6464,17 +6614,17 @@ function useInputContext(ctx) {
6464
6614
  }
6465
6615
 
6466
6616
  // src/hooks/usePlayerInput.ts
6467
- import { useMemo as useMemo6 } from "react";
6617
+ import { useMemo as useMemo5 } from "react";
6468
6618
  function usePlayerInput(playerId, bindings) {
6469
6619
  const input = useInput();
6470
- return useMemo6(() => createPlayerInput(playerId, bindings, input), [playerId, input, JSON.stringify(bindings)]);
6620
+ return useMemo5(() => createPlayerInput(playerId, bindings, input), [playerId, input, JSON.stringify(bindings)]);
6471
6621
  }
6472
6622
 
6473
6623
  // src/hooks/useLocalMultiplayer.ts
6474
- import { useMemo as useMemo7 } from "react";
6624
+ import { useMemo as useMemo6 } from "react";
6475
6625
  function useLocalMultiplayer(bindingsPerPlayer) {
6476
6626
  const input = useInput();
6477
- return useMemo7(
6627
+ return useMemo6(
6478
6628
  () => bindingsPerPlayer.map((bindings, i) => createPlayerInput(i + 1, bindings, input)),
6479
6629
  // eslint-disable-next-line react-hooks/exhaustive-deps
6480
6630
  [input, JSON.stringify(bindingsPerPlayer)]
@@ -6482,18 +6632,18 @@ function useLocalMultiplayer(bindingsPerPlayer) {
6482
6632
  }
6483
6633
 
6484
6634
  // src/hooks/useInputRecorder.ts
6485
- import { useMemo as useMemo8 } from "react";
6635
+ import { useMemo as useMemo7 } from "react";
6486
6636
  function useInputRecorder() {
6487
- return useMemo8(() => createInputRecorder(), []);
6637
+ return useMemo7(() => createInputRecorder(), []);
6488
6638
  }
6489
6639
 
6490
6640
  // src/hooks/useGamepad.ts
6491
- import { useEffect as useEffect29, useRef as useRef10, useState as useState7 } from "react";
6641
+ import { useEffect as useEffect31, useRef as useRef10, useState as useState7 } from "react";
6492
6642
  var EMPTY_STATE = { connected: false, axes: [], buttons: [] };
6493
6643
  function useGamepad(playerIndex = 0) {
6494
6644
  const [state, setState] = useState7(EMPTY_STATE);
6495
6645
  const rafRef = useRef10(0);
6496
- useEffect29(() => {
6646
+ useEffect31(() => {
6497
6647
  const poll = () => {
6498
6648
  const gp = navigator.getGamepads()[playerIndex];
6499
6649
  if (gp) {
@@ -6514,9 +6664,9 @@ function useGamepad(playerIndex = 0) {
6514
6664
  }
6515
6665
 
6516
6666
  // src/hooks/usePause.ts
6517
- import { useContext as useContext30, useState as useState8, useCallback as useCallback4 } from "react";
6667
+ import { useContext as useContext32, useState as useState8, useCallback as useCallback4 } from "react";
6518
6668
  function usePause() {
6519
- const engine = useContext30(EngineContext);
6669
+ const engine = useContext32(EngineContext);
6520
6670
  const [paused, setPaused] = useState8(false);
6521
6671
  const pause = useCallback4(() => {
6522
6672
  engine.loop.pause();
@@ -6534,7 +6684,7 @@ function usePause() {
6534
6684
  }
6535
6685
 
6536
6686
  // src/hooks/useProfiler.ts
6537
- import { useContext as useContext31, useEffect as useEffect30, useRef as useRef11, useState as useState9 } from "react";
6687
+ import { useContext as useContext33, useEffect as useEffect32, useRef as useRef11, useState as useState9 } from "react";
6538
6688
  var EMPTY = {
6539
6689
  fps: 0,
6540
6690
  frameTime: 0,
@@ -6542,12 +6692,12 @@ var EMPTY = {
6542
6692
  systemTimings: /* @__PURE__ */ new Map()
6543
6693
  };
6544
6694
  function useProfiler() {
6545
- const engine = useContext31(EngineContext);
6695
+ const engine = useContext33(EngineContext);
6546
6696
  const [data, setData] = useState9(EMPTY);
6547
6697
  const frameTimesRef = useRef11([]);
6548
6698
  const lastUpdateRef = useRef11(0);
6549
6699
  const prevTimeRef = useRef11(0);
6550
- useEffect30(() => {
6700
+ useEffect32(() => {
6551
6701
  if (!engine) return;
6552
6702
  let rafId;
6553
6703
  const frameTimes = frameTimesRef.current;
@@ -6587,10 +6737,10 @@ function useProfiler() {
6587
6737
  }
6588
6738
 
6589
6739
  // src/hooks/usePostProcess.ts
6590
- import { useEffect as useEffect31 } from "react";
6740
+ import { useEffect as useEffect33 } from "react";
6591
6741
  function usePostProcess(effect) {
6592
6742
  const engine = useGame();
6593
- useEffect31(() => {
6743
+ useEffect33(() => {
6594
6744
  engine.postProcessStack.add(effect);
6595
6745
  return () => {
6596
6746
  engine.postProcessStack.remove(effect);
@@ -6650,19 +6800,19 @@ function useAISteering() {
6650
6800
  }
6651
6801
 
6652
6802
  // ../gameplay/src/hooks/useDamageZone.ts
6653
- import { useContext as useContext32 } from "react";
6803
+ import { useContext as useContext34 } from "react";
6654
6804
  function useDamageZone(damage, opts = {}) {
6655
- const engine = useContext32(EngineContext);
6805
+ const engine = useContext34(EngineContext);
6656
6806
  useTriggerEnter((other) => {
6657
6807
  engine.events.emit(`damage:${other}`, { amount: damage });
6658
6808
  }, { tag: opts.tag, layer: opts.layer });
6659
6809
  }
6660
6810
 
6661
6811
  // ../gameplay/src/hooks/useDropThrough.ts
6662
- import { useContext as useContext33, useCallback as useCallback7 } from "react";
6812
+ import { useContext as useContext35, useCallback as useCallback7 } from "react";
6663
6813
  function useDropThrough(frames = 8) {
6664
- const engine = useContext33(EngineContext);
6665
- const entityId = useContext33(EntityContext);
6814
+ const engine = useContext35(EngineContext);
6815
+ const entityId = useContext35(EntityContext);
6666
6816
  const dropThrough = useCallback7(() => {
6667
6817
  const rb = engine.ecs.getComponent(entityId, "RigidBody");
6668
6818
  if (rb) rb.dropThrough = frames;
@@ -6671,14 +6821,14 @@ function useDropThrough(frames = 8) {
6671
6821
  }
6672
6822
 
6673
6823
  // ../gameplay/src/hooks/useGameStateMachine.ts
6674
- import { useState as useState11, useRef as useRef12, useCallback as useCallback8, useEffect as useEffect32, useContext as useContext34 } from "react";
6824
+ import { useState as useState11, useRef as useRef12, useCallback as useCallback8, useEffect as useEffect34, useContext as useContext36 } from "react";
6675
6825
  function useGameStateMachine(states, initial) {
6676
- const engine = useContext34(EngineContext);
6826
+ const engine = useContext36(EngineContext);
6677
6827
  const [state, setState] = useState11(initial);
6678
6828
  const stateRef = useRef12(initial);
6679
6829
  const statesRef = useRef12(states);
6680
6830
  statesRef.current = states;
6681
- useEffect32(() => {
6831
+ useEffect34(() => {
6682
6832
  statesRef.current[initial]?.onEnter?.();
6683
6833
  }, []);
6684
6834
  const transition = useCallback8((to) => {
@@ -6689,7 +6839,7 @@ function useGameStateMachine(states, initial) {
6689
6839
  setState(to);
6690
6840
  statesRef.current[to]?.onEnter?.();
6691
6841
  }, []);
6692
- useEffect32(() => {
6842
+ useEffect34(() => {
6693
6843
  const eid = engine.ecs.createEntity();
6694
6844
  engine.ecs.addComponent(eid, createScript((_id, _world, _input, dt) => {
6695
6845
  statesRef.current[stateRef.current]?.onUpdate?.(dt);
@@ -6702,19 +6852,19 @@ function useGameStateMachine(states, initial) {
6702
6852
  }
6703
6853
 
6704
6854
  // ../gameplay/src/hooks/useHealth.ts
6705
- import { useRef as useRef13, useEffect as useEffect33, useContext as useContext35, useCallback as useCallback9 } from "react";
6855
+ import { useRef as useRef13, useEffect as useEffect35, useContext as useContext37, useCallback as useCallback9 } from "react";
6706
6856
  function useHealth(maxHp, opts = {}) {
6707
- const engine = useContext35(EngineContext);
6708
- const entityId = useContext35(EntityContext);
6857
+ const engine = useContext37(EngineContext);
6858
+ const entityId = useContext37(EntityContext);
6709
6859
  const hpRef = useRef13(maxHp);
6710
6860
  const invincibleRef = useRef13(false);
6711
6861
  const iFrameDuration = opts.iFrames ?? 1;
6712
6862
  const onDeathRef = useRef13(opts.onDeath);
6713
6863
  const onDamageRef = useRef13(opts.onDamage);
6714
- useEffect33(() => {
6864
+ useEffect35(() => {
6715
6865
  onDeathRef.current = opts.onDeath;
6716
6866
  });
6717
- useEffect33(() => {
6867
+ useEffect35(() => {
6718
6868
  onDamageRef.current = opts.onDamage;
6719
6869
  });
6720
6870
  const timerRef = useRef13(
@@ -6733,10 +6883,10 @@ function useHealth(maxHp, opts = {}) {
6733
6883
  if (hpRef.current <= 0) onDeathRef.current?.();
6734
6884
  }, [iFrameDuration]);
6735
6885
  const takeDamageRef = useRef13(takeDamage);
6736
- useEffect33(() => {
6886
+ useEffect35(() => {
6737
6887
  takeDamageRef.current = takeDamage;
6738
6888
  }, [takeDamage]);
6739
- useEffect33(() => {
6889
+ useEffect35(() => {
6740
6890
  return engine.events.on(`damage:${entityId}`, ({ amount }) => {
6741
6891
  takeDamageRef.current(amount);
6742
6892
  });
@@ -6771,10 +6921,10 @@ function useHealth(maxHp, opts = {}) {
6771
6921
  }
6772
6922
 
6773
6923
  // ../gameplay/src/hooks/useKinematicBody.ts
6774
- import { useContext as useContext36, useCallback as useCallback10 } from "react";
6924
+ import { useContext as useContext38, useCallback as useCallback10 } from "react";
6775
6925
  function useKinematicBody() {
6776
- const engine = useContext36(EngineContext);
6777
- const entityId = useContext36(EntityContext);
6926
+ const engine = useContext38(EngineContext);
6927
+ const entityId = useContext38(EntityContext);
6778
6928
  const moveAndCollide = useCallback10((dx, dy) => {
6779
6929
  const transform = engine.ecs.getComponent(entityId, "Transform");
6780
6930
  if (!transform) return { dx: 0, dy: 0 };
@@ -6861,13 +7011,13 @@ function useLevelTransition(initial) {
6861
7011
  }
6862
7012
 
6863
7013
  // ../gameplay/src/hooks/usePlatformerController.ts
6864
- import { useContext as useContext37, useEffect as useEffect34 } from "react";
7014
+ import { useContext as useContext39, useEffect as useEffect36 } from "react";
6865
7015
  function normalizeKeys(val, defaults) {
6866
7016
  if (!val) return defaults;
6867
7017
  return Array.isArray(val) ? val : [val];
6868
7018
  }
6869
7019
  function usePlatformerController(entityId, opts = {}) {
6870
- const engine = useContext37(EngineContext);
7020
+ const engine = useContext39(EngineContext);
6871
7021
  const {
6872
7022
  speed = 200,
6873
7023
  jumpForce = -500,
@@ -6880,7 +7030,7 @@ function usePlatformerController(entityId, opts = {}) {
6880
7030
  const leftKeys = normalizeKeys(bindings?.left, ["ArrowLeft", "KeyA", "a"]);
6881
7031
  const rightKeys = normalizeKeys(bindings?.right, ["ArrowRight", "KeyD", "d"]);
6882
7032
  const jumpKeys = normalizeKeys(bindings?.jump, ["Space", "ArrowUp", "KeyW", "w"]);
6883
- useEffect34(() => {
7033
+ useEffect36(() => {
6884
7034
  const state = {
6885
7035
  coyoteTimer: 0,
6886
7036
  jumpBuffer: 0,
@@ -6944,9 +7094,9 @@ function usePathfinding() {
6944
7094
  }
6945
7095
 
6946
7096
  // ../gameplay/src/hooks/usePersistedBindings.ts
6947
- import { useState as useState13, useCallback as useCallback13, useMemo as useMemo9, useContext as useContext38 } from "react";
7097
+ import { useState as useState13, useCallback as useCallback13, useMemo as useMemo8, useContext as useContext40 } from "react";
6948
7098
  function usePersistedBindings(storageKey, defaults) {
6949
- const engine = useContext38(EngineContext);
7099
+ const engine = useContext40(EngineContext);
6950
7100
  const input = engine.input;
6951
7101
  const [bindings, setBindings] = useState13(() => {
6952
7102
  try {
@@ -6956,7 +7106,7 @@ function usePersistedBindings(storageKey, defaults) {
6956
7106
  }
6957
7107
  return defaults;
6958
7108
  });
6959
- const normalized = useMemo9(() => {
7109
+ const normalized = useMemo8(() => {
6960
7110
  const out = {};
6961
7111
  for (const [action, keys] of Object.entries(bindings)) {
6962
7112
  if (typeof keys === "string") out[action] = [keys];
@@ -6981,7 +7131,7 @@ function usePersistedBindings(storageKey, defaults) {
6981
7131
  }
6982
7132
  setBindings(defaults);
6983
7133
  }, [storageKey]);
6984
- return useMemo9(() => ({
7134
+ return useMemo8(() => ({
6985
7135
  bindings,
6986
7136
  rebind,
6987
7137
  reset,
@@ -7051,11 +7201,11 @@ function useSave(key, defaultValue, opts = {}) {
7051
7201
  }
7052
7202
 
7053
7203
  // ../gameplay/src/hooks/useTopDownMovement.ts
7054
- import { useContext as useContext39, useEffect as useEffect35 } from "react";
7204
+ import { useContext as useContext41, useEffect as useEffect37 } from "react";
7055
7205
  function useTopDownMovement(entityId, opts = {}) {
7056
- const engine = useContext39(EngineContext);
7206
+ const engine = useContext41(EngineContext);
7057
7207
  const { speed = 200, normalizeDiagonal = true } = opts;
7058
- useEffect35(() => {
7208
+ useEffect37(() => {
7059
7209
  const updateFn = (id, world, input) => {
7060
7210
  if (!world.hasEntity(id)) return;
7061
7211
  const rb = world.getComponent(id, "RigidBody");
@@ -7128,9 +7278,9 @@ function useDialogue() {
7128
7278
  }
7129
7279
 
7130
7280
  // ../gameplay/src/hooks/useCutscene.ts
7131
- import { useState as useState16, useCallback as useCallback17, useRef as useRef17, useEffect as useEffect36, useContext as useContext40 } from "react";
7281
+ import { useState as useState16, useCallback as useCallback17, useRef as useRef17, useEffect as useEffect38, useContext as useContext42 } from "react";
7132
7282
  function useCutscene() {
7133
- const engine = useContext40(EngineContext);
7283
+ const engine = useContext42(EngineContext);
7134
7284
  const [playing, setPlaying] = useState16(false);
7135
7285
  const [stepIndex, setStepIndex] = useState16(0);
7136
7286
  const stepsRef = useRef17([]);
@@ -7215,7 +7365,7 @@ function useCutscene() {
7215
7365
  }
7216
7366
  finish();
7217
7367
  }, [finish]);
7218
- useEffect36(() => {
7368
+ useEffect38(() => {
7219
7369
  return () => {
7220
7370
  if (entityRef.current !== null && engine.ecs.hasEntity(entityRef.current)) {
7221
7371
  engine.ecs.destroyEntity(entityRef.current);
@@ -7262,7 +7412,7 @@ function useGameStore(key, initialState) {
7262
7412
  }
7263
7413
 
7264
7414
  // ../gameplay/src/hooks/useTween.ts
7265
- import { useRef as useRef18, useCallback as useCallback19, useEffect as useEffect37 } from "react";
7415
+ import { useRef as useRef18, useCallback as useCallback19, useEffect as useEffect39 } from "react";
7266
7416
  function useTween(opts) {
7267
7417
  const rafRef = useRef18(null);
7268
7418
  const startTimeRef = useRef18(0);
@@ -7298,12 +7448,12 @@ function useTween(opts) {
7298
7448
  };
7299
7449
  rafRef.current = requestAnimationFrame(tick);
7300
7450
  }, [stop]);
7301
- useEffect37(() => {
7451
+ useEffect39(() => {
7302
7452
  if (opts.autoStart) {
7303
7453
  start();
7304
7454
  }
7305
7455
  }, []);
7306
- useEffect37(() => {
7456
+ useEffect39(() => {
7307
7457
  return () => {
7308
7458
  if (rafRef.current !== null) {
7309
7459
  cancelAnimationFrame(rafRef.current);
@@ -7322,7 +7472,7 @@ function useTween(opts) {
7322
7472
  }
7323
7473
 
7324
7474
  // ../gameplay/src/hooks/useObjectPool.ts
7325
- import { useRef as useRef19, useMemo as useMemo10, useEffect as useEffect38 } from "react";
7475
+ import { useRef as useRef19, useMemo as useMemo9, useEffect as useEffect40 } from "react";
7326
7476
  function useObjectPool(factory, reset, initialSize) {
7327
7477
  const poolRef = useRef19([]);
7328
7478
  const activeRef = useRef19(0);
@@ -7330,7 +7480,7 @@ function useObjectPool(factory, reset, initialSize) {
7330
7480
  factoryRef.current = factory;
7331
7481
  const resetRef = useRef19(reset);
7332
7482
  resetRef.current = reset;
7333
- useEffect38(() => {
7483
+ useEffect40(() => {
7334
7484
  if (initialSize != null && initialSize > 0) {
7335
7485
  const pool = poolRef.current;
7336
7486
  for (let i = 0; i < initialSize; i++) {
@@ -7338,7 +7488,7 @@ function useObjectPool(factory, reset, initialSize) {
7338
7488
  }
7339
7489
  }
7340
7490
  }, []);
7341
- return useMemo10(() => ({
7491
+ return useMemo9(() => ({
7342
7492
  acquire() {
7343
7493
  activeRef.current++;
7344
7494
  if (poolRef.current.length > 0) {
@@ -7366,7 +7516,7 @@ function useObjectPool(factory, reset, initialSize) {
7366
7516
  }
7367
7517
 
7368
7518
  // ../../packages/audio/src/useSound.ts
7369
- import { useEffect as useEffect39, useRef as useRef20 } from "react";
7519
+ import { useEffect as useEffect41, useRef as useRef20 } from "react";
7370
7520
  var _audioCtx = null;
7371
7521
  function getAudioCtx() {
7372
7522
  if (!_audioCtx) _audioCtx = new AudioContext();
@@ -7439,7 +7589,7 @@ function useSound(src, opts = {}) {
7439
7589
  const volRef = useRef20(opts.volume ?? 1);
7440
7590
  const loopRef = useRef20(opts.loop ?? false);
7441
7591
  const groupRef = useRef20(opts.group);
7442
- useEffect39(() => {
7592
+ useEffect41(() => {
7443
7593
  let cancelled = false;
7444
7594
  loadBuffer(src).then((buf) => {
7445
7595
  if (!cancelled) bufferRef.current = buf;
@@ -7580,6 +7730,23 @@ function createAtlas(names, _columns) {
7580
7730
  return atlas;
7581
7731
  }
7582
7732
 
7733
+ // src/utils/animationHelpers.ts
7734
+ function playClip(world, entityId, clipName) {
7735
+ const anim = world.getComponent(entityId, "AnimationState");
7736
+ if (anim) anim.currentClip = clipName;
7737
+ }
7738
+ function setAnimationState(world, entityId, stateName) {
7739
+ const animator = world.getComponent(entityId, "Animator");
7740
+ if (animator) {
7741
+ animator.currentState = stateName;
7742
+ animator._entered = false;
7743
+ }
7744
+ }
7745
+ function setAnimatorParam(world, entityId, name, value) {
7746
+ const animator = world.getComponent(entityId, "Animator");
7747
+ if (animator) animator.params[name] = value;
7748
+ }
7749
+
7583
7750
  // src/utils/prefab.ts
7584
7751
  import { memo } from "react";
7585
7752
  function definePrefab(name, defaults, render) {
@@ -7593,6 +7760,7 @@ function definePrefab(name, defaults, render) {
7593
7760
  export {
7594
7761
  AnimatedSprite,
7595
7762
  Animation,
7763
+ Animator,
7596
7764
  AssetLoader,
7597
7765
  BoxCollider,
7598
7766
  Camera2D,
@@ -7650,11 +7818,14 @@ export {
7650
7818
  overlapBox,
7651
7819
  overlapCircle,
7652
7820
  patrol,
7821
+ playClip,
7653
7822
  preloadManifest,
7654
7823
  raycast,
7655
7824
  raycastAll,
7656
7825
  scanlineEffect,
7657
7826
  seek,
7827
+ setAnimationState,
7828
+ setAnimatorParam,
7658
7829
  setGroupMute,
7659
7830
  setGroupVolume,
7660
7831
  setMasterVolume,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cubeforge",
3
- "version": "0.3.15",
3
+ "version": "0.3.16",
4
4
  "description": "React-first 2D browser game engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",