cubeforge 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +299 -235
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -137,6 +137,7 @@ var ECSWorld = class {
|
|
|
137
137
|
return id;
|
|
138
138
|
}
|
|
139
139
|
destroyEntity(id) {
|
|
140
|
+
if (!this.componentIndex.has(id)) return;
|
|
140
141
|
const comps = this.componentIndex.get(id);
|
|
141
142
|
if (comps) {
|
|
142
143
|
for (const type of comps.keys()) this.dirtyTypes.add(type);
|
|
@@ -152,10 +153,32 @@ var ECSWorld = class {
|
|
|
152
153
|
this.componentIndex.delete(id);
|
|
153
154
|
this.entityArchetype.delete(id);
|
|
154
155
|
this.dirtyAll = true;
|
|
156
|
+
if (this.destroyListeners.size > 0) {
|
|
157
|
+
for (const cb of Array.from(this.destroyListeners)) cb(id);
|
|
158
|
+
}
|
|
155
159
|
}
|
|
156
160
|
hasEntity(id) {
|
|
157
161
|
return this.componentIndex.has(id);
|
|
158
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Subscribe to entity-destroyed events. The callback fires after the entity
|
|
165
|
+
* has been removed from all archetypes and component storage. Returns an
|
|
166
|
+
* unsubscribe function.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* const off = ecs.onDestroyEntity((id) => console.log('gone', id))
|
|
171
|
+
* // ...
|
|
172
|
+
* off()
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
onDestroyEntity(cb) {
|
|
176
|
+
this.destroyListeners.add(cb);
|
|
177
|
+
return () => {
|
|
178
|
+
this.destroyListeners.delete(cb);
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
destroyListeners = /* @__PURE__ */ new Set();
|
|
159
182
|
addComponent(id, component) {
|
|
160
183
|
const comps = this.componentIndex.get(id);
|
|
161
184
|
if (!comps) return;
|
|
@@ -872,12 +895,12 @@ async function preloadManifest(manifest, assets) {
|
|
|
872
895
|
return;
|
|
873
896
|
}
|
|
874
897
|
let done = 0;
|
|
875
|
-
const
|
|
898
|
+
const tick2 = () => {
|
|
876
899
|
done++;
|
|
877
900
|
manifest.onProgress?.(done / total);
|
|
878
901
|
};
|
|
879
|
-
const imageLoads = imageUrls.map((src) => assets.loadImage(src).then(
|
|
880
|
-
const audioLoads = audioUrls.map((src) => assets.loadAudio(src).then(
|
|
902
|
+
const imageLoads = imageUrls.map((src) => assets.loadImage(src).then(tick2, tick2));
|
|
903
|
+
const audioLoads = audioUrls.map((src) => assets.loadAudio(src).then(tick2, tick2));
|
|
881
904
|
await Promise.allSettled([...imageLoads, ...audioLoads]);
|
|
882
905
|
}
|
|
883
906
|
|
|
@@ -915,8 +938,8 @@ function heuristic(ac, ar, bc, br) {
|
|
|
915
938
|
const dr = Math.abs(ar - br);
|
|
916
939
|
return dc + dr + (Math.SQRT2 - 2) * Math.min(dc, dr);
|
|
917
940
|
}
|
|
918
|
-
function findPath(grid,
|
|
919
|
-
const sc = worldToCell(grid,
|
|
941
|
+
function findPath(grid, start2, goal) {
|
|
942
|
+
const sc = worldToCell(grid, start2.x, start2.y);
|
|
920
943
|
const gc = worldToCell(grid, goal.x, goal.y);
|
|
921
944
|
if (!isWalkable(grid, sc.col, sc.row) || !isWalkable(grid, gc.col, gc.row)) {
|
|
922
945
|
return [];
|
|
@@ -1340,9 +1363,9 @@ function tween(from, to, duration, ease = Ease.linear, onUpdate, onComplete, opt
|
|
|
1340
1363
|
}
|
|
1341
1364
|
const t = duration > 0 ? elapsed / duration : 1;
|
|
1342
1365
|
const easedT = ease(t);
|
|
1343
|
-
const
|
|
1366
|
+
const start2 = forward ? from : to;
|
|
1344
1367
|
const end = forward ? to : from;
|
|
1345
|
-
onUpdate(
|
|
1368
|
+
onUpdate(start2 + (end - start2) * easedT);
|
|
1346
1369
|
}
|
|
1347
1370
|
};
|
|
1348
1371
|
}
|
|
@@ -1382,10 +1405,10 @@ function createTimeline(opts) {
|
|
|
1382
1405
|
}
|
|
1383
1406
|
const timeline = {
|
|
1384
1407
|
add(entry, addOpts) {
|
|
1385
|
-
const
|
|
1408
|
+
const start2 = resolveStartTime(addOpts);
|
|
1386
1409
|
const delay = addOpts?.delay ?? 0;
|
|
1387
|
-
segments.push({ startTime:
|
|
1388
|
-
cursor =
|
|
1410
|
+
segments.push({ startTime: start2, delay, entry, started: false, complete: false });
|
|
1411
|
+
cursor = start2 + delay + entry.duration;
|
|
1389
1412
|
return timeline;
|
|
1390
1413
|
},
|
|
1391
1414
|
addLabel(name) {
|
|
@@ -1393,12 +1416,12 @@ function createTimeline(opts) {
|
|
|
1393
1416
|
return timeline;
|
|
1394
1417
|
},
|
|
1395
1418
|
addParallel(entries, addOpts) {
|
|
1396
|
-
const
|
|
1419
|
+
const start2 = resolveStartTime(addOpts);
|
|
1397
1420
|
const delay = addOpts?.delay ?? 0;
|
|
1398
1421
|
let maxEnd = cursor;
|
|
1399
1422
|
for (const entry of entries) {
|
|
1400
|
-
segments.push({ startTime:
|
|
1401
|
-
const end =
|
|
1423
|
+
segments.push({ startTime: start2, delay, entry, started: false, complete: false });
|
|
1424
|
+
const end = start2 + delay + entry.duration;
|
|
1402
1425
|
if (end > maxEnd) maxEnd = end;
|
|
1403
1426
|
}
|
|
1404
1427
|
cursor = maxEnd;
|
|
@@ -5410,7 +5433,7 @@ function useAudioScheduler(opts) {
|
|
|
5410
5433
|
nextBeatIndexRef.current++;
|
|
5411
5434
|
}
|
|
5412
5435
|
};
|
|
5413
|
-
const
|
|
5436
|
+
const start2 = () => {
|
|
5414
5437
|
if (isRunningRef.current) return;
|
|
5415
5438
|
const ctx = getAudioCtx();
|
|
5416
5439
|
if (ctx.state === "suspended") void ctx.resume();
|
|
@@ -5438,7 +5461,7 @@ function useAudioScheduler(opts) {
|
|
|
5438
5461
|
return () => barHandlers.current.delete(handler);
|
|
5439
5462
|
};
|
|
5440
5463
|
return {
|
|
5441
|
-
start,
|
|
5464
|
+
start: start2,
|
|
5442
5465
|
stop,
|
|
5443
5466
|
onBeat,
|
|
5444
5467
|
onBar,
|
|
@@ -9031,7 +9054,7 @@ function TriangleCollider({
|
|
|
9031
9054
|
// src/components/SegmentCollider.tsx
|
|
9032
9055
|
import { useEffect as useEffect44, useContext as useContext34 } from "react";
|
|
9033
9056
|
function SegmentCollider({
|
|
9034
|
-
start,
|
|
9057
|
+
start: start2,
|
|
9035
9058
|
end,
|
|
9036
9059
|
isTrigger = false,
|
|
9037
9060
|
layer = "default",
|
|
@@ -9048,7 +9071,7 @@ function SegmentCollider({
|
|
|
9048
9071
|
useEffect44(() => {
|
|
9049
9072
|
engine.ecs.addComponent(
|
|
9050
9073
|
entityId,
|
|
9051
|
-
createSegmentCollider(
|
|
9074
|
+
createSegmentCollider(start2, end, {
|
|
9052
9075
|
isTrigger,
|
|
9053
9076
|
layer,
|
|
9054
9077
|
mask,
|
|
@@ -9393,7 +9416,7 @@ function useWorldQuery(...components) {
|
|
|
9393
9416
|
const mountedRef = useRef17(true);
|
|
9394
9417
|
useEffect50(() => {
|
|
9395
9418
|
mountedRef.current = true;
|
|
9396
|
-
const
|
|
9419
|
+
const tick2 = () => {
|
|
9397
9420
|
if (!mountedRef.current) return;
|
|
9398
9421
|
const next = engine.ecs.query(...components);
|
|
9399
9422
|
const prev = prevRef.current;
|
|
@@ -9401,9 +9424,9 @@ function useWorldQuery(...components) {
|
|
|
9401
9424
|
prevRef.current = next;
|
|
9402
9425
|
setResult([...next]);
|
|
9403
9426
|
}
|
|
9404
|
-
rafRef.current = requestAnimationFrame(
|
|
9427
|
+
rafRef.current = requestAnimationFrame(tick2);
|
|
9405
9428
|
};
|
|
9406
|
-
rafRef.current = requestAnimationFrame(
|
|
9429
|
+
rafRef.current = requestAnimationFrame(tick2);
|
|
9407
9430
|
return () => {
|
|
9408
9431
|
mountedRef.current = false;
|
|
9409
9432
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -9518,7 +9541,7 @@ function useProfiler() {
|
|
|
9518
9541
|
const prevTimeRef = useRef19(0);
|
|
9519
9542
|
useEffect53(() => {
|
|
9520
9543
|
if (!engine) return;
|
|
9521
|
-
let
|
|
9544
|
+
let rafId2;
|
|
9522
9545
|
const frameTimes = frameTimesRef.current;
|
|
9523
9546
|
const sample = (now) => {
|
|
9524
9547
|
if (prevTimeRef.current > 0) {
|
|
@@ -9542,11 +9565,11 @@ function useProfiler() {
|
|
|
9542
9565
|
systemTimings: new Map(engine.systemTimings)
|
|
9543
9566
|
});
|
|
9544
9567
|
}
|
|
9545
|
-
|
|
9568
|
+
rafId2 = requestAnimationFrame(sample);
|
|
9546
9569
|
};
|
|
9547
|
-
|
|
9570
|
+
rafId2 = requestAnimationFrame(sample);
|
|
9548
9571
|
return () => {
|
|
9549
|
-
cancelAnimationFrame(
|
|
9572
|
+
cancelAnimationFrame(rafId2);
|
|
9550
9573
|
frameTimes.length = 0;
|
|
9551
9574
|
prevTimeRef.current = 0;
|
|
9552
9575
|
lastUpdateRef.current = 0;
|
|
@@ -9802,7 +9825,7 @@ function useTimer(duration, onComplete, opts) {
|
|
|
9802
9825
|
if (engine.ecs.hasEntity(eid)) engine.ecs.destroyEntity(eid);
|
|
9803
9826
|
};
|
|
9804
9827
|
}, [engine.ecs]);
|
|
9805
|
-
const
|
|
9828
|
+
const start2 = useCallback5(() => {
|
|
9806
9829
|
timerRef.current.start();
|
|
9807
9830
|
}, []);
|
|
9808
9831
|
const stop = useCallback5(() => {
|
|
@@ -9812,7 +9835,7 @@ function useTimer(duration, onComplete, opts) {
|
|
|
9812
9835
|
timerRef.current.reset();
|
|
9813
9836
|
}, []);
|
|
9814
9837
|
return {
|
|
9815
|
-
start,
|
|
9838
|
+
start: start2,
|
|
9816
9839
|
stop,
|
|
9817
9840
|
reset,
|
|
9818
9841
|
get isRunning() {
|
|
@@ -9966,16 +9989,16 @@ function useSceneTransition(initialScene, defaultTransition) {
|
|
|
9966
9989
|
const effect = transState.effect;
|
|
9967
9990
|
if (!effect || effect.type === "instant") return;
|
|
9968
9991
|
const duration = (effect.duration ?? 0.4) / 2;
|
|
9969
|
-
let
|
|
9970
|
-
let
|
|
9992
|
+
let rafId2;
|
|
9993
|
+
let start2 = performance.now();
|
|
9971
9994
|
const animate = (now) => {
|
|
9972
|
-
const elapsed = (now -
|
|
9995
|
+
const elapsed = (now - start2) / 1e3;
|
|
9973
9996
|
const t = Math.min(elapsed / duration, 1);
|
|
9974
9997
|
if (transRef.current.phase === "out") {
|
|
9975
9998
|
setTransState((prev) => ({ ...prev, progress: t }));
|
|
9976
9999
|
if (t >= 1) {
|
|
9977
10000
|
transRef.current.pendingAction?.();
|
|
9978
|
-
|
|
10001
|
+
start2 = performance.now();
|
|
9979
10002
|
setTransState((prev) => ({ ...prev, phase: "in", progress: 1, pendingAction: null }));
|
|
9980
10003
|
}
|
|
9981
10004
|
} else if (transRef.current.phase === "in") {
|
|
@@ -9985,10 +10008,10 @@ function useSceneTransition(initialScene, defaultTransition) {
|
|
|
9985
10008
|
return;
|
|
9986
10009
|
}
|
|
9987
10010
|
}
|
|
9988
|
-
|
|
10011
|
+
rafId2 = requestAnimationFrame(animate);
|
|
9989
10012
|
};
|
|
9990
|
-
|
|
9991
|
-
return () => cancelAnimationFrame(
|
|
10013
|
+
rafId2 = requestAnimationFrame(animate);
|
|
10014
|
+
return () => cancelAnimationFrame(rafId2);
|
|
9992
10015
|
}, [transState.phase, transState.effect]);
|
|
9993
10016
|
const startTransition = useCallback7(
|
|
9994
10017
|
(effect, action) => {
|
|
@@ -10340,11 +10363,23 @@ function useHistory(options) {
|
|
|
10340
10363
|
}
|
|
10341
10364
|
|
|
10342
10365
|
// src/hooks/useSelection.tsx
|
|
10343
|
-
import { createContext as createContext2, useCallback as useCallback12, useContext as useContext55, useMemo as useMemo12, useState as useState15 } from "react";
|
|
10366
|
+
import { createContext as createContext2, useCallback as useCallback12, useContext as useContext55, useEffect as useEffect64, useMemo as useMemo12, useState as useState15 } from "react";
|
|
10344
10367
|
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
10345
10368
|
var SelectionContext = createContext2(null);
|
|
10346
10369
|
function Selection({ initial, onChange, children }) {
|
|
10370
|
+
const engine = useContext55(EngineContext);
|
|
10347
10371
|
const [selected, setSelected] = useState15(initial ?? []);
|
|
10372
|
+
useEffect64(() => {
|
|
10373
|
+
if (!engine) return;
|
|
10374
|
+
return engine.ecs.onDestroyEntity((destroyedId) => {
|
|
10375
|
+
setSelected((prev) => {
|
|
10376
|
+
if (!prev.includes(destroyedId)) return prev;
|
|
10377
|
+
const next = prev.filter((id) => id !== destroyedId);
|
|
10378
|
+
onChange?.(next);
|
|
10379
|
+
return next;
|
|
10380
|
+
});
|
|
10381
|
+
});
|
|
10382
|
+
}, [engine, onChange]);
|
|
10348
10383
|
const commit = useCallback12(
|
|
10349
10384
|
(next) => {
|
|
10350
10385
|
setSelected(next);
|
|
@@ -10403,7 +10438,38 @@ function useSelection() {
|
|
|
10403
10438
|
}
|
|
10404
10439
|
|
|
10405
10440
|
// src/components/TransformHandles.tsx
|
|
10406
|
-
import { useContext as useContext56, useEffect as
|
|
10441
|
+
import { useContext as useContext56, useEffect as useEffect66, useRef as useRef28 } from "react";
|
|
10442
|
+
|
|
10443
|
+
// src/hooks/useOverlayTick.ts
|
|
10444
|
+
import { useEffect as useEffect65 } from "react";
|
|
10445
|
+
var callbacks = /* @__PURE__ */ new Set();
|
|
10446
|
+
var rafId = 0;
|
|
10447
|
+
function tick() {
|
|
10448
|
+
for (const cb of callbacks) cb();
|
|
10449
|
+
if (callbacks.size > 0) rafId = requestAnimationFrame(tick);
|
|
10450
|
+
else rafId = 0;
|
|
10451
|
+
}
|
|
10452
|
+
function start() {
|
|
10453
|
+
if (rafId === 0) rafId = requestAnimationFrame(tick);
|
|
10454
|
+
}
|
|
10455
|
+
function register(cb) {
|
|
10456
|
+
callbacks.add(cb);
|
|
10457
|
+
start();
|
|
10458
|
+
return () => {
|
|
10459
|
+
callbacks.delete(cb);
|
|
10460
|
+
if (callbacks.size === 0 && rafId !== 0) {
|
|
10461
|
+
cancelAnimationFrame(rafId);
|
|
10462
|
+
rafId = 0;
|
|
10463
|
+
}
|
|
10464
|
+
};
|
|
10465
|
+
}
|
|
10466
|
+
function useOverlayTick(callback, deps = []) {
|
|
10467
|
+
useEffect65(() => {
|
|
10468
|
+
return register(callback);
|
|
10469
|
+
}, deps);
|
|
10470
|
+
}
|
|
10471
|
+
|
|
10472
|
+
// src/components/TransformHandles.tsx
|
|
10407
10473
|
import { Fragment as Fragment8, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
10408
10474
|
function TransformHandles({
|
|
10409
10475
|
showRotationHandle = true,
|
|
@@ -10416,47 +10482,41 @@ function TransformHandles({
|
|
|
10416
10482
|
const selection = useSelection();
|
|
10417
10483
|
const overlayRef = useRef28(null);
|
|
10418
10484
|
const dragRef = useRef28(null);
|
|
10419
|
-
|
|
10485
|
+
useOverlayTick(() => {
|
|
10420
10486
|
if (!engine) return;
|
|
10421
10487
|
const overlay = overlayRef.current;
|
|
10422
10488
|
if (!overlay) return;
|
|
10423
10489
|
const canvas = engine.canvas;
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
overlay.
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
node.style.transform = `translate(-50%, -50%) rotate(${t.rotation}rad)`;
|
|
10453
|
-
}
|
|
10454
|
-
rafId = requestAnimationFrame(tick);
|
|
10455
|
-
};
|
|
10456
|
-
rafId = requestAnimationFrame(tick);
|
|
10457
|
-
return () => cancelAnimationFrame(rafId);
|
|
10490
|
+
const rect = canvas.getBoundingClientRect();
|
|
10491
|
+
overlay.style.left = `${rect.left + window.scrollX}px`;
|
|
10492
|
+
overlay.style.top = `${rect.top + window.scrollY}px`;
|
|
10493
|
+
overlay.style.width = `${rect.width}px`;
|
|
10494
|
+
overlay.style.height = `${rect.height}px`;
|
|
10495
|
+
for (const id of selection.selected) {
|
|
10496
|
+
const node = overlay.querySelector(`[data-selection-id="${id}"]`);
|
|
10497
|
+
if (!node) continue;
|
|
10498
|
+
const bounds = getEntityBounds(engine.ecs, id);
|
|
10499
|
+
const t = engine.ecs.getComponent(id, "Transform");
|
|
10500
|
+
if (!bounds || !t) {
|
|
10501
|
+
node.style.display = "none";
|
|
10502
|
+
continue;
|
|
10503
|
+
}
|
|
10504
|
+
const screen = worldToScreenCss(engine, t.x, t.y);
|
|
10505
|
+
if (!screen) {
|
|
10506
|
+
node.style.display = "none";
|
|
10507
|
+
continue;
|
|
10508
|
+
}
|
|
10509
|
+
const w = bounds.baseWidth * Math.abs(t.scaleX) * screen.zoom;
|
|
10510
|
+
const h = bounds.baseHeight * Math.abs(t.scaleY) * screen.zoom;
|
|
10511
|
+
node.style.display = "block";
|
|
10512
|
+
node.style.left = `${screen.x}px`;
|
|
10513
|
+
node.style.top = `${screen.y}px`;
|
|
10514
|
+
node.style.width = `${w}px`;
|
|
10515
|
+
node.style.height = `${h}px`;
|
|
10516
|
+
node.style.transform = `translate(-50%, -50%) rotate(${t.rotation}rad)`;
|
|
10517
|
+
}
|
|
10458
10518
|
}, [engine, selection.selected]);
|
|
10459
|
-
|
|
10519
|
+
useEffect66(() => {
|
|
10460
10520
|
if (!engine) return;
|
|
10461
10521
|
const onMove = (e) => {
|
|
10462
10522
|
const drag = dragRef.current;
|
|
@@ -10771,7 +10831,7 @@ function getBounds(ecs, id) {
|
|
|
10771
10831
|
}
|
|
10772
10832
|
|
|
10773
10833
|
// src/components/EditableText.tsx
|
|
10774
|
-
import { useContext as useContext58, useEffect as
|
|
10834
|
+
import { useContext as useContext58, useEffect as useEffect67, useRef as useRef29 } from "react";
|
|
10775
10835
|
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
10776
10836
|
function EditableText({
|
|
10777
10837
|
value,
|
|
@@ -10797,39 +10857,33 @@ function EditableText({
|
|
|
10797
10857
|
const entityId = useContext58(EntityContext);
|
|
10798
10858
|
const containerRef = useRef29(null);
|
|
10799
10859
|
const inputRef = useRef29(null);
|
|
10800
|
-
|
|
10860
|
+
useOverlayTick(() => {
|
|
10801
10861
|
if (!engine || entityId === null || entityId === void 0) return;
|
|
10802
10862
|
const container = containerRef.current;
|
|
10803
10863
|
if (!container) return;
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
const
|
|
10807
|
-
if (
|
|
10808
|
-
const
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
} else {
|
|
10821
|
-
container.style.display = "none";
|
|
10822
|
-
}
|
|
10864
|
+
const t = engine.ecs.getComponent(entityId, "Transform");
|
|
10865
|
+
if (t) {
|
|
10866
|
+
const screen = worldToScreenCss2(engine, t.x, t.y);
|
|
10867
|
+
if (screen) {
|
|
10868
|
+
const zoom = screen.zoom;
|
|
10869
|
+
const cssW = width * zoom;
|
|
10870
|
+
const cssH = height * zoom;
|
|
10871
|
+
container.style.display = "block";
|
|
10872
|
+
container.style.left = `${screen.x - cssW / 2}px`;
|
|
10873
|
+
container.style.top = `${screen.y - cssH / 2}px`;
|
|
10874
|
+
container.style.width = `${cssW}px`;
|
|
10875
|
+
container.style.height = `${cssH}px`;
|
|
10876
|
+
container.style.transform = `rotate(${t.rotation}rad)`;
|
|
10877
|
+
container.style.fontSize = `${fontSize * zoom}px`;
|
|
10878
|
+
} else {
|
|
10879
|
+
container.style.display = "none";
|
|
10823
10880
|
}
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
};
|
|
10829
|
-
rafId = requestAnimationFrame(tick);
|
|
10830
|
-
return () => cancelAnimationFrame(rafId);
|
|
10881
|
+
}
|
|
10882
|
+
const rect = engine.canvas.getBoundingClientRect();
|
|
10883
|
+
container.style.setProperty("--cubeforge-canvas-left", `${rect.left + window.scrollX}px`);
|
|
10884
|
+
container.style.setProperty("--cubeforge-canvas-top", `${rect.top + window.scrollY}px`);
|
|
10831
10885
|
}, [engine, entityId, width, height, fontSize]);
|
|
10832
|
-
|
|
10886
|
+
useEffect67(() => {
|
|
10833
10887
|
if (autoFocus) inputRef.current?.focus();
|
|
10834
10888
|
}, [autoFocus]);
|
|
10835
10889
|
if (!engine) return null;
|
|
@@ -10960,7 +11014,7 @@ function copyRegion(source, region, scale) {
|
|
|
10960
11014
|
}
|
|
10961
11015
|
|
|
10962
11016
|
// src/components/A11yNode.tsx
|
|
10963
|
-
import { useContext as useContext59,
|
|
11017
|
+
import { useContext as useContext59, useRef as useRef30 } from "react";
|
|
10964
11018
|
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
10965
11019
|
function A11yNode({
|
|
10966
11020
|
label,
|
|
@@ -10977,29 +11031,21 @@ function A11yNode({
|
|
|
10977
11031
|
const engine = useContext59(EngineContext);
|
|
10978
11032
|
const entityId = useContext59(EntityContext);
|
|
10979
11033
|
const nodeRef = useRef30(null);
|
|
10980
|
-
|
|
11034
|
+
useOverlayTick(() => {
|
|
10981
11035
|
if (!engine || entityId === null || entityId === void 0) return;
|
|
10982
11036
|
const node = nodeRef.current;
|
|
10983
11037
|
if (!node) return;
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
node.style.width = `${Math.max(w, 1)}px`;
|
|
10996
|
-
node.style.height = `${Math.max(h, 1)}px`;
|
|
10997
|
-
}
|
|
10998
|
-
}
|
|
10999
|
-
rafId = requestAnimationFrame(tick);
|
|
11000
|
-
};
|
|
11001
|
-
rafId = requestAnimationFrame(tick);
|
|
11002
|
-
return () => cancelAnimationFrame(rafId);
|
|
11038
|
+
const t = engine.ecs.getComponent(entityId, "Transform");
|
|
11039
|
+
if (!t) return;
|
|
11040
|
+
const screen = worldToScreenCss3(engine, t.x, t.y);
|
|
11041
|
+
if (!screen) return;
|
|
11042
|
+
const size = bounds ?? deriveBounds(engine.ecs, entityId) ?? { width: 16, height: 16 };
|
|
11043
|
+
const w = size.width * Math.abs(t.scaleX) * screen.zoom;
|
|
11044
|
+
const h = size.height * Math.abs(t.scaleY) * screen.zoom;
|
|
11045
|
+
node.style.left = `${screen.x - Math.max(w, 1) / 2}px`;
|
|
11046
|
+
node.style.top = `${screen.y - Math.max(h, 1) / 2}px`;
|
|
11047
|
+
node.style.width = `${Math.max(w, 1)}px`;
|
|
11048
|
+
node.style.height = `${Math.max(h, 1)}px`;
|
|
11003
11049
|
}, [engine, entityId, bounds]);
|
|
11004
11050
|
if (!engine || entityId === null || entityId === void 0) return null;
|
|
11005
11051
|
const effectiveRole = role ?? (onActivate ? "button" : "presentation");
|
|
@@ -11073,7 +11119,7 @@ function worldToScreenCss3(engine, wx, wy) {
|
|
|
11073
11119
|
}
|
|
11074
11120
|
|
|
11075
11121
|
// src/components/VectorPath.tsx
|
|
11076
|
-
import { useContext as useContext60,
|
|
11122
|
+
import { useContext as useContext60, useRef as useRef31 } from "react";
|
|
11077
11123
|
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
11078
11124
|
function VectorPath({
|
|
11079
11125
|
d,
|
|
@@ -11090,37 +11136,29 @@ function VectorPath({
|
|
|
11090
11136
|
const engine = useContext60(EngineContext);
|
|
11091
11137
|
const entityId = useContext60(EntityContext);
|
|
11092
11138
|
const svgRef = useRef31(null);
|
|
11093
|
-
|
|
11139
|
+
useOverlayTick(() => {
|
|
11094
11140
|
if (!engine || entityId === null || entityId === void 0) return;
|
|
11095
11141
|
const svg = svgRef.current;
|
|
11096
11142
|
if (!svg) return;
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
path.setAttribute("stroke-width", `${strokeWidth * screen.zoom}`);
|
|
11117
|
-
}
|
|
11118
|
-
}
|
|
11119
|
-
}
|
|
11120
|
-
rafId = requestAnimationFrame(tick);
|
|
11121
|
-
};
|
|
11122
|
-
rafId = requestAnimationFrame(tick);
|
|
11123
|
-
return () => cancelAnimationFrame(rafId);
|
|
11143
|
+
const t = engine.ecs.getComponent(entityId, "Transform");
|
|
11144
|
+
if (!t) return;
|
|
11145
|
+
const screen = worldToScreenCss4(engine, t.x, t.y);
|
|
11146
|
+
if (!screen) return;
|
|
11147
|
+
svg.style.display = visible ? "block" : "none";
|
|
11148
|
+
const rect = engine.canvas.getBoundingClientRect();
|
|
11149
|
+
svg.style.left = `${rect.left + window.scrollX}px`;
|
|
11150
|
+
svg.style.top = `${rect.top + window.scrollY}px`;
|
|
11151
|
+
svg.style.width = `${rect.width}px`;
|
|
11152
|
+
svg.style.height = `${rect.height}px`;
|
|
11153
|
+
const path = svg.querySelector("path");
|
|
11154
|
+
if (path) {
|
|
11155
|
+
const degrees = t.rotation * 180 / Math.PI;
|
|
11156
|
+
path.setAttribute(
|
|
11157
|
+
"transform",
|
|
11158
|
+
`translate(${screen.x} ${screen.y}) rotate(${degrees}) scale(${t.scaleX * screen.zoom} ${t.scaleY * screen.zoom})`
|
|
11159
|
+
);
|
|
11160
|
+
path.setAttribute("stroke-width", `${strokeWidth * screen.zoom}`);
|
|
11161
|
+
}
|
|
11124
11162
|
}, [engine, entityId, visible, strokeWidth]);
|
|
11125
11163
|
if (!engine) return null;
|
|
11126
11164
|
const svgStyle = {
|
|
@@ -11356,12 +11394,16 @@ function useTurnSystem({
|
|
|
11356
11394
|
const [activeIndex, setActiveIndex] = useState17(initialIndex % players.length);
|
|
11357
11395
|
const [turn, setTurn] = useState17(0);
|
|
11358
11396
|
const [isPending, setIsPending] = useState17(false);
|
|
11359
|
-
const
|
|
11397
|
+
const pendingRafId = useRef33(null);
|
|
11398
|
+
const pendingRemaining = useRef33(0);
|
|
11399
|
+
const pendingTarget = useRef33(null);
|
|
11400
|
+
const pendingLastTime = useRef33(0);
|
|
11360
11401
|
const clearPending = useCallback15(() => {
|
|
11361
|
-
if (
|
|
11362
|
-
|
|
11363
|
-
|
|
11402
|
+
if (pendingRafId.current !== null) {
|
|
11403
|
+
cancelAnimationFrame(pendingRafId.current);
|
|
11404
|
+
pendingRafId.current = null;
|
|
11364
11405
|
}
|
|
11406
|
+
pendingTarget.current = null;
|
|
11365
11407
|
setIsPending(false);
|
|
11366
11408
|
}, []);
|
|
11367
11409
|
const startedOnce = useRef33(false);
|
|
@@ -11391,13 +11433,29 @@ function useTurnSystem({
|
|
|
11391
11433
|
return;
|
|
11392
11434
|
}
|
|
11393
11435
|
setIsPending(true);
|
|
11394
|
-
|
|
11395
|
-
|
|
11396
|
-
|
|
11397
|
-
|
|
11398
|
-
|
|
11436
|
+
pendingTarget.current = nextIndex;
|
|
11437
|
+
pendingRemaining.current = aiDelay;
|
|
11438
|
+
pendingLastTime.current = performance.now();
|
|
11439
|
+
const tick2 = (now) => {
|
|
11440
|
+
const dt = (now - pendingLastTime.current) / 1e3;
|
|
11441
|
+
pendingLastTime.current = now;
|
|
11442
|
+
const paused = engine?.loop.isPaused ?? false;
|
|
11443
|
+
if (!paused) {
|
|
11444
|
+
pendingRemaining.current -= dt;
|
|
11445
|
+
}
|
|
11446
|
+
if (pendingRemaining.current <= 0 && pendingTarget.current !== null) {
|
|
11447
|
+
const target = pendingTarget.current;
|
|
11448
|
+
pendingTarget.current = null;
|
|
11449
|
+
pendingRafId.current = null;
|
|
11450
|
+
setIsPending(false);
|
|
11451
|
+
applyChange(target);
|
|
11452
|
+
return;
|
|
11453
|
+
}
|
|
11454
|
+
pendingRafId.current = requestAnimationFrame(tick2);
|
|
11455
|
+
};
|
|
11456
|
+
pendingRafId.current = requestAnimationFrame(tick2);
|
|
11399
11457
|
},
|
|
11400
|
-
[aiDelay, applyChange, clearPending]
|
|
11458
|
+
[aiDelay, applyChange, clearPending, engine]
|
|
11401
11459
|
);
|
|
11402
11460
|
const nextTurn = useCallback15(() => scheduleChange(activeIndex + 1), [scheduleChange, activeIndex]);
|
|
11403
11461
|
const prevTurn = useCallback15(() => scheduleChange(activeIndex - 1), [scheduleChange, activeIndex]);
|
|
@@ -11422,7 +11480,7 @@ function useTurnSystem({
|
|
|
11422
11480
|
}, [players.length, initialIndex, clearPending, engine]);
|
|
11423
11481
|
useEffect68(
|
|
11424
11482
|
() => () => {
|
|
11425
|
-
if (
|
|
11483
|
+
if (pendingRafId.current !== null) cancelAnimationFrame(pendingRafId.current);
|
|
11426
11484
|
},
|
|
11427
11485
|
[]
|
|
11428
11486
|
);
|
|
@@ -11526,15 +11584,23 @@ function screenToWorld(engine, cssX, cssY) {
|
|
|
11526
11584
|
|
|
11527
11585
|
// src/hooks/useDragDrop.ts
|
|
11528
11586
|
import { useContext as useContext64, useEffect as useEffect70, useRef as useRef34, useState as useState19 } from "react";
|
|
11529
|
-
var
|
|
11530
|
-
function
|
|
11531
|
-
|
|
11532
|
-
for (const cb of activeDrag.subscribers) cb();
|
|
11587
|
+
var dragStateByEngine = /* @__PURE__ */ new WeakMap();
|
|
11588
|
+
function getActiveDrag(engine) {
|
|
11589
|
+
return dragStateByEngine.get(engine) ?? null;
|
|
11533
11590
|
}
|
|
11534
|
-
function
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11591
|
+
function setActiveDrag(engine, drag) {
|
|
11592
|
+
dragStateByEngine.set(engine, drag);
|
|
11593
|
+
}
|
|
11594
|
+
function notifyDragSubscribers(engine) {
|
|
11595
|
+
const drag = dragStateByEngine.get(engine);
|
|
11596
|
+
if (!drag) return;
|
|
11597
|
+
for (const cb of drag.subscribers) cb();
|
|
11598
|
+
}
|
|
11599
|
+
function subscribeToActiveDrag(engine, cb) {
|
|
11600
|
+
const drag = dragStateByEngine.get(engine);
|
|
11601
|
+
if (!drag) return () => void 0;
|
|
11602
|
+
drag.subscribers.add(cb);
|
|
11603
|
+
return () => dragStateByEngine.get(engine)?.subscribers.delete(cb);
|
|
11538
11604
|
}
|
|
11539
11605
|
function useDraggable(options) {
|
|
11540
11606
|
const engine = useContext64(EngineContext);
|
|
@@ -11571,7 +11637,7 @@ function useDraggable(options) {
|
|
|
11571
11637
|
startEntityY = t.y;
|
|
11572
11638
|
startWorldX = world.x;
|
|
11573
11639
|
startWorldY = world.y;
|
|
11574
|
-
|
|
11640
|
+
setActiveDrag(engine, {
|
|
11575
11641
|
entityId,
|
|
11576
11642
|
tag: optsRef.current?.tag ?? null,
|
|
11577
11643
|
pointerX: e.clientX,
|
|
@@ -11579,14 +11645,15 @@ function useDraggable(options) {
|
|
|
11579
11645
|
worldX: world.x,
|
|
11580
11646
|
worldY: world.y,
|
|
11581
11647
|
subscribers: /* @__PURE__ */ new Set()
|
|
11582
|
-
};
|
|
11648
|
+
});
|
|
11583
11649
|
setIsDragging(true);
|
|
11584
11650
|
setPosition({ x: t.x, y: t.y });
|
|
11585
11651
|
optsRef.current?.onDragStart?.({ entityId, x: t.x, y: t.y });
|
|
11586
11652
|
engine.loop.markDirty();
|
|
11587
11653
|
};
|
|
11588
11654
|
const onPointerMove = (e) => {
|
|
11589
|
-
|
|
11655
|
+
const drag = getActiveDrag(engine);
|
|
11656
|
+
if (!dragging || !drag) return;
|
|
11590
11657
|
const t = engine.ecs.getComponent(entityId, "Transform");
|
|
11591
11658
|
if (!t) return;
|
|
11592
11659
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -11607,11 +11674,11 @@ function useDraggable(options) {
|
|
|
11607
11674
|
}
|
|
11608
11675
|
t.x = nx;
|
|
11609
11676
|
t.y = ny;
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
notifyDragSubscribers();
|
|
11677
|
+
drag.pointerX = e.clientX;
|
|
11678
|
+
drag.pointerY = e.clientY;
|
|
11679
|
+
drag.worldX = world.x;
|
|
11680
|
+
drag.worldY = world.y;
|
|
11681
|
+
notifyDragSubscribers(engine);
|
|
11615
11682
|
setPosition({ x: nx, y: ny });
|
|
11616
11683
|
optsRef.current?.onDrag?.({ entityId, x: nx, y: ny });
|
|
11617
11684
|
engine.loop.markDirty();
|
|
@@ -11620,10 +11687,11 @@ function useDraggable(options) {
|
|
|
11620
11687
|
if (!dragging) return;
|
|
11621
11688
|
dragging = false;
|
|
11622
11689
|
const t = engine.ecs.getComponent(entityId, "Transform");
|
|
11623
|
-
const
|
|
11690
|
+
const drag = getActiveDrag(engine);
|
|
11691
|
+
const droppedOn = findDroppableUnder(engine, drag);
|
|
11624
11692
|
const finalX = t?.x ?? startEntityX;
|
|
11625
11693
|
const finalY = t?.y ?? startEntityY;
|
|
11626
|
-
|
|
11694
|
+
setActiveDrag(engine, null);
|
|
11627
11695
|
setIsDragging(false);
|
|
11628
11696
|
optsRef.current?.onDragEnd?.({
|
|
11629
11697
|
entityId,
|
|
@@ -11658,7 +11726,8 @@ function useDroppable(options) {
|
|
|
11658
11726
|
if (!engine || entityId === null || entityId === void 0 || options?.disabled) return;
|
|
11659
11727
|
let currentlyOver = false;
|
|
11660
11728
|
const check = () => {
|
|
11661
|
-
|
|
11729
|
+
const drag = getActiveDrag(engine);
|
|
11730
|
+
if (!drag) {
|
|
11662
11731
|
if (currentlyOver) {
|
|
11663
11732
|
currentlyOver = false;
|
|
11664
11733
|
setIsOver(false);
|
|
@@ -11669,15 +11738,15 @@ function useDroppable(options) {
|
|
|
11669
11738
|
return;
|
|
11670
11739
|
}
|
|
11671
11740
|
const accepts = optsRef.current?.accepts;
|
|
11672
|
-
if (accepts && (!
|
|
11741
|
+
if (accepts && (!drag.tag || !accepts.includes(drag.tag))) return;
|
|
11673
11742
|
const t = engine.ecs.getComponent(entityId, "Transform");
|
|
11674
11743
|
if (!t) return;
|
|
11675
11744
|
const bounds = optsRef.current?.bounds ?? deriveBounds3(engine.ecs, entityId);
|
|
11676
11745
|
if (!bounds) return;
|
|
11677
11746
|
const halfW = bounds.width * Math.abs(t.scaleX) / 2;
|
|
11678
11747
|
const halfH = bounds.height * Math.abs(t.scaleY) / 2;
|
|
11679
|
-
const inside = Math.abs(
|
|
11680
|
-
const dragId =
|
|
11748
|
+
const inside = Math.abs(drag.worldX - t.x) <= halfW && Math.abs(drag.worldY - t.y) <= halfH;
|
|
11749
|
+
const dragId = drag.entityId;
|
|
11681
11750
|
if (inside && !currentlyOver) {
|
|
11682
11751
|
currentlyOver = true;
|
|
11683
11752
|
setIsOver(true);
|
|
@@ -11690,12 +11759,13 @@ function useDroppable(options) {
|
|
|
11690
11759
|
optsRef.current?.onLeave?.({ droppedEntityId: dragId });
|
|
11691
11760
|
}
|
|
11692
11761
|
};
|
|
11693
|
-
const unsubscribe = subscribeToActiveDrag(check);
|
|
11762
|
+
const unsubscribe = subscribeToActiveDrag(engine, check);
|
|
11694
11763
|
const onUp = () => {
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
const
|
|
11698
|
-
const
|
|
11764
|
+
const drag = getActiveDrag(engine);
|
|
11765
|
+
if (!drag || !currentlyOver) return;
|
|
11766
|
+
const dragId = drag.entityId;
|
|
11767
|
+
const dragX = drag.worldX;
|
|
11768
|
+
const dragY = drag.worldY;
|
|
11699
11769
|
currentlyOver = false;
|
|
11700
11770
|
setIsOver(false);
|
|
11701
11771
|
setHoveredBy(null);
|
|
@@ -11949,39 +12019,33 @@ function FocusRing({
|
|
|
11949
12019
|
mq.addEventListener("change", cb);
|
|
11950
12020
|
return () => mq.removeEventListener("change", cb);
|
|
11951
12021
|
}, []);
|
|
11952
|
-
|
|
12022
|
+
useOverlayTick(() => {
|
|
11953
12023
|
if (!engine) return;
|
|
11954
12024
|
const ring = ringRef.current;
|
|
11955
12025
|
if (!ring) return;
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
}
|
|
11980
|
-
}
|
|
11981
|
-
rafId = requestAnimationFrame(tick);
|
|
11982
|
-
};
|
|
11983
|
-
rafId = requestAnimationFrame(tick);
|
|
11984
|
-
return () => cancelAnimationFrame(rafId);
|
|
12026
|
+
if (focused === null) {
|
|
12027
|
+
ring.style.display = "none";
|
|
12028
|
+
return;
|
|
12029
|
+
}
|
|
12030
|
+
const t = engine.ecs.getComponent(focused, "Transform");
|
|
12031
|
+
if (!t) {
|
|
12032
|
+
ring.style.display = "none";
|
|
12033
|
+
return;
|
|
12034
|
+
}
|
|
12035
|
+
const screen = worldToScreenCss5(engine, t.x, t.y);
|
|
12036
|
+
if (!screen) {
|
|
12037
|
+
ring.style.display = "none";
|
|
12038
|
+
return;
|
|
12039
|
+
}
|
|
12040
|
+
const bounds = deriveBounds4(engine.ecs, focused) ?? { width: 16, height: 16 };
|
|
12041
|
+
const w = bounds.width * Math.abs(t.scaleX) * screen.zoom + padding * 2;
|
|
12042
|
+
const h = bounds.height * Math.abs(t.scaleY) * screen.zoom + padding * 2;
|
|
12043
|
+
ring.style.display = "block";
|
|
12044
|
+
ring.style.left = `${screen.x - w / 2}px`;
|
|
12045
|
+
ring.style.top = `${screen.y - h / 2}px`;
|
|
12046
|
+
ring.style.width = `${w}px`;
|
|
12047
|
+
ring.style.height = `${h}px`;
|
|
12048
|
+
ring.style.transform = `rotate(${t.rotation}rad)`;
|
|
11985
12049
|
}, [engine, focused, padding]);
|
|
11986
12050
|
if (!engine) return null;
|
|
11987
12051
|
const style = {
|
|
@@ -12497,7 +12561,7 @@ function usePathfinding() {
|
|
|
12497
12561
|
(grid, col, row, walkable) => setWalkable(grid, col, row, walkable),
|
|
12498
12562
|
[]
|
|
12499
12563
|
);
|
|
12500
|
-
const findPath$ = useCallback23((grid,
|
|
12564
|
+
const findPath$ = useCallback23((grid, start2, goal) => findPath(grid, start2, goal), []);
|
|
12501
12565
|
return { createGrid: createGrid$, setWalkable: setWalkable$, findPath: findPath$ };
|
|
12502
12566
|
}
|
|
12503
12567
|
|
|
@@ -12759,7 +12823,7 @@ function useDialogue() {
|
|
|
12759
12823
|
const [active, setActive] = useState27(false);
|
|
12760
12824
|
const [currentId, setCurrentId] = useState27(null);
|
|
12761
12825
|
const scriptRef = useRef41(null);
|
|
12762
|
-
const
|
|
12826
|
+
const start2 = useCallback28((script, startId) => {
|
|
12763
12827
|
scriptRef.current = script;
|
|
12764
12828
|
const id = startId ?? Object.keys(script)[0];
|
|
12765
12829
|
setCurrentId(id);
|
|
@@ -12801,7 +12865,7 @@ function useDialogue() {
|
|
|
12801
12865
|
scriptRef.current = null;
|
|
12802
12866
|
}, []);
|
|
12803
12867
|
const current = scriptRef.current && currentId ? scriptRef.current[currentId] ?? null : null;
|
|
12804
|
-
return { active, current, currentId, start, advance, close };
|
|
12868
|
+
return { active, current, currentId, start: start2, advance, close };
|
|
12805
12869
|
}
|
|
12806
12870
|
|
|
12807
12871
|
// ../gameplay/src/components/DialogueBox.tsx
|
|
@@ -13069,11 +13133,11 @@ function useTween(opts) {
|
|
|
13069
13133
|
}
|
|
13070
13134
|
runningRef.current = false;
|
|
13071
13135
|
}, []);
|
|
13072
|
-
const
|
|
13136
|
+
const start2 = useCallback31(() => {
|
|
13073
13137
|
stop();
|
|
13074
13138
|
runningRef.current = true;
|
|
13075
13139
|
startTimeRef.current = performance.now();
|
|
13076
|
-
const
|
|
13140
|
+
const tick2 = (now) => {
|
|
13077
13141
|
if (!runningRef.current) return;
|
|
13078
13142
|
const { from, to, duration, ease, onUpdate, onComplete } = optsRef.current;
|
|
13079
13143
|
const easeFn = ease ?? Ease.linear;
|
|
@@ -13086,14 +13150,14 @@ function useTween(opts) {
|
|
|
13086
13150
|
rafRef.current = null;
|
|
13087
13151
|
onComplete?.();
|
|
13088
13152
|
} else {
|
|
13089
|
-
rafRef.current = requestAnimationFrame(
|
|
13153
|
+
rafRef.current = requestAnimationFrame(tick2);
|
|
13090
13154
|
}
|
|
13091
13155
|
};
|
|
13092
|
-
rafRef.current = requestAnimationFrame(
|
|
13156
|
+
rafRef.current = requestAnimationFrame(tick2);
|
|
13093
13157
|
}, [stop]);
|
|
13094
13158
|
useEffect78(() => {
|
|
13095
13159
|
if (opts.autoStart) {
|
|
13096
|
-
|
|
13160
|
+
start2();
|
|
13097
13161
|
}
|
|
13098
13162
|
}, []);
|
|
13099
13163
|
useEffect78(() => {
|
|
@@ -13106,7 +13170,7 @@ function useTween(opts) {
|
|
|
13106
13170
|
};
|
|
13107
13171
|
}, []);
|
|
13108
13172
|
return {
|
|
13109
|
-
start,
|
|
13173
|
+
start: start2,
|
|
13110
13174
|
stop,
|
|
13111
13175
|
get isRunning() {
|
|
13112
13176
|
return runningRef.current;
|
|
@@ -13717,12 +13781,12 @@ var ClientPrediction = class {
|
|
|
13717
13781
|
* @param inputs - Input state this tick. Stored for re-simulation when a
|
|
13718
13782
|
* server correction arrives. Pass an empty object if not using re-simulation.
|
|
13719
13783
|
*/
|
|
13720
|
-
saveFrame(
|
|
13721
|
-
this._frames.set(
|
|
13784
|
+
saveFrame(tick2, inputs = {}) {
|
|
13785
|
+
this._frames.set(tick2, {
|
|
13722
13786
|
snapshot: this._world.getSnapshot(),
|
|
13723
13787
|
inputs
|
|
13724
13788
|
});
|
|
13725
|
-
const minTick =
|
|
13789
|
+
const minTick = tick2 - this._bufferSize;
|
|
13726
13790
|
for (const key of this._frames.keys()) {
|
|
13727
13791
|
if (key < minTick) this._frames.delete(key);
|
|
13728
13792
|
}
|
|
@@ -13731,8 +13795,8 @@ var ClientPrediction = class {
|
|
|
13731
13795
|
* Restore world state to the snapshot saved at `tick`.
|
|
13732
13796
|
* Returns `true` if the snapshot was found, `false` otherwise.
|
|
13733
13797
|
*/
|
|
13734
|
-
rollbackTo(
|
|
13735
|
-
const entry = this._frames.get(
|
|
13798
|
+
rollbackTo(tick2) {
|
|
13799
|
+
const entry = this._frames.get(tick2);
|
|
13736
13800
|
if (!entry) return false;
|
|
13737
13801
|
this._world.restoreSnapshot(entry.snapshot);
|
|
13738
13802
|
return true;
|
|
@@ -13748,10 +13812,10 @@ var ClientPrediction = class {
|
|
|
13748
13812
|
* @param serverSnapshot - Authoritative world state from the server.
|
|
13749
13813
|
* @param tick - The tick the server snapshot corresponds to.
|
|
13750
13814
|
*/
|
|
13751
|
-
applyCorrection(serverSnapshot,
|
|
13815
|
+
applyCorrection(serverSnapshot, tick2) {
|
|
13752
13816
|
this._world.restoreSnapshot(serverSnapshot);
|
|
13753
13817
|
if (this._simulate) {
|
|
13754
|
-
const laterTicks = [...this._frames.keys()].filter((t) => t >
|
|
13818
|
+
const laterTicks = [...this._frames.keys()].filter((t) => t > tick2).sort((a, b) => a - b);
|
|
13755
13819
|
for (const t of laterTicks) {
|
|
13756
13820
|
const entry = this._frames.get(t);
|
|
13757
13821
|
if (entry) {
|
|
@@ -13760,7 +13824,7 @@ var ClientPrediction = class {
|
|
|
13760
13824
|
}
|
|
13761
13825
|
}
|
|
13762
13826
|
for (const key of this._frames.keys()) {
|
|
13763
|
-
if (key <=
|
|
13827
|
+
if (key <= tick2) this._frames.delete(key);
|
|
13764
13828
|
}
|
|
13765
13829
|
}
|
|
13766
13830
|
/** Number of frames currently held in the buffer. */
|