cubeforge 0.3.7 → 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 +766 -111
- 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,11 +1528,26 @@ 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);
|
|
1504
1548
|
if (cached) return cached;
|
|
1505
|
-
const
|
|
1549
|
+
const imgSrc = src.endsWith(":repeat") ? src.slice(0, -7) : src;
|
|
1550
|
+
const existing = this.imageCache.get(imgSrc);
|
|
1506
1551
|
if (existing && existing.complete && existing.naturalWidth > 0) {
|
|
1507
1552
|
const gl = this.gl;
|
|
1508
1553
|
const tex = gl.createTexture();
|
|
@@ -1518,7 +1563,8 @@ var RenderSystem = class {
|
|
|
1518
1563
|
}
|
|
1519
1564
|
if (!existing) {
|
|
1520
1565
|
const img = new Image();
|
|
1521
|
-
img.src =
|
|
1566
|
+
img.src = imgSrc;
|
|
1567
|
+
const tiled = src.endsWith(":repeat");
|
|
1522
1568
|
img.onload = () => {
|
|
1523
1569
|
const gl = this.gl;
|
|
1524
1570
|
const tex = gl.createTexture();
|
|
@@ -1527,11 +1573,12 @@ var RenderSystem = class {
|
|
|
1527
1573
|
gl.generateMipmap(gl.TEXTURE_2D);
|
|
1528
1574
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
1529
1575
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1530
|
-
|
|
1531
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.
|
|
1576
|
+
const wrap = tiled ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
1577
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
|
|
1578
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
|
|
1532
1579
|
this.textures.set(src, tex);
|
|
1533
1580
|
};
|
|
1534
|
-
this.imageCache.set(
|
|
1581
|
+
this.imageCache.set(imgSrc, img);
|
|
1535
1582
|
}
|
|
1536
1583
|
return this.whiteTexture;
|
|
1537
1584
|
}
|
|
@@ -1674,8 +1721,14 @@ var RenderSystem = class {
|
|
|
1674
1721
|
if (dy > halfH) cam.y = t.y - halfH;
|
|
1675
1722
|
else if (dy < -halfH) cam.y = t.y + halfH;
|
|
1676
1723
|
} else if (cam.smoothing > 0) {
|
|
1677
|
-
|
|
1678
|
-
|
|
1724
|
+
const distSq = (t.x - cam.x) ** 2 + (t.y - cam.y) ** 2;
|
|
1725
|
+
if (distSq > 16e4) {
|
|
1726
|
+
cam.x = t.x;
|
|
1727
|
+
cam.y = t.y;
|
|
1728
|
+
} else {
|
|
1729
|
+
cam.x += (t.x - cam.x) * (1 - cam.smoothing);
|
|
1730
|
+
cam.y += (t.y - cam.y) * (1 - cam.smoothing);
|
|
1731
|
+
}
|
|
1679
1732
|
} else {
|
|
1680
1733
|
cam.x = t.x;
|
|
1681
1734
|
cam.y = t.y;
|
|
@@ -1835,7 +1888,8 @@ var RenderSystem = class {
|
|
|
1835
1888
|
const ss = world.getComponent(id, "SquashStretch");
|
|
1836
1889
|
const scaleXMod = ss ? ss.currentScaleX : 1;
|
|
1837
1890
|
const scaleYMod = ss ? ss.currentScaleY : 1;
|
|
1838
|
-
const
|
|
1891
|
+
const hasTexture = sprite.image && sprite.image.complete && sprite.image.naturalWidth > 0;
|
|
1892
|
+
const [r, g, b, a] = hasTexture ? [1, 1, 1, 1] : parseCSSColor(sprite.color);
|
|
1839
1893
|
const uv = getUVRect(sprite);
|
|
1840
1894
|
this.writeInstance(
|
|
1841
1895
|
batchCount * FLOATS_PER_INSTANCE,
|
|
@@ -2004,6 +2058,84 @@ var RenderSystem = class {
|
|
|
2004
2058
|
}
|
|
2005
2059
|
if (tCount > 0) this.flush(tCount, "__color__");
|
|
2006
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;
|
|
2007
2139
|
}
|
|
2008
2140
|
};
|
|
2009
2141
|
|
|
@@ -2099,6 +2231,18 @@ function createCapsuleCollider(width, height, opts) {
|
|
|
2099
2231
|
};
|
|
2100
2232
|
}
|
|
2101
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
|
+
|
|
2102
2246
|
// ../../packages/physics/src/physicsSystem.ts
|
|
2103
2247
|
function getAABB(transform, collider) {
|
|
2104
2248
|
return {
|
|
@@ -2139,6 +2283,77 @@ function maskAllows(mask, layer) {
|
|
|
2139
2283
|
function canInteract(a, b) {
|
|
2140
2284
|
return maskAllows(a.mask, b.layer) && maskAllows(b.mask, a.layer);
|
|
2141
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
|
+
}
|
|
2142
2357
|
function pairKey(a, b) {
|
|
2143
2358
|
return a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
2144
2359
|
}
|
|
@@ -2153,6 +2368,7 @@ var PhysicsSystem = class {
|
|
|
2153
2368
|
activeTriggerPairs = /* @__PURE__ */ new Map();
|
|
2154
2369
|
activeCollisionPairs = /* @__PURE__ */ new Map();
|
|
2155
2370
|
activeCirclePairs = /* @__PURE__ */ new Map();
|
|
2371
|
+
activeCompoundPairs = /* @__PURE__ */ new Map();
|
|
2156
2372
|
// Previous-frame positions of static entities — used to compute platform carry delta.
|
|
2157
2373
|
staticPrevPos = /* @__PURE__ */ new Map();
|
|
2158
2374
|
setGravity(g) {
|
|
@@ -2207,6 +2423,12 @@ var PhysicsSystem = class {
|
|
|
2207
2423
|
this.activeCirclePairs.delete(key);
|
|
2208
2424
|
}
|
|
2209
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
|
+
}
|
|
2210
2432
|
const staticDelta = /* @__PURE__ */ new Map();
|
|
2211
2433
|
for (const sid of statics) {
|
|
2212
2434
|
const st = world.getComponent(sid, "Transform");
|
|
@@ -2509,6 +2731,101 @@ var PhysicsSystem = class {
|
|
|
2509
2731
|
}
|
|
2510
2732
|
this.activeCirclePairs = /* @__PURE__ */ new Map();
|
|
2511
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
|
+
}
|
|
2512
2829
|
}
|
|
2513
2830
|
};
|
|
2514
2831
|
|
|
@@ -2952,6 +3269,8 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
2952
3269
|
const [activeTab, setActiveTab] = useState("entities");
|
|
2953
3270
|
const [entitySearch, setEntitySearch] = useState("");
|
|
2954
3271
|
const [contactLog, setContactLog] = useState([]);
|
|
3272
|
+
const [showNavGrid, setShowNavGrid] = useState(false);
|
|
3273
|
+
const [showContactFlash, setShowContactFlash] = useState(false);
|
|
2955
3274
|
const frameRef = useRef2(0);
|
|
2956
3275
|
useEffect2(() => {
|
|
2957
3276
|
handle.onFrame = () => {
|
|
@@ -2977,10 +3296,24 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
2977
3296
|
if (next.length > 20) next.length = 20;
|
|
2978
3297
|
return next;
|
|
2979
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
|
+
}
|
|
2980
3313
|
})
|
|
2981
3314
|
);
|
|
2982
3315
|
return () => unsubs.forEach((u) => u());
|
|
2983
|
-
}, [engine]);
|
|
3316
|
+
}, [engine, showContactFlash]);
|
|
2984
3317
|
const totalFrames = handle.buffer.length;
|
|
2985
3318
|
const currentSnap = handle.buffer[selectedIdx];
|
|
2986
3319
|
const handlePauseResume = useCallback(() => {
|
|
@@ -3003,6 +3336,51 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
3003
3336
|
setSelectedIdx((i) => Math.min(handle.buffer.length - 1, i + 1));
|
|
3004
3337
|
setSelectedEntity(null);
|
|
3005
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]);
|
|
3006
3384
|
const frameLabel = totalFrames === 0 ? "0/0" : `${selectedIdx + 1}/${totalFrames}`;
|
|
3007
3385
|
const entities = currentSnap?.entities ?? [];
|
|
3008
3386
|
const filtered = entitySearch ? entities.filter(
|
|
@@ -3042,9 +3420,27 @@ function DevToolsOverlay({ handle, loop, ecs, engine }) {
|
|
|
3042
3420
|
selectedEntityData
|
|
3043
3421
|
}
|
|
3044
3422
|
),
|
|
3045
|
-
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
|
+
),
|
|
3046
3434
|
activeTab === "input" && /* @__PURE__ */ jsx(InputTab, { activeKeys, inputCtx }),
|
|
3047
|
-
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
|
+
),
|
|
3048
3444
|
activeTab === "assets" && /* @__PURE__ */ jsx(AssetsTab, { assetCache })
|
|
3049
3445
|
] }),
|
|
3050
3446
|
/* @__PURE__ */ jsxs("div", { style: s.bar, children: [
|
|
@@ -3143,7 +3539,7 @@ function EntitiesTab({ entities, entitySearch, onSearchChange, selectedEntity, o
|
|
|
3143
3539
|
] }, comp.type)) })
|
|
3144
3540
|
] });
|
|
3145
3541
|
}
|
|
3146
|
-
function PerfTab({ fps, entityCount, compCount, timings }) {
|
|
3542
|
+
function PerfTab({ fps, entityCount, compCount, timings, showNavGrid, onToggleNavGrid }) {
|
|
3147
3543
|
const maxMs = 16.67;
|
|
3148
3544
|
const stats = [
|
|
3149
3545
|
{ label: "FPS", value: String(fps), ok: fps >= 55 },
|
|
@@ -3155,6 +3551,10 @@ function PerfTab({ fps, entityCount, compCount, timings }) {
|
|
|
3155
3551
|
/* @__PURE__ */ jsx("span", { style: { fontSize: 9, color: C.muted }, children: label }),
|
|
3156
3552
|
/* @__PURE__ */ jsx("span", { style: { fontSize: 16, fontWeight: 700, color: ok ? C.ok : C.warn }, children: value })
|
|
3157
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
|
+
] }) }),
|
|
3158
3558
|
timings && timings.size > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
3159
3559
|
/* @__PURE__ */ jsx("div", { style: { color: C.muted, fontSize: 9, marginBottom: 6, letterSpacing: "0.08em" }, children: "SYSTEM TIMING" }),
|
|
3160
3560
|
Array.from(timings.entries()).map(([name, ms]) => {
|
|
@@ -3204,11 +3604,17 @@ function InputTab({ activeKeys, inputCtx }) {
|
|
|
3204
3604
|
] })
|
|
3205
3605
|
] });
|
|
3206
3606
|
}
|
|
3207
|
-
function ContactsTab({ log, onClear }) {
|
|
3607
|
+
function ContactsTab({ log, onClear, showFlash, onToggleFlash }) {
|
|
3208
3608
|
return /* @__PURE__ */ jsxs("div", { style: { padding: "4px 0" }, children: [
|
|
3209
3609
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 14px 8px" }, children: [
|
|
3210
3610
|
/* @__PURE__ */ jsx("span", { style: { color: C.muted, fontSize: 9 }, children: "Last 20 contact events" }),
|
|
3211
|
-
/* @__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
|
+
] })
|
|
3212
3618
|
] }),
|
|
3213
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: [
|
|
3214
3620
|
/* @__PURE__ */ jsxs("span", { style: { color: C.muted, fontSize: 9, minWidth: 46 }, children: [
|
|
@@ -3693,7 +4099,9 @@ function Sprite({
|
|
|
3693
4099
|
atlas,
|
|
3694
4100
|
frame,
|
|
3695
4101
|
tileX,
|
|
3696
|
-
tileY
|
|
4102
|
+
tileY,
|
|
4103
|
+
tileSizeX,
|
|
4104
|
+
tileSizeY
|
|
3697
4105
|
}) {
|
|
3698
4106
|
const resolvedFrameIndex = atlas && frame != null ? atlas[frame] ?? 0 : frameIndex;
|
|
3699
4107
|
const engine = useContext5(EngineContext);
|
|
@@ -3716,7 +4124,9 @@ function Sprite({
|
|
|
3716
4124
|
frameHeight,
|
|
3717
4125
|
frameColumns,
|
|
3718
4126
|
tileX,
|
|
3719
|
-
tileY
|
|
4127
|
+
tileY,
|
|
4128
|
+
tileSizeX,
|
|
4129
|
+
tileSizeY
|
|
3720
4130
|
});
|
|
3721
4131
|
engine.ecs.addComponent(entityId, comp);
|
|
3722
4132
|
if (src) {
|
|
@@ -3879,12 +4289,37 @@ function CapsuleCollider({
|
|
|
3879
4289
|
return null;
|
|
3880
4290
|
}
|
|
3881
4291
|
|
|
3882
|
-
// src/components/
|
|
4292
|
+
// src/components/CompoundCollider.tsx
|
|
3883
4293
|
import { useEffect as useEffect13, useContext as useContext11 } from "react";
|
|
3884
|
-
function
|
|
4294
|
+
function CompoundCollider({
|
|
4295
|
+
shapes,
|
|
4296
|
+
isTrigger = false,
|
|
4297
|
+
layer = "default",
|
|
4298
|
+
mask = "*"
|
|
4299
|
+
}) {
|
|
3885
4300
|
const engine = useContext11(EngineContext);
|
|
3886
4301
|
const entityId = useContext11(EntityContext);
|
|
3887
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(() => {
|
|
3888
4323
|
if (init) {
|
|
3889
4324
|
try {
|
|
3890
4325
|
init(entityId, engine.ecs);
|
|
@@ -3899,7 +4334,7 @@ function Script({ init, update }) {
|
|
|
3899
4334
|
}
|
|
3900
4335
|
|
|
3901
4336
|
// src/components/Camera2D.tsx
|
|
3902
|
-
import { useEffect as
|
|
4337
|
+
import { useEffect as useEffect15, useContext as useContext13 } from "react";
|
|
3903
4338
|
function Camera2D({
|
|
3904
4339
|
followEntity,
|
|
3905
4340
|
x = 0,
|
|
@@ -3912,8 +4347,8 @@ function Camera2D({
|
|
|
3912
4347
|
followOffsetX = 0,
|
|
3913
4348
|
followOffsetY = 0
|
|
3914
4349
|
}) {
|
|
3915
|
-
const engine =
|
|
3916
|
-
|
|
4350
|
+
const engine = useContext13(EngineContext);
|
|
4351
|
+
useEffect15(() => {
|
|
3917
4352
|
const entityId = engine.ecs.createEntity();
|
|
3918
4353
|
engine.ecs.addComponent(entityId, createCamera2D({
|
|
3919
4354
|
followEntityId: followEntity,
|
|
@@ -3929,7 +4364,7 @@ function Camera2D({
|
|
|
3929
4364
|
}));
|
|
3930
4365
|
return () => engine.ecs.destroyEntity(entityId);
|
|
3931
4366
|
}, []);
|
|
3932
|
-
|
|
4367
|
+
useEffect15(() => {
|
|
3933
4368
|
const camId = engine.ecs.queryOne("Camera2D");
|
|
3934
4369
|
if (camId === void 0) return;
|
|
3935
4370
|
const cam = engine.ecs.getComponent(camId, "Camera2D");
|
|
@@ -3948,11 +4383,11 @@ function Camera2D({
|
|
|
3948
4383
|
}
|
|
3949
4384
|
|
|
3950
4385
|
// src/components/Animation.tsx
|
|
3951
|
-
import { useEffect as
|
|
4386
|
+
import { useEffect as useEffect16, useContext as useContext14 } from "react";
|
|
3952
4387
|
function Animation({ frames, fps = 12, loop = true, playing = true, onComplete, frameEvents }) {
|
|
3953
|
-
const engine =
|
|
3954
|
-
const entityId =
|
|
3955
|
-
|
|
4388
|
+
const engine = useContext14(EngineContext);
|
|
4389
|
+
const entityId = useContext14(EntityContext);
|
|
4390
|
+
useEffect16(() => {
|
|
3956
4391
|
const state = {
|
|
3957
4392
|
type: "AnimationState",
|
|
3958
4393
|
frames,
|
|
@@ -3970,7 +4405,7 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete,
|
|
|
3970
4405
|
engine.ecs.removeComponent(entityId, "AnimationState");
|
|
3971
4406
|
};
|
|
3972
4407
|
}, []);
|
|
3973
|
-
|
|
4408
|
+
useEffect16(() => {
|
|
3974
4409
|
const anim = engine.ecs.getComponent(entityId, "AnimationState");
|
|
3975
4410
|
if (!anim) return;
|
|
3976
4411
|
const wasFramesChanged = anim.frames !== frames;
|
|
@@ -3990,11 +4425,11 @@ function Animation({ frames, fps = 12, loop = true, playing = true, onComplete,
|
|
|
3990
4425
|
}
|
|
3991
4426
|
|
|
3992
4427
|
// src/components/SquashStretch.tsx
|
|
3993
|
-
import { useEffect as
|
|
4428
|
+
import { useEffect as useEffect17, useContext as useContext15 } from "react";
|
|
3994
4429
|
function SquashStretch({ intensity = 0.2, recovery = 8 }) {
|
|
3995
|
-
const engine =
|
|
3996
|
-
const entityId =
|
|
3997
|
-
|
|
4430
|
+
const engine = useContext15(EngineContext);
|
|
4431
|
+
const entityId = useContext15(EntityContext);
|
|
4432
|
+
useEffect17(() => {
|
|
3998
4433
|
engine.ecs.addComponent(entityId, {
|
|
3999
4434
|
type: "SquashStretch",
|
|
4000
4435
|
intensity,
|
|
@@ -4008,7 +4443,7 @@ function SquashStretch({ intensity = 0.2, recovery = 8 }) {
|
|
|
4008
4443
|
}
|
|
4009
4444
|
|
|
4010
4445
|
// src/components/ParticleEmitter.tsx
|
|
4011
|
-
import { useEffect as
|
|
4446
|
+
import { useEffect as useEffect18, useContext as useContext16 } from "react";
|
|
4012
4447
|
|
|
4013
4448
|
// src/components/particlePresets.ts
|
|
4014
4449
|
var PARTICLE_PRESETS = {
|
|
@@ -4093,9 +4528,9 @@ function ParticleEmitter({
|
|
|
4093
4528
|
const resolvedColor = color ?? presetConfig.color ?? "#ffffff";
|
|
4094
4529
|
const resolvedGravity = gravity ?? presetConfig.gravity ?? 200;
|
|
4095
4530
|
const resolvedMaxParticles = maxParticles ?? presetConfig.maxParticles ?? 100;
|
|
4096
|
-
const engine =
|
|
4097
|
-
const entityId =
|
|
4098
|
-
|
|
4531
|
+
const engine = useContext16(EngineContext);
|
|
4532
|
+
const entityId = useContext16(EntityContext);
|
|
4533
|
+
useEffect18(() => {
|
|
4099
4534
|
engine.ecs.addComponent(entityId, {
|
|
4100
4535
|
type: "ParticlePool",
|
|
4101
4536
|
particles: [],
|
|
@@ -4113,7 +4548,7 @@ function ParticleEmitter({
|
|
|
4113
4548
|
});
|
|
4114
4549
|
return () => engine.ecs.removeComponent(entityId, "ParticlePool");
|
|
4115
4550
|
}, []);
|
|
4116
|
-
|
|
4551
|
+
useEffect18(() => {
|
|
4117
4552
|
const pool = engine.ecs.getComponent(entityId, "ParticlePool");
|
|
4118
4553
|
if (!pool) return;
|
|
4119
4554
|
pool.active = active;
|
|
@@ -4347,7 +4782,7 @@ function Checkpoint({
|
|
|
4347
4782
|
}
|
|
4348
4783
|
|
|
4349
4784
|
// src/components/Tilemap.tsx
|
|
4350
|
-
import { useEffect as
|
|
4785
|
+
import { useEffect as useEffect19, useState as useState5, useContext as useContext17 } from "react";
|
|
4351
4786
|
import { Fragment as Fragment4, jsx as jsx8 } from "react/jsx-runtime";
|
|
4352
4787
|
var animatedTiles = /* @__PURE__ */ new Map();
|
|
4353
4788
|
function getProperty(props, name) {
|
|
@@ -4372,9 +4807,9 @@ function Tilemap({
|
|
|
4372
4807
|
onTileProperty,
|
|
4373
4808
|
navGrid
|
|
4374
4809
|
}) {
|
|
4375
|
-
const engine =
|
|
4810
|
+
const engine = useContext17(EngineContext);
|
|
4376
4811
|
const [spawnedNodes, setSpawnedNodes] = useState5([]);
|
|
4377
|
-
|
|
4812
|
+
useEffect19(() => {
|
|
4378
4813
|
if (!engine) return;
|
|
4379
4814
|
const createdEntities = [];
|
|
4380
4815
|
async function load() {
|
|
@@ -4565,7 +5000,7 @@ function Tilemap({
|
|
|
4565
5000
|
}
|
|
4566
5001
|
|
|
4567
5002
|
// src/components/ParallaxLayer.tsx
|
|
4568
|
-
import { useEffect as
|
|
5003
|
+
import { useEffect as useEffect20, useContext as useContext18 } from "react";
|
|
4569
5004
|
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4570
5005
|
function ParallaxLayerInner({
|
|
4571
5006
|
src,
|
|
@@ -4577,9 +5012,9 @@ function ParallaxLayerInner({
|
|
|
4577
5012
|
offsetX,
|
|
4578
5013
|
offsetY
|
|
4579
5014
|
}) {
|
|
4580
|
-
const engine =
|
|
4581
|
-
const entityId =
|
|
4582
|
-
|
|
5015
|
+
const engine = useContext18(EngineContext);
|
|
5016
|
+
const entityId = useContext18(EntityContext);
|
|
5017
|
+
useEffect20(() => {
|
|
4583
5018
|
engine.ecs.addComponent(entityId, {
|
|
4584
5019
|
type: "ParallaxLayer",
|
|
4585
5020
|
src,
|
|
@@ -4595,7 +5030,7 @@ function ParallaxLayerInner({
|
|
|
4595
5030
|
});
|
|
4596
5031
|
return () => engine.ecs.removeComponent(entityId, "ParallaxLayer");
|
|
4597
5032
|
}, []);
|
|
4598
|
-
|
|
5033
|
+
useEffect20(() => {
|
|
4599
5034
|
const layer = engine.ecs.getComponent(entityId, "ParallaxLayer");
|
|
4600
5035
|
if (!layer) return;
|
|
4601
5036
|
layer.src = src;
|
|
@@ -4677,7 +5112,7 @@ var ScreenFlash = forwardRef((_, ref) => {
|
|
|
4677
5112
|
ScreenFlash.displayName = "ScreenFlash";
|
|
4678
5113
|
|
|
4679
5114
|
// src/components/CameraZone.tsx
|
|
4680
|
-
import { useEffect as
|
|
5115
|
+
import { useEffect as useEffect21, useContext as useContext19, useRef as useRef6 } from "react";
|
|
4681
5116
|
import { Fragment as Fragment5, jsx as jsx11 } from "react/jsx-runtime";
|
|
4682
5117
|
function CameraZone({
|
|
4683
5118
|
x,
|
|
@@ -4689,10 +5124,10 @@ function CameraZone({
|
|
|
4689
5124
|
targetY,
|
|
4690
5125
|
children
|
|
4691
5126
|
}) {
|
|
4692
|
-
const engine =
|
|
5127
|
+
const engine = useContext19(EngineContext);
|
|
4693
5128
|
const prevFollowRef = useRef6(void 0);
|
|
4694
5129
|
const activeRef = useRef6(false);
|
|
4695
|
-
|
|
5130
|
+
useEffect21(() => {
|
|
4696
5131
|
const eid = engine.ecs.createEntity();
|
|
4697
5132
|
engine.ecs.addComponent(eid, createScript(() => {
|
|
4698
5133
|
const cam = engine.ecs.queryOne("Camera2D");
|
|
@@ -4739,26 +5174,56 @@ function CameraZone({
|
|
|
4739
5174
|
}
|
|
4740
5175
|
|
|
4741
5176
|
// src/components/Trail.tsx
|
|
4742
|
-
import { useEffect as
|
|
5177
|
+
import { useEffect as useEffect22, useContext as useContext20 } from "react";
|
|
4743
5178
|
function Trail({ length = 20, color = "#ffffff", width = 3 }) {
|
|
4744
|
-
const engine =
|
|
4745
|
-
const entityId =
|
|
4746
|
-
|
|
5179
|
+
const engine = useContext20(EngineContext);
|
|
5180
|
+
const entityId = useContext20(EntityContext);
|
|
5181
|
+
useEffect22(() => {
|
|
4747
5182
|
engine.ecs.addComponent(entityId, createTrail({ length, color, width }));
|
|
4748
5183
|
return () => engine.ecs.removeComponent(entityId, "Trail");
|
|
4749
5184
|
}, []);
|
|
4750
5185
|
return null;
|
|
4751
5186
|
}
|
|
4752
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
|
+
|
|
4753
5218
|
// src/components/AssetLoader.tsx
|
|
4754
|
-
import { useEffect as
|
|
5219
|
+
import { useEffect as useEffect25 } from "react";
|
|
4755
5220
|
|
|
4756
5221
|
// src/hooks/usePreload.ts
|
|
4757
|
-
import { useState as useState6, useEffect as
|
|
5222
|
+
import { useState as useState6, useEffect as useEffect24, useContext as useContext22 } from "react";
|
|
4758
5223
|
function usePreload(assets) {
|
|
4759
|
-
const engine =
|
|
5224
|
+
const engine = useContext22(EngineContext);
|
|
4760
5225
|
const [state, setState] = useState6({ progress: assets.length === 0 ? 1 : 0, loaded: assets.length === 0, error: null });
|
|
4761
|
-
|
|
5226
|
+
useEffect24(() => {
|
|
4762
5227
|
if (assets.length === 0) {
|
|
4763
5228
|
setState({ progress: 1, loaded: true, error: null });
|
|
4764
5229
|
return;
|
|
@@ -4789,7 +5254,7 @@ function usePreload(assets) {
|
|
|
4789
5254
|
import { Fragment as Fragment6, jsx as jsx12 } from "react/jsx-runtime";
|
|
4790
5255
|
function AssetLoader({ assets, fallback = null, onError, children }) {
|
|
4791
5256
|
const { loaded, error } = usePreload(assets);
|
|
4792
|
-
|
|
5257
|
+
useEffect25(() => {
|
|
4793
5258
|
if (error && onError) onError(error);
|
|
4794
5259
|
}, [error, onError]);
|
|
4795
5260
|
if (!loaded) {
|
|
@@ -4799,9 +5264,9 @@ function AssetLoader({ assets, fallback = null, onError, children }) {
|
|
|
4799
5264
|
}
|
|
4800
5265
|
|
|
4801
5266
|
// src/hooks/useGame.ts
|
|
4802
|
-
import { useContext as
|
|
5267
|
+
import { useContext as useContext23 } from "react";
|
|
4803
5268
|
function useGame() {
|
|
4804
|
-
const engine =
|
|
5269
|
+
const engine = useContext23(EngineContext);
|
|
4805
5270
|
if (!engine) throw new Error("useGame must be used inside <Game>");
|
|
4806
5271
|
return engine;
|
|
4807
5272
|
}
|
|
@@ -4858,18 +5323,18 @@ function useSnapshot() {
|
|
|
4858
5323
|
}
|
|
4859
5324
|
|
|
4860
5325
|
// src/hooks/useEntity.ts
|
|
4861
|
-
import { useContext as
|
|
5326
|
+
import { useContext as useContext24 } from "react";
|
|
4862
5327
|
function useEntity() {
|
|
4863
|
-
const id =
|
|
5328
|
+
const id = useContext24(EntityContext);
|
|
4864
5329
|
if (id === null) throw new Error("useEntity must be used inside <Entity>");
|
|
4865
5330
|
return id;
|
|
4866
5331
|
}
|
|
4867
5332
|
|
|
4868
5333
|
// src/hooks/useDestroyEntity.ts
|
|
4869
|
-
import { useCallback as useCallback2, useContext as
|
|
5334
|
+
import { useCallback as useCallback2, useContext as useContext25 } from "react";
|
|
4870
5335
|
function useDestroyEntity() {
|
|
4871
|
-
const engine =
|
|
4872
|
-
const entityId =
|
|
5336
|
+
const engine = useContext25(EngineContext);
|
|
5337
|
+
const entityId = useContext25(EntityContext);
|
|
4873
5338
|
if (!engine) throw new Error("useDestroyEntity must be used inside <Game>");
|
|
4874
5339
|
if (entityId === null) throw new Error("useDestroyEntity must be used inside <Entity>");
|
|
4875
5340
|
return useCallback2(() => {
|
|
@@ -4880,9 +5345,9 @@ function useDestroyEntity() {
|
|
|
4880
5345
|
}
|
|
4881
5346
|
|
|
4882
5347
|
// src/hooks/useInput.ts
|
|
4883
|
-
import { useContext as
|
|
5348
|
+
import { useContext as useContext26 } from "react";
|
|
4884
5349
|
function useInput() {
|
|
4885
|
-
const engine =
|
|
5350
|
+
const engine = useContext26(EngineContext);
|
|
4886
5351
|
if (!engine) throw new Error("useInput must be used inside <Game>");
|
|
4887
5352
|
return engine.input;
|
|
4888
5353
|
}
|
|
@@ -4902,9 +5367,9 @@ function useInputMap(bindings) {
|
|
|
4902
5367
|
}
|
|
4903
5368
|
|
|
4904
5369
|
// src/hooks/useEvents.ts
|
|
4905
|
-
import { useContext as
|
|
5370
|
+
import { useContext as useContext27, useEffect as useEffect26, useRef as useRef7 } from "react";
|
|
4906
5371
|
function useEvents() {
|
|
4907
|
-
const engine =
|
|
5372
|
+
const engine = useContext27(EngineContext);
|
|
4908
5373
|
if (!engine) throw new Error("useEvents must be used inside <Game>");
|
|
4909
5374
|
return engine.events;
|
|
4910
5375
|
}
|
|
@@ -4912,15 +5377,15 @@ function useEvent(event, handler) {
|
|
|
4912
5377
|
const events = useEvents();
|
|
4913
5378
|
const handlerRef = useRef7(handler);
|
|
4914
5379
|
handlerRef.current = handler;
|
|
4915
|
-
|
|
5380
|
+
useEffect26(() => {
|
|
4916
5381
|
return events.on(event, (data) => handlerRef.current(data));
|
|
4917
5382
|
}, [events, event]);
|
|
4918
5383
|
}
|
|
4919
5384
|
|
|
4920
5385
|
// src/hooks/useCoordinates.ts
|
|
4921
|
-
import { useCallback as useCallback3, useContext as
|
|
5386
|
+
import { useCallback as useCallback3, useContext as useContext28 } from "react";
|
|
4922
5387
|
function useCoordinates() {
|
|
4923
|
-
const engine =
|
|
5388
|
+
const engine = useContext28(EngineContext);
|
|
4924
5389
|
const worldToScreen = useCallback3((wx, wy) => {
|
|
4925
5390
|
const canvas = engine.canvas;
|
|
4926
5391
|
const camId = engine.ecs.queryOne("Camera2D");
|
|
@@ -4947,9 +5412,9 @@ function useCoordinates() {
|
|
|
4947
5412
|
}
|
|
4948
5413
|
|
|
4949
5414
|
// src/hooks/useInputContext.ts
|
|
4950
|
-
import { useEffect as
|
|
5415
|
+
import { useEffect as useEffect27, useMemo as useMemo4 } from "react";
|
|
4951
5416
|
function useInputContext(ctx) {
|
|
4952
|
-
|
|
5417
|
+
useEffect27(() => {
|
|
4953
5418
|
if (!ctx) return;
|
|
4954
5419
|
globalInputContext.push(ctx);
|
|
4955
5420
|
return () => globalInputContext.pop(ctx);
|
|
@@ -4988,12 +5453,12 @@ function useInputRecorder() {
|
|
|
4988
5453
|
}
|
|
4989
5454
|
|
|
4990
5455
|
// src/hooks/useGamepad.ts
|
|
4991
|
-
import { useEffect as
|
|
5456
|
+
import { useEffect as useEffect28, useRef as useRef8, useState as useState7 } from "react";
|
|
4992
5457
|
var EMPTY_STATE = { connected: false, axes: [], buttons: [] };
|
|
4993
5458
|
function useGamepad(playerIndex = 0) {
|
|
4994
5459
|
const [state, setState] = useState7(EMPTY_STATE);
|
|
4995
5460
|
const rafRef = useRef8(0);
|
|
4996
|
-
|
|
5461
|
+
useEffect28(() => {
|
|
4997
5462
|
const poll = () => {
|
|
4998
5463
|
const gp = navigator.getGamepads()[playerIndex];
|
|
4999
5464
|
if (gp) {
|
|
@@ -5014,9 +5479,9 @@ function useGamepad(playerIndex = 0) {
|
|
|
5014
5479
|
}
|
|
5015
5480
|
|
|
5016
5481
|
// src/hooks/usePause.ts
|
|
5017
|
-
import { useContext as
|
|
5482
|
+
import { useContext as useContext29, useState as useState8, useCallback as useCallback4 } from "react";
|
|
5018
5483
|
function usePause() {
|
|
5019
|
-
const engine =
|
|
5484
|
+
const engine = useContext29(EngineContext);
|
|
5020
5485
|
const [paused, setPaused] = useState8(false);
|
|
5021
5486
|
const pause = useCallback4(() => {
|
|
5022
5487
|
engine.loop.pause();
|
|
@@ -5085,19 +5550,19 @@ function useAISteering() {
|
|
|
5085
5550
|
}
|
|
5086
5551
|
|
|
5087
5552
|
// ../gameplay/src/hooks/useDamageZone.ts
|
|
5088
|
-
import { useContext as
|
|
5553
|
+
import { useContext as useContext30 } from "react";
|
|
5089
5554
|
function useDamageZone(damage, opts = {}) {
|
|
5090
|
-
const engine =
|
|
5555
|
+
const engine = useContext30(EngineContext);
|
|
5091
5556
|
useTriggerEnter((other) => {
|
|
5092
5557
|
engine.events.emit(`damage:${other}`, { amount: damage });
|
|
5093
5558
|
}, { tag: opts.tag, layer: opts.layer });
|
|
5094
5559
|
}
|
|
5095
5560
|
|
|
5096
5561
|
// ../gameplay/src/hooks/useDropThrough.ts
|
|
5097
|
-
import { useContext as
|
|
5562
|
+
import { useContext as useContext31, useCallback as useCallback7 } from "react";
|
|
5098
5563
|
function useDropThrough(frames = 8) {
|
|
5099
|
-
const engine =
|
|
5100
|
-
const entityId =
|
|
5564
|
+
const engine = useContext31(EngineContext);
|
|
5565
|
+
const entityId = useContext31(EntityContext);
|
|
5101
5566
|
const dropThrough = useCallback7(() => {
|
|
5102
5567
|
const rb = engine.ecs.getComponent(entityId, "RigidBody");
|
|
5103
5568
|
if (rb) rb.dropThrough = frames;
|
|
@@ -5106,14 +5571,14 @@ function useDropThrough(frames = 8) {
|
|
|
5106
5571
|
}
|
|
5107
5572
|
|
|
5108
5573
|
// ../gameplay/src/hooks/useGameStateMachine.ts
|
|
5109
|
-
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";
|
|
5110
5575
|
function useGameStateMachine(states, initial) {
|
|
5111
|
-
const engine =
|
|
5576
|
+
const engine = useContext32(EngineContext);
|
|
5112
5577
|
const [state, setState] = useState10(initial);
|
|
5113
5578
|
const stateRef = useRef9(initial);
|
|
5114
5579
|
const statesRef = useRef9(states);
|
|
5115
5580
|
statesRef.current = states;
|
|
5116
|
-
|
|
5581
|
+
useEffect29(() => {
|
|
5117
5582
|
statesRef.current[initial]?.onEnter?.();
|
|
5118
5583
|
}, []);
|
|
5119
5584
|
const transition = useCallback8((to) => {
|
|
@@ -5124,7 +5589,7 @@ function useGameStateMachine(states, initial) {
|
|
|
5124
5589
|
setState(to);
|
|
5125
5590
|
statesRef.current[to]?.onEnter?.();
|
|
5126
5591
|
}, []);
|
|
5127
|
-
|
|
5592
|
+
useEffect29(() => {
|
|
5128
5593
|
const eid = engine.ecs.createEntity();
|
|
5129
5594
|
engine.ecs.addComponent(eid, createScript((_id, _world, _input, dt) => {
|
|
5130
5595
|
statesRef.current[stateRef.current]?.onUpdate?.(dt);
|
|
@@ -5137,19 +5602,19 @@ function useGameStateMachine(states, initial) {
|
|
|
5137
5602
|
}
|
|
5138
5603
|
|
|
5139
5604
|
// ../gameplay/src/hooks/useHealth.ts
|
|
5140
|
-
import { useRef as useRef10, useEffect as
|
|
5605
|
+
import { useRef as useRef10, useEffect as useEffect30, useContext as useContext33, useCallback as useCallback9 } from "react";
|
|
5141
5606
|
function useHealth(maxHp, opts = {}) {
|
|
5142
|
-
const engine =
|
|
5143
|
-
const entityId =
|
|
5607
|
+
const engine = useContext33(EngineContext);
|
|
5608
|
+
const entityId = useContext33(EntityContext);
|
|
5144
5609
|
const hpRef = useRef10(maxHp);
|
|
5145
5610
|
const invincibleRef = useRef10(false);
|
|
5146
5611
|
const iFrameDuration = opts.iFrames ?? 1;
|
|
5147
5612
|
const onDeathRef = useRef10(opts.onDeath);
|
|
5148
5613
|
const onDamageRef = useRef10(opts.onDamage);
|
|
5149
|
-
|
|
5614
|
+
useEffect30(() => {
|
|
5150
5615
|
onDeathRef.current = opts.onDeath;
|
|
5151
5616
|
});
|
|
5152
|
-
|
|
5617
|
+
useEffect30(() => {
|
|
5153
5618
|
onDamageRef.current = opts.onDamage;
|
|
5154
5619
|
});
|
|
5155
5620
|
const timerRef = useRef10(
|
|
@@ -5168,10 +5633,10 @@ function useHealth(maxHp, opts = {}) {
|
|
|
5168
5633
|
if (hpRef.current <= 0) onDeathRef.current?.();
|
|
5169
5634
|
}, [iFrameDuration]);
|
|
5170
5635
|
const takeDamageRef = useRef10(takeDamage);
|
|
5171
|
-
|
|
5636
|
+
useEffect30(() => {
|
|
5172
5637
|
takeDamageRef.current = takeDamage;
|
|
5173
5638
|
}, [takeDamage]);
|
|
5174
|
-
|
|
5639
|
+
useEffect30(() => {
|
|
5175
5640
|
return engine.events.on(`damage:${entityId}`, ({ amount }) => {
|
|
5176
5641
|
takeDamageRef.current(amount);
|
|
5177
5642
|
});
|
|
@@ -5206,10 +5671,10 @@ function useHealth(maxHp, opts = {}) {
|
|
|
5206
5671
|
}
|
|
5207
5672
|
|
|
5208
5673
|
// ../gameplay/src/hooks/useKinematicBody.ts
|
|
5209
|
-
import { useContext as
|
|
5674
|
+
import { useContext as useContext34, useCallback as useCallback10 } from "react";
|
|
5210
5675
|
function useKinematicBody() {
|
|
5211
|
-
const engine =
|
|
5212
|
-
const entityId =
|
|
5676
|
+
const engine = useContext34(EngineContext);
|
|
5677
|
+
const entityId = useContext34(EntityContext);
|
|
5213
5678
|
const moveAndCollide = useCallback10((dx, dy) => {
|
|
5214
5679
|
const transform = engine.ecs.getComponent(entityId, "Transform");
|
|
5215
5680
|
if (!transform) return { dx: 0, dy: 0 };
|
|
@@ -5296,13 +5761,13 @@ function useLevelTransition(initial) {
|
|
|
5296
5761
|
}
|
|
5297
5762
|
|
|
5298
5763
|
// ../gameplay/src/hooks/usePlatformerController.ts
|
|
5299
|
-
import { useContext as
|
|
5764
|
+
import { useContext as useContext35, useEffect as useEffect31 } from "react";
|
|
5300
5765
|
function normalizeKeys(val, defaults) {
|
|
5301
5766
|
if (!val) return defaults;
|
|
5302
5767
|
return Array.isArray(val) ? val : [val];
|
|
5303
5768
|
}
|
|
5304
5769
|
function usePlatformerController(entityId, opts = {}) {
|
|
5305
|
-
const engine =
|
|
5770
|
+
const engine = useContext35(EngineContext);
|
|
5306
5771
|
const {
|
|
5307
5772
|
speed = 200,
|
|
5308
5773
|
jumpForce = -500,
|
|
@@ -5315,7 +5780,7 @@ function usePlatformerController(entityId, opts = {}) {
|
|
|
5315
5780
|
const leftKeys = normalizeKeys(bindings?.left, ["ArrowLeft", "KeyA", "a"]);
|
|
5316
5781
|
const rightKeys = normalizeKeys(bindings?.right, ["ArrowRight", "KeyD", "d"]);
|
|
5317
5782
|
const jumpKeys = normalizeKeys(bindings?.jump, ["Space", "ArrowUp", "KeyW", "w"]);
|
|
5318
|
-
|
|
5783
|
+
useEffect31(() => {
|
|
5319
5784
|
const state = {
|
|
5320
5785
|
coyoteTimer: 0,
|
|
5321
5786
|
jumpBuffer: 0,
|
|
@@ -5379,9 +5844,9 @@ function usePathfinding() {
|
|
|
5379
5844
|
}
|
|
5380
5845
|
|
|
5381
5846
|
// ../gameplay/src/hooks/usePersistedBindings.ts
|
|
5382
|
-
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";
|
|
5383
5848
|
function usePersistedBindings(storageKey, defaults) {
|
|
5384
|
-
const engine =
|
|
5849
|
+
const engine = useContext36(EngineContext);
|
|
5385
5850
|
const input = engine.input;
|
|
5386
5851
|
const [bindings, setBindings] = useState12(() => {
|
|
5387
5852
|
try {
|
|
@@ -5486,11 +5951,11 @@ function useSave(key, defaultValue, opts = {}) {
|
|
|
5486
5951
|
}
|
|
5487
5952
|
|
|
5488
5953
|
// ../gameplay/src/hooks/useTopDownMovement.ts
|
|
5489
|
-
import { useContext as
|
|
5954
|
+
import { useContext as useContext37, useEffect as useEffect32 } from "react";
|
|
5490
5955
|
function useTopDownMovement(entityId, opts = {}) {
|
|
5491
|
-
const engine =
|
|
5956
|
+
const engine = useContext37(EngineContext);
|
|
5492
5957
|
const { speed = 200, normalizeDiagonal = true } = opts;
|
|
5493
|
-
|
|
5958
|
+
useEffect32(() => {
|
|
5494
5959
|
const updateFn = (id, world, input) => {
|
|
5495
5960
|
if (!world.hasEntity(id)) return;
|
|
5496
5961
|
const rb = world.getComponent(id, "RigidBody");
|
|
@@ -5514,8 +5979,190 @@ function useTopDownMovement(entityId, opts = {}) {
|
|
|
5514
5979
|
}, []);
|
|
5515
5980
|
}
|
|
5516
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
|
+
|
|
5517
6164
|
// ../../packages/audio/src/useSound.ts
|
|
5518
|
-
import { useEffect as
|
|
6165
|
+
import { useEffect as useEffect34, useRef as useRef15 } from "react";
|
|
5519
6166
|
var _audioCtx = null;
|
|
5520
6167
|
function getAudioCtx() {
|
|
5521
6168
|
if (!_audioCtx) _audioCtx = new AudioContext();
|
|
@@ -5582,13 +6229,13 @@ async function loadBuffer(src) {
|
|
|
5582
6229
|
return buf;
|
|
5583
6230
|
}
|
|
5584
6231
|
function useSound(src, opts = {}) {
|
|
5585
|
-
const bufferRef =
|
|
5586
|
-
const sourceRef =
|
|
5587
|
-
const gainRef =
|
|
5588
|
-
const volRef =
|
|
5589
|
-
const loopRef =
|
|
5590
|
-
const groupRef =
|
|
5591
|
-
|
|
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(() => {
|
|
5592
6239
|
let cancelled = false;
|
|
5593
6240
|
loadBuffer(src).then((buf) => {
|
|
5594
6241
|
if (!cancelled) bufferRef.current = buf;
|
|
@@ -5732,10 +6379,12 @@ export {
|
|
|
5732
6379
|
CapsuleCollider,
|
|
5733
6380
|
Checkpoint,
|
|
5734
6381
|
CircleCollider,
|
|
6382
|
+
CompoundCollider,
|
|
5735
6383
|
Ease,
|
|
5736
6384
|
Entity,
|
|
5737
6385
|
Game,
|
|
5738
6386
|
MovingPlatform,
|
|
6387
|
+
NineSlice,
|
|
5739
6388
|
ParallaxLayer,
|
|
5740
6389
|
ParticleEmitter,
|
|
5741
6390
|
RenderSystem,
|
|
@@ -5752,8 +6401,10 @@ export {
|
|
|
5752
6401
|
World,
|
|
5753
6402
|
arrive,
|
|
5754
6403
|
createAtlas,
|
|
6404
|
+
createCompoundCollider,
|
|
5755
6405
|
createInputMap,
|
|
5756
6406
|
createInputRecorder,
|
|
6407
|
+
createNineSlice,
|
|
5757
6408
|
createPlayerInput,
|
|
5758
6409
|
createSprite,
|
|
5759
6410
|
createTag,
|
|
@@ -5765,6 +6416,7 @@ export {
|
|
|
5765
6416
|
flee,
|
|
5766
6417
|
getGroupVolume,
|
|
5767
6418
|
globalInputContext,
|
|
6419
|
+
hotReloadPlugin,
|
|
5768
6420
|
overlapBox,
|
|
5769
6421
|
overlapCircle,
|
|
5770
6422
|
patrol,
|
|
@@ -5788,14 +6440,17 @@ export {
|
|
|
5788
6440
|
useCollisionExit,
|
|
5789
6441
|
useCollisionStay,
|
|
5790
6442
|
useCoordinates,
|
|
6443
|
+
useCutscene,
|
|
5791
6444
|
useDamageZone,
|
|
5792
6445
|
useDestroyEntity,
|
|
6446
|
+
useDialogue,
|
|
5793
6447
|
useDropThrough,
|
|
5794
6448
|
useEntity,
|
|
5795
6449
|
useEvent,
|
|
5796
6450
|
useEvents,
|
|
5797
6451
|
useGame,
|
|
5798
6452
|
useGameStateMachine,
|
|
6453
|
+
useGameStore,
|
|
5799
6454
|
useGamepad,
|
|
5800
6455
|
useHealth,
|
|
5801
6456
|
useInput,
|