@adatechnology/game-destruction 0.12.0 → 0.13.0
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 +7 -0
- package/dist/index.global.js +143 -18
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +143 -18
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -452,6 +452,11 @@ interface DestructionWorld {
|
|
|
452
452
|
* that target, and a `blast` event sums it up.
|
|
453
453
|
*/
|
|
454
454
|
explode(point: Vec3, energy: number, radius: number, source?: string, options?: BlastOptions): HitResult[];
|
|
455
|
+
/** Cost of the last `explode`: parts in range of the broad phase (`partsTested`) and those whose bounding sphere the blast reached (`partsNear`). */
|
|
456
|
+
blastProbe(): {
|
|
457
|
+
partsTested: number;
|
|
458
|
+
partsNear: number;
|
|
459
|
+
};
|
|
455
460
|
/** Raycast against every part shape (world space). */
|
|
456
461
|
raycast(origin: Vec3, direction: Vec3, maxDistance?: number): RayHit | null;
|
|
457
462
|
/**
|
|
@@ -550,6 +555,8 @@ interface PartRuntime {
|
|
|
550
555
|
breach?: Array<[number, number, number, number]>;
|
|
551
556
|
/** Energy each unit of the part took so far (`breach.chipEnergy`), by its index in the unit grid; -1 when the unit came out. */
|
|
552
557
|
chips?: Record<number, number>;
|
|
558
|
+
/** Face area (m²) the craters of shots took out of each unit so far, by its index in the unit grid: the rest of a unit comes out past `CRATER_SPENT`. */
|
|
559
|
+
craters?: Record<number, number>;
|
|
553
560
|
/** Mass in kg that left the part in a breach. */
|
|
554
561
|
removed?: number;
|
|
555
562
|
/** Damage taken by each band across a long wooden part (`splinter`), by band index from its negative end: the band that runs out breaks it there. */
|
package/dist/index.global.js
CHANGED
|
@@ -2967,6 +2967,7 @@ var GameDestruction = (() => {
|
|
|
2967
2967
|
var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
2968
2968
|
var VENT_DIAMETER = 8e-3;
|
|
2969
2969
|
var FLAME_LIGHTS_LEAK_C = 470;
|
|
2970
|
+
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
2970
2971
|
var BREACH_RADIUS = 0.55;
|
|
2971
2972
|
var BREACH_BLAST_FACTOR = 6;
|
|
2972
2973
|
var BREACH_AREA = 0.25;
|
|
@@ -2982,6 +2983,7 @@ var GameDestruction = (() => {
|
|
|
2982
2983
|
var BREACH_GRID_CAP = 4096;
|
|
2983
2984
|
var CHIP_ENERGY = 200;
|
|
2984
2985
|
var CHIP_SPLASH = 0.5;
|
|
2986
|
+
var CRATER_SPENT = 0.6;
|
|
2985
2987
|
var BAR_BOW = 0.08;
|
|
2986
2988
|
var BULLET_JOINT_SPAN = 0.3;
|
|
2987
2989
|
var CUT_SLICE = 1;
|
|
@@ -3002,12 +3004,22 @@ var GameDestruction = (() => {
|
|
|
3002
3004
|
const cells = p.st.chipped;
|
|
3003
3005
|
if (!cells?.length) return 0;
|
|
3004
3006
|
const [u, v] = planeAxes(p.thin);
|
|
3005
|
-
const
|
|
3007
|
+
const role = p.spec.structure?.role;
|
|
3008
|
+
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
3009
|
+
const upright = Math.abs(up[1]) > 0.7 ? v : Math.abs(rotateVec(p.q, [0, 0, 0].map((_, k) => k === u ? 1 : 0))[1]) > 0.7 ? u : -1;
|
|
3010
|
+
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
3006
3011
|
const across = along === u ? v : u;
|
|
3007
3012
|
let worst = 0;
|
|
3008
3013
|
for (const c of cells) {
|
|
3009
|
-
|
|
3010
|
-
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6)
|
|
3014
|
+
const spans = [];
|
|
3015
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) spans.push([d.center[across] - d.size[across] / 2, d.center[across] + d.size[across] / 2]);
|
|
3016
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
3017
|
+
let gone = 0, end = -Infinity;
|
|
3018
|
+
for (const [a, b] of spans) {
|
|
3019
|
+
if (b <= end) continue;
|
|
3020
|
+
gone += b - Math.max(a, end);
|
|
3021
|
+
end = b;
|
|
3022
|
+
}
|
|
3011
3023
|
worst = Math.max(worst, gone / Math.max(p.dims[across], 1e-6));
|
|
3012
3024
|
}
|
|
3013
3025
|
return Math.min(1, worst);
|
|
@@ -3303,7 +3315,18 @@ var GameDestruction = (() => {
|
|
|
3303
3315
|
if (k < 0 || j.spec.kind !== "tie") return k;
|
|
3304
3316
|
return partXf(o, o.parts[k]).c[1] > partXf(o, o.parts[r]).c[1] + 1e-6 ? k : -1;
|
|
3305
3317
|
};
|
|
3318
|
+
let poseVersion = 0;
|
|
3319
|
+
const contactMemo = /* @__PURE__ */ new WeakMap();
|
|
3306
3320
|
const contactBox = (p, support) => {
|
|
3321
|
+
let byTouching = contactMemo.get(p);
|
|
3322
|
+
if (!byTouching) contactMemo.set(p, byTouching = /* @__PURE__ */ new Map());
|
|
3323
|
+
const known = byTouching.get(support);
|
|
3324
|
+
if (known && known.version === poseVersion) return known.box;
|
|
3325
|
+
const box = measureContact(p, support);
|
|
3326
|
+
byTouching.set(support, { version: poseVersion, box });
|
|
3327
|
+
return box;
|
|
3328
|
+
};
|
|
3329
|
+
const measureContact = (p, support) => {
|
|
3307
3330
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
3308
3331
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
3309
3332
|
for (let k = 0; k < 3; k++) {
|
|
@@ -4098,7 +4121,7 @@ var GameDestruction = (() => {
|
|
|
4098
4121
|
p.st.holes.push(hole);
|
|
4099
4122
|
if (p.st.holes.length > maxHoles) p.st.holes.shift();
|
|
4100
4123
|
markDamaged(p);
|
|
4101
|
-
emit({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {} });
|
|
4124
|
+
emit({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {}, ...p.spec.powder ? { powder: { ...p.spec.powder } } : {} });
|
|
4102
4125
|
setStage(o, p, 1, {}, src);
|
|
4103
4126
|
return hole;
|
|
4104
4127
|
};
|
|
@@ -4115,6 +4138,22 @@ var GameDestruction = (() => {
|
|
|
4115
4138
|
const detachEvents = (o, opened, by, src) => {
|
|
4116
4139
|
for (const j of opened) emit({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
|
|
4117
4140
|
};
|
|
4141
|
+
const slideOffResting = (o, rotation) => {
|
|
4142
|
+
const upY = 1 - 2 * (rotation[0] * rotation[0] + rotation[2] * rotation[2]);
|
|
4143
|
+
if (upY >= RESTING_HOLD_COS) return;
|
|
4144
|
+
const opened = o.joints.filter((j) => j.spec.kind === "resting" && !j.st.open);
|
|
4145
|
+
if (opened.length === 0) return;
|
|
4146
|
+
run(() => {
|
|
4147
|
+
for (const j of opened) {
|
|
4148
|
+
j.st.open = true;
|
|
4149
|
+
j.st.openedBy = "break";
|
|
4150
|
+
}
|
|
4151
|
+
solveStructure(o);
|
|
4152
|
+
recomputeGroups(o);
|
|
4153
|
+
detachEvents(o, opened, "break");
|
|
4154
|
+
collapseCheck(o);
|
|
4155
|
+
});
|
|
4156
|
+
};
|
|
4118
4157
|
const checkDestroyed = (o, src) => {
|
|
4119
4158
|
if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {
|
|
4120
4159
|
o.st.destroyed = true;
|
|
@@ -4215,7 +4254,7 @@ var GameDestruction = (() => {
|
|
|
4215
4254
|
const localHolesOf = (p) => p.st.holes.map((h) => ({ point: toLocal(p.pos, p.q, h.point), normal: rotateVec(quat.conjugate(p.q), h.normal), diameter: h.diameter }));
|
|
4216
4255
|
const unitsOf = (p) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness], bond: "stack" };
|
|
4217
4256
|
const FRAGMENT_SHAPE = { shatter: "shard", splinter: "splinter", tear: "scrap" };
|
|
4218
|
-
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag) => {
|
|
4257
|
+
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag, inner = false) => {
|
|
4219
4258
|
const { c, q } = partXf(o, p);
|
|
4220
4259
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
4221
4260
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -4239,7 +4278,8 @@ var GameDestruction = (() => {
|
|
|
4239
4278
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],
|
|
4240
4279
|
angularVelocity: [(rnd() - 0.5) * 8, (rnd() - 0.5) * 8, (rnd() - 0.5) * 8],
|
|
4241
4280
|
mass: mass * faceArea(cell) / total,
|
|
4242
|
-
...marks.length > 0 ? { marks } : {}
|
|
4281
|
+
...marks.length > 0 ? { marks } : {},
|
|
4282
|
+
...inner ? { inner } : {}
|
|
4243
4283
|
};
|
|
4244
4284
|
});
|
|
4245
4285
|
};
|
|
@@ -4280,7 +4320,7 @@ var GameDestruction = (() => {
|
|
|
4280
4320
|
return cells.some((c) => Math.abs(lp[u] - c.center[u]) <= c.size[u] / 2 && Math.abs(lp[v] - c.center[v]) <= c.size[v] / 2);
|
|
4281
4321
|
};
|
|
4282
4322
|
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
4283
|
-
var _a, _b;
|
|
4323
|
+
var _a, _b, _c;
|
|
4284
4324
|
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
4285
4325
|
const rule = p.mat.hits?.[kind];
|
|
4286
4326
|
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
@@ -4290,7 +4330,9 @@ var GameDestruction = (() => {
|
|
|
4290
4330
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
4291
4331
|
const grid = gridOf(p);
|
|
4292
4332
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4333
|
+
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
4293
4334
|
const out = [];
|
|
4335
|
+
const shot = kind === "ballistic";
|
|
4294
4336
|
for (let i = 0; i < grid.length; i++) {
|
|
4295
4337
|
const cell = grid[i];
|
|
4296
4338
|
if (worn[i] === -1) continue;
|
|
@@ -4303,18 +4345,39 @@ var GameDestruction = (() => {
|
|
|
4303
4345
|
worn[i] = total;
|
|
4304
4346
|
continue;
|
|
4305
4347
|
}
|
|
4306
|
-
|
|
4307
|
-
|
|
4348
|
+
if (!shot) {
|
|
4349
|
+
worn[i] = -1;
|
|
4350
|
+
out.push(cell);
|
|
4351
|
+
continue;
|
|
4352
|
+
}
|
|
4353
|
+
const size = [cell.size[0], cell.size[1], cell.size[2]];
|
|
4354
|
+
const center = [cell.center[0], cell.center[1], cell.center[2]];
|
|
4355
|
+
for (const a of [u, v]) {
|
|
4356
|
+
const w = Math.min(cell.size[a], craterAcross(hitEnergy) * (0.85 + 0.3 * rnd()));
|
|
4357
|
+
const lo = cell.center[a] - cell.size[a] / 2 + w / 2, hi = cell.center[a] + cell.size[a] / 2 - w / 2;
|
|
4358
|
+
size[a] = w;
|
|
4359
|
+
center[a] = clamp2(lp[a], lo, hi);
|
|
4360
|
+
}
|
|
4361
|
+
const taken2 = (cut[i] ?? 0) + size[u] * size[v];
|
|
4362
|
+
if (taken2 >= cell.size[u] * cell.size[v] * CRATER_SPENT) {
|
|
4363
|
+
worn[i] = -1;
|
|
4364
|
+
out.push({ ...cell, crater: true });
|
|
4365
|
+
continue;
|
|
4366
|
+
}
|
|
4367
|
+
worn[i] = 0;
|
|
4368
|
+
cut[i] = taken2;
|
|
4369
|
+
out.push({ center, size, crater: true });
|
|
4308
4370
|
}
|
|
4309
4371
|
if (out.length === 0) return;
|
|
4310
4372
|
const { c, q } = partXf(o, p);
|
|
4311
|
-
const chipped = (
|
|
4373
|
+
const chipped = (_c = p.st).chipped ?? (_c.chipped = []);
|
|
4312
4374
|
const holes = localHolesOf(p);
|
|
4313
4375
|
for (const cell of out) {
|
|
4314
4376
|
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
4315
4377
|
const center = toWorld(c, q, cell.center);
|
|
4316
4378
|
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
4317
|
-
const
|
|
4379
|
+
const tag = `c${chipped.length}_`;
|
|
4380
|
+
const fragments = cell.crater ? craterFragments(o, p, cell, dir, speed, mass, tag) : cellFragments(o, p, [cell], wp, dir, speed, mass, tag, true);
|
|
4318
4381
|
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
4319
4382
|
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
4320
4383
|
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
@@ -4331,6 +4394,44 @@ var GameDestruction = (() => {
|
|
|
4331
4394
|
collapseCheck(o, src);
|
|
4332
4395
|
}
|
|
4333
4396
|
};
|
|
4397
|
+
const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
|
|
4398
|
+
const craterFragments = (o, p, cell, dir, speed, mass, tag) => {
|
|
4399
|
+
const { c, q } = partXf(o, p);
|
|
4400
|
+
const [u, v] = planeAxes(p.thin);
|
|
4401
|
+
const d = dir ?? rotateVec(q, [p.thin === 0 ? 1 : 0, p.thin === 1 ? 1 : 0, p.thin === 2 ? 1 : 0]);
|
|
4402
|
+
const exitSign = Math.sign(rotateVec(quat.conjugate(q), d)[p.thin]) || 1;
|
|
4403
|
+
const bit = Math.max(0.01, Math.min(cell.size[u], cell.size[v]) / 3);
|
|
4404
|
+
const n = clamp2(Math.round(cell.size[u] * cell.size[v] / (bit * bit * 3)), 4, 10) + Math.floor(rnd() * 3);
|
|
4405
|
+
const out = [];
|
|
4406
|
+
for (let i = 0; i < n; i++) {
|
|
4407
|
+
const back = i % 3 === 2;
|
|
4408
|
+
const lc = [cell.center[0], cell.center[1], cell.center[2]];
|
|
4409
|
+
lc[u] += (rnd() - 0.5) * cell.size[u] * 0.7;
|
|
4410
|
+
lc[v] += (rnd() - 0.5) * cell.size[v] * 0.7;
|
|
4411
|
+
lc[p.thin] += (back ? -exitSign : exitSign) * (p.thickness / 2 + 0.01);
|
|
4412
|
+
const size = [0, 0, 0];
|
|
4413
|
+
size[u] = bit * (0.6 + 0.8 * rnd());
|
|
4414
|
+
size[v] = bit * (0.5 + 0.7 * rnd());
|
|
4415
|
+
size[p.thin] = bit * (0.4 + 0.6 * rnd());
|
|
4416
|
+
const spread3 = [(rnd() - 0.5) * 0.9, (rnd() - 0.3) * 0.9, (rnd() - 0.5) * 0.9];
|
|
4417
|
+
const k = speed * (back ? 0.4 + 0.4 * rnd() : 0.7 + 0.8 * rnd());
|
|
4418
|
+
const vdir = vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3));
|
|
4419
|
+
out.push({
|
|
4420
|
+
id: `${p.spec.id}#${tag}${i}`,
|
|
4421
|
+
part: p.spec.id,
|
|
4422
|
+
material: p.spec.material,
|
|
4423
|
+
shape: "chunk",
|
|
4424
|
+
center: toWorld(c, q, lc),
|
|
4425
|
+
size,
|
|
4426
|
+
rotation: q,
|
|
4427
|
+
velocity: [vdir[0] * k, vdir[1] * k + 0.5 * rnd(), vdir[2] * k],
|
|
4428
|
+
angularVelocity: [(rnd() - 0.5) * 20, (rnd() - 0.5) * 20, (rnd() - 0.5) * 20],
|
|
4429
|
+
mass: mass / n,
|
|
4430
|
+
inner: true
|
|
4431
|
+
});
|
|
4432
|
+
}
|
|
4433
|
+
return out;
|
|
4434
|
+
};
|
|
4334
4435
|
const exposeRebar = (o, p, src) => {
|
|
4335
4436
|
const r = p.spec.reinforcement;
|
|
4336
4437
|
if (!r) return;
|
|
@@ -4618,7 +4719,7 @@ var GameDestruction = (() => {
|
|
|
4618
4719
|
});
|
|
4619
4720
|
}
|
|
4620
4721
|
const patch = [scale * 4, scale * 4, scale * 4];
|
|
4621
|
-
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry, size: patch, rotation: q, fragments });
|
|
4722
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry, size: patch, rotation: q, fragments, torn: true });
|
|
4622
4723
|
};
|
|
4623
4724
|
const ballistic = (o, p, entry, n, d, E, caliber, pathLen, src, round = {}) => {
|
|
4624
4725
|
const mat = p.mat;
|
|
@@ -4677,12 +4778,13 @@ var GameDestruction = (() => {
|
|
|
4677
4778
|
} else if (mat.ductility >= 0.5) {
|
|
4678
4779
|
addDent(o, p, entry, n, Math.max(caliber * 3 / 1e3, 0.012), clamp2(Math.min(Math.max(p.thickness * 3, 3e-3), ratio * 0.01), 1e-3, 0.05), src);
|
|
4679
4780
|
} else if (brittle) {
|
|
4680
|
-
addCrack(o, p, entry, n, crackLen, src);
|
|
4781
|
+
if (!p.spec.units) addCrack(o, p, entry, n, crackLen, src);
|
|
4681
4782
|
if (mat.fracture !== "shatter") addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
4682
4783
|
} else addHole(o, p, entry, n, caliber / 1e3, false, void 0, src);
|
|
4683
4784
|
debris(o, p, mat.hitDebris, entry, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
4684
4785
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
4685
|
-
const
|
|
4786
|
+
const hollowTear = mat.fracture === "tear" && p.spec.fill !== void 0 && p.spec.fill < 0.5;
|
|
4787
|
+
const absorbed = penetrated && (mat.ductility >= 0.5 && mat.fracture !== "tear" || hollowTear) ? Math.min(E, thr) : E;
|
|
4686
4788
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry, d, absorbed * 0.1, 12, src);
|
|
4687
4789
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
4688
4790
|
chipUnits(o, p, "ballistic", entry, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
@@ -4767,10 +4869,25 @@ var GameDestruction = (() => {
|
|
|
4767
4869
|
if (dist <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
4768
4870
|
return reach;
|
|
4769
4871
|
};
|
|
4872
|
+
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
4873
|
+
const blastCandidates = (o, point, radius) => {
|
|
4874
|
+
const local = toLocal(o.st.position, o.st.rotation, point);
|
|
4875
|
+
const near = [];
|
|
4876
|
+
for (const p of o.parts) {
|
|
4877
|
+
const reach = radius + p.bound;
|
|
4878
|
+
const dx = local[0] - p.pos[0];
|
|
4879
|
+
const dy = local[1] - p.pos[1];
|
|
4880
|
+
const dz = local[2] - p.pos[2];
|
|
4881
|
+
if (dx * dx + dy * dy + dz * dz < reach * reach) near.push(p);
|
|
4882
|
+
}
|
|
4883
|
+
blastProbe.partsTested += o.parts.length;
|
|
4884
|
+
blastProbe.partsNear += near.length;
|
|
4885
|
+
return near;
|
|
4886
|
+
};
|
|
4770
4887
|
const blastObject = (o, point, energy, radius, src) => {
|
|
4771
4888
|
const start = cur.length;
|
|
4772
4889
|
const res = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] };
|
|
4773
|
-
for (const p of o
|
|
4890
|
+
for (const p of blastCandidates(o, point, radius)) blastPart(o, p, point, energy, radius, src, res);
|
|
4774
4891
|
res.events = cur.slice(start);
|
|
4775
4892
|
return res;
|
|
4776
4893
|
};
|
|
@@ -4894,7 +5011,7 @@ var GameDestruction = (() => {
|
|
|
4894
5011
|
let order = 0;
|
|
4895
5012
|
for (const o of inRange) {
|
|
4896
5013
|
if (only && only !== o) continue;
|
|
4897
|
-
for (const p of o
|
|
5014
|
+
for (const p of blastCandidates(o, point, radius)) {
|
|
4898
5015
|
if (isLost(p)) continue;
|
|
4899
5016
|
const dist = distToPart(o, p, point);
|
|
4900
5017
|
if (dist < radius) items.push({ dist, order: order++, o, p });
|
|
@@ -5441,6 +5558,7 @@ var GameDestruction = (() => {
|
|
|
5441
5558
|
const p = getPart(o, partId);
|
|
5442
5559
|
p.pos = [position[0], position[1], position[2]];
|
|
5443
5560
|
p.q = q;
|
|
5561
|
+
poseVersion++;
|
|
5444
5562
|
updateBound(o);
|
|
5445
5563
|
},
|
|
5446
5564
|
remove(id) {
|
|
@@ -5459,6 +5577,7 @@ var GameDestruction = (() => {
|
|
|
5459
5577
|
const o = getObj(id);
|
|
5460
5578
|
o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
|
|
5461
5579
|
applySoilPose(o);
|
|
5580
|
+
slideOffResting(o, rotation);
|
|
5462
5581
|
},
|
|
5463
5582
|
splitGroup(id, group, newId) {
|
|
5464
5583
|
if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
|
|
@@ -5482,7 +5601,7 @@ var GameDestruction = (() => {
|
|
|
5482
5601
|
if (o.spec.name !== void 0) s.name = o.spec.name;
|
|
5483
5602
|
if (o.spec.tags) s.tags = [...o.spec.tags];
|
|
5484
5603
|
if (o.spec.soil !== void 0) s.soil = o.spec.soil;
|
|
5485
|
-
return
|
|
5604
|
+
return s;
|
|
5486
5605
|
};
|
|
5487
5606
|
const inNew = (pid) => moved.has(pid);
|
|
5488
5607
|
const inOld = (pid) => !moved.has(pid);
|
|
@@ -5509,7 +5628,12 @@ var GameDestruction = (() => {
|
|
|
5509
5628
|
const p = getPart(o, partId);
|
|
5510
5629
|
return whole(() => doHit(o, h, p));
|
|
5511
5630
|
},
|
|
5631
|
+
blastProbe() {
|
|
5632
|
+
return { ...blastProbe };
|
|
5633
|
+
},
|
|
5512
5634
|
explode(point, energy, radius, source, blast) {
|
|
5635
|
+
blastProbe.partsTested = 0;
|
|
5636
|
+
blastProbe.partsNear = 0;
|
|
5513
5637
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
5514
5638
|
},
|
|
5515
5639
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
@@ -5519,7 +5643,8 @@ var GameDestruction = (() => {
|
|
|
5519
5643
|
sweepBox(box, direction, distance, options2 = {}) {
|
|
5520
5644
|
const d = vec.normalize(direction);
|
|
5521
5645
|
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
5522
|
-
const
|
|
5646
|
+
const hosted = options2.ignore === void 0 || ignored ? void 0 : impls.get(options2.ignore);
|
|
5647
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : hosted ? new Set(hosted.impl.describe().parts.map((p) => `${hosted.impl.id}\0${p.id}`)) : void 0;
|
|
5523
5648
|
let best = null;
|
|
5524
5649
|
for (const origin of sweepOrigins(box, d, options2.spacing ?? 0.2)) {
|
|
5525
5650
|
const r = raycastInternal(origin, d, distance + SWEEP_BACK, ignore);
|