@adatechnology/game-destruction 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +19 -2
- package/dist/index.global.js +96 -47
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +96 -47
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1088,7 +1088,7 @@ var materials_default = [
|
|
|
1088
1088
|
{
|
|
1089
1089
|
id: "green_wood",
|
|
1090
1090
|
density: 650,
|
|
1091
|
-
hpPerKg:
|
|
1091
|
+
hpPerKg: 5,
|
|
1092
1092
|
penetration: 10,
|
|
1093
1093
|
ductility: 0.5,
|
|
1094
1094
|
fracture: "splinter",
|
|
@@ -1097,7 +1097,8 @@ var materials_default = [
|
|
|
1097
1097
|
0.2
|
|
1098
1098
|
],
|
|
1099
1099
|
damageScale: {
|
|
1100
|
-
slash: 1.3
|
|
1100
|
+
slash: 1.3,
|
|
1101
|
+
ballistic: 0.1
|
|
1101
1102
|
},
|
|
1102
1103
|
hitDebris: "splinters",
|
|
1103
1104
|
breakDebris: "leaves",
|
|
@@ -1410,6 +1411,15 @@ var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
|
1410
1411
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
1411
1412
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
1412
1413
|
var clone = (v) => structuredClone(v);
|
|
1414
|
+
var updateBound = (o) => {
|
|
1415
|
+
if (!o.parts.length) return;
|
|
1416
|
+
const c = [0, 0, 0];
|
|
1417
|
+
for (const p of o.parts) for (let k = 0; k < 3; k++) c[k] += p.pos[k] / o.parts.length;
|
|
1418
|
+
let r = 0;
|
|
1419
|
+
for (const p of o.parts) r = Math.max(r, vec.distance(c, p.pos) + p.bound);
|
|
1420
|
+
o.bc = c;
|
|
1421
|
+
o.br = r;
|
|
1422
|
+
};
|
|
1413
1423
|
var AXIS = { x: 0, y: 1, z: 2 };
|
|
1414
1424
|
var createDestruction = (options = {}) => {
|
|
1415
1425
|
const materials = /* @__PURE__ */ new Map();
|
|
@@ -1545,6 +1555,7 @@ var createDestruction = (options = {}) => {
|
|
|
1545
1555
|
mat,
|
|
1546
1556
|
st: pst,
|
|
1547
1557
|
q: quatFromEuler(...ps.rotation ?? [0, 0, 0]),
|
|
1558
|
+
pos: [ps.position[0], ps.position[1], ps.position[2]],
|
|
1548
1559
|
mass0,
|
|
1549
1560
|
fuel0,
|
|
1550
1561
|
area: shapeArea(ps.shape),
|
|
@@ -1583,14 +1594,8 @@ var createDestruction = (options = {}) => {
|
|
|
1583
1594
|
o.byPart.get(r.b).push(j);
|
|
1584
1595
|
o.st.joints.push(jst);
|
|
1585
1596
|
}
|
|
1586
|
-
if (o.
|
|
1587
|
-
|
|
1588
|
-
for (const p of o.parts) for (let k = 0; k < 3; k++) c[k] += p.spec.position[k] / o.parts.length;
|
|
1589
|
-
let r = 0;
|
|
1590
|
-
for (const p of o.parts) r = Math.max(r, vec.distance(c, p.spec.position) + p.bound);
|
|
1591
|
-
o.bc = c;
|
|
1592
|
-
o.br = r;
|
|
1593
|
-
}
|
|
1597
|
+
for (const [jid, c] of carry?.joints ?? []) if (c.st.open && !o.jointMap.has(jid)) o.st.joints.push(c.st);
|
|
1598
|
+
updateBound(o);
|
|
1594
1599
|
o.anyAnchor = o.parts.some((p) => p.anchored);
|
|
1595
1600
|
o.unsup = o.parts.map(() => false);
|
|
1596
1601
|
solveStructure(o);
|
|
@@ -1730,7 +1735,7 @@ var createDestruction = (options = {}) => {
|
|
|
1730
1735
|
if (!p) throw new Error(`unknown part "${id}" in object "${o.id}"`);
|
|
1731
1736
|
return p;
|
|
1732
1737
|
};
|
|
1733
|
-
const partXf = (o, p) => ({ c: toWorld(o.st.position, o.st.rotation, p.
|
|
1738
|
+
const partXf = (o, p) => ({ c: toWorld(o.st.position, o.st.rotation, p.pos), q: quat.mul(o.st.rotation, p.q) });
|
|
1734
1739
|
const objLocal = (o, w) => toLocal(o.st.position, o.st.rotation, w);
|
|
1735
1740
|
const objLocalDir = (o, w) => rotateVec(quat.conjugate(o.st.rotation), w);
|
|
1736
1741
|
const jointWorld = (o, j) => toWorld(o.st.position, o.st.rotation, j.pos);
|
|
@@ -1742,14 +1747,14 @@ var createDestruction = (options = {}) => {
|
|
|
1742
1747
|
const { c, q } = partXf(o, p);
|
|
1743
1748
|
return aabbOf(p.spec.shape, c, q);
|
|
1744
1749
|
};
|
|
1745
|
-
const rayPartRaw = (pos, rot, spec, pq, origin, d) => {
|
|
1746
|
-
const c = toWorld(pos, rot,
|
|
1750
|
+
const rayPartRaw = (pos, rot, spec, pq, origin, d, lp = spec.position) => {
|
|
1751
|
+
const c = toWorld(pos, rot, lp);
|
|
1747
1752
|
const q = quat.mul(rot, pq);
|
|
1748
1753
|
const qi = quat.conjugate(q);
|
|
1749
1754
|
const r = rayShape(spec.shape, rotateVec(qi, vec.sub(origin, c)), rotateVec(qi, d));
|
|
1750
1755
|
return r ? { t0: r.t0, t1: r.t1, n: rotateVec(q, r.n0) } : null;
|
|
1751
1756
|
};
|
|
1752
|
-
const rayPart = (o, p, origin, d) => rayPartRaw(o.st.position, o.st.rotation, p.spec, p.q, origin, d);
|
|
1757
|
+
const rayPart = (o, p, origin, d) => rayPartRaw(o.st.position, o.st.rotation, p.spec, p.q, origin, d, p.pos);
|
|
1753
1758
|
const raySphereMiss = (origin, d, c, r, maxDist) => {
|
|
1754
1759
|
const oc = vec.sub(origin, c);
|
|
1755
1760
|
const b = vec.dot(oc, d);
|
|
@@ -1774,7 +1779,8 @@ var createDestruction = (options = {}) => {
|
|
|
1774
1779
|
for (const im of impls.values()) {
|
|
1775
1780
|
for (const ps of im.impl.describe().parts) {
|
|
1776
1781
|
if (ignore?.has(`${im.impl.id}\0${ps.id}`)) continue;
|
|
1777
|
-
const
|
|
1782
|
+
const ov = im.poses.get(ps.id);
|
|
1783
|
+
const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]), origin, d, ov?.pos);
|
|
1778
1784
|
if (!r || r.t0 < 0 || r.t0 > maxDist) continue;
|
|
1779
1785
|
if (!best || r.t0 < best.distance) best = { object: im.impl.id, part: ps.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 };
|
|
1780
1786
|
}
|
|
@@ -1909,11 +1915,29 @@ var createDestruction = (options = {}) => {
|
|
|
1909
1915
|
collapseCheck(o, src);
|
|
1910
1916
|
checkDestroyed(o, src);
|
|
1911
1917
|
};
|
|
1912
|
-
const
|
|
1918
|
+
const jointReach = (o, j, point) => {
|
|
1919
|
+
const A = partWorldAabb(o, o.parts[j.ia]), B = partWorldAabb(o, o.parts[j.ib]);
|
|
1920
|
+
let d2 = 0;
|
|
1921
|
+
for (let k = 0; k < 3; k++) {
|
|
1922
|
+
const lo = Math.min(Math.max(A.min[k], B.min[k]), Math.min(A.max[k], B.max[k]));
|
|
1923
|
+
const hi = Math.max(Math.max(A.min[k], B.min[k]), Math.min(A.max[k], B.max[k]));
|
|
1924
|
+
const d = point[k] < lo ? lo - point[k] : point[k] > hi ? point[k] - hi : 0;
|
|
1925
|
+
d2 += d * d;
|
|
1926
|
+
}
|
|
1927
|
+
return Math.sqrt(d2);
|
|
1928
|
+
};
|
|
1929
|
+
const jointReachLimit = (o, j) => {
|
|
1930
|
+
const pa = o.parts[j.ia], pb = o.parts[j.ib];
|
|
1931
|
+
const small = pa.dims[0] * pa.dims[1] * pa.dims[2] <= pb.dims[0] * pb.dims[1] * pb.dims[2] ? pa : pb;
|
|
1932
|
+
return Math.max(0.06, 0.5 * Math.min(small.dims[0], small.dims[1], small.dims[2]));
|
|
1933
|
+
};
|
|
1934
|
+
const wearJoints = (o, p, amount, near, radius = 0.06, src, split = false) => {
|
|
1913
1935
|
if (amount <= 0) return;
|
|
1914
|
-
|
|
1936
|
+
const mine = (o.byPart.get(p.spec.id) ?? []).filter((j) => !j.st.open);
|
|
1937
|
+
if (split && mine.length) amount /= mine.length;
|
|
1938
|
+
for (const j of mine) {
|
|
1915
1939
|
if (j.st.open) continue;
|
|
1916
|
-
if (near &&
|
|
1940
|
+
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
1917
1941
|
j.st.hp -= amount;
|
|
1918
1942
|
if (j.st.hp <= 0) {
|
|
1919
1943
|
j.st.hp = 0;
|
|
@@ -2032,7 +2056,7 @@ var createDestruction = (options = {}) => {
|
|
|
2032
2056
|
if (mat.fracture !== "shatter") addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
2033
2057
|
} else addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
2034
2058
|
debris(o, p, mat.hitDebris, entry, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
2035
|
-
wearJoints(o, p, E * 0.
|
|
2059
|
+
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
2036
2060
|
const dmg = applyDamage(o, p, E * DMG_PER_J * scale, "ballistic", entry, d, E * 0.1, 12, src);
|
|
2037
2061
|
return { penetrated, exitEnergy: penetrated ? E - thr : 0, exitPoint, damage: dmg };
|
|
2038
2062
|
};
|
|
@@ -2107,8 +2131,9 @@ var createDestruction = (options = {}) => {
|
|
|
2107
2131
|
}
|
|
2108
2132
|
for (const im of [...impls.values()]) {
|
|
2109
2133
|
const inRange = im.impl.describe().parts.some((ps) => {
|
|
2110
|
-
const
|
|
2111
|
-
const
|
|
2134
|
+
const ov = im.poses.get(ps.id);
|
|
2135
|
+
const c = toWorld(im.position, im.rotation, ov?.pos ?? ps.position);
|
|
2136
|
+
const q = quat.mul(im.rotation, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]));
|
|
2112
2137
|
return distanceToShape(ps.shape, toLocal(c, q, point)) < radius;
|
|
2113
2138
|
});
|
|
2114
2139
|
if (inRange) out.push(implHit(im, { type: "explosive", point, energy, radius, ...src !== void 0 ? { source: src } : {} }));
|
|
@@ -2360,7 +2385,7 @@ var createDestruction = (options = {}) => {
|
|
|
2360
2385
|
const spread = (sources, dt) => {
|
|
2361
2386
|
for (const src of sources) {
|
|
2362
2387
|
if (!src.p.st.burning && !src.p.rt.jet) continue;
|
|
2363
|
-
const sc = toWorld(src.o.st.position, src.o.st.rotation, src.p.
|
|
2388
|
+
const sc = toWorld(src.o.st.position, src.o.st.rotation, src.p.pos);
|
|
2364
2389
|
const A = partWorldAabb(src.o, src.p);
|
|
2365
2390
|
for (const o of objects.values()) {
|
|
2366
2391
|
if (o === src.o) continue;
|
|
@@ -2406,9 +2431,23 @@ var createDestruction = (options = {}) => {
|
|
|
2406
2431
|
register(impl, opts = {}) {
|
|
2407
2432
|
const id = opts.id ?? impl.id;
|
|
2408
2433
|
if (taken(id)) throw new Error(`object "${id}" already exists`);
|
|
2409
|
-
impls.set(id, { impl, position: opts.position ?? [0, 0, 0], rotation: opts.rotation ?? [0, 0, 0, 1] });
|
|
2434
|
+
impls.set(id, { impl, position: opts.position ?? [0, 0, 0], rotation: opts.rotation ?? [0, 0, 0, 1], poses: /* @__PURE__ */ new Map() });
|
|
2410
2435
|
return id;
|
|
2411
2436
|
},
|
|
2437
|
+
setPartPose(objectId, partId, position, rotation) {
|
|
2438
|
+
const q = quatFromEuler(...rotation ?? [0, 0, 0]);
|
|
2439
|
+
const im = impls.get(objectId);
|
|
2440
|
+
if (im) {
|
|
2441
|
+
im.impl.setPartPose?.(partId, position, rotation);
|
|
2442
|
+
im.poses.set(partId, { pos: [position[0], position[1], position[2]], q });
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
const o = getObj(objectId);
|
|
2446
|
+
const p = getPart(o, partId);
|
|
2447
|
+
p.pos = [position[0], position[1], position[2]];
|
|
2448
|
+
p.q = q;
|
|
2449
|
+
updateBound(o);
|
|
2450
|
+
},
|
|
2412
2451
|
remove(id) {
|
|
2413
2452
|
objects.delete(id);
|
|
2414
2453
|
impls.delete(id);
|
|
@@ -2435,9 +2474,12 @@ var createDestruction = (options = {}) => {
|
|
|
2435
2474
|
const moved = new Set(names);
|
|
2436
2475
|
const nid = newId ?? nextId();
|
|
2437
2476
|
if (objects.has(nid)) throw new Error(`object "${nid}" already exists`);
|
|
2438
|
-
const carryFor = (keep) => ({
|
|
2477
|
+
const carryFor = (keep, keepOpen = () => false) => ({
|
|
2439
2478
|
parts: new Map(o.parts.filter((p) => keep(p.spec.id)).map((p) => [p.spec.id, { st: p.st, rt: p.rt }])),
|
|
2440
|
-
joints: new Map(
|
|
2479
|
+
joints: new Map([
|
|
2480
|
+
...o.joints.filter((j) => keep(j.spec.a) && keep(j.spec.b) || j.st.open && keepOpen(j.spec.a, j.spec.b)).map((j) => [j.spec.id, { st: j.st, step: j.step }]),
|
|
2481
|
+
...o.st.joints.filter((st) => st.open && !o.jointMap.has(st.id) && keepOpen(st.a, st.b)).map((st) => [st.id, { st, step: 0 }])
|
|
2482
|
+
]),
|
|
2441
2483
|
collapsed: [...o.collapsed].filter(keep)
|
|
2442
2484
|
});
|
|
2443
2485
|
const sub = (keep) => {
|
|
@@ -2449,8 +2491,8 @@ var createDestruction = (options = {}) => {
|
|
|
2449
2491
|
const inNew = (pid) => moved.has(pid);
|
|
2450
2492
|
const inOld = (pid) => !moved.has(pid);
|
|
2451
2493
|
const pos = o.st.position, rot = o.st.rotation;
|
|
2452
|
-
const fresh = build(sub(inNew), nid, pos, rot, carryFor(inNew));
|
|
2453
|
-
const rest = build(sub(inOld), id, pos, rot, carryFor(inOld));
|
|
2494
|
+
const fresh = build(sub(inNew), nid, pos, rot, carryFor(inNew, (a, b) => inNew(a) && inNew(b)));
|
|
2495
|
+
const rest = build(sub(inOld), id, pos, rot, carryFor(inOld, (a, b) => !(inNew(a) && inNew(b))));
|
|
2454
2496
|
objects.set(id, rest);
|
|
2455
2497
|
objects.set(nid, fresh);
|
|
2456
2498
|
return nid;
|
|
@@ -2548,34 +2590,37 @@ var createDestruction = (options = {}) => {
|
|
|
2548
2590
|
}
|
|
2549
2591
|
return getObj(objectId).joints.filter((j) => !j.st.open && j.spec.tools.includes(tool)).map((j) => j.st);
|
|
2550
2592
|
},
|
|
2551
|
-
nearestJoint(objectId, point, maxDistance = 0.3) {
|
|
2593
|
+
nearestJoint(objectId, point, maxDistance = 0.3, tool) {
|
|
2552
2594
|
const im = impls.get(objectId);
|
|
2595
|
+
const cands = [];
|
|
2553
2596
|
if (im) {
|
|
2554
|
-
|
|
2555
|
-
let bd2 = maxDistance;
|
|
2597
|
+
const spec = im.impl.describe();
|
|
2556
2598
|
for (const js of im.impl.state().joints) {
|
|
2557
2599
|
if (js.open) continue;
|
|
2558
2600
|
const pos = implJointPos(im, js.a, js.b);
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2561
|
-
bd2 = d;
|
|
2562
|
-
best2 = js;
|
|
2563
|
-
}
|
|
2601
|
+
const j = (spec.joints ?? []).find((x) => (x.id ?? `${x.a}~${x.b}`) === js.id);
|
|
2602
|
+
cands.push({ st: js, d: pos ? vec.distance(pos, point) : Infinity, ok: !!j && (j.tools ?? JOINT_DEFAULTS[j.kind].tools).includes(tool) });
|
|
2564
2603
|
}
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
for (const j of o.joints) {
|
|
2571
|
-
if (j.st.open) continue;
|
|
2572
|
-
const d = vec.distance(jointWorld(o, j), point);
|
|
2573
|
-
if (d <= bd) {
|
|
2574
|
-
bd = d;
|
|
2575
|
-
best = j.st;
|
|
2604
|
+
} else {
|
|
2605
|
+
const o = getObj(objectId);
|
|
2606
|
+
for (const j of o.joints) {
|
|
2607
|
+
if (j.st.open) continue;
|
|
2608
|
+
cands.push({ st: j.st, d: vec.distance(jointWorld(o, j), point), ok: tool ? j.spec.tools.includes(tool) : true });
|
|
2576
2609
|
}
|
|
2577
2610
|
}
|
|
2578
|
-
|
|
2611
|
+
const pick = (only) => {
|
|
2612
|
+
let best = null;
|
|
2613
|
+
let bd = maxDistance;
|
|
2614
|
+
for (const c of cands) {
|
|
2615
|
+
if (only && !c.ok) continue;
|
|
2616
|
+
if (c.d <= bd) {
|
|
2617
|
+
bd = c.d;
|
|
2618
|
+
best = c.st;
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
return best;
|
|
2622
|
+
};
|
|
2623
|
+
return (tool ? pick(true) : null) ?? pick(false);
|
|
2579
2624
|
},
|
|
2580
2625
|
extinguish(objectId, partId, coolC = 200) {
|
|
2581
2626
|
const im = impls.get(objectId);
|
|
@@ -2618,6 +2663,10 @@ var createDestruction = (options = {}) => {
|
|
|
2618
2663
|
}).events;
|
|
2619
2664
|
},
|
|
2620
2665
|
state: (id) => objects.get(id)?.st ?? impls.get(id)?.impl.state() ?? null,
|
|
2666
|
+
snapshot: (id) => {
|
|
2667
|
+
const st = objects.get(id)?.st ?? impls.get(id)?.impl.state();
|
|
2668
|
+
return st ? clone(st) : null;
|
|
2669
|
+
},
|
|
2621
2670
|
part: (id, partId) => objects.get(id)?.byId.get(partId)?.st ?? impls.get(id)?.impl.state().parts.find((p) => p.id === partId) ?? null,
|
|
2622
2671
|
reset(id) {
|
|
2623
2672
|
const im = impls.get(id);
|