cubeforge 0.5.1 → 0.5.2

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/advanced.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  resolveTOI,
31
31
  setDeterministicMode,
32
32
  sortEntities
33
- } from "./chunk-OT6I5RYW.js";
33
+ } from "./chunk-UMHYQ4EP.js";
34
34
  export {
35
35
  Float64Pool,
36
36
  IslandDetector,
@@ -5261,6 +5261,31 @@ var PhysicsSystem = class {
5261
5261
  bvhCache = /* @__PURE__ */ new Map();
5262
5262
  // Physics hooks for filtering and modifying contacts
5263
5263
  hooks = {};
5264
+ // ── Scratch containers — reused every frame to avoid GC pressure ──────────
5265
+ //
5266
+ // Previously these were allocated fresh inside update() every substep,
5267
+ // producing ~20 Map/Set allocations per frame. Now they live as instance
5268
+ // fields and are cleared at the start of each use. Never escape the class.
5269
+ //
5270
+ // The _current*Pairs fields are double-buffered with their active*Pairs
5271
+ // counterparts: at the end of each substep, the filled "current" is swapped
5272
+ // with "active", and next substep clears the new "current" (which was the
5273
+ // previous "active"). Same enter/stay/exit event semantics, zero allocation.
5274
+ _staticDelta = /* @__PURE__ */ new Map();
5275
+ _preStepPos = /* @__PURE__ */ new Map();
5276
+ _spatialGrid = /* @__PURE__ */ new Map();
5277
+ _jointExcludedPairs = /* @__PURE__ */ new Set();
5278
+ _solverBodies = /* @__PURE__ */ new Map();
5279
+ _triggerNormals = /* @__PURE__ */ new Map();
5280
+ _circleNormals = /* @__PURE__ */ new Map();
5281
+ /** Shared scratch for per-body "checked" neighbor dedup; cleared per iteration. */
5282
+ _checkedScratch = /* @__PURE__ */ new Set();
5283
+ _currentCollisionPairs = /* @__PURE__ */ new Map();
5284
+ _currentTriggerPairs = /* @__PURE__ */ new Map();
5285
+ _currentCirclePairs = /* @__PURE__ */ new Map();
5286
+ _currentCompoundPairs = /* @__PURE__ */ new Map();
5287
+ _currentCapsulePairs = /* @__PURE__ */ new Map();
5288
+ _currentPolygonPairs = /* @__PURE__ */ new Map();
5264
5289
  /** Set physics hooks for contact filtering and modification. */
5265
5290
  setHooks(hooks) {
5266
5291
  this.hooks = hooks;
@@ -5421,7 +5446,8 @@ var PhysicsSystem = class {
5421
5446
  }
5422
5447
  const nonDynamic = [...staticBox, ...kinematicBox];
5423
5448
  this.pruneDeadPairs(world);
5424
- const staticDelta = /* @__PURE__ */ new Map();
5449
+ const staticDelta = this._staticDelta;
5450
+ staticDelta.clear();
5425
5451
  for (const sid of nonDynamic) {
5426
5452
  const st = world.getComponent(sid, "Transform");
5427
5453
  const prev = this.staticPrevPos.get(sid);
@@ -5551,12 +5577,14 @@ var PhysicsSystem = class {
5551
5577
  }
5552
5578
  if (rb.dropThrough > 0) rb.dropThrough--;
5553
5579
  }
5554
- const preStepPos = /* @__PURE__ */ new Map();
5580
+ const preStepPos = this._preStepPos;
5581
+ preStepPos.clear();
5555
5582
  for (const id of dynamicBox) {
5556
5583
  const t = world.getComponent(id, "Transform");
5557
5584
  preStepPos.set(id, { x: t.x, y: t.y });
5558
5585
  }
5559
- const spatialGrid = /* @__PURE__ */ new Map();
5586
+ const spatialGrid = this._spatialGrid;
5587
+ spatialGrid.clear();
5560
5588
  for (const sid of nonDynamic) {
5561
5589
  const st = world.getComponent(sid, "Transform");
5562
5590
  const sc = world.getComponent(sid, "BoxCollider");
@@ -5571,7 +5599,8 @@ var PhysicsSystem = class {
5571
5599
  bucket.push(sid);
5572
5600
  }
5573
5601
  }
5574
- const jointExcludedPairs = /* @__PURE__ */ new Set();
5602
+ const jointExcludedPairs = this._jointExcludedPairs;
5603
+ jointExcludedPairs.clear();
5575
5604
  for (const jid of world.query("Joint")) {
5576
5605
  const j = world.getComponent(jid, "Joint");
5577
5606
  if (!j.enabled || j.broken) continue;
@@ -5581,7 +5610,8 @@ var PhysicsSystem = class {
5581
5610
  }
5582
5611
  }
5583
5612
  const manifolds = [];
5584
- const currentCollisionPairs = /* @__PURE__ */ new Map();
5613
+ const currentCollisionPairs = this._currentCollisionPairs;
5614
+ currentCollisionPairs.clear();
5585
5615
  for (const id of dynamicBox) {
5586
5616
  const rb = world.getComponent(id, "RigidBody");
5587
5617
  if (rb.sleeping) continue;
@@ -5590,7 +5620,8 @@ var PhysicsSystem = class {
5590
5620
  if (!col.enabled || col.isTrigger) continue;
5591
5621
  const dynAABB = getAABB2(transform, col);
5592
5622
  const candidateCells = this.getCells(dynAABB.cx, dynAABB.cy, dynAABB.hw, dynAABB.hh);
5593
- const checked = /* @__PURE__ */ new Set();
5623
+ const checked = this._checkedScratch;
5624
+ checked.clear();
5594
5625
  for (const cell of candidateCells) {
5595
5626
  const bucket = spatialGrid.get(cell);
5596
5627
  if (!bucket) continue;
@@ -5779,7 +5810,8 @@ var PhysicsSystem = class {
5779
5810
  const circleCy = ct.y + cc.offsetY;
5780
5811
  const circleAABB2 = { cx: circleCx, cy: circleCy, hw: cc.radius, hh: cc.radius };
5781
5812
  const candidateCells = this.getCells(circleAABB2.cx, circleAABB2.cy, circleAABB2.hw, circleAABB2.hh);
5782
- const checked = /* @__PURE__ */ new Set();
5813
+ const checked = this._checkedScratch;
5814
+ checked.clear();
5783
5815
  for (const cell of candidateCells) {
5784
5816
  const bucket = spatialGrid.get(cell);
5785
5817
  if (!bucket) continue;
@@ -5963,7 +5995,8 @@ var PhysicsSystem = class {
5963
5995
  const capHw = cc.width / 2;
5964
5996
  const capHh = cc.height / 2;
5965
5997
  const candidateCells = this.getCells(capCx, capCy, capHw, capHh);
5966
- const checked = /* @__PURE__ */ new Set();
5998
+ const checked = this._checkedScratch;
5999
+ checked.clear();
5967
6000
  for (const cell of candidateCells) {
5968
6001
  const bucket = spatialGrid.get(cell);
5969
6002
  if (!bucket) continue;
@@ -6202,7 +6235,8 @@ var PhysicsSystem = class {
6202
6235
  const polyCx = (minX + maxX) / 2;
6203
6236
  const polyCy = (minY + maxY) / 2;
6204
6237
  const candidateCells = this.getCells(polyCx, polyCy, polyHw, polyHh);
6205
- const checked = /* @__PURE__ */ new Set();
6238
+ const checked = this._checkedScratch;
6239
+ checked.clear();
6206
6240
  for (const cell of candidateCells) {
6207
6241
  const bucket = spatialGrid.get(cell);
6208
6242
  if (!bucket) continue;
@@ -6457,7 +6491,8 @@ var PhysicsSystem = class {
6457
6491
  const triCx = (minX + maxX) / 2;
6458
6492
  const triCy = (minY + maxY) / 2;
6459
6493
  const candidateCells = this.getCells(triCx, triCy, triHw, triHh);
6460
- const checked = /* @__PURE__ */ new Set();
6494
+ const checked = this._checkedScratch;
6495
+ checked.clear();
6461
6496
  for (const cell of candidateCells) {
6462
6497
  const bucket = spatialGrid.get(cell);
6463
6498
  if (!bucket) continue;
@@ -6956,7 +6991,8 @@ var PhysicsSystem = class {
6956
6991
  this.hooks.onContactModify(m);
6957
6992
  }
6958
6993
  }
6959
- const solverBodies = /* @__PURE__ */ new Map();
6994
+ const solverBodies = this._solverBodies;
6995
+ solverBodies.clear();
6960
6996
  for (const id of allBox) {
6961
6997
  const rb = world.getComponent(id, "RigidBody");
6962
6998
  const t = world.getComponent(id, "Transform");
@@ -7264,7 +7300,8 @@ var PhysicsSystem = class {
7264
7300
  (Math.max(startCx, endCx) - Math.min(startCx, endCx)) / 2 + hw,
7265
7301
  (Math.max(startCy, endCy) - Math.min(startCy, endCy)) / 2 + hh
7266
7302
  );
7267
- const checked = /* @__PURE__ */ new Set();
7303
+ const checked = this._checkedScratch;
7304
+ checked.clear();
7268
7305
  for (const cell of sweepCells) {
7269
7306
  const bucket = spatialGrid.get(cell);
7270
7307
  if (!bucket) continue;
@@ -7345,7 +7382,8 @@ var PhysicsSystem = class {
7345
7382
  hh: col.height / 2
7346
7383
  };
7347
7384
  const candidateCells = this.getCells(probeAABB.cx, probeAABB.cy, probeAABB.hw, probeAABB.hh);
7348
- const checked = /* @__PURE__ */ new Set();
7385
+ const checked = this._checkedScratch;
7386
+ checked.clear();
7349
7387
  outer: for (const cell of candidateCells) {
7350
7388
  const bucket = spatialGrid.get(cell);
7351
7389
  if (!bucket) continue;
@@ -7379,7 +7417,8 @@ var PhysicsSystem = class {
7379
7417
  hh: cap.height / 2
7380
7418
  };
7381
7419
  const candidateCells = this.getCells(probeAABB.cx, probeAABB.cy, probeAABB.hw, probeAABB.hh);
7382
- const checked = /* @__PURE__ */ new Set();
7420
+ const checked = this._checkedScratch;
7421
+ checked.clear();
7383
7422
  outerCap: for (const cell of candidateCells) {
7384
7423
  const bucket = spatialGrid.get(cell);
7385
7424
  if (!bucket) continue;
@@ -7723,10 +7762,16 @@ var PhysicsSystem = class {
7723
7762
  this.events?.emit("collisionExit", { a, b, normalX: 0, normalY: 0 });
7724
7763
  }
7725
7764
  }
7726
- this.activeCollisionPairs = currentCollisionPairs;
7765
+ {
7766
+ const prev = this.activeCollisionPairs;
7767
+ this.activeCollisionPairs = currentCollisionPairs;
7768
+ this._currentCollisionPairs = prev;
7769
+ }
7727
7770
  const allWithCollider = world.query("Transform", "BoxCollider");
7728
- const currentTriggerPairs = /* @__PURE__ */ new Map();
7729
- const triggerNormals = /* @__PURE__ */ new Map();
7771
+ const currentTriggerPairs = this._currentTriggerPairs;
7772
+ currentTriggerPairs.clear();
7773
+ const triggerNormals = this._triggerNormals;
7774
+ triggerNormals.clear();
7730
7775
  for (let i = 0; i < allWithCollider.length; i++) {
7731
7776
  for (let j = i + 1; j < allWithCollider.length; j++) {
7732
7777
  const ia = allWithCollider[i];
@@ -7762,10 +7807,16 @@ var PhysicsSystem = class {
7762
7807
  for (const [key, [a, b]] of this.activeTriggerPairs) {
7763
7808
  if (!currentTriggerPairs.has(key)) this.events?.emit("triggerExit", { a, b, normalX: 0, normalY: 0 });
7764
7809
  }
7765
- this.activeTriggerPairs = currentTriggerPairs;
7810
+ {
7811
+ const prev = this.activeTriggerPairs;
7812
+ this.activeTriggerPairs = currentTriggerPairs;
7813
+ this._currentTriggerPairs = prev;
7814
+ }
7766
7815
  if (allCircle.length > 0) {
7767
- const currentCirclePairs = /* @__PURE__ */ new Map();
7768
- const circleNormals = /* @__PURE__ */ new Map();
7816
+ const currentCirclePairs = this._currentCirclePairs;
7817
+ currentCirclePairs.clear();
7818
+ const circleNormals = this._circleNormals;
7819
+ circleNormals.clear();
7769
7820
  for (let i = 0; i < allCircle.length; i++) {
7770
7821
  for (let j = i + 1; j < allCircle.length; j++) {
7771
7822
  const ia = allCircle[i];
@@ -7832,14 +7883,19 @@ var PhysicsSystem = class {
7832
7883
  for (const [key, [a, b]] of this.activeCirclePairs) {
7833
7884
  if (!currentCirclePairs.has(key)) this.events?.emit("circleExit", { a, b, normalX: 0, normalY: 0 });
7834
7885
  }
7835
- this.activeCirclePairs = currentCirclePairs;
7886
+ {
7887
+ const prev = this.activeCirclePairs;
7888
+ this.activeCirclePairs = currentCirclePairs;
7889
+ this._currentCirclePairs = prev;
7890
+ }
7836
7891
  } else if (this.activeCirclePairs.size > 0) {
7837
7892
  for (const [, [a, b]] of this.activeCirclePairs) this.events?.emit("circleExit", { a, b, normalX: 0, normalY: 0 });
7838
- this.activeCirclePairs = /* @__PURE__ */ new Map();
7893
+ this.activeCirclePairs.clear();
7839
7894
  }
7840
7895
  const allCompound = world.query("Transform", "CompoundCollider");
7841
7896
  if (allCompound.length > 0) {
7842
- const currentCompoundPairs = /* @__PURE__ */ new Map();
7897
+ const currentCompoundPairs = this._currentCompoundPairs;
7898
+ currentCompoundPairs.clear();
7843
7899
  const allBoxEntities = world.query("Transform", "BoxCollider");
7844
7900
  for (const cid of allCompound) {
7845
7901
  const cc = world.getComponent(cid, "CompoundCollider");
@@ -7914,13 +7970,18 @@ var PhysicsSystem = class {
7914
7970
  for (const [key, [a, b]] of this.activeCompoundPairs) {
7915
7971
  if (!currentCompoundPairs.has(key)) this.events?.emit("compoundExit", { a, b });
7916
7972
  }
7917
- this.activeCompoundPairs = currentCompoundPairs;
7973
+ {
7974
+ const prev = this.activeCompoundPairs;
7975
+ this.activeCompoundPairs = currentCompoundPairs;
7976
+ this._currentCompoundPairs = prev;
7977
+ }
7918
7978
  } else if (this.activeCompoundPairs.size > 0) {
7919
7979
  for (const [, [a, b]] of this.activeCompoundPairs) this.events?.emit("compoundExit", { a, b });
7920
- this.activeCompoundPairs = /* @__PURE__ */ new Map();
7980
+ this.activeCompoundPairs.clear();
7921
7981
  }
7922
7982
  if (allCapsule.length > 0) {
7923
- const currentCapsulePairs = /* @__PURE__ */ new Map();
7983
+ const currentCapsulePairs = this._currentCapsulePairs;
7984
+ currentCapsulePairs.clear();
7924
7985
  const allBoxForCapsule = world.query("Transform", "BoxCollider");
7925
7986
  for (const cid of allCapsule) {
7926
7987
  const cc = world.getComponent(cid, "CapsuleCollider");
@@ -7958,14 +8019,19 @@ var PhysicsSystem = class {
7958
8019
  for (const [key, [a, b]] of this.activeCapsulePairs) {
7959
8020
  if (!currentCapsulePairs.has(key)) this.events?.emit("capsuleExit", { a, b });
7960
8021
  }
7961
- this.activeCapsulePairs = currentCapsulePairs;
8022
+ {
8023
+ const prev = this.activeCapsulePairs;
8024
+ this.activeCapsulePairs = currentCapsulePairs;
8025
+ this._currentCapsulePairs = prev;
8026
+ }
7962
8027
  } else if (this.activeCapsulePairs.size > 0) {
7963
8028
  for (const [, [a, b]] of this.activeCapsulePairs) this.events?.emit("capsuleExit", { a, b });
7964
- this.activeCapsulePairs = /* @__PURE__ */ new Map();
8029
+ this.activeCapsulePairs.clear();
7965
8030
  }
7966
8031
  const allPolygonEntities = [...allPolygon, ...allTriangle];
7967
8032
  if (allPolygonEntities.length > 0) {
7968
- const currentPolyPairs = /* @__PURE__ */ new Map();
8033
+ const currentPolyPairs = this._currentPolygonPairs;
8034
+ currentPolyPairs.clear();
7969
8035
  const allBoxForPoly = world.query("Transform", "BoxCollider");
7970
8036
  for (const pid of allPolygonEntities) {
7971
8037
  const isPoly = world.getComponent(pid, "ConvexPolygonCollider");
@@ -8046,10 +8112,14 @@ var PhysicsSystem = class {
8046
8112
  for (const [key, [a, b]] of this.activePolygonPairs) {
8047
8113
  if (!currentPolyPairs.has(key)) this.events?.emit("polygonExit", { a, b });
8048
8114
  }
8049
- this.activePolygonPairs = currentPolyPairs;
8115
+ {
8116
+ const prev = this.activePolygonPairs;
8117
+ this.activePolygonPairs = currentPolyPairs;
8118
+ this._currentPolygonPairs = prev;
8119
+ }
8050
8120
  } else if (this.activePolygonPairs.size > 0) {
8051
8121
  for (const [, [a, b]] of this.activePolygonPairs) this.events?.emit("polygonExit", { a, b });
8052
- this.activePolygonPairs = /* @__PURE__ */ new Map();
8122
+ this.activePolygonPairs.clear();
8053
8123
  }
8054
8124
  }
8055
8125
  // ── Pair pruning ────────────────────────────────────────────────────────
package/dist/index.d.ts CHANGED
@@ -67,6 +67,10 @@ interface GameProps$1 {
67
67
  * - 'onDemand' — sleeps until input arrives or a component calls markDirty().
68
68
  * Use for puzzle games, turn-based games, visual novels, level editors, or any
69
69
  * scene where nothing changes unless the user acts. Saves battery and CPU.
70
+ *
71
+ * **Captured at mount.** This prop is read once when the Game mounts and
72
+ * baked into the loop. Changing it later has no effect — to switch modes,
73
+ * unmount and remount the Game (e.g. via a `key` change).
70
74
  */
71
75
  mode?: GameLoopMode;
72
76
  style?: CSSProperties;
package/dist/index.js CHANGED
@@ -63,7 +63,7 @@ import {
63
63
  triangleArea,
64
64
  triangleMassProperties,
65
65
  velocityAtPoint
66
- } from "./chunk-OT6I5RYW.js";
66
+ } from "./chunk-UMHYQ4EP.js";
67
67
 
68
68
  // src/components/Game.tsx
69
69
  import { useEffect as useEffect11, useRef as useRef11, useState as useState3 } from "react";
@@ -1496,7 +1496,6 @@ function createTimeline(opts) {
1496
1496
  seek(time) {
1497
1497
  elapsed = 0;
1498
1498
  resetSegments();
1499
- elapsed = 0;
1500
1499
  const step = 1 / 60;
1501
1500
  while (elapsed < time) {
1502
1501
  const d = Math.min(step, time - elapsed);
@@ -5437,6 +5436,10 @@ function useAudioScheduler(opts) {
5437
5436
  if (isRunningRef.current) return;
5438
5437
  const ctx = getAudioCtx();
5439
5438
  if (ctx.state === "suspended") void ctx.resume();
5439
+ if (timerRef.current !== null) {
5440
+ clearInterval(timerRef.current);
5441
+ timerRef.current = null;
5442
+ }
5440
5443
  nextBeatTimeRef.current = ctx.currentTime;
5441
5444
  nextBeatIndexRef.current = 0;
5442
5445
  currentBeatRef.current = 0;
@@ -7047,7 +7050,7 @@ function Transform({ x = 0, y = 0, rotation = 0, scaleX = 1, scaleY = 1 }) {
7047
7050
  }
7048
7051
 
7049
7052
  // src/components/Sprite.tsx
7050
- import { useEffect as useEffect15, useContext as useContext6 } from "react";
7053
+ import { useEffect as useEffect15, useContext as useContext6, useRef as useRef12 } from "react";
7051
7054
  function Sprite({
7052
7055
  width,
7053
7056
  height,
@@ -7150,6 +7153,37 @@ function Sprite({
7150
7153
  }
7151
7154
  return () => engine.ecs.removeComponent(entityId, "Sprite");
7152
7155
  }, []);
7156
+ const didMount = useRef12(false);
7157
+ useEffect15(() => {
7158
+ if (!didMount.current) {
7159
+ didMount.current = true;
7160
+ return;
7161
+ }
7162
+ const comp = engine.ecs.getComponent(entityId, "Sprite");
7163
+ if (!comp) return;
7164
+ if (!src) {
7165
+ comp.image = void 0;
7166
+ comp.src = void 0;
7167
+ return;
7168
+ }
7169
+ let cancelled = false;
7170
+ engine.assets.loadImage(src).then((img) => {
7171
+ if (cancelled) return;
7172
+ const c = engine.ecs.getComponent(entityId, "Sprite");
7173
+ if (c) {
7174
+ c.image = img;
7175
+ c.src = img.src;
7176
+ }
7177
+ }).catch((err) => {
7178
+ if (cancelled) return;
7179
+ const message = err instanceof Error ? err.message : String(err);
7180
+ engine.events.emit("asset:error", { type: "image", src, entityId, error: err });
7181
+ console.error(`[Cubeforge] <Sprite> failed to reload image "${src}": ${message}`);
7182
+ });
7183
+ return () => {
7184
+ cancelled = true;
7185
+ };
7186
+ }, [src, engine, entityId]);
7153
7187
  useEffect15(() => {
7154
7188
  const comp = engine.ecs.getComponent(entityId, "Sprite");
7155
7189
  if (!comp) return;
@@ -7500,7 +7534,7 @@ function CompoundCollider({ shapes, isTrigger = false, layer = "default", mask =
7500
7534
  }
7501
7535
 
7502
7536
  // src/components/Script.tsx
7503
- import { useEffect as useEffect22, useContext as useContext13, useRef as useRef12 } from "react";
7537
+ import { useEffect as useEffect22, useContext as useContext13, useRef as useRef13 } from "react";
7504
7538
  function Script({ init, update }) {
7505
7539
  const engine = useContext13(EngineContext);
7506
7540
  const entityId = useContext13(EntityContext);
@@ -7509,9 +7543,9 @@ function Script({ init, update }) {
7509
7543
  console.warn("[Cubeforge] <Script> must be inside an <Entity>. No EntityContext found.");
7510
7544
  }
7511
7545
  }
7512
- const initRef = useRef12(init);
7546
+ const initRef = useRef13(init);
7513
7547
  initRef.current = init;
7514
- const updateRef = useRef12(update);
7548
+ const updateRef = useRef13(update);
7515
7549
  updateRef.current = update;
7516
7550
  useEffect22(() => {
7517
7551
  if (initRef.current) {
@@ -7971,7 +8005,7 @@ function ParticleEmitter({
7971
8005
  }
7972
8006
 
7973
8007
  // src/components/VirtualJoystick.tsx
7974
- import { useRef as useRef13 } from "react";
8008
+ import { useRef as useRef14 } from "react";
7975
8009
 
7976
8010
  // src/hooks/useVirtualInput.ts
7977
8011
  var _axes = { x: 0, y: 0 };
@@ -8005,10 +8039,10 @@ function VirtualJoystick({
8005
8039
  actionLabel = "A",
8006
8040
  actionName = "action"
8007
8041
  }) {
8008
- const baseRef = useRef13(null);
8009
- const stickRef = useRef13(null);
8010
- const activePtr = useRef13(null);
8011
- const baseCenterRef = useRef13({ x: 0, y: 0 });
8042
+ const baseRef = useRef14(null);
8043
+ const stickRef = useRef14(null);
8044
+ const activePtr = useRef14(null);
8045
+ const baseCenterRef = useRef14({ x: 0, y: 0 });
8012
8046
  const radius = size / 2 - 16;
8013
8047
  const applyStickPosition = (dx, dy) => {
8014
8048
  if (!stickRef.current) return;
@@ -8556,10 +8590,10 @@ function ParallaxLayer({
8556
8590
  }
8557
8591
 
8558
8592
  // src/components/ScreenFlash.tsx
8559
- import { forwardRef, useImperativeHandle, useRef as useRef14 } from "react";
8593
+ import { forwardRef, useImperativeHandle, useRef as useRef15 } from "react";
8560
8594
  import { jsx as jsx12 } from "react/jsx-runtime";
8561
8595
  var ScreenFlash = forwardRef((_, ref) => {
8562
- const divRef = useRef14(null);
8596
+ const divRef = useRef15(null);
8563
8597
  useImperativeHandle(ref, () => ({
8564
8598
  flash(color, duration) {
8565
8599
  const el = divRef.current;
@@ -8595,12 +8629,12 @@ var ScreenFlash = forwardRef((_, ref) => {
8595
8629
  ScreenFlash.displayName = "ScreenFlash";
8596
8630
 
8597
8631
  // src/components/CameraZone.tsx
8598
- import { useEffect as useEffect31, useContext as useContext22, useRef as useRef15 } from "react";
8632
+ import { useEffect as useEffect31, useContext as useContext22, useRef as useRef16 } from "react";
8599
8633
  import { Fragment as Fragment6, jsx as jsx13 } from "react/jsx-runtime";
8600
8634
  function CameraZone({ x, y, width, height, watchTag = "player", targetX, targetY, children }) {
8601
8635
  const engine = useContext22(EngineContext);
8602
- const prevFollowRef = useRef15(void 0);
8603
- const activeRef = useRef15(false);
8636
+ const prevFollowRef = useRef16(void 0);
8637
+ const activeRef = useRef16(false);
8604
8638
  useEffect31(() => {
8605
8639
  const eid = engine.ecs.createEntity();
8606
8640
  engine.ecs.addComponent(
@@ -9356,7 +9390,7 @@ function useInputMap(bindings) {
9356
9390
  }
9357
9391
 
9358
9392
  // src/hooks/useEvents.ts
9359
- import { useContext as useContext43, useEffect as useEffect49, useRef as useRef16 } from "react";
9393
+ import { useContext as useContext43, useEffect as useEffect49, useRef as useRef17 } from "react";
9360
9394
  function useEvents() {
9361
9395
  const engine = useContext43(EngineContext);
9362
9396
  if (!engine) throw new Error("useEvents must be used inside <Game>");
@@ -9364,7 +9398,7 @@ function useEvents() {
9364
9398
  }
9365
9399
  function useEvent(event, handler) {
9366
9400
  const events = useEvents();
9367
- const handlerRef = useRef16(handler);
9401
+ const handlerRef = useRef17(handler);
9368
9402
  handlerRef.current = handler;
9369
9403
  useEffect49(() => {
9370
9404
  return events.on(event, (data) => handlerRef.current(data));
@@ -9407,13 +9441,13 @@ function useCoordinates() {
9407
9441
  }
9408
9442
 
9409
9443
  // src/hooks/useWorldQuery.ts
9410
- import { useEffect as useEffect50, useRef as useRef17, useState as useState8 } from "react";
9444
+ import { useEffect as useEffect50, useRef as useRef18, useState as useState8 } from "react";
9411
9445
  function useWorldQuery(...components) {
9412
9446
  const engine = useGame();
9413
9447
  const [result, setResult] = useState8(() => engine.ecs.query(...components));
9414
- const prevRef = useRef17([]);
9415
- const rafRef = useRef17(0);
9416
- const mountedRef = useRef17(true);
9448
+ const prevRef = useRef18([]);
9449
+ const rafRef = useRef18(0);
9450
+ const mountedRef = useRef18(true);
9417
9451
  useEffect50(() => {
9418
9452
  mountedRef.current = true;
9419
9453
  const tick2 = () => {
@@ -9480,11 +9514,11 @@ function useInputRecorder() {
9480
9514
  }
9481
9515
 
9482
9516
  // src/hooks/useGamepad.ts
9483
- import { useEffect as useEffect52, useRef as useRef18, useState as useState9 } from "react";
9517
+ import { useEffect as useEffect52, useRef as useRef19, useState as useState9 } from "react";
9484
9518
  var EMPTY_STATE = { connected: false, axes: [], buttons: [] };
9485
9519
  function useGamepad(playerIndex = 0) {
9486
9520
  const [state, setState] = useState9(EMPTY_STATE);
9487
- const rafRef = useRef18(0);
9521
+ const rafRef = useRef19(0);
9488
9522
  useEffect52(() => {
9489
9523
  const poll = () => {
9490
9524
  const gp = navigator.getGamepads()[playerIndex];
@@ -9526,7 +9560,7 @@ function usePause() {
9526
9560
  }
9527
9561
 
9528
9562
  // src/hooks/useProfiler.ts
9529
- import { useContext as useContext46, useEffect as useEffect53, useRef as useRef19, useState as useState11 } from "react";
9563
+ import { useContext as useContext46, useEffect as useEffect53, useRef as useRef20, useState as useState11 } from "react";
9530
9564
  var EMPTY = {
9531
9565
  fps: 0,
9532
9566
  frameTime: 0,
@@ -9536,9 +9570,9 @@ var EMPTY = {
9536
9570
  function useProfiler() {
9537
9571
  const engine = useContext46(EngineContext);
9538
9572
  const [data, setData] = useState11(EMPTY);
9539
- const frameTimesRef = useRef19([]);
9540
- const lastUpdateRef = useRef19(0);
9541
- const prevTimeRef = useRef19(0);
9573
+ const frameTimesRef = useRef20([]);
9574
+ const lastUpdateRef = useRef20(0);
9575
+ const prevTimeRef = useRef20(0);
9542
9576
  useEffect53(() => {
9543
9577
  if (!engine) return;
9544
9578
  let rafId2;
@@ -9652,11 +9686,11 @@ function useTouch() {
9652
9686
  }
9653
9687
 
9654
9688
  // src/hooks/useGestures.ts
9655
- import { useEffect as useEffect57, useRef as useRef20 } from "react";
9689
+ import { useEffect as useEffect57, useRef as useRef21 } from "react";
9656
9690
  function useGestures(handlers, opts = {}) {
9657
- const handlersRef = useRef20(handlers);
9691
+ const handlersRef = useRef21(handlers);
9658
9692
  handlersRef.current = handlers;
9659
- const optsRef = useRef20(opts);
9693
+ const optsRef = useRef21(opts);
9660
9694
  optsRef.current = opts;
9661
9695
  useEffect57(() => {
9662
9696
  const target = optsRef.current.target ?? window;
@@ -9793,14 +9827,14 @@ function useGamepadHaptics(playerIndex = 0) {
9793
9827
  }
9794
9828
 
9795
9829
  // src/hooks/useTimer.ts
9796
- import { useRef as useRef21, useCallback as useCallback5, useEffect as useEffect58, useContext as useContext49 } from "react";
9830
+ import { useRef as useRef22, useCallback as useCallback5, useEffect as useEffect58, useContext as useContext49 } from "react";
9797
9831
  function useTimer(duration, onComplete, opts) {
9798
9832
  const engine = useContext49(EngineContext);
9799
- const onCompleteRef = useRef21(onComplete);
9833
+ const onCompleteRef = useRef22(onComplete);
9800
9834
  onCompleteRef.current = onComplete;
9801
- const loopRef = useRef21(opts?.loop ?? false);
9835
+ const loopRef = useRef22(opts?.loop ?? false);
9802
9836
  loopRef.current = opts?.loop ?? false;
9803
- const timerRef = useRef21(null);
9837
+ const timerRef = useRef22(null);
9804
9838
  if (!timerRef.current) {
9805
9839
  timerRef.current = createTimer(
9806
9840
  duration,
@@ -9854,14 +9888,14 @@ function useTimer(duration, onComplete, opts) {
9854
9888
  }
9855
9889
 
9856
9890
  // src/hooks/useCoroutine.ts
9857
- import { useRef as useRef22, useEffect as useEffect59, useContext as useContext50 } from "react";
9891
+ import { useRef as useRef23, useEffect as useEffect59, useContext as useContext50 } from "react";
9858
9892
  var wait = (seconds) => ({ type: "wait", seconds });
9859
9893
  var waitFrames = (frames) => ({ type: "waitFrames", frames });
9860
9894
  var waitUntil = (condition) => ({ type: "waitUntil", condition });
9861
9895
  var nextCoroutineId = 1;
9862
9896
  function useCoroutine() {
9863
9897
  const engine = useContext50(EngineContext);
9864
- const coroutinesRef = useRef22(/* @__PURE__ */ new Map());
9898
+ const coroutinesRef = useRef23(/* @__PURE__ */ new Map());
9865
9899
  useEffect59(() => {
9866
9900
  const eid = engine.ecs.createEntity();
9867
9901
  engine.ecs.addComponent(
@@ -9927,15 +9961,15 @@ function useCoroutine() {
9927
9961
  return coroutinesRef.current.size;
9928
9962
  }
9929
9963
  };
9930
- const controlsRef = useRef22(controls);
9964
+ const controlsRef = useRef23(controls);
9931
9965
  return controlsRef.current;
9932
9966
  }
9933
9967
 
9934
9968
  // src/hooks/useSceneManager.ts
9935
- import { useState as useState12, useCallback as useCallback6, useRef as useRef23 } from "react";
9969
+ import { useState as useState12, useCallback as useCallback6, useRef as useRef24 } from "react";
9936
9970
  function useSceneManager(initialScene) {
9937
9971
  const [stack, setStack] = useState12([initialScene]);
9938
- const stackRef = useRef23(stack);
9972
+ const stackRef = useRef24(stack);
9939
9973
  stackRef.current = stack;
9940
9974
  const push = useCallback6((scene) => {
9941
9975
  setStack((prev) => [...prev, scene]);
@@ -9971,7 +10005,7 @@ function useSceneManager(initialScene) {
9971
10005
  }
9972
10006
 
9973
10007
  // src/hooks/useSceneTransition.ts
9974
- import { useState as useState13, useCallback as useCallback7, useRef as useRef24, useEffect as useEffect60 } from "react";
10008
+ import { useState as useState13, useCallback as useCallback7, useRef as useRef25, useEffect as useEffect60 } from "react";
9975
10009
  function useSceneTransition(initialScene, defaultTransition) {
9976
10010
  const [stack, setStack] = useState13([initialScene]);
9977
10011
  const [transState, setTransState] = useState13({
@@ -9980,9 +10014,9 @@ function useSceneTransition(initialScene, defaultTransition) {
9980
10014
  effect: null,
9981
10015
  pendingAction: null
9982
10016
  });
9983
- const stackRef = useRef24(stack);
10017
+ const stackRef = useRef25(stack);
9984
10018
  stackRef.current = stack;
9985
- const transRef = useRef24(transState);
10019
+ const transRef = useRef25(transState);
9986
10020
  transRef.current = transState;
9987
10021
  useEffect60(() => {
9988
10022
  if (transState.phase === "idle") return;
@@ -10171,9 +10205,9 @@ function useInputBuffer(opts) {
10171
10205
  }
10172
10206
 
10173
10207
  // src/hooks/useComboDetector.ts
10174
- import { useMemo as useMemo10, useRef as useRef25 } from "react";
10208
+ import { useMemo as useMemo10, useRef as useRef26 } from "react";
10175
10209
  function useComboDetector(combos) {
10176
- const lastComboRef = useRef25(null);
10210
+ const lastComboRef = useRef26(null);
10177
10211
  const result = useMemo10(() => {
10178
10212
  const detector = new ComboDetector({ combos });
10179
10213
  const api = {
@@ -10237,17 +10271,17 @@ function useAccessibility() {
10237
10271
  }
10238
10272
 
10239
10273
  // src/hooks/useHMR.ts
10240
- import { useEffect as useEffect62, useRef as useRef26, useMemo as useMemo11 } from "react";
10274
+ import { useEffect as useEffect62, useRef as useRef27, useMemo as useMemo11 } from "react";
10241
10275
  var idCounter = 0;
10242
10276
  function useHMR(hmrKey) {
10243
- const idRef = useRef26(null);
10277
+ const idRef = useRef27(null);
10244
10278
  if (idRef.current === null) {
10245
10279
  idRef.current = hmrKey ?? `__hmr_${idCounter++}`;
10246
10280
  }
10247
10281
  const key = idRef.current;
10248
10282
  const isHotReload = hmrLoadState(key) !== void 0;
10249
- const disposeRef = useRef26(null);
10250
- const acceptRef = useRef26(null);
10283
+ const disposeRef = useRef27(null);
10284
+ const acceptRef = useRef27(null);
10251
10285
  useEffect62(() => {
10252
10286
  const hot = import.meta.hot;
10253
10287
  if (!hot) return;
@@ -10298,13 +10332,13 @@ function useSquashStretch() {
10298
10332
  }
10299
10333
 
10300
10334
  // src/hooks/useHistory.ts
10301
- import { useCallback as useCallback11, useContext as useContext54, useEffect as useEffect63, useRef as useRef27, useState as useState14 } from "react";
10335
+ import { useCallback as useCallback11, useContext as useContext54, useEffect as useEffect63, useRef as useRef28, useState as useState14 } from "react";
10302
10336
  function useHistory(options) {
10303
10337
  const engine = useContext54(EngineContext);
10304
10338
  const capacity = options?.capacity ?? 50;
10305
10339
  const bindKeys = options?.bindKeyboardShortcuts ?? false;
10306
- const stackRef = useRef27([]);
10307
- const indexRef = useRef27(-1);
10340
+ const stackRef = useRef28([]);
10341
+ const indexRef = useRef28(-1);
10308
10342
  const [version, setVersion] = useState14(0);
10309
10343
  const bump = useCallback11(() => setVersion((v) => v + 1), []);
10310
10344
  const push = useCallback11(() => {
@@ -10438,7 +10472,7 @@ function useSelection() {
10438
10472
  }
10439
10473
 
10440
10474
  // src/components/TransformHandles.tsx
10441
- import { useContext as useContext56, useEffect as useEffect66, useRef as useRef28 } from "react";
10475
+ import { useContext as useContext56, useEffect as useEffect66, useRef as useRef29 } from "react";
10442
10476
 
10443
10477
  // src/hooks/useOverlayTick.ts
10444
10478
  import { useEffect as useEffect65 } from "react";
@@ -10480,8 +10514,8 @@ function TransformHandles({
10480
10514
  }) {
10481
10515
  const engine = useContext56(EngineContext);
10482
10516
  const selection = useSelection();
10483
- const overlayRef = useRef28(null);
10484
- const dragRef = useRef28(null);
10517
+ const overlayRef = useRef29(null);
10518
+ const dragRef = useRef29(null);
10485
10519
  useOverlayTick(() => {
10486
10520
  if (!engine) return;
10487
10521
  const overlay = overlayRef.current;
@@ -10831,7 +10865,7 @@ function getBounds(ecs, id) {
10831
10865
  }
10832
10866
 
10833
10867
  // src/components/EditableText.tsx
10834
- import { useContext as useContext58, useEffect as useEffect67, useRef as useRef29 } from "react";
10868
+ import { useContext as useContext58, useEffect as useEffect67, useRef as useRef30 } from "react";
10835
10869
  import { jsx as jsx17 } from "react/jsx-runtime";
10836
10870
  function EditableText({
10837
10871
  value,
@@ -10855,8 +10889,8 @@ function EditableText({
10855
10889
  }) {
10856
10890
  const engine = useContext58(EngineContext);
10857
10891
  const entityId = useContext58(EntityContext);
10858
- const containerRef = useRef29(null);
10859
- const inputRef = useRef29(null);
10892
+ const containerRef = useRef30(null);
10893
+ const inputRef = useRef30(null);
10860
10894
  useOverlayTick(() => {
10861
10895
  if (!engine || entityId === null || entityId === void 0) return;
10862
10896
  const container = containerRef.current;
@@ -11014,7 +11048,7 @@ function copyRegion(source, region, scale) {
11014
11048
  }
11015
11049
 
11016
11050
  // src/components/A11yNode.tsx
11017
- import { useContext as useContext59, useRef as useRef30 } from "react";
11051
+ import { useContext as useContext59, useRef as useRef31 } from "react";
11018
11052
  import { jsx as jsx18 } from "react/jsx-runtime";
11019
11053
  function A11yNode({
11020
11054
  label,
@@ -11030,7 +11064,7 @@ function A11yNode({
11030
11064
  }) {
11031
11065
  const engine = useContext59(EngineContext);
11032
11066
  const entityId = useContext59(EntityContext);
11033
- const nodeRef = useRef30(null);
11067
+ const nodeRef = useRef31(null);
11034
11068
  useOverlayTick(() => {
11035
11069
  if (!engine || entityId === null || entityId === void 0) return;
11036
11070
  const node = nodeRef.current;
@@ -11119,7 +11153,7 @@ function worldToScreenCss3(engine, wx, wy) {
11119
11153
  }
11120
11154
 
11121
11155
  // src/components/VectorPath.tsx
11122
- import { useContext as useContext60, useRef as useRef31 } from "react";
11156
+ import { useContext as useContext60, useRef as useRef32 } from "react";
11123
11157
  import { jsx as jsx19 } from "react/jsx-runtime";
11124
11158
  function VectorPath({
11125
11159
  d,
@@ -11135,7 +11169,7 @@ function VectorPath({
11135
11169
  }) {
11136
11170
  const engine = useContext60(EngineContext);
11137
11171
  const entityId = useContext60(EntityContext);
11138
- const svgRef = useRef31(null);
11172
+ const svgRef = useRef32(null);
11139
11173
  useOverlayTick(() => {
11140
11174
  if (!engine || entityId === null || entityId === void 0) return;
11141
11175
  const svg = svgRef.current;
@@ -11196,12 +11230,12 @@ function worldToScreenCss4(engine, wx, wy) {
11196
11230
  }
11197
11231
 
11198
11232
  // src/hooks/useGrid.ts
11199
- import { useCallback as useCallback14, useContext as useContext61, useMemo as useMemo13, useRef as useRef32, useState as useState16 } from "react";
11233
+ import { useCallback as useCallback14, useContext as useContext61, useMemo as useMemo13, useRef as useRef33, useState as useState16 } from "react";
11200
11234
  function useGrid({ width, height, fill }) {
11201
11235
  const engine = useContext61(EngineContext);
11202
11236
  const [version, setVersion] = useState16(0);
11203
11237
  const bump = useCallback14(() => setVersion((v) => v + 1), []);
11204
- const dataRef = useRef32(null);
11238
+ const dataRef = useRef33(null);
11205
11239
  if (dataRef.current === null) {
11206
11240
  const arr = new Array(width * height);
11207
11241
  if (typeof fill === "function") {
@@ -11381,7 +11415,7 @@ function useGrid({ width, height, fill }) {
11381
11415
  }
11382
11416
 
11383
11417
  // src/hooks/useTurnSystem.ts
11384
- import { useCallback as useCallback15, useContext as useContext62, useEffect as useEffect68, useMemo as useMemo14, useRef as useRef33, useState as useState17 } from "react";
11418
+ import { useCallback as useCallback15, useContext as useContext62, useEffect as useEffect68, useMemo as useMemo14, useRef as useRef34, useState as useState17 } from "react";
11385
11419
  function useTurnSystem({
11386
11420
  players,
11387
11421
  initialIndex = 0,
@@ -11394,10 +11428,10 @@ function useTurnSystem({
11394
11428
  const [activeIndex, setActiveIndex] = useState17(initialIndex % players.length);
11395
11429
  const [turn, setTurn] = useState17(0);
11396
11430
  const [isPending, setIsPending] = useState17(false);
11397
- const pendingRafId = useRef33(null);
11398
- const pendingRemaining = useRef33(0);
11399
- const pendingTarget = useRef33(null);
11400
- const pendingLastTime = useRef33(0);
11431
+ const pendingRafId = useRef34(null);
11432
+ const pendingRemaining = useRef34(0);
11433
+ const pendingTarget = useRef34(null);
11434
+ const pendingLastTime = useRef34(0);
11401
11435
  const clearPending = useCallback15(() => {
11402
11436
  if (pendingRafId.current !== null) {
11403
11437
  cancelAnimationFrame(pendingRafId.current);
@@ -11406,7 +11440,7 @@ function useTurnSystem({
11406
11440
  pendingTarget.current = null;
11407
11441
  setIsPending(false);
11408
11442
  }, []);
11409
- const startedOnce = useRef33(false);
11443
+ const startedOnce = useRef34(false);
11410
11444
  useEffect68(() => {
11411
11445
  if (startedOnce.current) return;
11412
11446
  startedOnce.current = true;
@@ -11583,7 +11617,7 @@ function screenToWorld(engine, cssX, cssY) {
11583
11617
  }
11584
11618
 
11585
11619
  // src/hooks/useDragDrop.ts
11586
- import { useContext as useContext64, useEffect as useEffect70, useRef as useRef34, useState as useState19 } from "react";
11620
+ import { useContext as useContext64, useEffect as useEffect70, useRef as useRef35, useState as useState19 } from "react";
11587
11621
  var dragStateByEngine = /* @__PURE__ */ new WeakMap();
11588
11622
  function getActiveDrag(engine) {
11589
11623
  return dragStateByEngine.get(engine) ?? null;
@@ -11608,7 +11642,7 @@ function useDraggable(options) {
11608
11642
  const entityId = options?.entityId ?? ctxEntityId;
11609
11643
  const [isDragging, setIsDragging] = useState19(false);
11610
11644
  const [position, setPosition] = useState19({ x: 0, y: 0 });
11611
- const optsRef = useRef34(options);
11645
+ const optsRef = useRef35(options);
11612
11646
  optsRef.current = options;
11613
11647
  useEffect70(() => {
11614
11648
  if (!engine || entityId === null || entityId === void 0 || options?.disabled) return;
@@ -11720,7 +11754,7 @@ function useDroppable(options) {
11720
11754
  const entityId = options?.entityId ?? ctxEntityId;
11721
11755
  const [isOver, setIsOver] = useState19(false);
11722
11756
  const [hoveredBy, setHoveredBy] = useState19(null);
11723
- const optsRef = useRef34(options);
11757
+ const optsRef = useRef35(options);
11724
11758
  optsRef.current = options;
11725
11759
  useEffect70(() => {
11726
11760
  if (!engine || entityId === null || entityId === void 0 || options?.disabled) return;
@@ -11824,7 +11858,7 @@ function findDroppableUnder(engine, drag) {
11824
11858
  }
11825
11859
 
11826
11860
  // src/hooks/useKeyboardFocus.ts
11827
- import { useContext as useContext65, useEffect as useEffect71, useState as useState20 } from "react";
11861
+ import { useContext as useContext65, useEffect as useEffect71, useRef as useRef36, useState as useState20 } from "react";
11828
11862
  var registry = /* @__PURE__ */ new Map();
11829
11863
  var focusedId = null;
11830
11864
  var subscribers = /* @__PURE__ */ new Set();
@@ -11841,17 +11875,19 @@ function setFocused(id) {
11841
11875
  }
11842
11876
  function useFocusable(options) {
11843
11877
  const entityIdCtx = useContext65(EntityContext);
11878
+ const optsRef = useRef36(options);
11879
+ optsRef.current = options;
11844
11880
  useEffect71(() => {
11845
11881
  if (entityIdCtx === null || entityIdCtx === void 0) return;
11846
11882
  const id = entityIdCtx;
11847
11883
  const entry = {
11848
11884
  entityId: id,
11849
- onFocus: options?.onFocus,
11850
- onBlur: options?.onBlur,
11851
- onActivate: options?.onActivate
11885
+ onFocus: (eid) => optsRef.current?.onFocus?.(eid),
11886
+ onBlur: (eid) => optsRef.current?.onBlur?.(eid),
11887
+ onActivate: (eid) => optsRef.current?.onActivate?.(eid)
11852
11888
  };
11853
11889
  registry.set(id, entry);
11854
- if (options?.autoFocus && focusedId === null) setFocused(id);
11890
+ if (optsRef.current?.autoFocus && focusedId === null) setFocused(id);
11855
11891
  return () => {
11856
11892
  registry.delete(id);
11857
11893
  if (focusedId === id) setFocused(null);
@@ -11994,7 +12030,7 @@ function subscribeFocus(cb) {
11994
12030
  }
11995
12031
 
11996
12032
  // src/components/FocusRing.tsx
11997
- import { useContext as useContext66, useEffect as useEffect72, useRef as useRef35, useState as useState21 } from "react";
12033
+ import { useContext as useContext66, useEffect as useEffect72, useRef as useRef37, useState as useState21 } from "react";
11998
12034
  import { Fragment as Fragment9, jsx as jsx20, jsxs as jsxs9 } from "react/jsx-runtime";
11999
12035
  function FocusRing({
12000
12036
  color = "#4fc3f7",
@@ -12004,7 +12040,7 @@ function FocusRing({
12004
12040
  pulse = true
12005
12041
  }) {
12006
12042
  const engine = useContext66(EngineContext);
12007
- const ringRef = useRef35(null);
12043
+ const ringRef = useRef37(null);
12008
12044
  const [focused, setFocused2] = useState21(getFocusedEntityId());
12009
12045
  const [reducedMotion, setReducedMotion] = useState21(false);
12010
12046
  useEffect72(() => {
@@ -12248,12 +12284,12 @@ function useDropThrough(frames = 8) {
12248
12284
  }
12249
12285
 
12250
12286
  // ../gameplay/src/hooks/useGameStateMachine.ts
12251
- import { useState as useState23, useRef as useRef36, useCallback as useCallback19, useEffect as useEffect73, useContext as useContext69 } from "react";
12287
+ import { useState as useState23, useRef as useRef38, useCallback as useCallback19, useEffect as useEffect73, useContext as useContext69 } from "react";
12252
12288
  function useGameStateMachine(states, initial) {
12253
12289
  const engine = useContext69(EngineContext);
12254
12290
  const [state, setState] = useState23(initial);
12255
- const stateRef = useRef36(initial);
12256
- const statesRef = useRef36(states);
12291
+ const stateRef = useRef38(initial);
12292
+ const statesRef = useRef38(states);
12257
12293
  statesRef.current = states;
12258
12294
  useEffect73(() => {
12259
12295
  statesRef.current[initial]?.onEnter?.();
@@ -12282,22 +12318,22 @@ function useGameStateMachine(states, initial) {
12282
12318
  }
12283
12319
 
12284
12320
  // ../gameplay/src/hooks/useHealth.ts
12285
- import { useRef as useRef37, useEffect as useEffect74, useContext as useContext70, useCallback as useCallback20 } from "react";
12321
+ import { useRef as useRef39, useEffect as useEffect74, useContext as useContext70, useCallback as useCallback20 } from "react";
12286
12322
  function useHealth(maxHp, opts = {}) {
12287
12323
  const engine = useContext70(EngineContext);
12288
12324
  const entityId = useContext70(EntityContext);
12289
- const hpRef = useRef37(maxHp);
12290
- const invincibleRef = useRef37(false);
12325
+ const hpRef = useRef39(maxHp);
12326
+ const invincibleRef = useRef39(false);
12291
12327
  const iFrameDuration = opts.iFrames ?? 1;
12292
- const onDeathRef = useRef37(opts.onDeath);
12293
- const onDamageRef = useRef37(opts.onDamage);
12328
+ const onDeathRef = useRef39(opts.onDeath);
12329
+ const onDamageRef = useRef39(opts.onDamage);
12294
12330
  useEffect74(() => {
12295
12331
  onDeathRef.current = opts.onDeath;
12296
12332
  });
12297
12333
  useEffect74(() => {
12298
12334
  onDamageRef.current = opts.onDamage;
12299
12335
  });
12300
- const timerRef = useRef37(
12336
+ const timerRef = useRef39(
12301
12337
  createTimer(iFrameDuration, () => {
12302
12338
  invincibleRef.current = false;
12303
12339
  })
@@ -12315,7 +12351,7 @@ function useHealth(maxHp, opts = {}) {
12315
12351
  },
12316
12352
  [iFrameDuration]
12317
12353
  );
12318
- const takeDamageRef = useRef37(takeDamage);
12354
+ const takeDamageRef = useRef39(takeDamage);
12319
12355
  useEffect74(() => {
12320
12356
  takeDamageRef.current = takeDamage;
12321
12357
  }, [takeDamage]);
@@ -12421,11 +12457,11 @@ function useKinematicBody() {
12421
12457
  }
12422
12458
 
12423
12459
  // ../gameplay/src/hooks/useLevelTransition.ts
12424
- import { useState as useState24, useRef as useRef38, useCallback as useCallback22 } from "react";
12460
+ import { useState as useState24, useRef as useRef40, useCallback as useCallback22 } from "react";
12425
12461
  function useLevelTransition(initial) {
12426
12462
  const [currentLevel, setCurrentLevel] = useState24(initial);
12427
12463
  const [isTransitioning, setIsTransitioning] = useState24(false);
12428
- const overlayRef = useRef38(null);
12464
+ const overlayRef = useRef40(null);
12429
12465
  const transitionTo = useCallback22((level, opts = {}) => {
12430
12466
  const { duration = 0.4, type = "fade" } = opts;
12431
12467
  if (type === "instant") {
@@ -12630,10 +12666,10 @@ function useRestart() {
12630
12666
  }
12631
12667
 
12632
12668
  // ../gameplay/src/hooks/useSave.ts
12633
- import { useCallback as useCallback26, useRef as useRef39 } from "react";
12669
+ import { useCallback as useCallback26, useRef as useRef41 } from "react";
12634
12670
  function useSave(key, defaultValue, opts = {}) {
12635
12671
  const version = opts.version ?? 1;
12636
- const dataRef = useRef39(defaultValue);
12672
+ const dataRef = useRef41(defaultValue);
12637
12673
  const save = useCallback26(
12638
12674
  (value) => {
12639
12675
  dataRef.current = value;
@@ -12685,7 +12721,7 @@ function useSave(key, defaultValue, opts = {}) {
12685
12721
  }
12686
12722
 
12687
12723
  // ../gameplay/src/hooks/useIDBSave.ts
12688
- import { useCallback as useCallback27, useRef as useRef40 } from "react";
12724
+ import { useCallback as useCallback27, useRef as useRef42 } from "react";
12689
12725
  var DB_NAME = "cubeforge-saves";
12690
12726
  var STORE_NAME = "saves";
12691
12727
  function openDB() {
@@ -12703,7 +12739,7 @@ function openDB() {
12703
12739
  }
12704
12740
  function useIDBSave(key, defaultValue, opts = {}) {
12705
12741
  const version = opts.version ?? 1;
12706
- const dbRef = useRef40(null);
12742
+ const dbRef = useRef42(null);
12707
12743
  const getDB = useCallback27(() => {
12708
12744
  if (!dbRef.current) dbRef.current = openDB();
12709
12745
  return dbRef.current;
@@ -12818,11 +12854,11 @@ function useTopDownMovement(entityId, opts = {}) {
12818
12854
  }
12819
12855
 
12820
12856
  // ../gameplay/src/hooks/useDialogue.ts
12821
- import { useState as useState27, useCallback as useCallback28, useRef as useRef41 } from "react";
12857
+ import { useState as useState27, useCallback as useCallback28, useRef as useRef43 } from "react";
12822
12858
  function useDialogue() {
12823
12859
  const [active, setActive] = useState27(false);
12824
12860
  const [currentId, setCurrentId] = useState27(null);
12825
- const scriptRef = useRef41(null);
12861
+ const scriptRef = useRef43(null);
12826
12862
  const start2 = useCallback28((script, startId) => {
12827
12863
  scriptRef.current = script;
12828
12864
  const id = startId ?? Object.keys(script)[0];
@@ -12977,16 +13013,16 @@ function DialogueBox({ dialogue, onAdvance, onClose, style = {} }) {
12977
13013
  }
12978
13014
 
12979
13015
  // ../gameplay/src/hooks/useCutscene.ts
12980
- import { useState as useState28, useCallback as useCallback29, useRef as useRef42, useEffect as useEffect77, useContext as useContext75 } from "react";
13016
+ import { useState as useState28, useCallback as useCallback29, useRef as useRef44, useEffect as useEffect77, useContext as useContext75 } from "react";
12981
13017
  function useCutscene() {
12982
13018
  const engine = useContext75(EngineContext);
12983
13019
  const [playing, setPlaying] = useState28(false);
12984
13020
  const [stepIndex, setStepIndex] = useState28(0);
12985
- const stepsRef = useRef42([]);
12986
- const timerRef = useRef42(0);
12987
- const idxRef = useRef42(0);
12988
- const playingRef = useRef42(false);
12989
- const entityRef = useRef42(null);
13021
+ const stepsRef = useRef44([]);
13022
+ const timerRef = useRef44(0);
13023
+ const idxRef = useRef44(0);
13024
+ const playingRef = useRef44(false);
13025
+ const entityRef = useRef44(null);
12990
13026
  const finish = useCallback29(() => {
12991
13027
  playingRef.current = false;
12992
13028
  setPlaying(false);
@@ -13119,12 +13155,12 @@ function useGameStore(key, initialState) {
13119
13155
  }
13120
13156
 
13121
13157
  // ../gameplay/src/hooks/useTween.ts
13122
- import { useRef as useRef43, useCallback as useCallback31, useEffect as useEffect78 } from "react";
13158
+ import { useRef as useRef45, useCallback as useCallback31, useEffect as useEffect78 } from "react";
13123
13159
  function useTween(opts) {
13124
- const rafRef = useRef43(null);
13125
- const startTimeRef = useRef43(0);
13126
- const runningRef = useRef43(false);
13127
- const optsRef = useRef43(opts);
13160
+ const rafRef = useRef45(null);
13161
+ const startTimeRef = useRef45(0);
13162
+ const runningRef = useRef45(false);
13163
+ const optsRef = useRef45(opts);
13128
13164
  optsRef.current = opts;
13129
13165
  const stop = useCallback31(() => {
13130
13166
  if (rafRef.current !== null) {
@@ -13179,13 +13215,13 @@ function useTween(opts) {
13179
13215
  }
13180
13216
 
13181
13217
  // ../gameplay/src/hooks/useObjectPool.ts
13182
- import { useRef as useRef44, useMemo as useMemo16, useEffect as useEffect79 } from "react";
13218
+ import { useRef as useRef46, useMemo as useMemo16, useEffect as useEffect79 } from "react";
13183
13219
  function useObjectPool(factory, reset, initialSize) {
13184
- const poolRef = useRef44([]);
13185
- const activeRef = useRef44(0);
13186
- const factoryRef = useRef44(factory);
13220
+ const poolRef = useRef46([]);
13221
+ const activeRef = useRef46(0);
13222
+ const factoryRef = useRef46(factory);
13187
13223
  factoryRef.current = factory;
13188
- const resetRef = useRef44(reset);
13224
+ const resetRef = useRef46(reset);
13189
13225
  resetRef.current = reset;
13190
13226
  useEffect79(() => {
13191
13227
  if (initialSize != null && initialSize > 0) {
@@ -13351,7 +13387,7 @@ function definePrefab(name, defaults, render) {
13351
13387
  }
13352
13388
 
13353
13389
  // src/hooks/useNetworkSync.ts
13354
- import { useEffect as useEffect81, useRef as useRef46 } from "react";
13390
+ import { useEffect as useEffect81, useRef as useRef48 } from "react";
13355
13391
 
13356
13392
  // ../../packages/net/src/transport.ts
13357
13393
  function isBinaryTransport(t) {
@@ -13696,7 +13732,7 @@ function syncEntity(config) {
13696
13732
  }
13697
13733
 
13698
13734
  // ../../packages/net/src/useNetworkInput.ts
13699
- import { useState as useState29, useEffect as useEffect80, useRef as useRef45 } from "react";
13735
+ import { useState as useState29, useEffect as useEffect80, useRef as useRef47 } from "react";
13700
13736
  var INPUT_MSG_TYPE = "input:state";
13701
13737
  function useNetworkInput(config) {
13702
13738
  const { room, keys, input, tickRate = 20 } = config;
@@ -13705,7 +13741,7 @@ function useNetworkInput(config) {
13705
13741
  () => Object.fromEntries(keys.map((k) => [k, false]))
13706
13742
  );
13707
13743
  const [remoteInputs] = useState29(() => /* @__PURE__ */ new Map());
13708
- const localInputRef = useRef45(localInput);
13744
+ const localInputRef = useRef47(localInput);
13709
13745
  useEffect80(() => {
13710
13746
  let cleanupDom = null;
13711
13747
  if (!input) {
@@ -13920,7 +13956,7 @@ function lerpState(a, b, t) {
13920
13956
 
13921
13957
  // src/hooks/useNetworkSync.ts
13922
13958
  function useNetworkSync(entityId, components, room, world, owner, opts = {}) {
13923
- const optsRef = useRef46(opts);
13959
+ const optsRef = useRef48(opts);
13924
13960
  optsRef.current = opts;
13925
13961
  useEffect81(() => {
13926
13962
  const sync = syncEntity({
@@ -13937,16 +13973,16 @@ function useNetworkSync(entityId, components, room, world, owner, opts = {}) {
13937
13973
  }
13938
13974
 
13939
13975
  // src/hooks/useRemotePlayer.ts
13940
- import { useEffect as useEffect82, useRef as useRef47, useState as useState30 } from "react";
13976
+ import { useEffect as useEffect82, useRef as useRef49, useState as useState30 } from "react";
13941
13977
  var PEER_JOIN_MSG = "peer:join";
13942
13978
  var PEER_LEAVE_MSG = "peer:leave";
13943
13979
  function useRemotePlayer(config) {
13944
13980
  const { room, world, createEntity, destroyEntity } = config;
13945
13981
  const [players, setPlayers] = useState30(() => /* @__PURE__ */ new Map());
13946
- const playersRef = useRef47(players);
13947
- const createRef = useRef47(createEntity);
13982
+ const playersRef = useRef49(players);
13983
+ const createRef = useRef49(createEntity);
13948
13984
  createRef.current = createEntity;
13949
- const destroyRef = useRef47(destroyEntity);
13985
+ const destroyRef = useRef49(destroyEntity);
13950
13986
  destroyRef.current = destroyEntity;
13951
13987
  useEffect82(() => {
13952
13988
  function spawnPeer(peerId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cubeforge",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "React-first 2D browser game engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",