cubeforge 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +59 -6
- package/dist/index.js +748 -103
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -805,6 +805,20 @@ function definePlugin(plugin) {
|
|
|
805
805
|
return plugin;
|
|
806
806
|
}
|
|
807
807
|
|
|
808
|
+
// ../../packages/core/src/pluginHotReload.ts
|
|
809
|
+
function hotReloadPlugin(engine, oldPlugin, newPlugin, oldSystems) {
|
|
810
|
+
const { ecs } = engine;
|
|
811
|
+
const toRemove = oldSystems ?? oldPlugin.systems;
|
|
812
|
+
for (const system of toRemove) {
|
|
813
|
+
ecs.removeSystem(system);
|
|
814
|
+
}
|
|
815
|
+
oldPlugin.onDestroy?.(engine);
|
|
816
|
+
for (const system of newPlugin.systems) {
|
|
817
|
+
ecs.addSystem(system);
|
|
818
|
+
}
|
|
819
|
+
newPlugin.onInit?.(engine);
|
|
820
|
+
}
|
|
821
|
+
|
|
808
822
|
// ../../packages/input/src/keyboard.ts
|
|
809
823
|
var Keyboard = class {
|
|
810
824
|
held = /* @__PURE__ */ new Set();
|
|
@@ -1162,6 +1176,22 @@ function createTrail(opts) {
|
|
|
1162
1176
|
};
|
|
1163
1177
|
}
|
|
1164
1178
|
|
|
1179
|
+
// ../../packages/renderer/src/components/nineSlice.ts
|
|
1180
|
+
function createNineSlice(src, width, height, opts) {
|
|
1181
|
+
return {
|
|
1182
|
+
type: "NineSlice",
|
|
1183
|
+
src,
|
|
1184
|
+
width,
|
|
1185
|
+
height,
|
|
1186
|
+
borderTop: 8,
|
|
1187
|
+
borderRight: 8,
|
|
1188
|
+
borderBottom: 8,
|
|
1189
|
+
borderLeft: 8,
|
|
1190
|
+
zIndex: 0,
|
|
1191
|
+
...opts
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1165
1195
|
// ../../packages/renderer/src/shaders.ts
|
|
1166
1196
|
var VERT_SRC = `#version 300 es
|
|
1167
1197
|
layout(location = 0) in vec2 a_quadPos;
|
|
@@ -1498,6 +1528,20 @@ var RenderSystem = class {
|
|
|
1498
1528
|
textureCache = /* @__PURE__ */ new Map();
|
|
1499
1529
|
/** Insertion-order key list for LRU-style eviction. */
|
|
1500
1530
|
textureCacheKeys = [];
|
|
1531
|
+
// ── Debug overlays ──────────────────────────────────────────────────────
|
|
1532
|
+
debugNavGrid = null;
|
|
1533
|
+
contactFlashPoints = [];
|
|
1534
|
+
// FPS tracking
|
|
1535
|
+
frameTimes = [];
|
|
1536
|
+
lastTimestamp = 0;
|
|
1537
|
+
/** Overlay a nav grid: green = walkable, red = blocked. Pass null to clear. */
|
|
1538
|
+
setDebugNavGrid(grid) {
|
|
1539
|
+
this.debugNavGrid = grid;
|
|
1540
|
+
}
|
|
1541
|
+
/** Flash a point on the canvas for one frame (world-space coords). */
|
|
1542
|
+
flashContactPoint(x, y) {
|
|
1543
|
+
this.contactFlashPoints.push({ x, y, ttl: 1 });
|
|
1544
|
+
}
|
|
1501
1545
|
// ── Texture management (sprite textures — CLAMP_TO_EDGE) ──────────────────
|
|
1502
1546
|
loadTexture(src) {
|
|
1503
1547
|
const cached = this.textures.get(src);
|
|
@@ -2014,6 +2058,84 @@ var RenderSystem = class {
|
|
|
2014
2058
|
}
|
|
2015
2059
|
if (tCount > 0) this.flush(tCount, "__color__");
|
|
2016
2060
|
}
|
|
2061
|
+
if (this.debugNavGrid) {
|
|
2062
|
+
const g = this.debugNavGrid;
|
|
2063
|
+
let ngCount = 0;
|
|
2064
|
+
for (let row = 0; row < g.rows; row++) {
|
|
2065
|
+
for (let col = 0; col < g.cols; col++) {
|
|
2066
|
+
if (ngCount >= MAX_INSTANCES) {
|
|
2067
|
+
this.flush(ngCount, "__color__");
|
|
2068
|
+
ngCount = 0;
|
|
2069
|
+
}
|
|
2070
|
+
const walkable = g.walkable[row * g.cols + col];
|
|
2071
|
+
const cx = col * g.cellSize + g.cellSize / 2;
|
|
2072
|
+
const cy = row * g.cellSize + g.cellSize / 2;
|
|
2073
|
+
this.writeInstance(
|
|
2074
|
+
ngCount * FLOATS_PER_INSTANCE,
|
|
2075
|
+
cx,
|
|
2076
|
+
cy,
|
|
2077
|
+
g.cellSize,
|
|
2078
|
+
g.cellSize,
|
|
2079
|
+
0,
|
|
2080
|
+
0.5,
|
|
2081
|
+
0.5,
|
|
2082
|
+
0,
|
|
2083
|
+
0,
|
|
2084
|
+
false,
|
|
2085
|
+
walkable ? 0 : 1,
|
|
2086
|
+
walkable ? 1 : 0,
|
|
2087
|
+
0,
|
|
2088
|
+
walkable ? 0.08 : 0.25,
|
|
2089
|
+
0,
|
|
2090
|
+
0,
|
|
2091
|
+
1,
|
|
2092
|
+
1
|
|
2093
|
+
);
|
|
2094
|
+
ngCount++;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
if (ngCount > 0) this.flush(ngCount, "__color__");
|
|
2098
|
+
}
|
|
2099
|
+
if (this.contactFlashPoints.length > 0) {
|
|
2100
|
+
let cfCount = 0;
|
|
2101
|
+
for (const pt of this.contactFlashPoints) {
|
|
2102
|
+
if (cfCount >= MAX_INSTANCES) {
|
|
2103
|
+
this.flush(cfCount, "__color__");
|
|
2104
|
+
cfCount = 0;
|
|
2105
|
+
}
|
|
2106
|
+
this.writeInstance(
|
|
2107
|
+
cfCount * FLOATS_PER_INSTANCE,
|
|
2108
|
+
pt.x,
|
|
2109
|
+
pt.y,
|
|
2110
|
+
8,
|
|
2111
|
+
8,
|
|
2112
|
+
0,
|
|
2113
|
+
0.5,
|
|
2114
|
+
0.5,
|
|
2115
|
+
0,
|
|
2116
|
+
0,
|
|
2117
|
+
false,
|
|
2118
|
+
1,
|
|
2119
|
+
0.3,
|
|
2120
|
+
0.3,
|
|
2121
|
+
0.9,
|
|
2122
|
+
0,
|
|
2123
|
+
0,
|
|
2124
|
+
1,
|
|
2125
|
+
1
|
|
2126
|
+
);
|
|
2127
|
+
cfCount++;
|
|
2128
|
+
pt.ttl--;
|
|
2129
|
+
}
|
|
2130
|
+
if (cfCount > 0) this.flush(cfCount, "__color__");
|
|
2131
|
+
this.contactFlashPoints = this.contactFlashPoints.filter((p) => p.ttl > 0);
|
|
2132
|
+
}
|
|
2133
|
+
const now = performance.now();
|
|
2134
|
+
if (this.lastTimestamp > 0) {
|
|
2135
|
+
this.frameTimes.push(now - this.lastTimestamp);
|
|
2136
|
+
if (this.frameTimes.length > 60) this.frameTimes.shift();
|
|
2137
|
+
}
|
|
2138
|
+
this.lastTimestamp = now;
|
|
2017
2139
|
}
|
|
2018
2140
|
};
|
|
2019
2141
|
|
|
@@ -2109,6 +2231,18 @@ function createCapsuleCollider(width, height, opts) {
|
|
|
2109
2231
|
};
|
|
2110
2232
|
}
|
|
2111
2233
|
|
|
2234
|
+
// ../../packages/physics/src/components/compoundCollider.ts
|
|
2235
|
+
function createCompoundCollider(shapes, opts) {
|
|
2236
|
+
return {
|
|
2237
|
+
type: "CompoundCollider",
|
|
2238
|
+
shapes,
|
|
2239
|
+
isTrigger: false,
|
|
2240
|
+
layer: "default",
|
|
2241
|
+
mask: "*",
|
|
2242
|
+
...opts
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2112
2246
|
// ../../packages/physics/src/physicsSystem.ts
|
|
2113
2247
|
function getAABB(transform, collider) {
|
|
2114
2248
|
return {
|
|
@@ -2149,6 +2283,77 @@ function maskAllows(mask, layer) {
|
|
|
2149
2283
|
function canInteract(a, b) {
|
|
2150
2284
|
return maskAllows(a.mask, b.layer) && maskAllows(b.mask, a.layer);
|
|
2151
2285
|
}
|
|
2286
|
+
function shapeToAABB(tx, ty, shape) {
|
|
2287
|
+
if (shape.type === "box") {
|
|
2288
|
+
return {
|
|
2289
|
+
cx: tx + shape.offsetX,
|
|
2290
|
+
cy: ty + shape.offsetY,
|
|
2291
|
+
hw: (shape.width ?? 0) / 2,
|
|
2292
|
+
hh: (shape.height ?? 0) / 2
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
const r = shape.radius ?? 0;
|
|
2296
|
+
return {
|
|
2297
|
+
cx: tx + shape.offsetX,
|
|
2298
|
+
cy: ty + shape.offsetY,
|
|
2299
|
+
hw: r,
|
|
2300
|
+
hh: r
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
function shapeOverlapsAABB(tx, ty, shape, other) {
|
|
2304
|
+
if (shape.type === "box") {
|
|
2305
|
+
return getOverlap(shapeToAABB(tx, ty, shape), other);
|
|
2306
|
+
}
|
|
2307
|
+
const r = shape.radius ?? 0;
|
|
2308
|
+
const cx = tx + shape.offsetX;
|
|
2309
|
+
const cy = ty + shape.offsetY;
|
|
2310
|
+
const nearX = Math.max(other.cx - other.hw, Math.min(cx, other.cx + other.hw));
|
|
2311
|
+
const nearY = Math.max(other.cy - other.hh, Math.min(cy, other.cy + other.hh));
|
|
2312
|
+
const dx = cx - nearX;
|
|
2313
|
+
const dy = cy - nearY;
|
|
2314
|
+
if (dx * dx + dy * dy >= r * r) return null;
|
|
2315
|
+
return getOverlap(shapeToAABB(tx, ty, shape), other);
|
|
2316
|
+
}
|
|
2317
|
+
function shapeOverlapsCircle(tx, ty, shape, cx, cy, cr) {
|
|
2318
|
+
if (shape.type === "circle") {
|
|
2319
|
+
const r = shape.radius ?? 0;
|
|
2320
|
+
const dx2 = tx + shape.offsetX - cx;
|
|
2321
|
+
const dy2 = ty + shape.offsetY - cy;
|
|
2322
|
+
return dx2 * dx2 + dy2 * dy2 < (r + cr) * (r + cr);
|
|
2323
|
+
}
|
|
2324
|
+
const aabb = shapeToAABB(tx, ty, shape);
|
|
2325
|
+
const nearX = Math.max(aabb.cx - aabb.hw, Math.min(cx, aabb.cx + aabb.hw));
|
|
2326
|
+
const nearY = Math.max(aabb.cy - aabb.hh, Math.min(cy, aabb.cy + aabb.hh));
|
|
2327
|
+
const dx = cx - nearX;
|
|
2328
|
+
const dy = cy - nearY;
|
|
2329
|
+
return dx * dx + dy * dy < cr * cr;
|
|
2330
|
+
}
|
|
2331
|
+
function getCompoundBounds(tx, ty, shapes) {
|
|
2332
|
+
let minX = Infinity;
|
|
2333
|
+
let minY = Infinity;
|
|
2334
|
+
let maxX = -Infinity;
|
|
2335
|
+
let maxY = -Infinity;
|
|
2336
|
+
for (const s2 of shapes) {
|
|
2337
|
+
const a = shapeToAABB(tx, ty, s2);
|
|
2338
|
+
const l = a.cx - a.hw;
|
|
2339
|
+
const r = a.cx + a.hw;
|
|
2340
|
+
const t = a.cy - a.hh;
|
|
2341
|
+
const b = a.cy + a.hh;
|
|
2342
|
+
if (l < minX) minX = l;
|
|
2343
|
+
if (r > maxX) maxX = r;
|
|
2344
|
+
if (t < minY) minY = t;
|
|
2345
|
+
if (b > maxY) maxY = b;
|
|
2346
|
+
}
|
|
2347
|
+
return {
|
|
2348
|
+
cx: (minX + maxX) / 2,
|
|
2349
|
+
cy: (minY + maxY) / 2,
|
|
2350
|
+
hw: (maxX - minX) / 2,
|
|
2351
|
+
hh: (maxY - minY) / 2
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
function canInteractGeneric(aLayer, aMask, bLayer, bMask) {
|
|
2355
|
+
return maskAllows(aMask, bLayer) && maskAllows(bMask, aLayer);
|
|
2356
|
+
}
|
|
2152
2357
|
function pairKey(a, b) {
|
|
2153
2358
|
return a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
2154
2359
|
}
|
|
@@ -2163,6 +2368,7 @@ var PhysicsSystem = class {
|
|
|
2163
2368
|
activeTriggerPairs = /* @__PURE__ */ new Map();
|
|
2164
2369
|
activeCollisionPairs = /* @__PURE__ */ new Map();
|
|
2165
2370
|
activeCirclePairs = /* @__PURE__ */ new Map();
|
|
2371
|
+
activeCompoundPairs = /* @__PURE__ */ new Map();
|
|
2166
2372
|
// Previous-frame positions of static entities — used to compute platform carry delta.
|
|
2167
2373
|
staticPrevPos = /* @__PURE__ */ new Map();
|
|
2168
2374
|
setGravity(g) {
|
|
@@ -2217,6 +2423,12 @@ var PhysicsSystem = class {
|
|
|
2217
2423
|
this.activeCirclePairs.delete(key);
|
|
2218
2424
|
}
|
|
2219
2425
|
}
|
|
2426
|
+
for (const [key, [a, b]] of this.activeCompoundPairs) {
|
|
2427
|
+
if (!world.hasEntity(a) || !world.hasEntity(b)) {
|
|
2428
|
+
this.events?.emit("compoundExit", { a, b });
|
|
2429
|
+
this.activeCompoundPairs.delete(key);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2220
2432
|
const staticDelta = /* @__PURE__ */ new Map();
|
|
2221
2433
|
for (const sid of statics) {
|
|
2222
2434
|
const st = world.getComponent(sid, "Transform");
|
|
@@ -2519,6 +2731,101 @@ var PhysicsSystem = class {
|
|
|
2519
2731
|
}
|
|
2520
2732
|
this.activeCirclePairs = /* @__PURE__ */ new Map();
|
|
2521
2733
|
}
|
|
2734
|
+
const allCompound = world.query("Transform", "CompoundCollider");
|
|
2735
|
+
if (allCompound.length > 0) {
|
|
2736
|
+
const currentCompoundPairs = /* @__PURE__ */ new Map();
|
|
2737
|
+
const allBoxEntities = world.query("Transform", "BoxCollider");
|
|
2738
|
+
for (const cid of allCompound) {
|
|
2739
|
+
const cc = world.getComponent(cid, "CompoundCollider");
|
|
2740
|
+
const ct = world.getComponent(cid, "Transform");
|
|
2741
|
+
for (const bid of allBoxEntities) {
|
|
2742
|
+
if (bid === cid) continue;
|
|
2743
|
+
const bc = world.getComponent(bid, "BoxCollider");
|
|
2744
|
+
if (!canInteractGeneric(cc.layer, cc.mask, bc.layer, bc.mask)) continue;
|
|
2745
|
+
const bt = world.getComponent(bid, "Transform");
|
|
2746
|
+
const boxAABB = getAABB(bt, bc);
|
|
2747
|
+
let hit = false;
|
|
2748
|
+
for (const shape of cc.shapes) {
|
|
2749
|
+
if (shapeOverlapsAABB(ct.x, ct.y, shape, boxAABB)) {
|
|
2750
|
+
hit = true;
|
|
2751
|
+
break;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
if (hit) currentCompoundPairs.set(pairKey(cid, bid), [cid, bid]);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
for (const cid of allCompound) {
|
|
2758
|
+
const cc = world.getComponent(cid, "CompoundCollider");
|
|
2759
|
+
const ct = world.getComponent(cid, "Transform");
|
|
2760
|
+
for (const oid of allCircles) {
|
|
2761
|
+
if (oid === cid) continue;
|
|
2762
|
+
const oc = world.getComponent(oid, "CircleCollider");
|
|
2763
|
+
if (!canInteractGeneric(cc.layer, cc.mask, oc.layer, oc.mask)) continue;
|
|
2764
|
+
const ot = world.getComponent(oid, "Transform");
|
|
2765
|
+
let hit = false;
|
|
2766
|
+
for (const shape of cc.shapes) {
|
|
2767
|
+
if (shapeOverlapsCircle(ct.x, ct.y, shape, ot.x + oc.offsetX, ot.y + oc.offsetY, oc.radius)) {
|
|
2768
|
+
hit = true;
|
|
2769
|
+
break;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
if (hit) currentCompoundPairs.set(pairKey(cid, oid), [cid, oid]);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
for (let i = 0; i < allCompound.length; i++) {
|
|
2776
|
+
for (let j = i + 1; j < allCompound.length; j++) {
|
|
2777
|
+
const ia = allCompound[i];
|
|
2778
|
+
const ib = allCompound[j];
|
|
2779
|
+
const ca = world.getComponent(ia, "CompoundCollider");
|
|
2780
|
+
const cb = world.getComponent(ib, "CompoundCollider");
|
|
2781
|
+
if (!canInteractGeneric(ca.layer, ca.mask, cb.layer, cb.mask)) continue;
|
|
2782
|
+
const ta = world.getComponent(ia, "Transform");
|
|
2783
|
+
const tb = world.getComponent(ib, "Transform");
|
|
2784
|
+
const boundsA = getCompoundBounds(ta.x, ta.y, ca.shapes);
|
|
2785
|
+
const boundsB = getCompoundBounds(tb.x, tb.y, cb.shapes);
|
|
2786
|
+
if (!getOverlap(boundsA, boundsB)) continue;
|
|
2787
|
+
let hit = false;
|
|
2788
|
+
outer2: for (const sa of ca.shapes) {
|
|
2789
|
+
const aabb = shapeToAABB(ta.x, ta.y, sa);
|
|
2790
|
+
for (const sb of cb.shapes) {
|
|
2791
|
+
if (sb.type === "circle") {
|
|
2792
|
+
const r = sb.radius ?? 0;
|
|
2793
|
+
if (shapeOverlapsCircle(ta.x, ta.y, sa, tb.x + sb.offsetX, tb.y + sb.offsetY, r)) {
|
|
2794
|
+
hit = true;
|
|
2795
|
+
break outer2;
|
|
2796
|
+
}
|
|
2797
|
+
} else {
|
|
2798
|
+
const bAABB = shapeToAABB(tb.x, tb.y, sb);
|
|
2799
|
+
if (getOverlap(aabb, bAABB)) {
|
|
2800
|
+
hit = true;
|
|
2801
|
+
break outer2;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
if (hit) currentCompoundPairs.set(pairKey(ia, ib), [ia, ib]);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
for (const [key, [a, b]] of currentCompoundPairs) {
|
|
2810
|
+
if (!this.activeCompoundPairs.has(key)) {
|
|
2811
|
+
this.events?.emit("compoundEnter", { a, b });
|
|
2812
|
+
} else {
|
|
2813
|
+
this.events?.emit("compoundStay", { a, b });
|
|
2814
|
+
}
|
|
2815
|
+
this.events?.emit("compound", { a, b });
|
|
2816
|
+
}
|
|
2817
|
+
for (const [key, [a, b]] of this.activeCompoundPairs) {
|
|
2818
|
+
if (!currentCompoundPairs.has(key)) {
|
|
2819
|
+
this.events?.emit("compoundExit", { a, b });
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
this.activeCompoundPairs = currentCompoundPairs;
|
|
2823
|
+
} else if (this.activeCompoundPairs.size > 0) {
|
|
2824
|
+
for (const [, [a, b]] of this.activeCompoundPairs) {
|
|
2825
|
+
this.events?.emit("compoundExit", { a, b });
|
|
2826
|
+
}
|
|
2827
|
+
this.activeCompoundPairs = /* @__PURE__ */ new Map();
|
|
2828
|
+
}
|
|
2522
2829
|
}
|
|
2523
2830
|
};
|
|
2524
2831
|
|
|
@@ -2962,6 +3269,8 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
2962
3269
|
const [activeTab, setActiveTab] = useState("entities");
|
|
2963
3270
|
const [entitySearch, setEntitySearch] = useState("");
|
|
2964
3271
|
const [contactLog, setContactLog] = useState([]);
|
|
3272
|
+
const [showNavGrid, setShowNavGrid] = useState(false);
|
|
3273
|
+
const [showContactFlash, setShowContactFlash] = useState(false);
|
|
2965
3274
|
const frameRef = useRef2(0);
|
|
2966
3275
|
useEffect2(() => {
|
|
2967
3276
|
handle.onFrame = () => {
|
|
@@ -2987,10 +3296,24 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
2987
3296
|
if (next.length > 20) next.length = 20;
|
|
2988
3297
|
return next;
|
|
2989
3298
|
});
|
|
3299
|
+
if (showContactFlash) {
|
|
3300
|
+
const renderer = engine.activeRenderSystem;
|
|
3301
|
+
if (renderer?.flashContactPoint) {
|
|
3302
|
+
const tA = engine.ecs.getComponent(a, "Transform");
|
|
3303
|
+
const tB = engine.ecs.getComponent(b, "Transform");
|
|
3304
|
+
if (tA && tB) {
|
|
3305
|
+
renderer.flashContactPoint((tA.x + tB.x) / 2, (tA.y + tB.y) / 2);
|
|
3306
|
+
} else if (tA) {
|
|
3307
|
+
renderer.flashContactPoint(tA.x, tA.y);
|
|
3308
|
+
} else if (tB) {
|
|
3309
|
+
renderer.flashContactPoint(tB.x, tB.y);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
2990
3313
|
})
|
|
2991
3314
|
);
|
|
2992
3315
|
return () => unsubs.forEach((u) => u());
|
|
2993
|
-
}, [engine]);
|
|
3316
|
+
}, [engine, showContactFlash]);
|
|
2994
3317
|
const totalFrames = handle.buffer.length;
|
|
2995
3318
|
const currentSnap = handle.buffer[selectedIdx];
|
|
2996
3319
|
const handlePauseResume = useCallback(() => {
|
|
@@ -3013,6 +3336,51 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
3013
3336
|
setSelectedIdx((i) => Math.min(handle.buffer.length - 1, i + 1));
|
|
3014
3337
|
setSelectedEntity(null);
|
|
3015
3338
|
}, [handle]);
|
|
3339
|
+
const handleToggleNavGrid = useCallback(() => {
|
|
3340
|
+
if (!engine) return;
|
|
3341
|
+
const renderer = engine.activeRenderSystem;
|
|
3342
|
+
if (!renderer?.setDebugNavGrid) return;
|
|
3343
|
+
if (showNavGrid) {
|
|
3344
|
+
renderer.setDebugNavGrid(null);
|
|
3345
|
+
setShowNavGrid(false);
|
|
3346
|
+
} else {
|
|
3347
|
+
const snap = handle.buffer[handle.buffer.length - 1];
|
|
3348
|
+
if (snap) {
|
|
3349
|
+
let maxX = 0, maxY = 0;
|
|
3350
|
+
for (const e of snap.entities) {
|
|
3351
|
+
const t = e.components.find((c) => c.type === "Transform");
|
|
3352
|
+
if (t) {
|
|
3353
|
+
if (t.x > maxX) maxX = t.x;
|
|
3354
|
+
if (t.y > maxY) maxY = t.y;
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
const cellSize = 16;
|
|
3358
|
+
const cols = Math.ceil((maxX + 200) / cellSize);
|
|
3359
|
+
const rows = Math.ceil((maxY + 200) / cellSize);
|
|
3360
|
+
const walkable = new Uint8Array(cols * rows).fill(1);
|
|
3361
|
+
for (const e of snap.entities) {
|
|
3362
|
+
const t = e.components.find((c) => c.type === "Transform");
|
|
3363
|
+
const bc = e.components.find((c) => c.type === "BoxCollider");
|
|
3364
|
+
const rb = e.components.find((c) => c.type === "RigidBody");
|
|
3365
|
+
if (t && bc && (!rb || rb.isStatic)) {
|
|
3366
|
+
const left = t.x + (bc.offsetX ?? 0) - bc.width / 2;
|
|
3367
|
+
const top = t.y + (bc.offsetY ?? 0) - bc.height / 2;
|
|
3368
|
+
const c0 = Math.max(0, Math.floor(left / cellSize));
|
|
3369
|
+
const c1 = Math.min(cols - 1, Math.floor((left + bc.width) / cellSize));
|
|
3370
|
+
const r0 = Math.max(0, Math.floor(top / cellSize));
|
|
3371
|
+
const r1 = Math.min(rows - 1, Math.floor((top + bc.height) / cellSize));
|
|
3372
|
+
for (let r = r0; r <= r1; r++) {
|
|
3373
|
+
for (let c = c0; c <= c1; c++) {
|
|
3374
|
+
walkable[r * cols + c] = 0;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
renderer.setDebugNavGrid({ cols, rows, cellSize, walkable });
|
|
3380
|
+
setShowNavGrid(true);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
}, [engine, showNavGrid, handle]);
|
|
3016
3384
|
const frameLabel = totalFrames === 0 ? "0/0" : `${selectedIdx + 1}/${totalFrames}`;
|
|
3017
3385
|
const entities = currentSnap?.entities ?? [];
|
|
3018
3386
|
const filtered = entitySearch ? entities.filter(
|
|
@@ -3052,9 +3420,27 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
3052
3420
|
selectedEntityData
|
|
3053
3421
|
}
|
|
3054
3422
|
),
|
|
3055
|
-
activeTab === "perf" && /* @__PURE__ */ jsx(
|
|
3423
|
+
activeTab === "perf" && /* @__PURE__ */ jsx(
|
|
3424
|
+
PerfTab,
|
|
3425
|
+
{
|
|
3426
|
+
fps,
|
|
3427
|
+
entityCount,
|
|
3428
|
+
compCount,
|
|
3429
|
+
timings,
|
|
3430
|
+
showNavGrid,
|
|
3431
|
+
onToggleNavGrid: handleToggleNavGrid
|
|
3432
|
+
}
|
|
3433
|
+
),
|
|
3056
3434
|
activeTab === "input" && /* @__PURE__ */ jsx(InputTab, { activeKeys, inputCtx }),
|
|
3057
|
-
activeTab === "contacts" && /* @__PURE__ */ jsx(
|
|
3435
|
+
activeTab === "contacts" && /* @__PURE__ */ jsx(
|
|
3436
|
+
ContactsTab,
|
|
3437
|
+
{
|
|
3438
|
+
log: contactLog,
|
|
3439
|
+
onClear: () => setContactLog([]),
|
|
3440
|
+
showFlash: showContactFlash,
|
|
3441
|
+
onToggleFlash: () => setShowContactFlash((v) => !v)
|
|
3442
|
+
}
|
|
3443
|
+
),
|
|
3058
3444
|
activeTab === "assets" && /* @__PURE__ */ jsx(AssetsTab, { assetCache })
|
|
3059
3445
|
] }),
|
|
3060
3446
|
/* @__PURE__ */ jsxs("div", { style: s.bar, children: [
|
|
@@ -3153,7 +3539,7 @@ function EntitiesTab({ entities, entitySearch, onSearchChange, selectedEntity, o
|
|
|
3153
3539
|
] }, comp.type)) })
|
|
3154
3540
|
] });
|
|
3155
3541
|
}
|
|
3156
|
-
function PerfTab({ fps, entityCount, compCount, timings }) {
|
|
3542
|
+
function PerfTab({ fps, entityCount, compCount, timings, showNavGrid, onToggleNavGrid }) {
|
|
3157
3543
|
const maxMs = 16.67;
|
|
3158
3544
|
const stats = [
|
|
3159
3545
|
{ label: "FPS", value: String(fps), ok: fps >= 55 },
|
|
@@ -3165,6 +3551,10 @@ function PerfTab({ fps, entityCount, compCount, timings }) {
|
|
|
3165
3551
|
/* @__PURE__ */ jsx("span", { style: { fontSize: 9, color: C.muted }, children: label }),
|
|
3166
3552
|
/* @__PURE__ */ jsx("span", { style: { fontSize: 16, fontWeight: 700, color: ok ? C.ok : C.warn }, children: value })
|
|
3167
3553
|
] }, label)) }),
|
|
3554
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8, marginBottom: 10 }, children: /* @__PURE__ */ jsxs("button", { style: s.btn(showNavGrid), onClick: onToggleNavGrid, children: [
|
|
3555
|
+
showNavGrid ? "Hide" : "Show",
|
|
3556
|
+
" Nav Grid"
|
|
3557
|
+
] }) }),
|
|
3168
3558
|
timings && timings.size > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
3169
3559
|
/* @__PURE__ */ jsx("div", { style: { color: C.muted, fontSize: 9, marginBottom: 6, letterSpacing: "0.08em" }, children: "SYSTEM TIMING" }),
|
|
3170
3560
|
Array.from(timings.entries()).map(([name, ms]) => {
|
|
@@ -3214,11 +3604,17 @@ function InputTab({ activeKeys, inputCtx }) {
|
|
|
3214
3604
|
] })
|
|
3215
3605
|
] });
|
|
3216
3606
|
}
|
|
3217
|
-
function ContactsTab({ log, onClear }) {
|
|
3607
|
+
function ContactsTab({ log, onClear, showFlash, onToggleFlash }) {
|
|
3218
3608
|
return /* @__PURE__ */ jsxs("div", { style: { padding: "4px 0" }, children: [
|
|
3219
3609
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 14px 8px" }, children: [
|
|
3220
3610
|
/* @__PURE__ */ jsx("span", { style: { color: C.muted, fontSize: 9 }, children: "Last 20 contact events" }),
|
|
3221
|
-
/* @__PURE__ */
|
|
3611
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6 }, children: [
|
|
3612
|
+
/* @__PURE__ */ jsxs("button", { style: s.btn(showFlash), onClick: onToggleFlash, children: [
|
|
3613
|
+
showFlash ? "Hide" : "Show",
|
|
3614
|
+
" Flash"
|
|
3615
|
+
] }),
|
|
3616
|
+
/* @__PURE__ */ jsx("button", { style: s.btn(false, true), onClick: onClear, children: "Clear" })
|
|
3617
|
+
] })
|
|
3222
3618
|
] }),
|
|
3223
3619
|
log.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: "4px 14px", color: C.muted }, children: "No contacts yet" }) : log.map((entry, i) => /* @__PURE__ */ jsxs("div", { style: { padding: "3px 14px", display: "flex", gap: 10, alignItems: "center" }, children: [
|
|
3224
3620
|
/* @__PURE__ */ jsxs("span", { style: { color: C.muted, fontSize: 9, minWidth: 46 }, children: [
|
|
@@ -3703,7 +4099,9 @@ function Sprite({
|
|
|
3703
4099
|
atlas,
|
|
3704
4100
|
frame,
|
|
3705
4101
|
tileX,
|
|
3706
|
-
tileY
|
|
4102
|
+
tileY,
|
|
4103
|
+
tileSizeX,
|
|
4104
|
+
tileSizeY
|
|
3707
4105
|
}) {
|
|
3708
4106
|
const resolvedFrameIndex = atlas && frame != null ? atlas[frame] ?? 0 : frameIndex;
|
|
3709
4107
|
const engine = useContext5(EngineContext);
|
|
@@ -3726,7 +4124,9 @@ function Sprite({
|
|
|
3726
4124
|
frameHeight,
|
|
3727
4125
|
frameColumns,
|
|
3728
4126
|
tileX,
|
|
3729
|
-
tileY
|
|
4127
|
+
tileY,
|
|
4128
|
+
tileSizeX,
|
|
4129
|
+
tileSizeY
|
|
3730
4130
|
});
|
|
3731
4131
|
engine.ecs.addComponent(entityId, comp);
|
|
3732
4132
|
if (src) {
|
|
@@ -3889,12 +4289,37 @@ function CapsuleCollider({
|
|
|
3889
4289
|
return null;
|
|
3890
4290
|
}
|
|
3891
4291
|
|
|
3892
|
-
// src/components/
|
|
4292
|
+
// src/components/CompoundCollider.tsx
|
|
3893
4293
|
import { useEffect as useEffect13, useContext as useContext11 } from "react";
|
|
3894
|
-
function
|
|
4294
|
+
function CompoundCollider({
|
|
4295
|
+
shapes,
|
|
4296
|
+
isTrigger = false,
|
|
4297
|
+
layer = "default",
|
|
4298
|
+
mask = "*"
|
|
4299
|
+
}) {
|
|
3895
4300
|
const engine = useContext11(EngineContext);
|
|
3896
4301
|
const entityId = useContext11(EntityContext);
|
|
3897
4302
|
useEffect13(() => {
|
|
4303
|
+
engine.ecs.addComponent(entityId, createCompoundCollider(shapes, { isTrigger, layer, mask }));
|
|
4304
|
+
const checkId = setTimeout(() => {
|
|
4305
|
+
if (engine.ecs.hasEntity(entityId) && !engine.ecs.hasComponent(entityId, "Transform")) {
|
|
4306
|
+
console.warn(`[Cubeforge] CompoundCollider on entity ${entityId} has no Transform. Physics requires Transform.`);
|
|
4307
|
+
}
|
|
4308
|
+
}, 0);
|
|
4309
|
+
return () => {
|
|
4310
|
+
clearTimeout(checkId);
|
|
4311
|
+
engine.ecs.removeComponent(entityId, "CompoundCollider");
|
|
4312
|
+
};
|
|
4313
|
+
}, []);
|
|
4314
|
+
return null;
|
|
4315
|
+
}
|
|
4316
|
+
|
|
4317
|
+
// src/components/Script.tsx
|
|
4318
|
+
import { useEffect as useEffect14, useContext as useContext12 } from "react";
|
|
4319
|
+
function Script({ init, update }) {
|
|
4320
|
+
const engine = useContext12(EngineContext);
|
|
4321
|
+
const entityId = useContext12(EntityContext);
|
|
4322
|
+
useEffect14(() => {
|
|
3898
4323
|
if (init) {
|
|
3899
4324
|
try {
|
|
3900
4325
|
init(entityId, engine.ecs);
|
|
@@ -3909,7 +4334,7 @@ function Script({ init, update }) {
|
|
|
3909
4334
|
}
|
|
3910
4335
|
|
|
3911
4336
|
// src/components/Camera2D.tsx
|
|
3912
|
-
import { useEffect as
|
|
4337
|
+
import { useEffect as useEffect15, useContext as useContext13 } from "react";
|
|
3913
4338
|
function Camera2D({
|
|
3914
4339
|
followEntity,
|
|
3915
4340
|
x = 0,
|
|
@@ -3922,8 +4347,8 @@ function Camera2D({
|
|
|
3922
4347
|
followOffsetX = 0,
|
|
3923
4348
|
followOffsetY = 0
|
|
3924
4349
|
}) {
|
|
3925
|
-
const engine =
|
|
3926
|
-
|
|
4350
|
+
const engine = useContext13(EngineContext);
|
|
4351
|
+
useEffect15(() => {
|
|
3927
4352
|
const entityId = engine.ecs.createEntity();
|
|
3928
4353
|
engine.ecs.addComponent(entityId, createCamera2D({
|
|
3929
4354
|
followEntityId: followEntity,
|
|
@@ -3939,7 +4364,7 @@ function Camera2D({
|
|
|
3939
4364
|
}));
|
|
3940
4365
|
return () => engine.ecs.destroyEntity(entityId);
|
|
3941
4366
|
}, []);
|
|
3942
|
-
|
|
4367
|
+
useEffect15(() => {
|
|
3943
4368
|
const camId = engine.ecs.queryOne("Camera2D");
|
|
3944
4369
|
if (camId === void 0) return;
|
|
3945
4370
|
const cam = engine.ecs.getComponent(camId, "Camera2D");
|
|
@@ -3958,11 +4383,11 @@ function Camera2D({
|
|
|
3958
4383
|
}
|
|
3959
4384
|
|
|
3960
4385
|
// src/components/Animation.tsx
|
|
3961
|
-
import { useEffect as
|
|
4386
|
+
import { useEffect as useEffect16, useContext as useContext14 } from "react";
|
|
3962
4387
|
function Animation({ frames, fps = 12, loop = true, playing = true, onComplete, frameEvents }) {
|
|
3963
|
-
const engine =
|
|
3964
|
-
const entityId =
|
|
3965
|
-
|
|
4388
|
+
const engine = useContext14(EngineContext);
|
|
4389
|
+
const entityId = useContext14(EntityContext);
|
|
4390
|
+
useEffect16(() => {
|
|
3966
4391
|
const state = {
|
|
3967
4392
|
type: "AnimationState",
|
|
3968
4393
|
frames,
|
|
@@ -3980,7 +4405,7 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete,
|
|
|
3980
4405
|
engine.ecs.removeComponent(entityId, "AnimationState");
|
|
3981
4406
|
};
|
|
3982
4407
|
}, []);
|
|
3983
|
-
|
|
4408
|
+
useEffect16(() => {
|
|
3984
4409
|
const anim = engine.ecs.getComponent(entityId, "AnimationState");
|
|
3985
4410
|
if (!anim) return;
|
|
3986
4411
|
const wasFramesChanged = anim.frames !== frames;
|
|
@@ -4000,11 +4425,11 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete,
|
|
|
4000
4425
|
}
|
|
4001
4426
|
|
|
4002
4427
|
// src/components/SquashStretch.tsx
|
|
4003
|
-
import { useEffect as
|
|
4428
|
+
import { useEffect as useEffect17, useContext as useContext15 } from "react";
|
|
4004
4429
|
function SquashStretch({ intensity = 0.2, recovery = 8 }) {
|
|
4005
|
-
const engine =
|
|
4006
|
-
const entityId =
|
|
4007
|
-
|
|
4430
|
+
const engine = useContext15(EngineContext);
|
|
4431
|
+
const entityId = useContext15(EntityContext);
|
|
4432
|
+
useEffect17(() => {
|
|
4008
4433
|
engine.ecs.addComponent(entityId, {
|
|
4009
4434
|
type: "SquashStretch",
|
|
4010
4435
|
intensity,
|
|
@@ -4018,7 +4443,7 @@ function SquashStretch({ intensity = 0.2, recovery = 8 }) {
|
|
|
4018
4443
|
}
|
|
4019
4444
|
|
|
4020
4445
|
// src/components/ParticleEmitter.tsx
|
|
4021
|
-
import { useEffect as
|
|
4446
|
+
import { useEffect as useEffect18, useContext as useContext16 } from "react";
|
|
4022
4447
|
|
|
4023
4448
|
// src/components/particlePresets.ts
|
|
4024
4449
|
var PARTICLE_PRESETS = {
|
|
@@ -4103,9 +4528,9 @@ function ParticleEmitter({
|
|
|
4103
4528
|
const resolvedColor = color ?? presetConfig.color ?? "#ffffff";
|
|
4104
4529
|
const resolvedGravity = gravity ?? presetConfig.gravity ?? 200;
|
|
4105
4530
|
const resolvedMaxParticles = maxParticles ?? presetConfig.maxParticles ?? 100;
|
|
4106
|
-
const engine =
|
|
4107
|
-
const entityId =
|
|
4108
|
-
|
|
4531
|
+
const engine = useContext16(EngineContext);
|
|
4532
|
+
const entityId = useContext16(EntityContext);
|
|
4533
|
+
useEffect18(() => {
|
|
4109
4534
|
engine.ecs.addComponent(entityId, {
|
|
4110
4535
|
type: "ParticlePool",
|
|
4111
4536
|
particles: [],
|
|
@@ -4123,7 +4548,7 @@ function ParticleEmitter({
|
|
|
4123
4548
|
});
|
|
4124
4549
|
return () => engine.ecs.removeComponent(entityId, "ParticlePool");
|
|
4125
4550
|
}, []);
|
|
4126
|
-
|
|
4551
|
+
useEffect18(() => {
|
|
4127
4552
|
const pool = engine.ecs.getComponent(entityId, "ParticlePool");
|
|
4128
4553
|
if (!pool) return;
|
|
4129
4554
|
pool.active = active;
|
|
@@ -4357,7 +4782,7 @@ function Checkpoint({
|
|
|
4357
4782
|
}
|
|
4358
4783
|
|
|
4359
4784
|
// src/components/Tilemap.tsx
|
|
4360
|
-
import { useEffect as
|
|
4785
|
+
import { useEffect as useEffect19, useState as useState5, useContext as useContext17 } from "react";
|
|
4361
4786
|
import { Fragment as Fragment4, jsx as jsx8 } from "react/jsx-runtime";
|
|
4362
4787
|
var animatedTiles = /* @__PURE__ */ new Map();
|
|
4363
4788
|
function getProperty(props, name) {
|
|
@@ -4382,9 +4807,9 @@ function Tilemap({
|
|
|
4382
4807
|
onTileProperty,
|
|
4383
4808
|
navGrid
|
|
4384
4809
|
}) {
|
|
4385
|
-
const engine =
|
|
4810
|
+
const engine = useContext17(EngineContext);
|
|
4386
4811
|
const [spawnedNodes, setSpawnedNodes] = useState5([]);
|
|
4387
|
-
|
|
4812
|
+
useEffect19(() => {
|
|
4388
4813
|
if (!engine) return;
|
|
4389
4814
|
const createdEntities = [];
|
|
4390
4815
|
async function load() {
|
|
@@ -4575,7 +5000,7 @@ function Tilemap({
|
|
|
4575
5000
|
}
|
|
4576
5001
|
|
|
4577
5002
|
// src/components/ParallaxLayer.tsx
|
|
4578
|
-
import { useEffect as
|
|
5003
|
+
import { useEffect as useEffect20, useContext as useContext18 } from "react";
|
|
4579
5004
|
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4580
5005
|
function ParallaxLayerInner({
|
|
4581
5006
|
src,
|
|
@@ -4587,9 +5012,9 @@ function ParallaxLayerInner({
|
|
|
4587
5012
|
offsetX,
|
|
4588
5013
|
offsetY
|
|
4589
5014
|
}) {
|
|
4590
|
-
const engine =
|
|
4591
|
-
const entityId =
|
|
4592
|
-
|
|
5015
|
+
const engine = useContext18(EngineContext);
|
|
5016
|
+
const entityId = useContext18(EntityContext);
|
|
5017
|
+
useEffect20(() => {
|
|
4593
5018
|
engine.ecs.addComponent(entityId, {
|
|
4594
5019
|
type: "ParallaxLayer",
|
|
4595
5020
|
src,
|
|
@@ -4605,7 +5030,7 @@ function ParallaxLayerInner({
|
|
|
4605
5030
|
});
|
|
4606
5031
|
return () => engine.ecs.removeComponent(entityId, "ParallaxLayer");
|
|
4607
5032
|
}, []);
|
|
4608
|
-
|
|
5033
|
+
useEffect20(() => {
|
|
4609
5034
|
const layer = engine.ecs.getComponent(entityId, "ParallaxLayer");
|
|
4610
5035
|
if (!layer) return;
|
|
4611
5036
|
layer.src = src;
|
|
@@ -4687,7 +5112,7 @@ var ScreenFlash = forwardRef((_, ref) => {
|
|
|
4687
5112
|
ScreenFlash.displayName = "ScreenFlash";
|
|
4688
5113
|
|
|
4689
5114
|
// src/components/CameraZone.tsx
|
|
4690
|
-
import { useEffect as
|
|
5115
|
+
import { useEffect as useEffect21, useContext as useContext19, useRef as useRef6 } from "react";
|
|
4691
5116
|
import { Fragment as Fragment5, jsx as jsx11 } from "react/jsx-runtime";
|
|
4692
5117
|
function CameraZone({
|
|
4693
5118
|
x,
|
|
@@ -4699,10 +5124,10 @@ function CameraZone({
|
|
|
4699
5124
|
targetY,
|
|
4700
5125
|
children
|
|
4701
5126
|
}) {
|
|
4702
|
-
const engine =
|
|
5127
|
+
const engine = useContext19(EngineContext);
|
|
4703
5128
|
const prevFollowRef = useRef6(void 0);
|
|
4704
5129
|
const activeRef = useRef6(false);
|
|
4705
|
-
|
|
5130
|
+
useEffect21(() => {
|
|
4706
5131
|
const eid = engine.ecs.createEntity();
|
|
4707
5132
|
engine.ecs.addComponent(eid, createScript(() => {
|
|
4708
5133
|
const cam = engine.ecs.queryOne("Camera2D");
|
|
@@ -4749,26 +5174,56 @@ function CameraZone({
|
|
|
4749
5174
|
}
|
|
4750
5175
|
|
|
4751
5176
|
// src/components/Trail.tsx
|
|
4752
|
-
import { useEffect as
|
|
5177
|
+
import { useEffect as useEffect22, useContext as useContext20 } from "react";
|
|
4753
5178
|
function Trail({ length = 20, color = "#ffffff", width = 3 }) {
|
|
4754
|
-
const engine =
|
|
4755
|
-
const entityId =
|
|
4756
|
-
|
|
5179
|
+
const engine = useContext20(EngineContext);
|
|
5180
|
+
const entityId = useContext20(EntityContext);
|
|
5181
|
+
useEffect22(() => {
|
|
4757
5182
|
engine.ecs.addComponent(entityId, createTrail({ length, color, width }));
|
|
4758
5183
|
return () => engine.ecs.removeComponent(entityId, "Trail");
|
|
4759
5184
|
}, []);
|
|
4760
5185
|
return null;
|
|
4761
5186
|
}
|
|
4762
5187
|
|
|
5188
|
+
// src/components/NineSlice.tsx
|
|
5189
|
+
import { useEffect as useEffect23, useContext as useContext21 } from "react";
|
|
5190
|
+
function NineSlice({
|
|
5191
|
+
src,
|
|
5192
|
+
width,
|
|
5193
|
+
height,
|
|
5194
|
+
borderTop = 8,
|
|
5195
|
+
borderRight = 8,
|
|
5196
|
+
borderBottom = 8,
|
|
5197
|
+
borderLeft = 8,
|
|
5198
|
+
zIndex = 0
|
|
5199
|
+
}) {
|
|
5200
|
+
const engine = useContext21(EngineContext);
|
|
5201
|
+
const entityId = useContext21(EntityContext);
|
|
5202
|
+
useEffect23(() => {
|
|
5203
|
+
engine.ecs.addComponent(
|
|
5204
|
+
entityId,
|
|
5205
|
+
createNineSlice(src, width, height, {
|
|
5206
|
+
borderTop,
|
|
5207
|
+
borderRight,
|
|
5208
|
+
borderBottom,
|
|
5209
|
+
borderLeft,
|
|
5210
|
+
zIndex
|
|
5211
|
+
})
|
|
5212
|
+
);
|
|
5213
|
+
return () => engine.ecs.removeComponent(entityId, "NineSlice");
|
|
5214
|
+
}, []);
|
|
5215
|
+
return null;
|
|
5216
|
+
}
|
|
5217
|
+
|
|
4763
5218
|
// src/components/AssetLoader.tsx
|
|
4764
|
-
import { useEffect as
|
|
5219
|
+
import { useEffect as useEffect25 } from "react";
|
|
4765
5220
|
|
|
4766
5221
|
// src/hooks/usePreload.ts
|
|
4767
|
-
import { useState as useState6, useEffect as
|
|
5222
|
+
import { useState as useState6, useEffect as useEffect24, useContext as useContext22 } from "react";
|
|
4768
5223
|
function usePreload(assets) {
|
|
4769
|
-
const engine =
|
|
5224
|
+
const engine = useContext22(EngineContext);
|
|
4770
5225
|
const [state, setState] = useState6({ progress: assets.length === 0 ? 1 : 0, loaded: assets.length === 0, error: null });
|
|
4771
|
-
|
|
5226
|
+
useEffect24(() => {
|
|
4772
5227
|
if (assets.length === 0) {
|
|
4773
5228
|
setState({ progress: 1, loaded: true, error: null });
|
|
4774
5229
|
return;
|
|
@@ -4799,7 +5254,7 @@ function usePreload(assets) {
|
|
|
4799
5254
|
import { Fragment as Fragment6, jsx as jsx12 } from "react/jsx-runtime";
|
|
4800
5255
|
function AssetLoader({ assets, fallback = null, onError, children }) {
|
|
4801
5256
|
const { loaded, error } = usePreload(assets);
|
|
4802
|
-
|
|
5257
|
+
useEffect25(() => {
|
|
4803
5258
|
if (error && onError) onError(error);
|
|
4804
5259
|
}, [error, onError]);
|
|
4805
5260
|
if (!loaded) {
|
|
@@ -4809,9 +5264,9 @@ function AssetLoader({ assets, fallback = null, onError, children }) {
|
|
|
4809
5264
|
}
|
|
4810
5265
|
|
|
4811
5266
|
// src/hooks/useGame.ts
|
|
4812
|
-
import { useContext as
|
|
5267
|
+
import { useContext as useContext23 } from "react";
|
|
4813
5268
|
function useGame() {
|
|
4814
|
-
const engine =
|
|
5269
|
+
const engine = useContext23(EngineContext);
|
|
4815
5270
|
if (!engine) throw new Error("useGame must be used inside <Game>");
|
|
4816
5271
|
return engine;
|
|
4817
5272
|
}
|
|
@@ -4868,18 +5323,18 @@ function useSnapshot() {
|
|
|
4868
5323
|
}
|
|
4869
5324
|
|
|
4870
5325
|
// src/hooks/useEntity.ts
|
|
4871
|
-
import { useContext as
|
|
5326
|
+
import { useContext as useContext24 } from "react";
|
|
4872
5327
|
function useEntity() {
|
|
4873
|
-
const id =
|
|
5328
|
+
const id = useContext24(EntityContext);
|
|
4874
5329
|
if (id === null) throw new Error("useEntity must be used inside <Entity>");
|
|
4875
5330
|
return id;
|
|
4876
5331
|
}
|
|
4877
5332
|
|
|
4878
5333
|
// src/hooks/useDestroyEntity.ts
|
|
4879
|
-
import { useCallback as useCallback2, useContext as
|
|
5334
|
+
import { useCallback as useCallback2, useContext as useContext25 } from "react";
|
|
4880
5335
|
function useDestroyEntity() {
|
|
4881
|
-
const engine =
|
|
4882
|
-
const entityId =
|
|
5336
|
+
const engine = useContext25(EngineContext);
|
|
5337
|
+
const entityId = useContext25(EntityContext);
|
|
4883
5338
|
if (!engine) throw new Error("useDestroyEntity must be used inside <Game>");
|
|
4884
5339
|
if (entityId === null) throw new Error("useDestroyEntity must be used inside <Entity>");
|
|
4885
5340
|
return useCallback2(() => {
|
|
@@ -4890,9 +5345,9 @@ function useDestroyEntity() {
|
|
|
4890
5345
|
}
|
|
4891
5346
|
|
|
4892
5347
|
// src/hooks/useInput.ts
|
|
4893
|
-
import { useContext as
|
|
5348
|
+
import { useContext as useContext26 } from "react";
|
|
4894
5349
|
function useInput() {
|
|
4895
|
-
const engine =
|
|
5350
|
+
const engine = useContext26(EngineContext);
|
|
4896
5351
|
if (!engine) throw new Error("useInput must be used inside <Game>");
|
|
4897
5352
|
return engine.input;
|
|
4898
5353
|
}
|
|
@@ -4912,9 +5367,9 @@ function useInputMap(bindings) {
|
|
|
4912
5367
|
}
|
|
4913
5368
|
|
|
4914
5369
|
// src/hooks/useEvents.ts
|
|
4915
|
-
import { useContext as
|
|
5370
|
+
import { useContext as useContext27, useEffect as useEffect26, useRef as useRef7 } from "react";
|
|
4916
5371
|
function useEvents() {
|
|
4917
|
-
const engine =
|
|
5372
|
+
const engine = useContext27(EngineContext);
|
|
4918
5373
|
if (!engine) throw new Error("useEvents must be used inside <Game>");
|
|
4919
5374
|
return engine.events;
|
|
4920
5375
|
}
|
|
@@ -4922,15 +5377,15 @@ function useEvent(event, handler) {
|
|
|
4922
5377
|
const events = useEvents();
|
|
4923
5378
|
const handlerRef = useRef7(handler);
|
|
4924
5379
|
handlerRef.current = handler;
|
|
4925
|
-
|
|
5380
|
+
useEffect26(() => {
|
|
4926
5381
|
return events.on(event, (data) => handlerRef.current(data));
|
|
4927
5382
|
}, [events, event]);
|
|
4928
5383
|
}
|
|
4929
5384
|
|
|
4930
5385
|
// src/hooks/useCoordinates.ts
|
|
4931
|
-
import { useCallback as useCallback3, useContext as
|
|
5386
|
+
import { useCallback as useCallback3, useContext as useContext28 } from "react";
|
|
4932
5387
|
function useCoordinates() {
|
|
4933
|
-
const engine =
|
|
5388
|
+
const engine = useContext28(EngineContext);
|
|
4934
5389
|
const worldToScreen = useCallback3((wx, wy) => {
|
|
4935
5390
|
const canvas = engine.canvas;
|
|
4936
5391
|
const camId = engine.ecs.queryOne("Camera2D");
|
|
@@ -4957,9 +5412,9 @@ function useCoordinates() {
|
|
|
4957
5412
|
}
|
|
4958
5413
|
|
|
4959
5414
|
// src/hooks/useInputContext.ts
|
|
4960
|
-
import { useEffect as
|
|
5415
|
+
import { useEffect as useEffect27, useMemo as useMemo4 } from "react";
|
|
4961
5416
|
function useInputContext(ctx) {
|
|
4962
|
-
|
|
5417
|
+
useEffect27(() => {
|
|
4963
5418
|
if (!ctx) return;
|
|
4964
5419
|
globalInputContext.push(ctx);
|
|
4965
5420
|
return () => globalInputContext.pop(ctx);
|
|
@@ -4998,12 +5453,12 @@ function useInputRecorder() {
|
|
|
4998
5453
|
}
|
|
4999
5454
|
|
|
5000
5455
|
// src/hooks/useGamepad.ts
|
|
5001
|
-
import { useEffect as
|
|
5456
|
+
import { useEffect as useEffect28, useRef as useRef8, useState as useState7 } from "react";
|
|
5002
5457
|
var EMPTY_STATE = { connected: false, axes: [], buttons: [] };
|
|
5003
5458
|
function useGamepad(playerIndex = 0) {
|
|
5004
5459
|
const [state, setState] = useState7(EMPTY_STATE);
|
|
5005
5460
|
const rafRef = useRef8(0);
|
|
5006
|
-
|
|
5461
|
+
useEffect28(() => {
|
|
5007
5462
|
const poll = () => {
|
|
5008
5463
|
const gp = navigator.getGamepads()[playerIndex];
|
|
5009
5464
|
if (gp) {
|
|
@@ -5024,9 +5479,9 @@ function useGamepad(playerIndex = 0) {
|
|
|
5024
5479
|
}
|
|
5025
5480
|
|
|
5026
5481
|
// src/hooks/usePause.ts
|
|
5027
|
-
import { useContext as
|
|
5482
|
+
import { useContext as useContext29, useState as useState8, useCallback as useCallback4 } from "react";
|
|
5028
5483
|
function usePause() {
|
|
5029
|
-
const engine =
|
|
5484
|
+
const engine = useContext29(EngineContext);
|
|
5030
5485
|
const [paused, setPaused] = useState8(false);
|
|
5031
5486
|
const pause = useCallback4(() => {
|
|
5032
5487
|
engine.loop.pause();
|
|
@@ -5095,19 +5550,19 @@ function useAISteering() {
|
|
|
5095
5550
|
}
|
|
5096
5551
|
|
|
5097
5552
|
// ../gameplay/src/hooks/useDamageZone.ts
|
|
5098
|
-
import { useContext as
|
|
5553
|
+
import { useContext as useContext30 } from "react";
|
|
5099
5554
|
function useDamageZone(damage, opts = {}) {
|
|
5100
|
-
const engine =
|
|
5555
|
+
const engine = useContext30(EngineContext);
|
|
5101
5556
|
useTriggerEnter((other) => {
|
|
5102
5557
|
engine.events.emit(`damage:${other}`, { amount: damage });
|
|
5103
5558
|
}, { tag: opts.tag, layer: opts.layer });
|
|
5104
5559
|
}
|
|
5105
5560
|
|
|
5106
5561
|
// ../gameplay/src/hooks/useDropThrough.ts
|
|
5107
|
-
import { useContext as
|
|
5562
|
+
import { useContext as useContext31, useCallback as useCallback7 } from "react";
|
|
5108
5563
|
function useDropThrough(frames = 8) {
|
|
5109
|
-
const engine =
|
|
5110
|
-
const entityId =
|
|
5564
|
+
const engine = useContext31(EngineContext);
|
|
5565
|
+
const entityId = useContext31(EntityContext);
|
|
5111
5566
|
const dropThrough = useCallback7(() => {
|
|
5112
5567
|
const rb = engine.ecs.getComponent(entityId, "RigidBody");
|
|
5113
5568
|
if (rb) rb.dropThrough = frames;
|
|
@@ -5116,14 +5571,14 @@ function useDropThrough(frames = 8) {
|
|
|
5116
5571
|
}
|
|
5117
5572
|
|
|
5118
5573
|
// ../gameplay/src/hooks/useGameStateMachine.ts
|
|
5119
|
-
import { useState as useState10, useRef as useRef9, useCallback as useCallback8, useEffect as
|
|
5574
|
+
import { useState as useState10, useRef as useRef9, useCallback as useCallback8, useEffect as useEffect29, useContext as useContext32 } from "react";
|
|
5120
5575
|
function useGameStateMachine(states, initial) {
|
|
5121
|
-
const engine =
|
|
5576
|
+
const engine = useContext32(EngineContext);
|
|
5122
5577
|
const [state, setState] = useState10(initial);
|
|
5123
5578
|
const stateRef = useRef9(initial);
|
|
5124
5579
|
const statesRef = useRef9(states);
|
|
5125
5580
|
statesRef.current = states;
|
|
5126
|
-
|
|
5581
|
+
useEffect29(() => {
|
|
5127
5582
|
statesRef.current[initial]?.onEnter?.();
|
|
5128
5583
|
}, []);
|
|
5129
5584
|
const transition = useCallback8((to) => {
|
|
@@ -5134,7 +5589,7 @@ function useGameStateMachine(states, initial) {
|
|
|
5134
5589
|
setState(to);
|
|
5135
5590
|
statesRef.current[to]?.onEnter?.();
|
|
5136
5591
|
}, []);
|
|
5137
|
-
|
|
5592
|
+
useEffect29(() => {
|
|
5138
5593
|
const eid = engine.ecs.createEntity();
|
|
5139
5594
|
engine.ecs.addComponent(eid, createScript((_id, _world, _input, dt) => {
|
|
5140
5595
|
statesRef.current[stateRef.current]?.onUpdate?.(dt);
|
|
@@ -5147,19 +5602,19 @@ function useGameStateMachine(states, initial) {
|
|
|
5147
5602
|
}
|
|
5148
5603
|
|
|
5149
5604
|
// ../gameplay/src/hooks/useHealth.ts
|
|
5150
|
-
import { useRef as useRef10, useEffect as
|
|
5605
|
+
import { useRef as useRef10, useEffect as useEffect30, useContext as useContext33, useCallback as useCallback9 } from "react";
|
|
5151
5606
|
function useHealth(maxHp, opts = {}) {
|
|
5152
|
-
const engine =
|
|
5153
|
-
const entityId =
|
|
5607
|
+
const engine = useContext33(EngineContext);
|
|
5608
|
+
const entityId = useContext33(EntityContext);
|
|
5154
5609
|
const hpRef = useRef10(maxHp);
|
|
5155
5610
|
const invincibleRef = useRef10(false);
|
|
5156
5611
|
const iFrameDuration = opts.iFrames ?? 1;
|
|
5157
5612
|
const onDeathRef = useRef10(opts.onDeath);
|
|
5158
5613
|
const onDamageRef = useRef10(opts.onDamage);
|
|
5159
|
-
|
|
5614
|
+
useEffect30(() => {
|
|
5160
5615
|
onDeathRef.current = opts.onDeath;
|
|
5161
5616
|
});
|
|
5162
|
-
|
|
5617
|
+
useEffect30(() => {
|
|
5163
5618
|
onDamageRef.current = opts.onDamage;
|
|
5164
5619
|
});
|
|
5165
5620
|
const timerRef = useRef10(
|
|
@@ -5178,10 +5633,10 @@ function useHealth(maxHp, opts = {}) {
|
|
|
5178
5633
|
if (hpRef.current <= 0) onDeathRef.current?.();
|
|
5179
5634
|
}, [iFrameDuration]);
|
|
5180
5635
|
const takeDamageRef = useRef10(takeDamage);
|
|
5181
|
-
|
|
5636
|
+
useEffect30(() => {
|
|
5182
5637
|
takeDamageRef.current = takeDamage;
|
|
5183
5638
|
}, [takeDamage]);
|
|
5184
|
-
|
|
5639
|
+
useEffect30(() => {
|
|
5185
5640
|
return engine.events.on(`damage:${entityId}`, ({ amount }) => {
|
|
5186
5641
|
takeDamageRef.current(amount);
|
|
5187
5642
|
});
|
|
@@ -5216,10 +5671,10 @@ function useHealth(maxHp, opts = {}) {
|
|
|
5216
5671
|
}
|
|
5217
5672
|
|
|
5218
5673
|
// ../gameplay/src/hooks/useKinematicBody.ts
|
|
5219
|
-
import { useContext as
|
|
5674
|
+
import { useContext as useContext34, useCallback as useCallback10 } from "react";
|
|
5220
5675
|
function useKinematicBody() {
|
|
5221
|
-
const engine =
|
|
5222
|
-
const entityId =
|
|
5676
|
+
const engine = useContext34(EngineContext);
|
|
5677
|
+
const entityId = useContext34(EntityContext);
|
|
5223
5678
|
const moveAndCollide = useCallback10((dx, dy) => {
|
|
5224
5679
|
const transform = engine.ecs.getComponent(entityId, "Transform");
|
|
5225
5680
|
if (!transform) return { dx: 0, dy: 0 };
|
|
@@ -5306,13 +5761,13 @@ function useLevelTransition(initial) {
|
|
|
5306
5761
|
}
|
|
5307
5762
|
|
|
5308
5763
|
// ../gameplay/src/hooks/usePlatformerController.ts
|
|
5309
|
-
import { useContext as
|
|
5764
|
+
import { useContext as useContext35, useEffect as useEffect31 } from "react";
|
|
5310
5765
|
function normalizeKeys(val, defaults) {
|
|
5311
5766
|
if (!val) return defaults;
|
|
5312
5767
|
return Array.isArray(val) ? val : [val];
|
|
5313
5768
|
}
|
|
5314
5769
|
function usePlatformerController(entityId, opts = {}) {
|
|
5315
|
-
const engine =
|
|
5770
|
+
const engine = useContext35(EngineContext);
|
|
5316
5771
|
const {
|
|
5317
5772
|
speed = 200,
|
|
5318
5773
|
jumpForce = -500,
|
|
@@ -5325,7 +5780,7 @@ function usePlatformerController(entityId, opts = {}) {
|
|
|
5325
5780
|
const leftKeys = normalizeKeys(bindings?.left, ["ArrowLeft", "KeyA", "a"]);
|
|
5326
5781
|
const rightKeys = normalizeKeys(bindings?.right, ["ArrowRight", "KeyD", "d"]);
|
|
5327
5782
|
const jumpKeys = normalizeKeys(bindings?.jump, ["Space", "ArrowUp", "KeyW", "w"]);
|
|
5328
|
-
|
|
5783
|
+
useEffect31(() => {
|
|
5329
5784
|
const state = {
|
|
5330
5785
|
coyoteTimer: 0,
|
|
5331
5786
|
jumpBuffer: 0,
|
|
@@ -5389,9 +5844,9 @@ function usePathfinding() {
|
|
|
5389
5844
|
}
|
|
5390
5845
|
|
|
5391
5846
|
// ../gameplay/src/hooks/usePersistedBindings.ts
|
|
5392
|
-
import { useState as useState12, useCallback as useCallback13, useMemo as useMemo8, useContext as
|
|
5847
|
+
import { useState as useState12, useCallback as useCallback13, useMemo as useMemo8, useContext as useContext36 } from "react";
|
|
5393
5848
|
function usePersistedBindings(storageKey, defaults) {
|
|
5394
|
-
const engine =
|
|
5849
|
+
const engine = useContext36(EngineContext);
|
|
5395
5850
|
const input = engine.input;
|
|
5396
5851
|
const [bindings, setBindings] = useState12(() => {
|
|
5397
5852
|
try {
|
|
@@ -5496,11 +5951,11 @@ function useSave(key, defaultValue, opts = {}) {
|
|
|
5496
5951
|
}
|
|
5497
5952
|
|
|
5498
5953
|
// ../gameplay/src/hooks/useTopDownMovement.ts
|
|
5499
|
-
import { useContext as
|
|
5954
|
+
import { useContext as useContext37, useEffect as useEffect32 } from "react";
|
|
5500
5955
|
function useTopDownMovement(entityId, opts = {}) {
|
|
5501
|
-
const engine =
|
|
5956
|
+
const engine = useContext37(EngineContext);
|
|
5502
5957
|
const { speed = 200, normalizeDiagonal = true } = opts;
|
|
5503
|
-
|
|
5958
|
+
useEffect32(() => {
|
|
5504
5959
|
const updateFn = (id, world, input) => {
|
|
5505
5960
|
if (!world.hasEntity(id)) return;
|
|
5506
5961
|
const rb = world.getComponent(id, "RigidBody");
|
|
@@ -5524,8 +5979,190 @@ function useTopDownMovement(entityId, opts = {}) {
|
|
|
5524
5979
|
}, []);
|
|
5525
5980
|
}
|
|
5526
5981
|
|
|
5982
|
+
// ../gameplay/src/hooks/useDialogue.ts
|
|
5983
|
+
import { useState as useState14, useCallback as useCallback16, useRef as useRef13 } from "react";
|
|
5984
|
+
function useDialogue() {
|
|
5985
|
+
const [active, setActive] = useState14(false);
|
|
5986
|
+
const [currentId, setCurrentId] = useState14(null);
|
|
5987
|
+
const scriptRef = useRef13(null);
|
|
5988
|
+
const start = useCallback16((script, startId) => {
|
|
5989
|
+
scriptRef.current = script;
|
|
5990
|
+
const id = startId ?? Object.keys(script)[0];
|
|
5991
|
+
setCurrentId(id);
|
|
5992
|
+
setActive(true);
|
|
5993
|
+
}, []);
|
|
5994
|
+
const advance = useCallback16((choiceIndex) => {
|
|
5995
|
+
if (!scriptRef.current || !currentId) return;
|
|
5996
|
+
const line = scriptRef.current[currentId];
|
|
5997
|
+
if (!line) {
|
|
5998
|
+
setActive(false);
|
|
5999
|
+
setCurrentId(null);
|
|
6000
|
+
return;
|
|
6001
|
+
}
|
|
6002
|
+
if (line.choices && choiceIndex !== void 0) {
|
|
6003
|
+
const choice = line.choices[choiceIndex];
|
|
6004
|
+
if (choice?.next && scriptRef.current[choice.next]) {
|
|
6005
|
+
setCurrentId(choice.next);
|
|
6006
|
+
} else {
|
|
6007
|
+
setActive(false);
|
|
6008
|
+
setCurrentId(null);
|
|
6009
|
+
}
|
|
6010
|
+
} else {
|
|
6011
|
+
const keys = Object.keys(scriptRef.current);
|
|
6012
|
+
const idx = keys.indexOf(currentId);
|
|
6013
|
+
if (idx >= 0 && idx + 1 < keys.length) {
|
|
6014
|
+
setCurrentId(keys[idx + 1]);
|
|
6015
|
+
} else {
|
|
6016
|
+
setActive(false);
|
|
6017
|
+
setCurrentId(null);
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
}, [currentId]);
|
|
6021
|
+
const close = useCallback16(() => {
|
|
6022
|
+
setActive(false);
|
|
6023
|
+
setCurrentId(null);
|
|
6024
|
+
scriptRef.current = null;
|
|
6025
|
+
}, []);
|
|
6026
|
+
const current = scriptRef.current && currentId ? scriptRef.current[currentId] ?? null : null;
|
|
6027
|
+
return { active, current, currentId, start, advance, close };
|
|
6028
|
+
}
|
|
6029
|
+
|
|
6030
|
+
// ../gameplay/src/hooks/useCutscene.ts
|
|
6031
|
+
import { useState as useState15, useCallback as useCallback17, useRef as useRef14, useEffect as useEffect33, useContext as useContext38 } from "react";
|
|
6032
|
+
function useCutscene() {
|
|
6033
|
+
const engine = useContext38(EngineContext);
|
|
6034
|
+
const [playing, setPlaying] = useState15(false);
|
|
6035
|
+
const [stepIndex, setStepIndex] = useState15(0);
|
|
6036
|
+
const stepsRef = useRef14([]);
|
|
6037
|
+
const timerRef = useRef14(0);
|
|
6038
|
+
const idxRef = useRef14(0);
|
|
6039
|
+
const playingRef = useRef14(false);
|
|
6040
|
+
const entityRef = useRef14(null);
|
|
6041
|
+
const finish = useCallback17(() => {
|
|
6042
|
+
playingRef.current = false;
|
|
6043
|
+
setPlaying(false);
|
|
6044
|
+
setStepIndex(0);
|
|
6045
|
+
idxRef.current = 0;
|
|
6046
|
+
if (entityRef.current !== null && engine.ecs.hasEntity(entityRef.current)) {
|
|
6047
|
+
engine.ecs.destroyEntity(entityRef.current);
|
|
6048
|
+
entityRef.current = null;
|
|
6049
|
+
}
|
|
6050
|
+
}, [engine.ecs]);
|
|
6051
|
+
const fireStep = useCallback17((step) => {
|
|
6052
|
+
if (step.type === "call") step.fn();
|
|
6053
|
+
if (step.type === "parallel") step.steps.forEach((s2) => {
|
|
6054
|
+
if (s2.type === "call") s2.fn();
|
|
6055
|
+
});
|
|
6056
|
+
}, []);
|
|
6057
|
+
const play = useCallback17((steps) => {
|
|
6058
|
+
stepsRef.current = steps;
|
|
6059
|
+
idxRef.current = 0;
|
|
6060
|
+
timerRef.current = 0;
|
|
6061
|
+
playingRef.current = true;
|
|
6062
|
+
setPlaying(true);
|
|
6063
|
+
setStepIndex(0);
|
|
6064
|
+
const step = steps[0];
|
|
6065
|
+
if (step) fireStep(step);
|
|
6066
|
+
const eid = engine.ecs.createEntity();
|
|
6067
|
+
entityRef.current = eid;
|
|
6068
|
+
engine.ecs.addComponent(eid, createScript((_id, _world, _input, dt) => {
|
|
6069
|
+
if (!playingRef.current) return;
|
|
6070
|
+
const allSteps = stepsRef.current;
|
|
6071
|
+
const idx = idxRef.current;
|
|
6072
|
+
if (idx >= allSteps.length) {
|
|
6073
|
+
finish();
|
|
6074
|
+
return;
|
|
6075
|
+
}
|
|
6076
|
+
const current = allSteps[idx];
|
|
6077
|
+
if (current.type === "wait") {
|
|
6078
|
+
timerRef.current += dt;
|
|
6079
|
+
if (timerRef.current >= current.duration) {
|
|
6080
|
+
timerRef.current = 0;
|
|
6081
|
+
idxRef.current++;
|
|
6082
|
+
setStepIndex(idxRef.current);
|
|
6083
|
+
const next = allSteps[idxRef.current];
|
|
6084
|
+
if (next) fireStep(next);
|
|
6085
|
+
if (idxRef.current >= allSteps.length) finish();
|
|
6086
|
+
}
|
|
6087
|
+
} else if (current.type === "call") {
|
|
6088
|
+
idxRef.current++;
|
|
6089
|
+
setStepIndex(idxRef.current);
|
|
6090
|
+
const next = allSteps[idxRef.current];
|
|
6091
|
+
if (next) fireStep(next);
|
|
6092
|
+
if (idxRef.current >= allSteps.length) finish();
|
|
6093
|
+
} else if (current.type === "parallel") {
|
|
6094
|
+
const waits = current.steps.filter((s2) => s2.type === "wait");
|
|
6095
|
+
const maxDuration = waits.length > 0 ? Math.max(...waits.map((w) => w.duration)) : 0;
|
|
6096
|
+
timerRef.current += dt;
|
|
6097
|
+
if (timerRef.current >= maxDuration) {
|
|
6098
|
+
timerRef.current = 0;
|
|
6099
|
+
idxRef.current++;
|
|
6100
|
+
setStepIndex(idxRef.current);
|
|
6101
|
+
const next = allSteps[idxRef.current];
|
|
6102
|
+
if (next) fireStep(next);
|
|
6103
|
+
if (idxRef.current >= allSteps.length) finish();
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
}));
|
|
6107
|
+
}, [engine.ecs, finish, fireStep]);
|
|
6108
|
+
const skip = useCallback17(() => {
|
|
6109
|
+
for (let i = idxRef.current; i < stepsRef.current.length; i++) {
|
|
6110
|
+
const step = stepsRef.current[i];
|
|
6111
|
+
if (step.type === "call") step.fn();
|
|
6112
|
+
if (step.type === "parallel") step.steps.forEach((s2) => {
|
|
6113
|
+
if (s2.type === "call") s2.fn();
|
|
6114
|
+
});
|
|
6115
|
+
}
|
|
6116
|
+
finish();
|
|
6117
|
+
}, [finish]);
|
|
6118
|
+
useEffect33(() => {
|
|
6119
|
+
return () => {
|
|
6120
|
+
if (entityRef.current !== null && engine.ecs.hasEntity(entityRef.current)) {
|
|
6121
|
+
engine.ecs.destroyEntity(entityRef.current);
|
|
6122
|
+
}
|
|
6123
|
+
};
|
|
6124
|
+
}, [engine.ecs]);
|
|
6125
|
+
return { playing, stepIndex, play, skip };
|
|
6126
|
+
}
|
|
6127
|
+
|
|
6128
|
+
// ../gameplay/src/hooks/useGameStore.ts
|
|
6129
|
+
import { useSyncExternalStore, useCallback as useCallback18 } from "react";
|
|
6130
|
+
function createStore(initialState) {
|
|
6131
|
+
let state = { ...initialState };
|
|
6132
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
6133
|
+
return {
|
|
6134
|
+
getState: () => state,
|
|
6135
|
+
setState: (partial) => {
|
|
6136
|
+
const updates = typeof partial === "function" ? partial(state) : partial;
|
|
6137
|
+
state = { ...state, ...updates };
|
|
6138
|
+
listeners.forEach((l) => l());
|
|
6139
|
+
},
|
|
6140
|
+
subscribe: (listener) => {
|
|
6141
|
+
listeners.add(listener);
|
|
6142
|
+
return () => {
|
|
6143
|
+
listeners.delete(listener);
|
|
6144
|
+
};
|
|
6145
|
+
}
|
|
6146
|
+
};
|
|
6147
|
+
}
|
|
6148
|
+
var stores = /* @__PURE__ */ new Map();
|
|
6149
|
+
function useGameStore(key, initialState) {
|
|
6150
|
+
if (!stores.has(key)) {
|
|
6151
|
+
stores.set(key, createStore(initialState));
|
|
6152
|
+
}
|
|
6153
|
+
const store = stores.get(key);
|
|
6154
|
+
const state = useSyncExternalStore(
|
|
6155
|
+
store.subscribe,
|
|
6156
|
+
store.getState
|
|
6157
|
+
);
|
|
6158
|
+
const setState = useCallback18((partial) => {
|
|
6159
|
+
store.setState(partial);
|
|
6160
|
+
}, [store]);
|
|
6161
|
+
return [state, setState];
|
|
6162
|
+
}
|
|
6163
|
+
|
|
5527
6164
|
// ../../packages/audio/src/useSound.ts
|
|
5528
|
-
import { useEffect as
|
|
6165
|
+
import { useEffect as useEffect34, useRef as useRef15 } from "react";
|
|
5529
6166
|
var _audioCtx = null;
|
|
5530
6167
|
function getAudioCtx() {
|
|
5531
6168
|
if (!_audioCtx) _audioCtx = new AudioContext();
|
|
@@ -5592,13 +6229,13 @@ async function loadBuffer(src) {
|
|
|
5592
6229
|
return buf;
|
|
5593
6230
|
}
|
|
5594
6231
|
function useSound(src, opts = {}) {
|
|
5595
|
-
const bufferRef =
|
|
5596
|
-
const sourceRef =
|
|
5597
|
-
const gainRef =
|
|
5598
|
-
const volRef =
|
|
5599
|
-
const loopRef =
|
|
5600
|
-
const groupRef =
|
|
5601
|
-
|
|
6232
|
+
const bufferRef = useRef15(null);
|
|
6233
|
+
const sourceRef = useRef15(null);
|
|
6234
|
+
const gainRef = useRef15(null);
|
|
6235
|
+
const volRef = useRef15(opts.volume ?? 1);
|
|
6236
|
+
const loopRef = useRef15(opts.loop ?? false);
|
|
6237
|
+
const groupRef = useRef15(opts.group);
|
|
6238
|
+
useEffect34(() => {
|
|
5602
6239
|
let cancelled = false;
|
|
5603
6240
|
loadBuffer(src).then((buf) => {
|
|
5604
6241
|
if (!cancelled) bufferRef.current = buf;
|
|
@@ -5742,10 +6379,12 @@ export {
|
|
|
5742
6379
|
CapsuleCollider,
|
|
5743
6380
|
Checkpoint,
|
|
5744
6381
|
CircleCollider,
|
|
6382
|
+
CompoundCollider,
|
|
5745
6383
|
Ease,
|
|
5746
6384
|
Entity,
|
|
5747
6385
|
Game,
|
|
5748
6386
|
MovingPlatform,
|
|
6387
|
+
NineSlice,
|
|
5749
6388
|
ParallaxLayer,
|
|
5750
6389
|
ParticleEmitter,
|
|
5751
6390
|
RenderSystem,
|
|
@@ -5762,8 +6401,10 @@ export {
|
|
|
5762
6401
|
World,
|
|
5763
6402
|
arrive,
|
|
5764
6403
|
createAtlas,
|
|
6404
|
+
createCompoundCollider,
|
|
5765
6405
|
createInputMap,
|
|
5766
6406
|
createInputRecorder,
|
|
6407
|
+
createNineSlice,
|
|
5767
6408
|
createPlayerInput,
|
|
5768
6409
|
createSprite,
|
|
5769
6410
|
createTag,
|
|
@@ -5775,6 +6416,7 @@ export {
|
|
|
5775
6416
|
flee,
|
|
5776
6417
|
getGroupVolume,
|
|
5777
6418
|
globalInputContext,
|
|
6419
|
+
hotReloadPlugin,
|
|
5778
6420
|
overlapBox,
|
|
5779
6421
|
overlapCircle,
|
|
5780
6422
|
patrol,
|
|
@@ -5798,14 +6440,17 @@ export {
|
|
|
5798
6440
|
useCollisionExit,
|
|
5799
6441
|
useCollisionStay,
|
|
5800
6442
|
useCoordinates,
|
|
6443
|
+
useCutscene,
|
|
5801
6444
|
useDamageZone,
|
|
5802
6445
|
useDestroyEntity,
|
|
6446
|
+
useDialogue,
|
|
5803
6447
|
useDropThrough,
|
|
5804
6448
|
useEntity,
|
|
5805
6449
|
useEvent,
|
|
5806
6450
|
useEvents,
|
|
5807
6451
|
useGame,
|
|
5808
6452
|
useGameStateMachine,
|
|
6453
|
+
useGameStore,
|
|
5809
6454
|
useGamepad,
|
|
5810
6455
|
useHealth,
|
|
5811
6456
|
useInput,
|