@adatechnology/game-weapons-three 0.10.4 → 0.11.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/README.md +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.global.js +833 -212
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +158 -52
- package/dist/index.js.map +1 -1
- package/package.json +12 -13
package/dist/index.global.js
CHANGED
|
@@ -19264,26 +19264,26 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19264
19264
|
for (const e of planned.items) if (e.type === "wall_switch") e.lit = (e.connections.controls || []).some((id) => (planned.items.find((x) => x.id === id) || {}).lit === true);
|
|
19265
19265
|
}
|
|
19266
19266
|
for (const e of planned.items) if (e.type === "lantern_lamp") e.item.on = !!e.lit;
|
|
19267
|
-
let
|
|
19267
|
+
let scratch2 = null;
|
|
19268
19268
|
function probe(e, hinge, setback) {
|
|
19269
|
-
if (!
|
|
19269
|
+
if (!scratch2) scratch2 = GI.createWorld({ locale, strings: hh.strings, items: hh.items, loot: hh.loot, grid: false });
|
|
19270
19270
|
const it = e.item;
|
|
19271
19271
|
const x = it.x + Math.sin(it.ry) * setback;
|
|
19272
19272
|
const z = it.z + Math.cos(it.ry) * setback;
|
|
19273
19273
|
const spec = hh.build(e.type, Object.assign({}, it, { hinge }), HH.createRecorder().api);
|
|
19274
|
-
|
|
19274
|
+
scratch2.add("__probe", spec, { x, y: e.floorY, z, ry: it.ry });
|
|
19275
19275
|
const blocks = (px, pz) => isBlocked2(px, pz, e.floor);
|
|
19276
19276
|
let sum2 = 0;
|
|
19277
19277
|
let min2 = 1;
|
|
19278
19278
|
let n = 0;
|
|
19279
19279
|
for (const p of spec.parts || []) {
|
|
19280
19280
|
if (p.motion.kind !== "hinge" || !(p.bounds || p.sweep)) continue;
|
|
19281
|
-
const t =
|
|
19281
|
+
const t = scratch2.fitSweep("__probe", p.id, blocks);
|
|
19282
19282
|
sum2 += t;
|
|
19283
19283
|
min2 = Math.min(min2, t);
|
|
19284
19284
|
n++;
|
|
19285
19285
|
}
|
|
19286
|
-
|
|
19286
|
+
scratch2.remove("__probe");
|
|
19287
19287
|
return { sum: sum2, min: min2, n };
|
|
19288
19288
|
}
|
|
19289
19289
|
const better = (a, b) => a.sum > b.sum + 1e-9 || Math.abs(a.sum - b.sum) <= 1e-9 && a.min > b.min + 1e-9;
|
|
@@ -21972,6 +21972,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21972
21972
|
var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
21973
21973
|
var VENT_DIAMETER = 8e-3;
|
|
21974
21974
|
var FLAME_LIGHTS_LEAK_C = 470;
|
|
21975
|
+
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
21975
21976
|
var BREACH_RADIUS = 0.55;
|
|
21976
21977
|
var BREACH_BLAST_FACTOR = 6;
|
|
21977
21978
|
var BREACH_AREA = 0.25;
|
|
@@ -21987,6 +21988,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21987
21988
|
var BREACH_GRID_CAP = 4096;
|
|
21988
21989
|
var CHIP_ENERGY = 200;
|
|
21989
21990
|
var CHIP_SPLASH = 0.5;
|
|
21991
|
+
var CRATER_SPENT = 0.6;
|
|
21990
21992
|
var BAR_BOW = 0.08;
|
|
21991
21993
|
var BULLET_JOINT_SPAN = 0.3;
|
|
21992
21994
|
var CUT_SLICE = 1;
|
|
@@ -22007,12 +22009,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22007
22009
|
const cells = p.st.chipped;
|
|
22008
22010
|
if (!cells?.length) return 0;
|
|
22009
22011
|
const [u, v] = planeAxes(p.thin);
|
|
22010
|
-
const
|
|
22012
|
+
const role = p.spec.structure?.role;
|
|
22013
|
+
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
22014
|
+
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;
|
|
22015
|
+
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
22011
22016
|
const across2 = along === u ? v : u;
|
|
22012
22017
|
let worst = 0;
|
|
22013
22018
|
for (const c of cells) {
|
|
22014
|
-
|
|
22015
|
-
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6)
|
|
22019
|
+
const spans = [];
|
|
22020
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) spans.push([d.center[across2] - d.size[across2] / 2, d.center[across2] + d.size[across2] / 2]);
|
|
22021
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
22022
|
+
let gone = 0, end3 = -Infinity;
|
|
22023
|
+
for (const [a, b] of spans) {
|
|
22024
|
+
if (b <= end3) continue;
|
|
22025
|
+
gone += b - Math.max(a, end3);
|
|
22026
|
+
end3 = b;
|
|
22027
|
+
}
|
|
22016
22028
|
worst = Math.max(worst, gone / Math.max(p.dims[across2], 1e-6));
|
|
22017
22029
|
}
|
|
22018
22030
|
return Math.min(1, worst);
|
|
@@ -22308,7 +22320,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22308
22320
|
if (k < 0 || j.spec.kind !== "tie") return k;
|
|
22309
22321
|
return partXf(o, o.parts[k]).c[1] > partXf(o, o.parts[r]).c[1] + 1e-6 ? k : -1;
|
|
22310
22322
|
};
|
|
22323
|
+
let poseVersion = 0;
|
|
22324
|
+
const contactMemo = /* @__PURE__ */ new WeakMap();
|
|
22311
22325
|
const contactBox = (p, support) => {
|
|
22326
|
+
let byTouching = contactMemo.get(p);
|
|
22327
|
+
if (!byTouching) contactMemo.set(p, byTouching = /* @__PURE__ */ new Map());
|
|
22328
|
+
const known = byTouching.get(support);
|
|
22329
|
+
if (known && known.version === poseVersion) return known.box;
|
|
22330
|
+
const box5 = measureContact(p, support);
|
|
22331
|
+
byTouching.set(support, { version: poseVersion, box: box5 });
|
|
22332
|
+
return box5;
|
|
22333
|
+
};
|
|
22334
|
+
const measureContact = (p, support) => {
|
|
22312
22335
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
22313
22336
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
22314
22337
|
for (let k = 0; k < 3; k++) {
|
|
@@ -23103,7 +23126,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23103
23126
|
p.st.holes.push(hole);
|
|
23104
23127
|
if (p.st.holes.length > maxHoles) p.st.holes.shift();
|
|
23105
23128
|
markDamaged(p);
|
|
23106
|
-
emit2({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {} });
|
|
23129
|
+
emit2({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {}, ...p.spec.powder ? { powder: { ...p.spec.powder } } : {} });
|
|
23107
23130
|
setStage(o, p, 1, {}, src);
|
|
23108
23131
|
return hole;
|
|
23109
23132
|
};
|
|
@@ -23120,6 +23143,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23120
23143
|
const detachEvents = (o, opened, by, src) => {
|
|
23121
23144
|
for (const j of opened) emit2({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
|
|
23122
23145
|
};
|
|
23146
|
+
const slideOffResting = (o, rotation) => {
|
|
23147
|
+
const upY = 1 - 2 * (rotation[0] * rotation[0] + rotation[2] * rotation[2]);
|
|
23148
|
+
if (upY >= RESTING_HOLD_COS) return;
|
|
23149
|
+
const opened = o.joints.filter((j) => j.spec.kind === "resting" && !j.st.open);
|
|
23150
|
+
if (opened.length === 0) return;
|
|
23151
|
+
run(() => {
|
|
23152
|
+
for (const j of opened) {
|
|
23153
|
+
j.st.open = true;
|
|
23154
|
+
j.st.openedBy = "break";
|
|
23155
|
+
}
|
|
23156
|
+
solveStructure(o);
|
|
23157
|
+
recomputeGroups(o);
|
|
23158
|
+
detachEvents(o, opened, "break");
|
|
23159
|
+
collapseCheck(o);
|
|
23160
|
+
});
|
|
23161
|
+
};
|
|
23123
23162
|
const checkDestroyed = (o, src) => {
|
|
23124
23163
|
if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {
|
|
23125
23164
|
o.st.destroyed = true;
|
|
@@ -23220,7 +23259,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23220
23259
|
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 }));
|
|
23221
23260
|
const unitsOf2 = (p) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness], bond: "stack" };
|
|
23222
23261
|
const FRAGMENT_SHAPE = { shatter: "shard", splinter: "splinter", tear: "scrap" };
|
|
23223
|
-
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag) => {
|
|
23262
|
+
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag, inner = false) => {
|
|
23224
23263
|
const { c, q } = partXf(o, p);
|
|
23225
23264
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
23226
23265
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -23244,7 +23283,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23244
23283
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd2(), dv[2] * k],
|
|
23245
23284
|
angularVelocity: [(rnd2() - 0.5) * 8, (rnd2() - 0.5) * 8, (rnd2() - 0.5) * 8],
|
|
23246
23285
|
mass: mass * faceArea(cell) / total,
|
|
23247
|
-
...marks.length > 0 ? { marks } : {}
|
|
23286
|
+
...marks.length > 0 ? { marks } : {},
|
|
23287
|
+
...inner ? { inner } : {}
|
|
23248
23288
|
};
|
|
23249
23289
|
});
|
|
23250
23290
|
};
|
|
@@ -23285,7 +23325,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23285
23325
|
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);
|
|
23286
23326
|
};
|
|
23287
23327
|
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
23288
|
-
var _a, _b;
|
|
23328
|
+
var _a, _b, _c;
|
|
23289
23329
|
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
23290
23330
|
const rule = p.mat.hits?.[kind];
|
|
23291
23331
|
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
@@ -23295,7 +23335,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23295
23335
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
23296
23336
|
const grid = gridOf(p);
|
|
23297
23337
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
23338
|
+
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
23298
23339
|
const out = [];
|
|
23340
|
+
const shot = kind === "ballistic";
|
|
23299
23341
|
for (let i = 0; i < grid.length; i++) {
|
|
23300
23342
|
const cell = grid[i];
|
|
23301
23343
|
if (worn[i] === -1) continue;
|
|
@@ -23308,18 +23350,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23308
23350
|
worn[i] = total;
|
|
23309
23351
|
continue;
|
|
23310
23352
|
}
|
|
23311
|
-
|
|
23312
|
-
|
|
23353
|
+
if (!shot) {
|
|
23354
|
+
worn[i] = -1;
|
|
23355
|
+
out.push(cell);
|
|
23356
|
+
continue;
|
|
23357
|
+
}
|
|
23358
|
+
const size = [cell.size[0], cell.size[1], cell.size[2]];
|
|
23359
|
+
const center = [cell.center[0], cell.center[1], cell.center[2]];
|
|
23360
|
+
for (const a of [u, v]) {
|
|
23361
|
+
const w = Math.min(cell.size[a], craterAcross(hitEnergy) * (0.85 + 0.3 * rnd2()));
|
|
23362
|
+
const lo = cell.center[a] - cell.size[a] / 2 + w / 2, hi = cell.center[a] + cell.size[a] / 2 - w / 2;
|
|
23363
|
+
size[a] = w;
|
|
23364
|
+
center[a] = clamp2(lp[a], lo, hi);
|
|
23365
|
+
}
|
|
23366
|
+
const taken2 = (cut[i] ?? 0) + size[u] * size[v];
|
|
23367
|
+
if (taken2 >= cell.size[u] * cell.size[v] * CRATER_SPENT) {
|
|
23368
|
+
worn[i] = -1;
|
|
23369
|
+
out.push({ ...cell, crater: true });
|
|
23370
|
+
continue;
|
|
23371
|
+
}
|
|
23372
|
+
worn[i] = 0;
|
|
23373
|
+
cut[i] = taken2;
|
|
23374
|
+
out.push({ center, size, crater: true });
|
|
23313
23375
|
}
|
|
23314
23376
|
if (out.length === 0) return;
|
|
23315
23377
|
const { c, q } = partXf(o, p);
|
|
23316
|
-
const chipped = (
|
|
23378
|
+
const chipped = (_c = p.st).chipped ?? (_c.chipped = []);
|
|
23317
23379
|
const holes = localHolesOf(p);
|
|
23318
23380
|
for (const cell of out) {
|
|
23319
23381
|
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
23320
23382
|
const center = toWorld(c, q, cell.center);
|
|
23321
23383
|
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
23322
|
-
const
|
|
23384
|
+
const tag = `c${chipped.length}_`;
|
|
23385
|
+
const fragments = cell.crater ? craterFragments(o, p, cell, dir, speed, mass, tag) : cellFragments(o, p, [cell], wp, dir, speed, mass, tag, true);
|
|
23323
23386
|
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
23324
23387
|
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
23325
23388
|
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
@@ -23336,6 +23399,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23336
23399
|
collapseCheck(o, src);
|
|
23337
23400
|
}
|
|
23338
23401
|
};
|
|
23402
|
+
const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
|
|
23403
|
+
const craterFragments = (o, p, cell, dir, speed, mass, tag) => {
|
|
23404
|
+
const { c, q } = partXf(o, p);
|
|
23405
|
+
const [u, v] = planeAxes(p.thin);
|
|
23406
|
+
const d = dir ?? rotateVec(q, [p.thin === 0 ? 1 : 0, p.thin === 1 ? 1 : 0, p.thin === 2 ? 1 : 0]);
|
|
23407
|
+
const exitSign = Math.sign(rotateVec(quat.conjugate(q), d)[p.thin]) || 1;
|
|
23408
|
+
const bit = Math.max(0.01, Math.min(cell.size[u], cell.size[v]) / 3);
|
|
23409
|
+
const n = clamp2(Math.round(cell.size[u] * cell.size[v] / (bit * bit * 3)), 4, 10) + Math.floor(rnd2() * 3);
|
|
23410
|
+
const out = [];
|
|
23411
|
+
for (let i = 0; i < n; i++) {
|
|
23412
|
+
const back = i % 3 === 2;
|
|
23413
|
+
const lc = [cell.center[0], cell.center[1], cell.center[2]];
|
|
23414
|
+
lc[u] += (rnd2() - 0.5) * cell.size[u] * 0.7;
|
|
23415
|
+
lc[v] += (rnd2() - 0.5) * cell.size[v] * 0.7;
|
|
23416
|
+
lc[p.thin] += (back ? -exitSign : exitSign) * (p.thickness / 2 + 0.01);
|
|
23417
|
+
const size = [0, 0, 0];
|
|
23418
|
+
size[u] = bit * (0.6 + 0.8 * rnd2());
|
|
23419
|
+
size[v] = bit * (0.5 + 0.7 * rnd2());
|
|
23420
|
+
size[p.thin] = bit * (0.4 + 0.6 * rnd2());
|
|
23421
|
+
const spread3 = [(rnd2() - 0.5) * 0.9, (rnd2() - 0.3) * 0.9, (rnd2() - 0.5) * 0.9];
|
|
23422
|
+
const k = speed * (back ? 0.4 + 0.4 * rnd2() : 0.7 + 0.8 * rnd2());
|
|
23423
|
+
const vdir = vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3));
|
|
23424
|
+
out.push({
|
|
23425
|
+
id: `${p.spec.id}#${tag}${i}`,
|
|
23426
|
+
part: p.spec.id,
|
|
23427
|
+
material: p.spec.material,
|
|
23428
|
+
shape: "chunk",
|
|
23429
|
+
center: toWorld(c, q, lc),
|
|
23430
|
+
size,
|
|
23431
|
+
rotation: q,
|
|
23432
|
+
velocity: [vdir[0] * k, vdir[1] * k + 0.5 * rnd2(), vdir[2] * k],
|
|
23433
|
+
angularVelocity: [(rnd2() - 0.5) * 20, (rnd2() - 0.5) * 20, (rnd2() - 0.5) * 20],
|
|
23434
|
+
mass: mass / n,
|
|
23435
|
+
inner: true
|
|
23436
|
+
});
|
|
23437
|
+
}
|
|
23438
|
+
return out;
|
|
23439
|
+
};
|
|
23339
23440
|
const exposeRebar = (o, p, src) => {
|
|
23340
23441
|
const r = p.spec.reinforcement;
|
|
23341
23442
|
if (!r) return;
|
|
@@ -23623,7 +23724,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23623
23724
|
});
|
|
23624
23725
|
}
|
|
23625
23726
|
const patch = [scale * 4, scale * 4, scale * 4];
|
|
23626
|
-
emit2({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments });
|
|
23727
|
+
emit2({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments, torn: true });
|
|
23627
23728
|
};
|
|
23628
23729
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round5 = {}) => {
|
|
23629
23730
|
const mat = p.mat;
|
|
@@ -23682,12 +23783,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23682
23783
|
} else if (mat.ductility >= 0.5) {
|
|
23683
23784
|
addDent(o, p, entry2, 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);
|
|
23684
23785
|
} else if (brittle) {
|
|
23685
|
-
addCrack(o, p, entry2, n, crackLen, src);
|
|
23786
|
+
if (!p.spec.units) addCrack(o, p, entry2, n, crackLen, src);
|
|
23686
23787
|
if (mat.fracture !== "shatter") addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
23687
23788
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
23688
23789
|
debris2(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
23689
23790
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
23690
|
-
const
|
|
23791
|
+
const hollowTear = mat.fracture === "tear" && p.spec.fill !== void 0 && p.spec.fill < 0.5;
|
|
23792
|
+
const absorbed = penetrated && (mat.ductility >= 0.5 && mat.fracture !== "tear" || hollowTear) ? Math.min(E, thr) : E;
|
|
23691
23793
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
23692
23794
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
23693
23795
|
chipUnits(o, p, "ballistic", entry2, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
@@ -23772,10 +23874,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23772
23874
|
if (dist2 <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
23773
23875
|
return reach;
|
|
23774
23876
|
};
|
|
23877
|
+
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
23878
|
+
const blastCandidates = (o, point, radius) => {
|
|
23879
|
+
const local2 = toLocal(o.st.position, o.st.rotation, point);
|
|
23880
|
+
const near = [];
|
|
23881
|
+
for (const p of o.parts) {
|
|
23882
|
+
const reach = radius + p.bound;
|
|
23883
|
+
const dx = local2[0] - p.pos[0];
|
|
23884
|
+
const dy = local2[1] - p.pos[1];
|
|
23885
|
+
const dz = local2[2] - p.pos[2];
|
|
23886
|
+
if (dx * dx + dy * dy + dz * dz < reach * reach) near.push(p);
|
|
23887
|
+
}
|
|
23888
|
+
blastProbe.partsTested += o.parts.length;
|
|
23889
|
+
blastProbe.partsNear += near.length;
|
|
23890
|
+
return near;
|
|
23891
|
+
};
|
|
23775
23892
|
const blastObject = (o, point, energy, radius, src) => {
|
|
23776
23893
|
const start = cur.length;
|
|
23777
23894
|
const res = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] };
|
|
23778
|
-
for (const p of o
|
|
23895
|
+
for (const p of blastCandidates(o, point, radius)) blastPart(o, p, point, energy, radius, src, res);
|
|
23779
23896
|
res.events = cur.slice(start);
|
|
23780
23897
|
return res;
|
|
23781
23898
|
};
|
|
@@ -23899,7 +24016,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23899
24016
|
let order = 0;
|
|
23900
24017
|
for (const o of inRange) {
|
|
23901
24018
|
if (only && only !== o) continue;
|
|
23902
|
-
for (const p of o
|
|
24019
|
+
for (const p of blastCandidates(o, point, radius)) {
|
|
23903
24020
|
if (isLost(p)) continue;
|
|
23904
24021
|
const dist2 = distToPart(o, p, point);
|
|
23905
24022
|
if (dist2 < radius) items.push({ dist: dist2, order: order++, o, p });
|
|
@@ -24446,6 +24563,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24446
24563
|
const p = getPart(o, partId);
|
|
24447
24564
|
p.pos = [position[0], position[1], position[2]];
|
|
24448
24565
|
p.q = q;
|
|
24566
|
+
poseVersion++;
|
|
24449
24567
|
updateBound(o);
|
|
24450
24568
|
},
|
|
24451
24569
|
remove(id) {
|
|
@@ -24464,6 +24582,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24464
24582
|
const o = getObj(id);
|
|
24465
24583
|
o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
|
|
24466
24584
|
applySoilPose(o);
|
|
24585
|
+
slideOffResting(o, rotation);
|
|
24467
24586
|
},
|
|
24468
24587
|
splitGroup(id, group, newId) {
|
|
24469
24588
|
if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
|
|
@@ -24487,7 +24606,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24487
24606
|
if (o.spec.name !== void 0) s.name = o.spec.name;
|
|
24488
24607
|
if (o.spec.tags) s.tags = [...o.spec.tags];
|
|
24489
24608
|
if (o.spec.soil !== void 0) s.soil = o.spec.soil;
|
|
24490
|
-
return
|
|
24609
|
+
return s;
|
|
24491
24610
|
};
|
|
24492
24611
|
const inNew = (pid) => moved.has(pid);
|
|
24493
24612
|
const inOld = (pid) => !moved.has(pid);
|
|
@@ -24514,7 +24633,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24514
24633
|
const p = getPart(o, partId);
|
|
24515
24634
|
return whole(() => doHit(o, h, p));
|
|
24516
24635
|
},
|
|
24636
|
+
blastProbe() {
|
|
24637
|
+
return { ...blastProbe };
|
|
24638
|
+
},
|
|
24517
24639
|
explode(point, energy, radius, source, blast) {
|
|
24640
|
+
blastProbe.partsTested = 0;
|
|
24641
|
+
blastProbe.partsNear = 0;
|
|
24518
24642
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
24519
24643
|
},
|
|
24520
24644
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
@@ -24524,7 +24648,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
24524
24648
|
sweepBox(box5, direction, distance3, options2 = {}) {
|
|
24525
24649
|
const d = vec.normalize(direction);
|
|
24526
24650
|
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
24527
|
-
const
|
|
24651
|
+
const hosted = options2.ignore === void 0 || ignored ? void 0 : impls.get(options2.ignore);
|
|
24652
|
+
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;
|
|
24528
24653
|
let best = null;
|
|
24529
24654
|
for (const origin of sweepOrigins(box5, d, options2.spacing ?? 0.2)) {
|
|
24530
24655
|
const r = raycastInternal(origin, d, distance3 + SWEEP_BACK, ignore);
|
|
@@ -34127,6 +34252,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34127
34252
|
function collapseRange(mesh3, v0, v1) {
|
|
34128
34253
|
const pos = mesh3.geometry.attributes.position;
|
|
34129
34254
|
const a = pos.array;
|
|
34255
|
+
const u = mesh3.userData;
|
|
34256
|
+
if (!u._p0) u._p0 = a.slice();
|
|
34257
|
+
const list2 = u.collapsedRanges ?? (u.collapsedRanges = []);
|
|
34258
|
+
if (!list2.some((r) => r[0] === v0 && r[1] === v1)) list2.push([v0, v1]);
|
|
34130
34259
|
const saved = a.slice(v0 * 3, v1 * 3);
|
|
34131
34260
|
const x = a[v0 * 3], y = a[v0 * 3 + 1], z = a[v0 * 3 + 2];
|
|
34132
34261
|
for (let k = v0; k < v1; k++) {
|
|
@@ -34141,6 +34270,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34141
34270
|
const pos = mesh3.geometry.attributes.position;
|
|
34142
34271
|
pos.array.set(saved, v0 * 3);
|
|
34143
34272
|
pos.needsUpdate = true;
|
|
34273
|
+
const list2 = mesh3.userData.collapsedRanges;
|
|
34274
|
+
if (list2) mesh3.userData.collapsedRanges = list2.filter((r) => r[0] !== v0);
|
|
34144
34275
|
}
|
|
34145
34276
|
function noise2(x, y, cell) {
|
|
34146
34277
|
const h = (i2, j2) => {
|
|
@@ -34155,13 +34286,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34155
34286
|
var SPALL_MIN = 0.025;
|
|
34156
34287
|
var SPALL_RAGGED = 0.045;
|
|
34157
34288
|
var COVER2 = 0.015;
|
|
34289
|
+
var MAX_FLAKES = 64;
|
|
34158
34290
|
function installDestructionBreach(K, _shared) {
|
|
34159
34291
|
const { THREE, fragmentMaterial, invalidate, invalidateShadow } = K;
|
|
34160
34292
|
function createBreachView(opts) {
|
|
34161
34293
|
const { house, structure } = opts;
|
|
34162
34294
|
const views = /* @__PURE__ */ new Map();
|
|
34295
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
34296
|
+
const paintBudgetMs = opts.paintBudgetMs ?? Infinity;
|
|
34297
|
+
const dropped = [];
|
|
34163
34298
|
let lod = 0;
|
|
34164
|
-
const unitMaterial = new THREE.MeshLambertMaterial({ color: 16777215 });
|
|
34165
34299
|
const unitGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
34166
34300
|
const tmp = { m: new THREE.Matrix4(), q: new THREE.Quaternion(), p: new THREE.Vector3(), s: new THREE.Vector3(), c: new THREE.Color() };
|
|
34167
34301
|
const local2 = (p) => {
|
|
@@ -34230,6 +34364,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34230
34364
|
const mesh3 = new THREE.Mesh(g, material);
|
|
34231
34365
|
mesh3.name = "breach-shell";
|
|
34232
34366
|
mesh3.userData.breachMask = mask;
|
|
34367
|
+
mesh3.userData.cutAt = (p) => {
|
|
34368
|
+
const v = local2([p.x, p.y, p.z]);
|
|
34369
|
+
const i = Math.floor(((mask.alongU ? v[0] : v[2]) - mask.start) / mask.len * mask.w), j = Math.floor((v[1] - mask.y0) / mask.height * mask.h);
|
|
34370
|
+
return i >= 0 && i < mask.w && j >= 0 && j < mask.h && mask.data[j * mask.w + i] > 127;
|
|
34371
|
+
};
|
|
34233
34372
|
mesh3.position.copy(it.mesh.position);
|
|
34234
34373
|
mesh3.quaternion.copy(it.mesh.quaternion);
|
|
34235
34374
|
mesh3.scale.copy(it.mesh.scale);
|
|
@@ -34245,6 +34384,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34245
34384
|
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh3.matrixWorld);
|
|
34246
34385
|
return mesh3;
|
|
34247
34386
|
}
|
|
34387
|
+
function unitsMaterial(core) {
|
|
34388
|
+
const material = new THREE.MeshLambertMaterial({ color: 16777215, side: THREE.DoubleSide });
|
|
34389
|
+
material.onBeforeCompile = (sh) => {
|
|
34390
|
+
sh.uniforms.uCore = { value: core.texture };
|
|
34391
|
+
sh.uniforms.uCoreRect = { value: core.rect };
|
|
34392
|
+
sh.uniforms.uCoreAlongX = { value: core.alongU ? 1 : 0 };
|
|
34393
|
+
sh.vertexShader = sh.vertexShader.replace("void main() {", "varying vec3 vHousePos;\nvoid main() {").replace("#include <begin_vertex>", "#include <begin_vertex>\n#ifdef USE_INSTANCING\nvHousePos = (instanceMatrix * vec4(transformed, 1.0)).xyz;\n#else\nvHousePos = transformed;\n#endif");
|
|
34394
|
+
sh.fragmentShader = sh.fragmentShader.replace("void main() {", "uniform sampler2D uCore;\nuniform vec4 uCoreRect;\nuniform float uCoreAlongX;\nvarying vec3 vHousePos;\nvoid main() {\n vec2 cUv = vec2(((uCoreAlongX > 0.5 ? vHousePos.x : vHousePos.z) - uCoreRect.x) / uCoreRect.z, (vHousePos.y - uCoreRect.y) / uCoreRect.w);\n if (cUv.x >= 0.0 && cUv.x <= 1.0 && cUv.y >= 0.0 && cUv.y <= 1.0 && texture2D(uCore, cUv).r > 0.5) discard;").replace("#include <color_fragment>", "#include <color_fragment>\nif (!gl_FrontFacing) diffuseColor.rgb *= 0.55;");
|
|
34395
|
+
};
|
|
34396
|
+
material.customProgramCacheKey = () => "breachCore";
|
|
34397
|
+
material.userData.breachCore = core;
|
|
34398
|
+
return material;
|
|
34399
|
+
}
|
|
34248
34400
|
function maskOf(hostId, parts) {
|
|
34249
34401
|
const host = structure.byId.get(hostId);
|
|
34250
34402
|
const [u0, v0, u1, v1] = host.aabb;
|
|
@@ -34260,41 +34412,73 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34260
34412
|
return { data, texture, w, h, alongU, start: alongU ? u0 : v0, len, y0, height: y1 - y0, rect: new THREE.Vector4(alongU ? u0 : v0, y0, len, y1 - y0) };
|
|
34261
34413
|
}
|
|
34262
34414
|
const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
|
|
34415
|
+
const ellipseDistance = (s, y, cs, cy, rs, ry) => (Math.hypot((s - cs) / Math.max(rs, 1e-4), (y - cy) / Math.max(ry, 1e-4)) - 1) * Math.min(rs, ry);
|
|
34416
|
+
function covered(c, boxes) {
|
|
34417
|
+
const n = 8;
|
|
34418
|
+
let hit = 0;
|
|
34419
|
+
for (let j = 0; j < n; j++) {
|
|
34420
|
+
const y = c.y0 + (j + 0.5) / n * (c.y1 - c.y0);
|
|
34421
|
+
for (let i = 0; i < n; i++) {
|
|
34422
|
+
const s = c.a + (i + 0.5) / n * (c.b - c.a);
|
|
34423
|
+
if (boxes.some((k) => s >= k.s0 && s <= k.s1 && y >= k.y0 && y <= k.y1)) hit++;
|
|
34424
|
+
}
|
|
34425
|
+
}
|
|
34426
|
+
return hit / (n * n);
|
|
34427
|
+
}
|
|
34263
34428
|
function paint2(view) {
|
|
34264
|
-
const { mask } = view;
|
|
34429
|
+
const { mask, core } = view;
|
|
34265
34430
|
const along = (p) => (mask.alongU ? p[0] : p[2]) - mask.start;
|
|
34266
34431
|
const holes = [];
|
|
34432
|
+
const pits = [];
|
|
34267
34433
|
const discs = [];
|
|
34434
|
+
const flakes = [];
|
|
34268
34435
|
const shown = [];
|
|
34269
34436
|
for (const id of view.parts) {
|
|
34270
|
-
const part = structure.byId.get(id), units2 = specOf(id).units, chips2 = view.chips.get(id), opening = view.openings.get(id);
|
|
34437
|
+
const part = structure.byId.get(id), units2 = specOf(id).units, chips2 = view.chips.get(id), opening = view.openings.get(id), spalls = view.spalls.get(id);
|
|
34271
34438
|
const all = cells(part, units2);
|
|
34272
34439
|
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
34273
34440
|
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
34274
34441
|
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
34275
|
-
|
|
34442
|
+
for (const k of spalls) flakes.push({ s: along(k.center), y: k.center[1], r: k.radius });
|
|
34443
|
+
const boxes = chips2.map((k) => ({ s0: along(k.center) - k.along, s1: along(k.center) + k.along, y0: k.center[1] - k.up, y1: k.center[1] + k.up }));
|
|
34444
|
+
const gone = (c) => covered(c, boxes.filter((k) => k.s1 > c.a && k.s0 < c.b && k.y1 > c.y0 && k.y0 < c.y1)) >= 0.9 || opening && Math.hypot((c.a + c.b) / 2 - along(opening.center), (c.y0 + c.y1) / 2 - opening.center[1]) < opening.radius;
|
|
34276
34445
|
const kept = [];
|
|
34277
34446
|
for (const c of all) (gone(c) ? holes : kept).push(c);
|
|
34278
34447
|
for (const c of kept) shown.push({ c, part, units: units2, thick, mid });
|
|
34448
|
+
for (const k of boxes) {
|
|
34449
|
+
if (holes.some((h) => k.s0 >= h.a - 1e-4 && k.s1 <= h.b + 1e-4 && k.y0 >= h.y0 - 1e-4 && k.y1 <= h.y1 + 1e-4)) continue;
|
|
34450
|
+
pits.push({ s: (k.s0 + k.s1) / 2, y: (k.y0 + k.y1) / 2, rs: (k.s1 - k.s0) / 2, ry: (k.y1 - k.y0) / 2 });
|
|
34451
|
+
}
|
|
34279
34452
|
}
|
|
34280
34453
|
const reach = SPALL_MIN + SPALL_RAGGED;
|
|
34454
|
+
const ragged = (s, y) => 0.6 * noise2(s, y, 0.03) + 0.4 * noise2(s + 7.3, y - 3.1, 0.011);
|
|
34281
34455
|
mask.data.fill(0);
|
|
34282
|
-
|
|
34283
|
-
|
|
34284
|
-
const
|
|
34456
|
+
core.data.fill(0);
|
|
34457
|
+
const stamp = (m, s0, s1, y0, y1, inside2) => {
|
|
34458
|
+
const i0 = Math.max(0, Math.floor((s0 - reach) / m.len * m.w)), i1 = Math.min(m.w - 1, Math.ceil((s1 + reach) / m.len * m.w));
|
|
34459
|
+
const j0 = Math.max(0, Math.floor((y0 - reach - m.y0) / m.height * m.h)), j1 = Math.min(m.h - 1, Math.ceil((y1 + reach - m.y0) / m.height * m.h));
|
|
34285
34460
|
for (let j = j0; j <= j1; j++) {
|
|
34286
|
-
const y =
|
|
34461
|
+
const y = m.y0 + (j + 0.5) / m.h * m.height;
|
|
34287
34462
|
for (let i = i0; i <= i1; i++) {
|
|
34288
|
-
const s = (i + 0.5) /
|
|
34289
|
-
|
|
34290
|
-
if (inside2(s, y) <= edge) mask.data[j * mask.w + i] = 255;
|
|
34463
|
+
const s = (i + 0.5) / m.w * m.len;
|
|
34464
|
+
if (inside2(s, y)) m.data[j * m.w + i] = 255;
|
|
34291
34465
|
}
|
|
34292
34466
|
}
|
|
34293
34467
|
};
|
|
34294
|
-
|
|
34295
|
-
for (const
|
|
34468
|
+
const plaster = (d, s, y) => d <= SPALL_MIN + SPALL_RAGGED * ragged(s, y);
|
|
34469
|
+
for (const c of holes) stamp(mask, c.a, c.b, c.y0, c.y1, (s, y) => plaster(boxDistance(s, y, c.a, c.b, c.y0, c.y1), s, y));
|
|
34470
|
+
for (const d of discs) stamp(mask, d.s - d.r, d.s + d.r, d.y - d.r, d.y + d.r, (s, y) => plaster(Math.max(0, Math.hypot(s - d.s, y - d.y) - d.r), s, y));
|
|
34471
|
+
for (const f of flakes) {
|
|
34472
|
+
const rough = (s, y) => Math.hypot(s - f.s, y - f.y) - f.r * (0.65 + 0.7 * ragged(s * 2.3, y * 2.3));
|
|
34473
|
+
stamp(mask, f.s - 2 * f.r, f.s + 2 * f.r, f.y - 2 * f.r, f.y + 2 * f.r, (s, y) => rough(s, y) <= 0);
|
|
34474
|
+
}
|
|
34475
|
+
for (const p of pits) {
|
|
34476
|
+
stamp(mask, p.s - p.rs, p.s + p.rs, p.y - p.ry, p.y + p.ry, (s, y) => plaster(Math.max(0, ellipseDistance(s, y, p.s, p.y, p.rs, p.ry)), s, y));
|
|
34477
|
+
stamp(core, p.s - p.rs, p.s + p.rs, p.y - p.ry, p.y + p.ry, (s, y) => ellipseDistance(s, y, p.s, p.y, p.rs, p.ry) <= -0.35 * Math.min(p.rs, p.ry) * noise2(s * 1.7, y * 1.3, 0.012));
|
|
34478
|
+
}
|
|
34296
34479
|
mask.texture.needsUpdate = true;
|
|
34297
|
-
|
|
34480
|
+
core.texture.needsUpdate = true;
|
|
34481
|
+
const near = (c) => holes.some((h) => boxDistance((c.a + c.b) / 2, (c.y0 + c.y1) / 2, h.a - (c.b - c.a) / 2, h.b + (c.b - c.a) / 2, h.y0 - (c.y1 - c.y0) / 2, h.y1 + (c.y1 - c.y0) / 2) <= reach) || discs.some((d) => boxDistance(d.s, d.y, c.a, c.b, c.y0, c.y1) <= d.r + reach) || pits.some((p) => boxDistance(p.s, p.y, c.a, c.b, c.y0, c.y1) <= Math.max(p.rs, p.ry) + reach) || flakes.some((f) => boxDistance(f.s, f.y, c.a, c.b, c.y0, c.y1) <= 2 * f.r);
|
|
34298
34482
|
const list2 = [];
|
|
34299
34483
|
const tone = (hex2, k) => tmp.c.setHex(hex2).multiplyScalar(k).getHex();
|
|
34300
34484
|
for (const { c, part, units: units2, thick, mid } of shown) {
|
|
@@ -34304,7 +34488,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34304
34488
|
const base = fragmentMaterial(part.material).color.getHex();
|
|
34305
34489
|
const n = noise2(centre * 3.1, y * 2.7, 0.07);
|
|
34306
34490
|
if (units2.mortar === "concrete") {
|
|
34307
|
-
at3(len, h, thick - 2 * COVER2
|
|
34491
|
+
at3(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
|
|
34308
34492
|
} else {
|
|
34309
34493
|
at3(len, h, thick - 2 * COVER2 - 0.012, 10327948);
|
|
34310
34494
|
at3(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
|
|
@@ -34314,7 +34498,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34314
34498
|
view.units.removeFromParent();
|
|
34315
34499
|
view.units.dispose();
|
|
34316
34500
|
}
|
|
34317
|
-
const units = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
34501
|
+
const units = new THREE.InstancedMesh(unitGeometry, view.unitMaterial, Math.max(1, list2.length));
|
|
34318
34502
|
units.name = "breach:" + view.host;
|
|
34319
34503
|
list2.forEach((u, i) => {
|
|
34320
34504
|
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
@@ -34330,6 +34514,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34330
34514
|
house.add(units);
|
|
34331
34515
|
view.units = units;
|
|
34332
34516
|
view.gone = holes.length;
|
|
34517
|
+
view.pits = pits.length;
|
|
34518
|
+
view.flakes = flakes.length;
|
|
34519
|
+
}
|
|
34520
|
+
function chipBox(chip, alongU) {
|
|
34521
|
+
house.updateMatrixWorld(true);
|
|
34522
|
+
const q = tmp.q.set(chip.rotation[0], chip.rotation[1], chip.rotation[2], chip.rotation[3]).premultiply(house.getWorldQuaternion(new THREE.Quaternion()).invert());
|
|
34523
|
+
let a = 0, u = 0;
|
|
34524
|
+
for (let k = 0; k < 3; k++) {
|
|
34525
|
+
const axis2 = tmp.p.set(k === 0 ? 1 : 0, k === 1 ? 1 : 0, k === 2 ? 1 : 0).applyQuaternion(q);
|
|
34526
|
+
a += Math.abs(alongU ? axis2.x : axis2.z) * chip.size[k] / 2;
|
|
34527
|
+
u += Math.abs(axis2.y) * chip.size[k] / 2;
|
|
34528
|
+
}
|
|
34529
|
+
return { center: local2(chip.center), along: a, up: u };
|
|
34333
34530
|
}
|
|
34334
34531
|
function draw2(partId, change) {
|
|
34335
34532
|
if (lod !== 0) return false;
|
|
@@ -34341,23 +34538,49 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34341
34538
|
if (!items.length) return false;
|
|
34342
34539
|
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
34343
34540
|
if (!parts.includes(partId)) return false;
|
|
34344
|
-
const mask = maskOf(hostId, parts);
|
|
34345
|
-
view = {
|
|
34541
|
+
const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
|
|
34542
|
+
view = {
|
|
34543
|
+
host: hostId,
|
|
34544
|
+
parts,
|
|
34545
|
+
items,
|
|
34546
|
+
mask,
|
|
34547
|
+
core,
|
|
34548
|
+
unitMaterial: unitsMaterial(core),
|
|
34549
|
+
shells: [],
|
|
34550
|
+
saved: null,
|
|
34551
|
+
units: null,
|
|
34552
|
+
gone: 0,
|
|
34553
|
+
pits: 0,
|
|
34554
|
+
chips: new Map(parts.map((id) => [id, []])),
|
|
34555
|
+
spalls: new Map(parts.map((id) => [id, []])),
|
|
34556
|
+
openings: /* @__PURE__ */ new Map(),
|
|
34557
|
+
flakes: 0
|
|
34558
|
+
};
|
|
34346
34559
|
}
|
|
34347
34560
|
if (!view.chips.has(partId)) return false;
|
|
34348
34561
|
if (change.opening) {
|
|
34349
34562
|
if (view.openings.has(partId)) return false;
|
|
34350
34563
|
view.openings.set(partId, { center: local2(change.opening.center), radius: change.opening.radius });
|
|
34351
34564
|
}
|
|
34352
|
-
if (change.chip) view.chips.get(partId).push(
|
|
34565
|
+
if (change.chip) view.chips.get(partId).push(chipBox(change.chip, view.mask.alongU));
|
|
34566
|
+
if (change.spall) {
|
|
34567
|
+
const list2 = view.spalls.get(partId);
|
|
34568
|
+
list2.push({ center: local2(change.spall.center), radius: change.spall.radius });
|
|
34569
|
+
if (list2.length > MAX_FLAKES) list2.shift();
|
|
34570
|
+
}
|
|
34353
34571
|
if (!view.saved) {
|
|
34354
34572
|
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
34573
|
+
for (const m of view.shells) m.userData.behind = () => view.units;
|
|
34355
34574
|
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
34356
34575
|
}
|
|
34357
|
-
paint2(view);
|
|
34358
34576
|
views.set(hostId, view);
|
|
34359
|
-
|
|
34360
|
-
|
|
34577
|
+
if (opts.deferPaint && change.spall) dirty.add(view);
|
|
34578
|
+
else {
|
|
34579
|
+
dirty.delete(view);
|
|
34580
|
+
paint2(view);
|
|
34581
|
+
invalidate();
|
|
34582
|
+
invalidateShadow();
|
|
34583
|
+
}
|
|
34361
34584
|
return true;
|
|
34362
34585
|
}
|
|
34363
34586
|
function plain2(view, shown) {
|
|
@@ -34366,24 +34589,70 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34366
34589
|
else s.data = collapseRange(s.it.mesh, s.v0, s.it.v1);
|
|
34367
34590
|
}
|
|
34368
34591
|
}
|
|
34592
|
+
function flush(budgetMs = paintBudgetMs) {
|
|
34593
|
+
if (dirty.size === 0) return 0;
|
|
34594
|
+
const deadline = performance.now() + budgetMs;
|
|
34595
|
+
for (const view of dirty) {
|
|
34596
|
+
dirty.delete(view);
|
|
34597
|
+
paint2(view);
|
|
34598
|
+
if (performance.now() >= deadline) break;
|
|
34599
|
+
}
|
|
34600
|
+
invalidate();
|
|
34601
|
+
invalidateShadow();
|
|
34602
|
+
return dirty.size;
|
|
34603
|
+
}
|
|
34369
34604
|
function dispose(view) {
|
|
34370
|
-
|
|
34371
|
-
view.units
|
|
34605
|
+
dirty.delete(view);
|
|
34606
|
+
if (view.units) {
|
|
34607
|
+
view.units.removeFromParent();
|
|
34608
|
+
view.units.dispose();
|
|
34609
|
+
}
|
|
34610
|
+
view.unitMaterial.dispose();
|
|
34372
34611
|
for (const m of view.shells) {
|
|
34373
34612
|
m.removeFromParent();
|
|
34374
34613
|
m.geometry.dispose();
|
|
34375
34614
|
m.material.dispose();
|
|
34376
34615
|
}
|
|
34377
34616
|
view.mask.texture.dispose();
|
|
34617
|
+
view.core.texture.dispose();
|
|
34378
34618
|
}
|
|
34379
34619
|
return {
|
|
34380
34620
|
/** Handles a `stage` event of the house's object. Returns true when the wall was cut. */
|
|
34381
34621
|
stage(event) {
|
|
34382
34622
|
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
34383
34623
|
},
|
|
34384
|
-
/**
|
|
34624
|
+
/**
|
|
34625
|
+
* Handles a `spall` of a wall of units: the coat comes off in a ragged patch and the units show under it, drawn by the house (the destruction
|
|
34626
|
+
* renderer leaves its flat patch out on the meshes `claim` marked). Returns true when the wall was cut.
|
|
34627
|
+
*/
|
|
34628
|
+
spall(event) {
|
|
34629
|
+
const r = Math.sqrt(Math.max(0, event.area) / Math.PI);
|
|
34630
|
+
return r > 0 ? draw2(event.part, { spall: { center: event.point, radius: r } }) : false;
|
|
34631
|
+
},
|
|
34632
|
+
/**
|
|
34633
|
+
* Marks the meshes that draw a wall of units as drawing its spalls itself (`userData.drawnSpalls`, the part ids): the destruction renderer then
|
|
34634
|
+
* throws the bits and leaves its patch out. Call it once the spec is known.
|
|
34635
|
+
*/
|
|
34636
|
+
claim() {
|
|
34637
|
+
var _a;
|
|
34638
|
+
for (const q of opts.spec()?.parts ?? []) {
|
|
34639
|
+
if (!q.units) continue;
|
|
34640
|
+
const host = hostOf(q.id);
|
|
34641
|
+
if (!host) continue;
|
|
34642
|
+
for (const it of structure.meshesOf(host)) {
|
|
34643
|
+
if (!it.mesh) continue;
|
|
34644
|
+
const list2 = (_a = it.mesh.userData).drawnSpalls ?? (_a.drawnSpalls = []);
|
|
34645
|
+
if (!list2.includes(q.id)) list2.push(q.id);
|
|
34646
|
+
}
|
|
34647
|
+
}
|
|
34648
|
+
},
|
|
34649
|
+
/**
|
|
34650
|
+
* Handles a `chip` event: what came out (a unit, or a crater a shot broke through one) leaves a hole in the wall where it was; a `torn` chip (what a blow
|
|
34651
|
+
* tore off a face) leaves the marks the renderer drew. Returns true when the wall was cut.
|
|
34652
|
+
*/
|
|
34385
34653
|
chip(event) {
|
|
34386
|
-
|
|
34654
|
+
if (event.torn) return false;
|
|
34655
|
+
return draw2(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation: event.rotation ?? [0, 0, 0, 1] } });
|
|
34387
34656
|
},
|
|
34388
34657
|
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
34389
34658
|
drop(partId) {
|
|
@@ -34391,9 +34660,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34391
34660
|
if (!v) return;
|
|
34392
34661
|
dispose(v);
|
|
34393
34662
|
views.delete(partId);
|
|
34663
|
+
if (v.saved) dropped.push(v);
|
|
34394
34664
|
},
|
|
34395
34665
|
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
34396
34666
|
setLod(level) {
|
|
34667
|
+
flush(Infinity);
|
|
34397
34668
|
const was = lod;
|
|
34398
34669
|
lod = level;
|
|
34399
34670
|
if (was === 0 === (level === 0)) return;
|
|
@@ -34412,21 +34683,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34412
34683
|
if (lod === 0) plain2(v, true);
|
|
34413
34684
|
}
|
|
34414
34685
|
views.clear();
|
|
34686
|
+
for (const v of dropped) plain2(v, true);
|
|
34687
|
+
dropped.length = 0;
|
|
34415
34688
|
},
|
|
34689
|
+
flush,
|
|
34416
34690
|
/** The wall has damage drawn. */
|
|
34417
34691
|
has(partId) {
|
|
34418
34692
|
return views.has(partId);
|
|
34419
34693
|
},
|
|
34420
|
-
/** Walls with damage drawn, units out of them, and units laid bare at the edge of the cuts. */
|
|
34421
|
-
statistics: () => ({
|
|
34694
|
+
/** Walls with damage drawn, units out of them, craters shots broke through units that stay, and units laid bare at the edge of the cuts. */
|
|
34695
|
+
statistics: () => (flush(Infinity), {
|
|
34422
34696
|
walls: views.size,
|
|
34423
34697
|
gone: [...views.values()].reduce((n, v) => n + v.gone, 0),
|
|
34698
|
+
craters: [...views.values()].reduce((n, v) => n + v.pits, 0),
|
|
34699
|
+
flakes: [...views.values()].reduce((n, v) => n + v.flakes, 0),
|
|
34424
34700
|
units: [...views.values()].reduce((n, v) => n + v.units.count, 0)
|
|
34425
34701
|
})
|
|
34426
34702
|
};
|
|
34427
34703
|
}
|
|
34428
34704
|
Object.assign(K, { createBreachView });
|
|
34429
34705
|
}
|
|
34706
|
+
var FOLLOW_FLUSH_FALLBACK_MS = 100;
|
|
34430
34707
|
function installDestructionWorld(K, shared) {
|
|
34431
34708
|
const { THREE, cfg, invalidateShadow, invalidate, rngString, FRAGMENT_GEO, fragmentMaterial, createBreachView } = K;
|
|
34432
34709
|
function createHouseDestruction(root, house, structure, parts, colliders, water) {
|
|
@@ -34435,7 +34712,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34435
34712
|
const R = cfg.destructionRenderer || null, listeners = /* @__PURE__ */ new Set();
|
|
34436
34713
|
D.renderer = R;
|
|
34437
34714
|
D.spec = null;
|
|
34438
|
-
const breach = createBreachView({ house, structure, spec: () => D.spec });
|
|
34715
|
+
const breach = createBreachView({ house, structure, spec: () => D.spec, deferPaint: true, ...cfg.breachPaintBudgetMs !== void 0 ? { paintBudgetMs: cfg.breachPaintBudgetMs } : {} });
|
|
34439
34716
|
D.breach = breach;
|
|
34440
34717
|
D.onEvent = (fn) => {
|
|
34441
34718
|
listeners.add(fn);
|
|
@@ -34620,6 +34897,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34620
34897
|
case "chip":
|
|
34621
34898
|
breach.chip(e);
|
|
34622
34899
|
break;
|
|
34900
|
+
case "spall":
|
|
34901
|
+
breach.spall(e);
|
|
34902
|
+
break;
|
|
34623
34903
|
case "collapse":
|
|
34624
34904
|
for (const id of e.parts) {
|
|
34625
34905
|
breach.drop(id);
|
|
@@ -34661,7 +34941,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34661
34941
|
D.specOptions = specOptions || null;
|
|
34662
34942
|
D.spec = structure.spec(specOptions);
|
|
34663
34943
|
D.id = D.world.add(D.spec, { id: "house3d:" + ++shared.destructionSeq, position: [p.x, p.y, p.z], rotation: [q.x, q.y, q.z, q.w] });
|
|
34664
|
-
|
|
34944
|
+
breach.claim();
|
|
34945
|
+
if (R) R.bind(D.id, house, { parts: rangesOf(), onFrame: () => breach.flush() });
|
|
34665
34946
|
off = D.world.on("*", handleEvent);
|
|
34666
34947
|
D.active = true;
|
|
34667
34948
|
return D;
|
|
@@ -34676,6 +34957,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34676
34957
|
};
|
|
34677
34958
|
D.follow = (world, id, spec) => {
|
|
34678
34959
|
D.spec = spec;
|
|
34960
|
+
breach.claim();
|
|
34961
|
+
let fallback2 = null;
|
|
34679
34962
|
const offFollow = world.on("*", (e) => {
|
|
34680
34963
|
if (e.object !== id) return;
|
|
34681
34964
|
switch (e.type) {
|
|
@@ -34685,6 +34968,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34685
34968
|
case "chip":
|
|
34686
34969
|
breach.chip(e);
|
|
34687
34970
|
break;
|
|
34971
|
+
case "spall":
|
|
34972
|
+
breach.spall(e);
|
|
34973
|
+
break;
|
|
34688
34974
|
case "fracture":
|
|
34689
34975
|
breach.drop(e.part);
|
|
34690
34976
|
break;
|
|
@@ -34694,10 +34980,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34694
34980
|
default:
|
|
34695
34981
|
break;
|
|
34696
34982
|
}
|
|
34983
|
+
if (!fallback2 && typeof setTimeout === "function") fallback2 = setTimeout(() => {
|
|
34984
|
+
fallback2 = null;
|
|
34985
|
+
breach.flush(Infinity);
|
|
34986
|
+
}, FOLLOW_FLUSH_FALLBACK_MS);
|
|
34697
34987
|
});
|
|
34698
34988
|
return { restore: () => breach.restore(), dispose: () => {
|
|
34699
34989
|
offFollow();
|
|
34700
34990
|
breach.restore();
|
|
34991
|
+
}, update: () => {
|
|
34992
|
+
breach.flush();
|
|
34701
34993
|
} };
|
|
34702
34994
|
};
|
|
34703
34995
|
D.piece = (id) => D.active ? D.world.part(D.id, id) : null;
|
|
@@ -34730,6 +35022,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34730
35022
|
return r;
|
|
34731
35023
|
};
|
|
34732
35024
|
D.animate = (dt) => {
|
|
35025
|
+
breach.flush();
|
|
34733
35026
|
if (R) return;
|
|
34734
35027
|
let moved = false;
|
|
34735
35028
|
for (const [id, list2] of bodies) {
|
|
@@ -36752,6 +37045,54 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
36752
37045
|
store.buffer ?? (store.buffer = bufferOf(context, [samples], context.sampleRate * seconds));
|
|
36753
37046
|
return store.buffer;
|
|
36754
37047
|
}
|
|
37048
|
+
var nowMs = () => typeof performance === "undefined" ? Date.now() : performance.now();
|
|
37049
|
+
function scheduleTask(task) {
|
|
37050
|
+
if (typeof MessageChannel === "undefined") {
|
|
37051
|
+
setTimeout(task, 0);
|
|
37052
|
+
return;
|
|
37053
|
+
}
|
|
37054
|
+
const channel = new MessageChannel();
|
|
37055
|
+
channel.port1.onmessage = () => {
|
|
37056
|
+
channel.port1.close();
|
|
37057
|
+
task();
|
|
37058
|
+
};
|
|
37059
|
+
channel.port2.postMessage(0);
|
|
37060
|
+
}
|
|
37061
|
+
function createStartQueue(options = {}) {
|
|
37062
|
+
const budgetMs = options.budgetMs ?? 2;
|
|
37063
|
+
const maxLatencyMs = options.maxLatencyMs ?? 250;
|
|
37064
|
+
let jobs = [];
|
|
37065
|
+
let scheduled = false;
|
|
37066
|
+
const drain = () => {
|
|
37067
|
+
scheduled = false;
|
|
37068
|
+
const began = nowMs();
|
|
37069
|
+
while (jobs.length > 0) {
|
|
37070
|
+
const job = jobs.shift();
|
|
37071
|
+
if (nowMs() - job.queuedAt > maxLatencyMs) job.drop();
|
|
37072
|
+
else job.run();
|
|
37073
|
+
if (nowMs() - began >= budgetMs) break;
|
|
37074
|
+
}
|
|
37075
|
+
if (jobs.length > 0) schedule();
|
|
37076
|
+
};
|
|
37077
|
+
const schedule = () => {
|
|
37078
|
+
if (scheduled) return;
|
|
37079
|
+
scheduled = true;
|
|
37080
|
+
scheduleTask(drain);
|
|
37081
|
+
};
|
|
37082
|
+
return {
|
|
37083
|
+
push(job) {
|
|
37084
|
+
jobs.push({ ...job, queuedAt: nowMs() });
|
|
37085
|
+
schedule();
|
|
37086
|
+
},
|
|
37087
|
+
clear() {
|
|
37088
|
+
for (const job of jobs) job.drop();
|
|
37089
|
+
jobs = [];
|
|
37090
|
+
},
|
|
37091
|
+
get size() {
|
|
37092
|
+
return jobs.length;
|
|
37093
|
+
}
|
|
37094
|
+
};
|
|
37095
|
+
}
|
|
36755
37096
|
var end = (layer2, start) => start + layer2.duration + layer2.tail;
|
|
36756
37097
|
var firstFilter = (layer2) => layer2.filters?.bank[0];
|
|
36757
37098
|
var offsetOf = (layer2) => layer2.source.kind === "noise" ? layer2.source.offset : 0;
|
|
@@ -37192,15 +37533,43 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37192
37533
|
...playOptions.params ? { params: playOptions.params } : {},
|
|
37193
37534
|
...options.occlusion && playOptions.position && recipe.falloff?.model !== "none" ? { occlusion: options.occlusion(playOptions.position) } : {}
|
|
37194
37535
|
});
|
|
37195
|
-
const
|
|
37536
|
+
const startQueue = options.deferredStart ? createStartQueue(options.deferredStart) : null;
|
|
37537
|
+
const build = (plan, store, delay) => {
|
|
37196
37538
|
const audio = context;
|
|
37197
|
-
|
|
37539
|
+
return realize(plan, {
|
|
37198
37540
|
context: audio,
|
|
37199
37541
|
output: outputFor(plan.bus),
|
|
37200
37542
|
...chain?.wet ? { wet: chain.wet } : {},
|
|
37201
37543
|
noise: () => noiseBuffer(store, audio, mix3.noise.seconds),
|
|
37202
37544
|
time: audio.currentTime + delay
|
|
37203
37545
|
});
|
|
37546
|
+
};
|
|
37547
|
+
const deferVoice = (plan, store, voiceId, delay) => {
|
|
37548
|
+
let built = null;
|
|
37549
|
+
let cancelled = false;
|
|
37550
|
+
const pending = {
|
|
37551
|
+
get sources() {
|
|
37552
|
+
return built?.sources ?? [];
|
|
37553
|
+
},
|
|
37554
|
+
stop() {
|
|
37555
|
+
cancelled = true;
|
|
37556
|
+
built?.stop();
|
|
37557
|
+
}
|
|
37558
|
+
};
|
|
37559
|
+
startQueue.push({
|
|
37560
|
+
run() {
|
|
37561
|
+
if (!cancelled && context) built = build(plan, store, delay);
|
|
37562
|
+
},
|
|
37563
|
+
drop() {
|
|
37564
|
+
if (voiceId === void 0) return;
|
|
37565
|
+
voices.release(voiceId);
|
|
37566
|
+
active.delete(voiceId);
|
|
37567
|
+
}
|
|
37568
|
+
});
|
|
37569
|
+
return pending;
|
|
37570
|
+
};
|
|
37571
|
+
const start = (plan, store, voiceId, delay = 0) => {
|
|
37572
|
+
const realized = startQueue ? deferVoice(plan, store, voiceId, delay) : build(plan, store, delay);
|
|
37204
37573
|
if (voiceId === void 0) return null;
|
|
37205
37574
|
voices.setLength(voiceId, plan.length + delay);
|
|
37206
37575
|
active.set(voiceId, realized);
|
|
@@ -37316,6 +37685,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37316
37685
|
const audio = ready();
|
|
37317
37686
|
if (audio) unlockContext(audio);
|
|
37318
37687
|
},
|
|
37688
|
+
warmUp() {
|
|
37689
|
+
const audio = ready();
|
|
37690
|
+
if (!audio) return false;
|
|
37691
|
+
noiseBuffer(shared, audio, mix3.noise.seconds);
|
|
37692
|
+
return true;
|
|
37693
|
+
},
|
|
37319
37694
|
level(bus) {
|
|
37320
37695
|
watched.add(bus);
|
|
37321
37696
|
ensureBus(bus);
|
|
@@ -37339,6 +37714,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37339
37714
|
};
|
|
37340
37715
|
},
|
|
37341
37716
|
dispose() {
|
|
37717
|
+
startQueue?.clear();
|
|
37342
37718
|
for (const id of active.keys()) voices.release(id);
|
|
37343
37719
|
for (const loop of [...loops]) loop.stop();
|
|
37344
37720
|
active.clear();
|
|
@@ -38574,6 +38950,83 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
38574
38950
|
};
|
|
38575
38951
|
};
|
|
38576
38952
|
|
|
38953
|
+
// src/fx/body-hold.ts
|
|
38954
|
+
var BODY_HOLD = {
|
|
38955
|
+
/** With no body to lend the frame: the pocket of the right shoulder from the eye, in the eye's yaw frame (right, up, back). */
|
|
38956
|
+
eyeToPocket: [0.2, -0.275, 0.17],
|
|
38957
|
+
/** A shouldered gun's bore runs this far above the shoulder pocket (the cheek on the comb). */
|
|
38958
|
+
boreAbove: 0.05,
|
|
38959
|
+
/** A handgun's (or a short two-hand gun's) grip: toward the middle of the chest, a little low, at the reach of bent arms. */
|
|
38960
|
+
handgunGrip: [-0.15, -0.04, -0.42],
|
|
38961
|
+
/** A one-hand tool's grip: low in front of the body, the elbow bent. */
|
|
38962
|
+
toolGrip: [0.02, -0.34, -0.3],
|
|
38963
|
+
/** A tool at least this long, held by two hands, is shouldered (its stock in the pocket). */
|
|
38964
|
+
shoulderedFrom: 0.55,
|
|
38965
|
+
/** The farthest a shouldered gun's grip sits ahead of the pocket; a launcher whose grip is farther slides back, its tube behind the shoulder. */
|
|
38966
|
+
shoulderGripMax: 0.42,
|
|
38967
|
+
/** The nearest a shouldered gun's grip sits ahead of the pocket; a gun with no stock (a pistol grip at its rear) slides forward, its grip not behind the eye. */
|
|
38968
|
+
shoulderGripMin: 0.25,
|
|
38969
|
+
/** Where such a grip is brought to, ahead of the pocket. */
|
|
38970
|
+
shoulderGripTo: 0.34,
|
|
38971
|
+
/** The farthest apart two hands hold a long gun (a support hand past it slides back along the gun, toward the grip). */
|
|
38972
|
+
supportSpan: 0.5,
|
|
38973
|
+
/** How much of the aim's pitch a tool held low in one hand follows. */
|
|
38974
|
+
toolPitch: 0.5
|
|
38975
|
+
};
|
|
38976
|
+
var scratch = { a: null, b: null, euler: null };
|
|
38977
|
+
var placeOnBody = (placement) => {
|
|
38978
|
+
const { THREE, hand, holder, frame: frame2 } = placement;
|
|
38979
|
+
scratch.a ?? (scratch.a = new THREE.Vector3());
|
|
38980
|
+
scratch.b ?? (scratch.b = new THREE.Vector3());
|
|
38981
|
+
holder.scale.setScalar(1);
|
|
38982
|
+
hand.position.set(...frame2.position);
|
|
38983
|
+
hand.quaternion.set(...frame2.quaternion);
|
|
38984
|
+
const shouldered = !placement.handgun && placement.left !== null && placement.lengthMetres >= BODY_HOLD.shoulderedFrom;
|
|
38985
|
+
if (shouldered) {
|
|
38986
|
+
holder.position.set(0, BODY_HOLD.boreAbove, placement.rear);
|
|
38987
|
+
hand.updateMatrixWorld(true);
|
|
38988
|
+
if (!placement.right) return;
|
|
38989
|
+
const grip2 = hand.worldToLocal(placement.right.part.localToWorld(scratch.a.copy(placement.right.offset)));
|
|
38990
|
+
if (-grip2.z <= BODY_HOLD.shoulderGripMax && -grip2.z >= BODY_HOLD.shoulderGripMin) return;
|
|
38991
|
+
holder.position.z += -grip2.z - BODY_HOLD.shoulderGripTo;
|
|
38992
|
+
hand.updateMatrixWorld(true);
|
|
38993
|
+
return;
|
|
38994
|
+
}
|
|
38995
|
+
scratch.euler ?? (scratch.euler = new THREE.Euler());
|
|
38996
|
+
const turn2 = scratch.euler.setFromQuaternion(hand.quaternion, "YXZ");
|
|
38997
|
+
hand.quaternion.setFromEuler(turn2.set(turn2.x * BODY_HOLD.toolPitch, turn2.y, 0, "YXZ"));
|
|
38998
|
+
holder.position.set(0, 0, 0);
|
|
38999
|
+
hand.updateMatrixWorld(true);
|
|
39000
|
+
if (!placement.right) return;
|
|
39001
|
+
const grip = placement.handgun || placement.left ? BODY_HOLD.handgunGrip : BODY_HOLD.toolGrip;
|
|
39002
|
+
const want = scratch.a.set(grip[0], grip[1], grip[2]).applyQuaternion(hand.quaternion).add(hand.position);
|
|
39003
|
+
const now = placement.right.part.localToWorld(scratch.b.copy(placement.right.offset));
|
|
39004
|
+
hand.position.add(want.sub(now));
|
|
39005
|
+
hand.updateMatrixWorld(true);
|
|
39006
|
+
};
|
|
39007
|
+
var limitSupportSpan = (targets) => {
|
|
39008
|
+
const right = targets.R?.position;
|
|
39009
|
+
const left = targets.L?.position;
|
|
39010
|
+
if (!right || !left || !targets.L) return targets;
|
|
39011
|
+
const span = Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2]);
|
|
39012
|
+
if (span <= BODY_HOLD.supportSpan) return targets;
|
|
39013
|
+
const k = BODY_HOLD.supportSpan / span;
|
|
39014
|
+
const position = [right[0] + (left[0] - right[0]) * k, right[1] + (left[1] - right[1]) * k, right[2] + (left[2] - right[2]) * k];
|
|
39015
|
+
return { ...targets, L: { ...targets.L, position } };
|
|
39016
|
+
};
|
|
39017
|
+
var createEyeFrame = (THREE, camera2) => {
|
|
39018
|
+
const frame2 = { position: [0, 0, 0], quaternion: [0, 0, 0, 1] };
|
|
39019
|
+
const turn2 = new THREE.Euler(0, 0, 0, "YXZ");
|
|
39020
|
+
const pocket = new THREE.Vector3();
|
|
39021
|
+
return () => {
|
|
39022
|
+
turn2.setFromQuaternion(camera2.quaternion, "YXZ");
|
|
39023
|
+
pocket.set(...BODY_HOLD.eyeToPocket).applyEuler(turn2.set(0, turn2.y, 0)).add(camera2.position);
|
|
39024
|
+
frame2.position = [pocket.x, pocket.y, pocket.z];
|
|
39025
|
+
frame2.quaternion = [camera2.quaternion.x, camera2.quaternion.y, camera2.quaternion.z, camera2.quaternion.w];
|
|
39026
|
+
return frame2;
|
|
39027
|
+
};
|
|
39028
|
+
};
|
|
39029
|
+
|
|
38577
39030
|
// src/fx/hand-hold.ts
|
|
38578
39031
|
var HAND_HOLD = {
|
|
38579
39032
|
/** Half the width of a pistol grip, the palm sits this far (plus the palm) off its middle. */
|
|
@@ -38602,9 +39055,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
38602
39055
|
/** How far the shooting hand slides on its grip so the thumb reaches the safety lever. */
|
|
38603
39056
|
thumbReach: 0.02,
|
|
38604
39057
|
/** How fast the supporting hand goes to the magazine and back, per second. */
|
|
38605
|
-
magRate: 7
|
|
38606
|
-
/** The hands shrink with the tool: a tool drawn at scale `s` takes hands at `s ** handScaleExponent`. */
|
|
38607
|
-
handScaleExponent: 0.55
|
|
39058
|
+
magRate: 7
|
|
38608
39059
|
};
|
|
38609
39060
|
var handFrame = (THREE, x, y) => {
|
|
38610
39061
|
const ax = x.clone().normalize();
|
|
@@ -38694,7 +39145,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
38694
39145
|
right,
|
|
38695
39146
|
left,
|
|
38696
39147
|
leftMagazine,
|
|
38697
|
-
scale:
|
|
39148
|
+
// the hands at the tool's scale: a tool drawn smaller than the hands looks like a toy in them
|
|
39149
|
+
scale: holderScale,
|
|
38698
39150
|
grip: HAND_HOLD.closed,
|
|
38699
39151
|
gripLeft: HAND_HOLD.forend
|
|
38700
39152
|
};
|
|
@@ -38709,7 +39161,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
38709
39161
|
const long = head && ["smg", "carbine", "rifle", "sniper", "shotgun", "crossbow", "flame"].includes(category);
|
|
38710
39162
|
const headSize = head ? size(head) : null;
|
|
38711
39163
|
const left = long && head && headSize ? { part: group, offset: head.position.clone().add(V(THREE, -headSize.x * 0.15, -headSize.y / 2 - HAND_HOLD.palm, 0)), quaternion: forendFrame(THREE) } : null;
|
|
38712
|
-
return { right, left, leftMagazine: null, scale
|
|
39164
|
+
return { right, left, leftMagazine: null, scale, grip: HAND_HOLD.closed, gripLeft: HAND_HOLD.forend };
|
|
38713
39165
|
}
|
|
38714
39166
|
|
|
38715
39167
|
// ../game-fluids/dist/index.js
|
|
@@ -39847,6 +40299,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39847
40299
|
liquid: "honey",
|
|
39848
40300
|
fill: 0.9
|
|
39849
40301
|
}
|
|
40302
|
+
},
|
|
40303
|
+
shapes: {
|
|
40304
|
+
box: {
|
|
40305
|
+
hollow: true,
|
|
40306
|
+
welded: true
|
|
40307
|
+
}
|
|
39850
40308
|
}
|
|
39851
40309
|
};
|
|
39852
40310
|
var DESTRUCTION_MATERIAL_BY_TAG = {
|
|
@@ -39860,6 +40318,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39860
40318
|
};
|
|
39861
40319
|
var FAMILIES = destruction_default.families;
|
|
39862
40320
|
var CONTENTS = destruction_default.contents;
|
|
40321
|
+
var SHAPES2 = destruction_default.shapes;
|
|
40322
|
+
var shapeDestruction = (shapeId) => SHAPES2[shapeId];
|
|
39863
40323
|
var FAMILY_OF_MODEL = new Map(Object.entries(FAMILIES).flatMap(([family, entry2]) => entry2.models.map((model) => [model, family])));
|
|
39864
40324
|
var itemDestruction = (modelId) => {
|
|
39865
40325
|
const family = FAMILY_OF_MODEL.get(modelId);
|
|
@@ -39973,9 +40433,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39973
40433
|
root.userData.tessellationTriangles = added;
|
|
39974
40434
|
}
|
|
39975
40435
|
function tagForDestruction(root, options = {}) {
|
|
39976
|
-
const { modelId, weight, size, three } = options;
|
|
40436
|
+
const { modelId, shapeId, weight, size, powder, three } = options;
|
|
39977
40437
|
if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
|
|
39978
|
-
const rule = modelId
|
|
40438
|
+
const rule = modelId !== void 0 ? itemDestruction(modelId) : shapeId !== void 0 ? shapeDestruction(shapeId) : void 0;
|
|
39979
40439
|
if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
|
|
39980
40440
|
const solid2 = [];
|
|
39981
40441
|
root.traverse((object2) => {
|
|
@@ -39990,6 +40450,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39990
40450
|
return;
|
|
39991
40451
|
}
|
|
39992
40452
|
if (rule?.hollow) mesh3.userData.hollow = true;
|
|
40453
|
+
if (powder) mesh3.userData.destructionPowder = { color: powder.color };
|
|
39993
40454
|
solid2.push(mesh3);
|
|
39994
40455
|
});
|
|
39995
40456
|
if (!rule?.welded || solid2.length < 2) return;
|
|
@@ -42467,10 +42928,41 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42467
42928
|
const m = metal2(9277590);
|
|
42468
42929
|
return G(mesh3(cyl(0.32, 0.32, 0.52), m), mesh3(cyl(0.326, 0.326, 0.34, 14), food(10172964)), mesh3(cyl(0.33, 0.33, 0.06), paper2(14861162), [0, 0.02, 0]), mesh3(torus2(0.31, 0.025), m, [0, 0.26, 0], [Math.PI / 2, 0, 0]), mesh3(torus2(0.31, 0.025), m, [0, -0.26, 0], [Math.PI / 2, 0, 0]));
|
|
42469
42930
|
};
|
|
42470
|
-
var
|
|
42931
|
+
var opening_default = {
|
|
42932
|
+
families: {
|
|
42933
|
+
drink_can: { style: "pull_tab" },
|
|
42934
|
+
food_tin: { style: "cut_lid" }
|
|
42935
|
+
},
|
|
42936
|
+
models: {
|
|
42937
|
+
empty_can: { style: "none" },
|
|
42938
|
+
canned_beans: { food: "#7a2e1c" },
|
|
42939
|
+
canned_corn_and_peas: { food: "#c7b03c" },
|
|
42940
|
+
canned_sausages: { food: "#c98d5c" },
|
|
42941
|
+
canned_sardines: { style: "peel_lid", food: "#8e979c" },
|
|
42942
|
+
condensed_milk: { food: "#e3cf9c" },
|
|
42943
|
+
energy_drink: { style: "pull_tab" },
|
|
42944
|
+
honey_jar: { style: "screw_cap", food: "#d99a1c" },
|
|
42945
|
+
home_preserves: { style: "screw_cap", food: "#8e1f3a" },
|
|
42946
|
+
cooking_oil: { style: "screw_cap", food: "#e3b830" },
|
|
42947
|
+
water_bottle: { style: "screw_cap" },
|
|
42948
|
+
water_jug_5l: { style: "screw_cap" },
|
|
42949
|
+
dirty_water: { style: "screw_cap" },
|
|
42950
|
+
sports_drink: { style: "screw_cap" },
|
|
42951
|
+
coconut_water: { style: "screw_cap" },
|
|
42952
|
+
cachaca: { style: "screw_cap" },
|
|
42953
|
+
communion_wine: { style: "screw_cap" }
|
|
42954
|
+
}
|
|
42955
|
+
};
|
|
42956
|
+
var FAMILIES2 = opening_default.families;
|
|
42957
|
+
var MODELS = opening_default.models;
|
|
42958
|
+
var asLid = (mesh3) => {
|
|
42959
|
+
mesh3.userData.itemLid = true;
|
|
42960
|
+
return mesh3;
|
|
42961
|
+
};
|
|
42962
|
+
var waterBottle = ({ mesh: mesh3, cyl, lathe: lathe2, G, plastic, paper: paper2, glass }) => G(mesh3(lathe2([[0, -0.6], [0.2, -0.6], [0.22, -0.5], [0.22, 0.2], [0.19, 0.32], [0.09, 0.45], [0.08, 0.55], [0, 0.55]]), glass(9421788, 0.55)), asLid(mesh3(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0])), mesh3(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh3(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
|
|
42471
42963
|
var rice = ({ mesh: mesh3, bx, G, paper: paper2, food }) => G(mesh3(bx(0.7, 0.95, 0.28), paper2(15129792)), mesh3(bx(0.72, 0.12, 0.1), paper2(14010792), [0, 0.52, 0]), mesh3(bx(0.5, 0.3, 0.02), food(11883039), [0, 0.05, 0.15]), mesh3(bx(0.3, 0.1, 0.02), paper2(4160069), [0, -0.25, 0.15]));
|
|
42472
42964
|
var militaryRation = ({ mesh: mesh3, bx, G, plastic, paper: paper2 }) => G(mesh3(bx(0.75, 1, 0.22), plastic(5923142)), mesh3(bx(0.77, 0.1, 0.23), plastic(4672823), [0, 0.48, 0]), mesh3(bx(0.5, 0.14, 0.01), paper2(2237727), [0, 0.18, 0.115]), mesh3(bx(0.36, 0.06, 0.01), paper2(2237727), [0, -0.02, 0.115]));
|
|
42473
|
-
var cachaca = ({ mesh: mesh3, cyl, lathe: lathe2, G, wood, paper: paper2, food, glass }) => G(mesh3(lathe2([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), mesh3(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0]), mesh3(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh3(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
|
|
42965
|
+
var cachaca = ({ mesh: mesh3, cyl, lathe: lathe2, G, wood, paper: paper2, food, glass }) => G(mesh3(lathe2([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), asLid(mesh3(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0])), mesh3(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh3(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
|
|
42474
42966
|
var firstAidKit = ({ mesh: mesh3, bx, torus: torus2, G, plastic }) => {
|
|
42475
42967
|
const r = plastic(11680300);
|
|
42476
42968
|
return G(mesh3(bx(1.05, 0.72, 0.38), plastic(15131608)), mesh3(bx(1.07, 0.06, 0.4), plastic(12433578), [0, 0, 0]), mesh3(bx(0.36, 0.1, 0.02), r, [0, 0.1, 0.2]), mesh3(bx(0.1, 0.36, 0.02), r, [0, 0.1, 0.2]), mesh3(torus2(0.16, 0.035, Math.PI), plastic(5591884), [0, 0.36, 0]));
|
|
@@ -42630,7 +43122,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42630
43122
|
mesh3(cyl(0.41, 0.41, 0.86, 12), food(9314106), [0, -0.2, 0]),
|
|
42631
43123
|
mesh3(cyl(0.34, 0.445, 0.12, 12), glass(13625070, 0.5), [0, 0.39, 0]),
|
|
42632
43124
|
mesh3(cyl(0.34, 0.34, 0.05, 12), glass(13625070, 0.5), [0, 0.475, 0]),
|
|
42633
|
-
mesh3(cyl(0.38, 0.38, 0.15, 12), metal2(12163644), [0, 0.575, 0]),
|
|
43125
|
+
asLid(mesh3(cyl(0.38, 0.38, 0.15, 12), metal2(12163644), [0, 0.575, 0])),
|
|
42634
43126
|
mesh3(cyl(0.45, 0.45, 0.4, 12), paper2(15327172), [0, -0.2, 0]),
|
|
42635
43127
|
mesh3(cyl(0.452, 0.452, 0.08, 12), cloth2(10172986), [0, -0.2, 0])
|
|
42636
43128
|
);
|
|
@@ -42805,7 +43297,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42805
43297
|
mesh3(cyl(0.395, 0.395, 0.68, 12), glass(14925936, 0.55), [0, -0.16, 0]),
|
|
42806
43298
|
mesh3(cyl(0.37, 0.37, 0.6, 12), food(14260764), [0, -0.2, 0]),
|
|
42807
43299
|
mesh3(cyl(0.3, 0.395, 0.14, 12), glass(14925936, 0.55), [0, 0.25, 0]),
|
|
42808
|
-
mesh3(cyl(0.3, 0.3, 0.18, 12), metal2(7031324), [0, 0.41, 0]),
|
|
43300
|
+
asLid(mesh3(cyl(0.3, 0.3, 0.18, 12), metal2(7031324), [0, 0.41, 0])),
|
|
42809
43301
|
mesh3(cyl(0.4, 0.4, 0.34, 12), paper2(15853512), [0, -0.2, 0]),
|
|
42810
43302
|
mesh3(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.1, 0]),
|
|
42811
43303
|
mesh3(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.3, 0])
|
|
@@ -42891,7 +43383,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42891
43383
|
mesh3(cyl(0.345, 0.345, 1.7, 12), glass(14923824, 0.65), [0, -0.45, 0]),
|
|
42892
43384
|
mesh3(cyl(0.12, 0.345, 0.5, 12), glass(14923824, 0.65), [0, 0.65, 0]),
|
|
42893
43385
|
mesh3(cyl(0.12, 0.12, 0.25, 12), glass(14923824, 0.65), [0, 1.025, 0]),
|
|
42894
|
-
mesh3(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0]),
|
|
43386
|
+
asLid(mesh3(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0])),
|
|
42895
43387
|
mesh3(cyl(0.35, 0.35, 0.8, 12), paper2(15853512), [0, -0.5, 0]),
|
|
42896
43388
|
mesh3(cyl(0.352, 0.352, 0.2, 12), paper2(4160042), [0, -0.5, 0])
|
|
42897
43389
|
);
|
|
@@ -42923,7 +43415,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42923
43415
|
const body = mesh3(bx(1.5, 2.3, 1.5), glass(10473710, 0.75), [0, -0.5, 0]);
|
|
42924
43416
|
const shoulder = mesh3(bx(1.2, 0.45, 1.2), glass(10473710, 0.75), [0, 0.875, 0]);
|
|
42925
43417
|
const neck = mesh3(cyl(0.34, 0.34, 0.3, 12), dark, [0, 1.25, 0]);
|
|
42926
|
-
const cap = mesh3(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]);
|
|
43418
|
+
const cap = asLid(mesh3(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]));
|
|
42927
43419
|
const grip = mesh3(bx(0.16, 0.9, 0.3), dark, [1.07, 0.6, 0]);
|
|
42928
43420
|
const upperBar = mesh3(bx(0.45, 0.16, 0.3), dark, [0.925, 0.97, 0]);
|
|
42929
43421
|
const lowerBar = mesh3(bx(0.45, 0.16, 0.3), dark, [0.925, 0.23, 0]);
|
|
@@ -42936,7 +43428,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42936
43428
|
const label = mesh3(cyl(0.332, 0.332, 0.7, 12), paper2(1448738), [0, -0.12, 0]);
|
|
42937
43429
|
const stripe = mesh3(cyl(0.332, 0.332, 0.1, 12), paper2(15753232), [0, 0.28, 0]);
|
|
42938
43430
|
const collar = mesh3(cyl(0.2, 0.2, 0.04, 12), plastic(15263976), [0, 0.705, 0]);
|
|
42939
|
-
const cap = mesh3(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]);
|
|
43431
|
+
const cap = asLid(mesh3(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]));
|
|
42940
43432
|
return G(bottle2, label, stripe, collar, cap);
|
|
42941
43433
|
};
|
|
42942
43434
|
var milkCarton = ({ mesh: mesh3, bx, cyl, extrude: extrude2, G, paper: paper2, plastic }) => {
|
|
@@ -42975,7 +43467,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42975
43467
|
const goldBand = mesh3(cyl(0.382, 0.382, 0.05, 12), metal2(11896880), [0, 0.075, 0]);
|
|
42976
43468
|
const crossUpright = mesh3(bx(0.06, 0.26, 0.02), paper2(8003371), [0, -0.35, 0.375]);
|
|
42977
43469
|
const crossArm = mesh3(bx(0.18, 0.06, 0.02), paper2(8003371), [0, -0.3, 0.375]);
|
|
42978
|
-
const foil = mesh3(cyl(0.15, 0.15, 0.46, 12), metal2(11896880), [0, 1.27, 0]);
|
|
43470
|
+
const foil = asLid(mesh3(cyl(0.15, 0.15, 0.46, 12), metal2(11896880), [0, 1.27, 0]));
|
|
42979
43471
|
return G(bottle2, label, goldBand, crossUpright, crossArm, foil);
|
|
42980
43472
|
};
|
|
42981
43473
|
var coconutWater = ({ mesh: mesh3, bx, cyl, lathe: lathe2, G, plastic, paper: paper2 }) => {
|
|
@@ -42984,7 +43476,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42984
43476
|
const nut = mesh3(cyl(0.11, 0.11, 0.03, 10), paper2(7031339), [0, -0.12, 0.285], [Math.PI / 2, 0, 0]);
|
|
42985
43477
|
const leafLeft = mesh3(bx(0.2, 0.05, 0.01), paper2(15135448), [-0.12, 0.08, 0.292], [0, 0, 0.5]);
|
|
42986
43478
|
const leafRight = mesh3(bx(0.2, 0.05, 0.01), paper2(15135448), [0.12, 0.08, 0.292], [0, 0, -0.5]);
|
|
42987
|
-
const cap = mesh3(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]);
|
|
43479
|
+
const cap = asLid(mesh3(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]));
|
|
42988
43480
|
return G(bottle2, label, nut, leafLeft, leafRight, cap);
|
|
42989
43481
|
};
|
|
42990
43482
|
var dirtyWater = ({ mesh: mesh3, cyl, lathe: lathe2, G, plastic, paper: paper2, glass, food }) => {
|
|
@@ -42992,7 +43484,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42992
43484
|
const murk = mesh3(lathe2([[0, -0.89], [0.27, -0.89], [0.315, -0.84], [0.315, 0.1], [0, 0.1]], 12), food(7035444));
|
|
42993
43485
|
const sediment = mesh3(cyl(0.31, 0.27, 0.12, 8), food(4865314), [0, -0.82, 0]);
|
|
42994
43486
|
const label = mesh3(cyl(0.332, 0.332, 0.22, 12), paper2(13617588), [0, 0.38, 0]);
|
|
42995
|
-
const cap = mesh3(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]);
|
|
43487
|
+
const cap = asLid(mesh3(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]));
|
|
42996
43488
|
return G(bottle2, murk, sediment, label, cap);
|
|
42997
43489
|
};
|
|
42998
43490
|
var painkillers = ({ mesh: mesh3, bx, cyl, G, paper: paper2, metal: metal2, glass }) => {
|
|
@@ -45608,7 +46100,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
45608
46100
|
const materials = kit.materials.slice(first);
|
|
45609
46101
|
if (options.wear) setWear(materials, options.wear);
|
|
45610
46102
|
root.userData.wear = options.wear ?? 0;
|
|
45611
|
-
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, three: T2 });
|
|
46103
|
+
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...shape === void 0 ? {} : { shapeId: shape }, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, ...item.powder ? { powder: item.powder } : {}, three: T2 });
|
|
45612
46104
|
markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
|
|
45613
46105
|
return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
|
|
45614
46106
|
}
|
|
@@ -48895,7 +49387,6 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48895
49387
|
var MAX_CASINGS = 120;
|
|
48896
49388
|
var MAX_MAGAZINES = 12;
|
|
48897
49389
|
var LIFETIME_S = 25;
|
|
48898
|
-
var HAND_OFFSET = [0.2, -0.2, -0.42];
|
|
48899
49390
|
var GRAVITY2 = 9.81;
|
|
48900
49391
|
function createBoxFx(options) {
|
|
48901
49392
|
const { THREE, scene, camera: camera2, arsenal, groundAt } = options;
|
|
@@ -48917,6 +49408,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48917
49408
|
let dip = 0;
|
|
48918
49409
|
let reloading = false;
|
|
48919
49410
|
const v = new THREE.Vector3();
|
|
49411
|
+
const v2 = new THREE.Vector3();
|
|
49412
|
+
const eyeFrame = createEyeFrame(THREE, camera2);
|
|
49413
|
+
let rear = 0;
|
|
49414
|
+
let lengthMetres = 0;
|
|
48920
49415
|
const q = new THREE.Quaternion();
|
|
48921
49416
|
const q2 = new THREE.Quaternion();
|
|
48922
49417
|
const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
|
|
@@ -48928,13 +49423,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48928
49423
|
if (!def || BODY_ACTIONS.has(def.id)) return;
|
|
48929
49424
|
model = buildWeaponModel(THREE, def);
|
|
48930
49425
|
const length2 = new THREE.Box3().setFromObject(model).getSize(v).x;
|
|
48931
|
-
|
|
49426
|
+
rear = new THREE.Box3().setFromObject(model).min.x;
|
|
49427
|
+
lengthMetres = length2;
|
|
48932
49428
|
model.quaternion.copy(pointForward);
|
|
48933
49429
|
hand.add(model);
|
|
48934
49430
|
inHand = def.id;
|
|
48935
|
-
hold = holdOfProp(THREE, model, def.category,
|
|
49431
|
+
hold = holdOfProp(THREE, model, def.category, 1);
|
|
49432
|
+
};
|
|
49433
|
+
const handPoint = (x, y, z, out) => {
|
|
49434
|
+
if (!model) return out.set(x, y, z).applyQuaternion(camera2.quaternion).add(camera2.position);
|
|
49435
|
+
return out.set(x, y, z).applyQuaternion(camera2.quaternion).add(model.getWorldPosition(v2));
|
|
48936
49436
|
};
|
|
48937
|
-
const handPoint = (x, y, z, out) => out.set(HAND_OFFSET[0] + x, HAND_OFFSET[1] + y, HAND_OFFSET[2] + z).applyQuaternion(camera2.quaternion).add(camera2.position);
|
|
48938
49437
|
const throwBody = (kind, mesh3, from, velocity, radius) => {
|
|
48939
49438
|
if (!(kind === "casing" ? layers.casings : layers.magazines)) {
|
|
48940
49439
|
mesh3.geometry.dispose();
|
|
@@ -49045,10 +49544,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
49045
49544
|
update(dt) {
|
|
49046
49545
|
kick *= Math.exp(-dt * 14);
|
|
49047
49546
|
dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8);
|
|
49048
|
-
if (inHand) {
|
|
49049
|
-
|
|
49547
|
+
if (inHand && model) {
|
|
49548
|
+
const handgun = def?.category === "pistol" || def?.category === "revolver";
|
|
49549
|
+
placeOnBody({ THREE, hand, holder: model, frame: options.bodyHold?.() ?? eyeFrame(), rear, lengthMetres, handgun, right: hold?.right ?? null, left: hold?.left ?? null });
|
|
49550
|
+
hand.position.add(v.set(0, -0.12 * dip, 0.05 * kick).applyQuaternion(camera2.quaternion));
|
|
49050
49551
|
q.setFromEuler(new THREE.Euler(0.14 * kick - 0.25 * dip, 0, 0.6 * dip));
|
|
49051
|
-
hand.quaternion.
|
|
49552
|
+
hand.quaternion.multiply(q);
|
|
49052
49553
|
}
|
|
49053
49554
|
for (const body of [...bodies]) {
|
|
49054
49555
|
stepBody(body, dt);
|
|
@@ -57769,6 +58270,11 @@ void main(){
|
|
|
57769
58270
|
var MAX_CONFORM_STEPS = 48;
|
|
57770
58271
|
var MIN_CONFORM_STEPS = 16;
|
|
57771
58272
|
var BLOOD_GROW_RATE = 1.2;
|
|
58273
|
+
var BLOOD_START_GROWTH = 0.05;
|
|
58274
|
+
var DEFAULT_MAX_GROUND_MARKS = 64;
|
|
58275
|
+
var DEFAULT_MAX_ROCKS = 512;
|
|
58276
|
+
var DEFAULT_MAX_BLOOD = 256;
|
|
58277
|
+
var MAX_SPARE_GROUND_GEOMETRIES = 16;
|
|
57772
58278
|
function createMarkLayer(THREE, options) {
|
|
57773
58279
|
const group = new THREE.Group();
|
|
57774
58280
|
const textures = createMarkTextures(THREE, options.createCanvas);
|
|
@@ -57792,110 +58298,168 @@ void main(){
|
|
|
57792
58298
|
const bloodMaterial = new THREE.MeshStandardMaterial({ color: 6031882, roughness: 0.15, metalness: 0.1, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 });
|
|
57793
58299
|
const bloodGeometry = new THREE.CircleGeometry(0.5, 9).rotateX(-Math.PI / 2);
|
|
57794
58300
|
owned.push(bloodMaterial, bloodGeometry);
|
|
57795
|
-
const
|
|
57796
|
-
const
|
|
57797
|
-
|
|
57798
|
-
|
|
57799
|
-
|
|
57800
|
-
|
|
57801
|
-
material = new THREE.MeshStandardMaterial({ color: color2, roughness: 0.9, metalness: 0, flatShading: true });
|
|
57802
|
-
rockMaterials.set(color2, material);
|
|
57803
|
-
owned.push(material);
|
|
58301
|
+
const maxGroundMarks = options.maxGroundMarks ?? DEFAULT_MAX_GROUND_MARKS;
|
|
58302
|
+
const instanced = {};
|
|
58303
|
+
function rockMaterial() {
|
|
58304
|
+
if (!instanced.rockMaterial) {
|
|
58305
|
+
instanced.rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.9, metalness: 0, flatShading: true });
|
|
58306
|
+
owned.push(instanced.rockMaterial);
|
|
57804
58307
|
}
|
|
57805
|
-
return
|
|
58308
|
+
return instanced.rockMaterial;
|
|
58309
|
+
}
|
|
58310
|
+
function bloodMesh() {
|
|
58311
|
+
return instanced.blood ?? (instanced.blood = attach(createInstanced(bloodGeometry, bloodMaterial, options.maxBlood ?? DEFAULT_MAX_BLOOD)));
|
|
58312
|
+
}
|
|
58313
|
+
function rockMesh() {
|
|
58314
|
+
return instanced.rock ?? (instanced.rock = attach(createInstanced(options.rockGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
58315
|
+
}
|
|
58316
|
+
function plateMesh() {
|
|
58317
|
+
return instanced.plate ?? (instanced.plate = attach(createInstanced(options.plateGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
58318
|
+
}
|
|
58319
|
+
function attach(mesh3, castShadow = false) {
|
|
58320
|
+
mesh3.castShadow = castShadow;
|
|
58321
|
+
group.add(mesh3);
|
|
58322
|
+
return mesh3;
|
|
58323
|
+
}
|
|
58324
|
+
const groundMarks = [];
|
|
58325
|
+
const spareGeometries = [];
|
|
58326
|
+
const blood = [];
|
|
58327
|
+
const placed = { blood: 0, rock: 0, plate: 0 };
|
|
58328
|
+
const matrix = new THREE.Matrix4();
|
|
58329
|
+
const position = new THREE.Vector3();
|
|
58330
|
+
const quaternion2 = new THREE.Quaternion();
|
|
58331
|
+
const euler = new THREE.Euler();
|
|
58332
|
+
const scale = new THREE.Vector3();
|
|
58333
|
+
const color2 = new THREE.Color();
|
|
58334
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
58335
|
+
function createInstanced(geometry, material, capacity) {
|
|
58336
|
+
const mesh3 = new THREE.InstancedMesh(geometry, material, capacity);
|
|
58337
|
+
mesh3.count = 0;
|
|
58338
|
+
mesh3.frustumCulled = false;
|
|
58339
|
+
mesh3.receiveShadow = true;
|
|
58340
|
+
return mesh3;
|
|
58341
|
+
}
|
|
58342
|
+
function takeSpare(vertexCount) {
|
|
58343
|
+
const found = spareGeometries.findIndex((geometry) => geometry.getAttribute("position").count === vertexCount);
|
|
58344
|
+
return found < 0 ? void 0 : spareGeometries.splice(found, 1)[0];
|
|
58345
|
+
}
|
|
58346
|
+
function evictOldestGroundMark() {
|
|
58347
|
+
const oldest = groundMarks.shift();
|
|
58348
|
+
group.remove(oldest.mesh);
|
|
58349
|
+
spareGeometries.push(oldest.mesh.geometry);
|
|
58350
|
+
if (spareGeometries.length > MAX_SPARE_GROUND_GEOMETRIES) spareGeometries.shift().dispose();
|
|
57806
58351
|
}
|
|
57807
58352
|
function conform(mark, material) {
|
|
57808
58353
|
const radius = mark.radius;
|
|
57809
58354
|
const steps = Math.max(MIN_CONFORM_STEPS, Math.min(MAX_CONFORM_STEPS, Math.round(radius * 8)));
|
|
57810
|
-
|
|
57811
|
-
const
|
|
58355
|
+
if (groundMarks.length >= maxGroundMarks) evictOldestGroundMark();
|
|
58356
|
+
const geometry = takeSpare((steps + 1) * (steps + 1)) ?? new THREE.PlaneGeometry(2, 2, steps, steps).rotateX(-Math.PI / 2);
|
|
58357
|
+
const grid = geometry.attributes.position;
|
|
58358
|
+
const cell = 2 / steps;
|
|
57812
58359
|
const cos2 = Math.cos(mark.rotation);
|
|
57813
58360
|
const sin2 = Math.sin(mark.rotation);
|
|
57814
|
-
for (let index = 0; index <
|
|
57815
|
-
const localX =
|
|
57816
|
-
const localZ =
|
|
58361
|
+
for (let index = 0; index < grid.count; index++) {
|
|
58362
|
+
const localX = Math.fround(index % (steps + 1) * cell - 1) * radius;
|
|
58363
|
+
const localZ = Math.fround(Math.floor(index / (steps + 1)) * cell - 1) * radius;
|
|
57817
58364
|
const worldX = mark.x + localX * cos2 - localZ * sin2;
|
|
57818
58365
|
const worldZ = mark.z + localX * sin2 + localZ * cos2;
|
|
57819
|
-
|
|
58366
|
+
grid.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
|
|
57820
58367
|
}
|
|
58368
|
+
grid.needsUpdate = true;
|
|
57821
58369
|
geometry.computeVertexNormals();
|
|
58370
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
58371
|
+
geometry.boundingSphere = null;
|
|
58372
|
+
geometry.boundingBox = null;
|
|
57822
58373
|
const mesh3 = new THREE.Mesh(geometry, material);
|
|
57823
58374
|
mesh3.receiveShadow = true;
|
|
57824
|
-
|
|
58375
|
+
group.add(mesh3);
|
|
58376
|
+
groundMarks.push({ mesh: mesh3, steps });
|
|
57825
58377
|
}
|
|
57826
58378
|
function addRock(mark) {
|
|
57827
|
-
const mesh3 =
|
|
57828
|
-
|
|
57829
|
-
|
|
57830
|
-
|
|
57831
|
-
mesh3.
|
|
57832
|
-
mesh3.
|
|
57833
|
-
|
|
58379
|
+
const mesh3 = mark.plate ? plateMesh() : rockMesh();
|
|
58380
|
+
const slot = mark.plate ? "plate" : "rock";
|
|
58381
|
+
const index = placed[slot]++ % mesh3.instanceMatrix.count;
|
|
58382
|
+
quaternion2.setFromEuler(euler.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]));
|
|
58383
|
+
mesh3.setMatrixAt(index, matrix.compose(position.set(mark.x, mark.y, mark.z), quaternion2, scale.set(mark.scale[0], mark.scale[1], mark.scale[2])));
|
|
58384
|
+
mesh3.setColorAt(index, color2.set(mark.color));
|
|
58385
|
+
mesh3.count = Math.min(placed[slot], mesh3.instanceMatrix.count);
|
|
58386
|
+
mesh3.instanceMatrix.needsUpdate = true;
|
|
58387
|
+
mesh3.instanceColor.needsUpdate = true;
|
|
58388
|
+
}
|
|
58389
|
+
function writeBlood(index, drawn, growth) {
|
|
58390
|
+
quaternion2.setFromAxisAngle(up, drawn.rotation);
|
|
58391
|
+
const mesh3 = bloodMesh();
|
|
58392
|
+
mesh3.setMatrixAt(index, matrix.compose(position.set(drawn.position[0], drawn.position[1], drawn.position[2]), quaternion2, scale.set(drawn.scaleX * growth, 1, drawn.scaleZ * growth)));
|
|
58393
|
+
mesh3.instanceMatrix.needsUpdate = true;
|
|
57834
58394
|
}
|
|
57835
58395
|
function addBlood(mark) {
|
|
57836
|
-
const
|
|
57837
|
-
|
|
57838
|
-
|
|
57839
|
-
|
|
57840
|
-
|
|
57841
|
-
mesh3.scale.multiplyScalar(0.05);
|
|
57842
|
-
blood.push({ mesh: mesh3, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
|
|
57843
|
-
return mesh3;
|
|
58396
|
+
const index = placed.blood++ % bloodMesh().instanceMatrix.count;
|
|
58397
|
+
const drawn = { position: [mark.x, mark.y, mark.z], rotation: mark.rotation, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 };
|
|
58398
|
+
blood[index] = drawn;
|
|
58399
|
+
writeBlood(index, drawn, BLOOD_START_GROWTH);
|
|
58400
|
+
bloodMesh().count = Math.min(placed.blood, bloodMesh().instanceMatrix.count);
|
|
57844
58401
|
}
|
|
57845
58402
|
function build(mark) {
|
|
57846
|
-
if (mark.kind === "scorch")
|
|
57847
|
-
if (mark.kind === "dirt")
|
|
57848
|
-
if (mark.kind === "rock")
|
|
57849
|
-
|
|
57850
|
-
}
|
|
57851
|
-
function
|
|
57852
|
-
for (const
|
|
57853
|
-
|
|
57854
|
-
|
|
57855
|
-
|
|
57856
|
-
|
|
58403
|
+
if (mark.kind === "scorch") conform(mark, scorchMaterials[mark.variant]);
|
|
58404
|
+
else if (mark.kind === "dirt") conform(mark, dirtMaterial);
|
|
58405
|
+
else if (mark.kind === "rock") addRock(mark);
|
|
58406
|
+
else addBlood(mark);
|
|
58407
|
+
}
|
|
58408
|
+
function clearMarks() {
|
|
58409
|
+
for (const drawn of groundMarks) group.remove(drawn.mesh);
|
|
58410
|
+
for (const drawn of groundMarks) drawn.mesh.geometry.dispose();
|
|
58411
|
+
groundMarks.length = 0;
|
|
58412
|
+
for (const geometry of spareGeometries) geometry.dispose();
|
|
58413
|
+
spareGeometries.length = 0;
|
|
57857
58414
|
blood.length = 0;
|
|
57858
|
-
|
|
58415
|
+
placed.blood = placed.rock = placed.plate = 0;
|
|
58416
|
+
for (const mesh3 of [instanced.blood, instanced.rock, instanced.plate]) if (mesh3) mesh3.count = 0;
|
|
57859
58417
|
}
|
|
57860
58418
|
return {
|
|
57861
58419
|
group,
|
|
57862
58420
|
handle(event) {
|
|
57863
|
-
if (event.type === "clear")
|
|
57864
|
-
|
|
57865
|
-
|
|
57866
|
-
|
|
57867
|
-
|
|
57868
|
-
|
|
58421
|
+
if (event.type === "clear") clearMarks();
|
|
58422
|
+
else build(event.mark);
|
|
58423
|
+
},
|
|
58424
|
+
prepare() {
|
|
58425
|
+
bloodMesh();
|
|
58426
|
+
rockMesh();
|
|
58427
|
+
plateMesh();
|
|
57869
58428
|
},
|
|
57870
58429
|
update(dt) {
|
|
57871
|
-
for (
|
|
57872
|
-
|
|
58430
|
+
for (let index = 0; index < blood.length; index++) {
|
|
58431
|
+
const drawn = blood[index];
|
|
58432
|
+
if (!drawn || drawn.age > 1) continue;
|
|
57873
58433
|
drawn.age += dt * BLOOD_GROW_RATE;
|
|
57874
|
-
|
|
57875
|
-
drawn.mesh.scale.set(drawn.scaleX * Math.max(0.05, ease3), 1, drawn.scaleZ * Math.max(0.05, ease3));
|
|
58434
|
+
writeBlood(index, drawn, Math.max(BLOOD_START_GROWTH, 1 - Math.pow(1 - Math.min(1, drawn.age), 2.5)));
|
|
57876
58435
|
}
|
|
57877
58436
|
},
|
|
57878
|
-
count: () =>
|
|
58437
|
+
count: () => groundMarks.length + [instanced.blood, instanced.rock, instanced.plate].filter((mesh3) => mesh3 && mesh3.count > 0).length,
|
|
57879
58438
|
dispose() {
|
|
57880
|
-
|
|
58439
|
+
clearMarks();
|
|
58440
|
+
for (const mesh3 of [instanced.blood, instanced.rock, instanced.plate]) mesh3?.dispose();
|
|
57881
58441
|
for (const item of owned) item.dispose();
|
|
57882
58442
|
}
|
|
57883
58443
|
};
|
|
57884
58444
|
}
|
|
57885
58445
|
var SPHERE_DEFAULT_WIDTH = 8;
|
|
57886
58446
|
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
58447
|
+
var MAX_PARKED_MESHES_PER_KIND = 256;
|
|
57887
58448
|
function geometryOf(THREE, particle) {
|
|
57888
58449
|
switch (particle.shape) {
|
|
57889
58450
|
case "cone":
|
|
57890
|
-
return new THREE.ConeGeometry(
|
|
58451
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, false);
|
|
57891
58452
|
case "cone_open":
|
|
57892
|
-
return new THREE.ConeGeometry(
|
|
58453
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, true);
|
|
57893
58454
|
case "sphere":
|
|
57894
|
-
return new THREE.SphereGeometry(
|
|
58455
|
+
return new THREE.SphereGeometry(1, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
57895
58456
|
case "icosahedron":
|
|
57896
|
-
return new THREE.IcosahedronGeometry(
|
|
58457
|
+
return new THREE.IcosahedronGeometry(1, particle.segments);
|
|
57897
58458
|
}
|
|
57898
58459
|
}
|
|
58460
|
+
function isCone(particle) {
|
|
58461
|
+
return particle.shape === "cone" || particle.shape === "cone_open";
|
|
58462
|
+
}
|
|
57899
58463
|
function materialOf(THREE, particle) {
|
|
57900
58464
|
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
57901
58465
|
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
@@ -57906,51 +58470,82 @@ void main(){
|
|
|
57906
58470
|
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
57907
58471
|
);
|
|
57908
58472
|
}
|
|
58473
|
+
function geometryKeyOf(particle) {
|
|
58474
|
+
return `${particle.shape}:${particle.segments}:${particle.segments2}`;
|
|
58475
|
+
}
|
|
57909
58476
|
function createMeshLayer(THREE, pool) {
|
|
57910
58477
|
const group = new THREE.Group();
|
|
57911
58478
|
group.name = "workshop-meshes";
|
|
57912
58479
|
const drawn = /* @__PURE__ */ new Map();
|
|
58480
|
+
const geometries = /* @__PURE__ */ new Map();
|
|
58481
|
+
const parked = /* @__PURE__ */ new Map();
|
|
57913
58482
|
const frame2 = new THREE.Quaternion();
|
|
57914
58483
|
const euler = new THREE.Euler();
|
|
57915
58484
|
const turn2 = new THREE.Quaternion();
|
|
57916
58485
|
const offset = new THREE.Vector3();
|
|
57917
|
-
|
|
57918
|
-
|
|
57919
|
-
|
|
57920
|
-
|
|
58486
|
+
let stamp = 0;
|
|
58487
|
+
function geometryFor(particle) {
|
|
58488
|
+
const key2 = geometryKeyOf(particle);
|
|
58489
|
+
let geometry = geometries.get(key2);
|
|
58490
|
+
if (!geometry) {
|
|
58491
|
+
geometry = geometryOf(THREE, particle);
|
|
58492
|
+
geometries.set(key2, geometry);
|
|
58493
|
+
}
|
|
58494
|
+
return geometry;
|
|
58495
|
+
}
|
|
58496
|
+
function acquire(particle) {
|
|
58497
|
+
const parkingKey = `${geometryKeyOf(particle)}|${particle.lit}|${particle.additive}`;
|
|
58498
|
+
const mesh3 = parked.get(parkingKey)?.pop() ?? new THREE.Mesh(geometryFor(particle), materialOf(THREE, particle));
|
|
58499
|
+
mesh3.material.color.set(particle.color);
|
|
58500
|
+
mesh3.frustumCulled = false;
|
|
58501
|
+
mesh3.renderOrder = particle.additive ? 5 : 3;
|
|
58502
|
+
group.add(mesh3);
|
|
58503
|
+
return { mesh: mesh3, parkingKey, stamp };
|
|
58504
|
+
}
|
|
58505
|
+
function park(entry2) {
|
|
58506
|
+
entry2.mesh.removeFromParent();
|
|
58507
|
+
let free = parked.get(entry2.parkingKey);
|
|
58508
|
+
if (!free) {
|
|
58509
|
+
free = [];
|
|
58510
|
+
parked.set(entry2.parkingKey, free);
|
|
58511
|
+
}
|
|
58512
|
+
if (free.length < MAX_PARKED_MESHES_PER_KIND) free.push(entry2.mesh);
|
|
58513
|
+
else entry2.mesh.material.dispose();
|
|
57921
58514
|
}
|
|
57922
58515
|
return {
|
|
57923
58516
|
group,
|
|
57924
58517
|
sync() {
|
|
57925
|
-
|
|
58518
|
+
stamp++;
|
|
57926
58519
|
for (const particle of pool.particles) {
|
|
57927
|
-
|
|
57928
|
-
|
|
57929
|
-
|
|
57930
|
-
|
|
57931
|
-
mesh3.frustumCulled = false;
|
|
57932
|
-
mesh3.renderOrder = particle.additive ? 5 : 3;
|
|
57933
|
-
drawn.set(particle.id, mesh3);
|
|
57934
|
-
group.add(mesh3);
|
|
58520
|
+
let entry2 = drawn.get(particle.id);
|
|
58521
|
+
if (!entry2) {
|
|
58522
|
+
entry2 = acquire(particle);
|
|
58523
|
+
drawn.set(particle.id, entry2);
|
|
57935
58524
|
}
|
|
58525
|
+
entry2.stamp = stamp;
|
|
58526
|
+
const mesh3 = entry2.mesh;
|
|
57936
58527
|
frame2.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
57937
58528
|
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame2);
|
|
57938
58529
|
mesh3.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
57939
58530
|
turn2.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
57940
58531
|
mesh3.quaternion.copy(frame2).multiply(turn2);
|
|
57941
|
-
mesh3.scale.
|
|
58532
|
+
mesh3.scale.set(particle.radius * particle.scale, (isCone(particle) ? particle.height : particle.radius) * particle.scale, particle.radius * particle.scale);
|
|
57942
58533
|
mesh3.material.opacity = particle.opacity;
|
|
57943
58534
|
}
|
|
57944
|
-
for (const [id,
|
|
57945
|
-
if (
|
|
57946
|
-
|
|
58535
|
+
for (const [id, entry2] of drawn) {
|
|
58536
|
+
if (entry2.stamp === stamp) continue;
|
|
58537
|
+
park(entry2);
|
|
57947
58538
|
drawn.delete(id);
|
|
57948
58539
|
}
|
|
57949
58540
|
return pool.count;
|
|
57950
58541
|
},
|
|
57951
58542
|
dispose() {
|
|
57952
|
-
for (const
|
|
58543
|
+
for (const entry2 of drawn.values()) park(entry2);
|
|
57953
58544
|
drawn.clear();
|
|
58545
|
+
for (const free of parked.values()) for (const mesh3 of free) mesh3.material.dispose();
|
|
58546
|
+
parked.clear();
|
|
58547
|
+
for (const geometry of geometries.values()) geometry.dispose();
|
|
58548
|
+
geometries.clear();
|
|
57954
58549
|
group.removeFromParent();
|
|
57955
58550
|
}
|
|
57956
58551
|
};
|
|
@@ -58518,7 +59113,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58518
59113
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
58519
59114
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
58520
59115
|
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
58521
|
-
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry });
|
|
59116
|
+
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
58522
59117
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
58523
59118
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
58524
59119
|
const overlay = options.overlay ? createScreenOverlay(options.overlay) : void 0;
|
|
@@ -58544,6 +59139,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58544
59139
|
const position = options.camera.position;
|
|
58545
59140
|
world.setCamera([position.x, position.y, position.z]);
|
|
58546
59141
|
}
|
|
59142
|
+
function attachWorkshopLayers() {
|
|
59143
|
+
const engine = world.peekWorkshop();
|
|
59144
|
+
if (!engine || engine === workshopEngine) return;
|
|
59145
|
+
workshopLayers?.dispose();
|
|
59146
|
+
if (workshopLayers) root.remove(workshopLayers.group);
|
|
59147
|
+
workshopEngine = engine;
|
|
59148
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
59149
|
+
root.add(workshopLayers.group);
|
|
59150
|
+
applyLayers();
|
|
59151
|
+
}
|
|
58547
59152
|
function syncAll() {
|
|
58548
59153
|
lastDrawn.puffs = puffs.sync();
|
|
58549
59154
|
lastDrawn.sparks = sparks.sync();
|
|
@@ -58552,15 +59157,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58552
59157
|
lastDrawn.shocks = shocks.sync();
|
|
58553
59158
|
lastDrawn.sprites = sprites.sync();
|
|
58554
59159
|
lights.sync();
|
|
58555
|
-
|
|
58556
|
-
if (engine && engine !== workshopEngine) {
|
|
58557
|
-
workshopLayers?.dispose();
|
|
58558
|
-
if (workshopLayers) root.remove(workshopLayers.group);
|
|
58559
|
-
workshopEngine = engine;
|
|
58560
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
58561
|
-
root.add(workshopLayers.group);
|
|
58562
|
-
applyLayers();
|
|
58563
|
-
}
|
|
59160
|
+
attachWorkshopLayers();
|
|
58564
59161
|
const workshopCalls = workshopLayers?.sync() ?? 0;
|
|
58565
59162
|
const glow = pools.lights.glow;
|
|
58566
59163
|
uniforms.uGlowPos.value.set(glow.x, glow.y, glow.z);
|
|
@@ -58613,6 +59210,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58613
59210
|
},
|
|
58614
59211
|
warmUpProbes() {
|
|
58615
59212
|
releaseAnchors();
|
|
59213
|
+
marks.prepare();
|
|
59214
|
+
world.workshop();
|
|
59215
|
+
attachWorkshopLayers();
|
|
58616
59216
|
const meshes = createMeshLayerProbes(THREE);
|
|
58617
59217
|
for (const mesh3 of meshes) mesh3.frustumCulled = false;
|
|
58618
59218
|
const group = new THREE.Group();
|
|
@@ -58679,6 +59279,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58679
59279
|
|
|
58680
59280
|
// src/fx/heavy.ts
|
|
58681
59281
|
var PI14 = Math.PI;
|
|
59282
|
+
var ROCKET_LIGHT_NAME = "rocket-light";
|
|
59283
|
+
var rocketsInFlight = /* @__PURE__ */ new WeakMap();
|
|
59284
|
+
function createRocketLight(THREE) {
|
|
59285
|
+
const light = new THREE.PointLight(16751168, 0, 2.5);
|
|
59286
|
+
light.name = ROCKET_LIGHT_NAME;
|
|
59287
|
+
light.position.set(0, -50, 0);
|
|
59288
|
+
return light;
|
|
59289
|
+
}
|
|
58682
59290
|
var flameMaterials = (kit) => {
|
|
58683
59291
|
const { THREE } = kit;
|
|
58684
59292
|
return [
|
|
@@ -58708,8 +59316,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58708
59316
|
exhaust.rotation.z = PI14 / 2;
|
|
58709
59317
|
exhaust.position.x = -0.08;
|
|
58710
59318
|
rocket.add(exhaust);
|
|
58711
|
-
const light =
|
|
58712
|
-
|
|
59319
|
+
const light = context.rocketLight;
|
|
59320
|
+
light.intensity = 3;
|
|
59321
|
+
rocketsInFlight.set(light, (rocketsInFlight.get(light) ?? 0) + 1);
|
|
58713
59322
|
context.every(
|
|
58714
59323
|
1.6,
|
|
58715
59324
|
(progress, dt) => {
|
|
@@ -58717,6 +59326,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58717
59326
|
const distance3 = 1.2 * flight + (flight > 0.25 ? 9 * (flight - 0.25) ** 2 : 0);
|
|
58718
59327
|
rocket.position.copy(muzzle2).addScaledVector(forward, distance3);
|
|
58719
59328
|
rocket.rotation.x += 0.25 * dt * 60;
|
|
59329
|
+
light.position.copy(rocket.position);
|
|
58720
59330
|
exhaust.scale.set(1, 0.6 + random() * 0.8, 1);
|
|
58721
59331
|
if (random() < 0.9) context.effects.smoke(rocket.position.clone().addScaledVector(forward, -0.12), new THREE.Vector3((random() - 0.5) * 0.1, 0.05, (random() - 0.5) * 0.1), 0.02 + random() * 0.015, 2.2, 0.5);
|
|
58722
59332
|
},
|
|
@@ -58724,7 +59334,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58724
59334
|
rocket.removeFromParent();
|
|
58725
59335
|
exhaust.geometry.dispose();
|
|
58726
59336
|
exhaust.material.dispose();
|
|
58727
|
-
|
|
59337
|
+
const remaining = (rocketsInFlight.get(light) ?? 1) - 1;
|
|
59338
|
+
rocketsInFlight.set(light, remaining);
|
|
59339
|
+
if (remaining > 0) return;
|
|
59340
|
+
light.intensity = 0;
|
|
59341
|
+
light.position.set(0, -50, 0);
|
|
58728
59342
|
}
|
|
58729
59343
|
);
|
|
58730
59344
|
}
|
|
@@ -59539,11 +60153,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59539
60153
|
|
|
59540
60154
|
// src/fx/viewmodel.ts
|
|
59541
60155
|
var PI16 = Math.PI;
|
|
59542
|
-
var HAND_OFFSET2 = [0.2, -0.2, -0.42];
|
|
59543
|
-
var HANDGUN_OFFSET = [0.1, -0.12, -0.44];
|
|
59544
60156
|
var HEAT_DISPLAY_MAX_FALLBACK = 120;
|
|
59545
|
-
var WALL_PULL = { margin: 0.03, max:
|
|
59546
|
-
var HAND_IN_VIEW = { floor: -0.88, max: 0.
|
|
60157
|
+
var WALL_PULL = { margin: 0.03, max: 1.2, rate: 14, probe: 3 };
|
|
60158
|
+
var HAND_IN_VIEW = { floor: -0.88, max: 0.3, rate: 10 };
|
|
59547
60159
|
var LAUNCH_VOLUME = 0.6;
|
|
59548
60160
|
var fittedOf2 = (parts) => parts.map((part) => ({ id: part.id, mount: part.mount, ...part.integral ? { integral: true } : {}, ...part.on === void 0 ? {} : { on: part.on } }));
|
|
59549
60161
|
var collapsedStockPose = (move2) => {
|
|
@@ -59573,6 +60185,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59573
60185
|
const hand = new THREE.Group();
|
|
59574
60186
|
hand.name = VIEWMODEL_NODE_NAME;
|
|
59575
60187
|
scene.add(hand);
|
|
60188
|
+
const rocketLight = createRocketLight(THREE);
|
|
60189
|
+
scene.add(rocketLight);
|
|
59576
60190
|
const holder = new THREE.Group();
|
|
59577
60191
|
holder.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), PI16 / 2);
|
|
59578
60192
|
hand.add(holder);
|
|
@@ -59629,7 +60243,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59629
60243
|
positionOf: (part) => worldPositionOf(current, part, new THREE.Vector3()),
|
|
59630
60244
|
orientation: () => current.model.root.getWorldQuaternion(new THREE.Quaternion()),
|
|
59631
60245
|
every: (seconds, step, done) => void tasks.push({ delay: 0, seconds, elapsed: 0, step, done }),
|
|
59632
|
-
groundAt
|
|
60246
|
+
groundAt,
|
|
60247
|
+
rocketLight
|
|
59633
60248
|
});
|
|
59634
60249
|
const equip = (weapon) => {
|
|
59635
60250
|
const def = arsenal.weapons.get(weapon);
|
|
@@ -59642,14 +60257,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59642
60257
|
reach = 0;
|
|
59643
60258
|
pull = 0;
|
|
59644
60259
|
lift = 0;
|
|
59645
|
-
handHeight = 0;
|
|
59646
|
-
handDepth = 0;
|
|
59647
60260
|
rigStock(THREE, model, def);
|
|
59648
60261
|
rigUnderbarrel(THREE, model, def);
|
|
59649
|
-
const
|
|
59650
|
-
const
|
|
59651
|
-
|
|
59652
|
-
holder.position.set(0, 0, Math.min(0.25, 0.25 * length2 * scale));
|
|
60262
|
+
const modelBox = new THREE.Box3().setFromObject(model.root);
|
|
60263
|
+
const length2 = modelBox.getSize(new THREE.Vector3()).x;
|
|
60264
|
+
modelRear = modelBox.min.x;
|
|
59653
60265
|
holder.add(model.root);
|
|
59654
60266
|
const caliber = caliberOf(arsenal, def);
|
|
59655
60267
|
const dims = caliber?.cartridge;
|
|
@@ -59661,7 +60273,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59661
60273
|
def,
|
|
59662
60274
|
caliber,
|
|
59663
60275
|
model,
|
|
59664
|
-
holderScale:
|
|
60276
|
+
holderScale: 1,
|
|
59665
60277
|
lengthMetres: length2,
|
|
59666
60278
|
caseType: caliber?.caseType ?? "rifle",
|
|
59667
60279
|
soundProfile: soundProfileOf(weapon, model.parts.family),
|
|
@@ -59691,7 +60303,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59691
60303
|
applyCondition(kit, model, condition);
|
|
59692
60304
|
lastAmmo = options.tool?.().ammo ?? 0;
|
|
59693
60305
|
hand.updateMatrixWorld(true);
|
|
59694
|
-
hold = holdOfModel(THREE, model,
|
|
60306
|
+
hold = holdOfModel(THREE, model, 1);
|
|
59695
60307
|
hold = { ...hold, works: handWorkOf(THREE, model, weapon, hold.right) };
|
|
59696
60308
|
triggerRest = model.parts.trigger?.rotation.z ?? 0;
|
|
59697
60309
|
lastShot = -Infinity;
|
|
@@ -59711,8 +60323,6 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59711
60323
|
let reach = 0;
|
|
59712
60324
|
let pull = 0;
|
|
59713
60325
|
let lift = 0;
|
|
59714
|
-
let handHeight = 0;
|
|
59715
|
-
let handDepth = 0;
|
|
59716
60326
|
const ahead = new THREE.Vector3();
|
|
59717
60327
|
const up = new THREE.Vector3();
|
|
59718
60328
|
const seen = new THREE.Vector3();
|
|
@@ -59725,23 +60335,35 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59725
60335
|
for (const x of [bounds.min.x, bounds.max.x]) for (const y of [bounds.min.y, bounds.max.y]) for (const z of [bounds.min.z, bounds.max.z]) farthest = Math.max(farthest, corner.set(x, y, z).sub(camera2.position).dot(ahead));
|
|
59726
60336
|
return farthest;
|
|
59727
60337
|
};
|
|
60338
|
+
let modelRear = 0;
|
|
60339
|
+
const eyeFrame = createEyeFrame(THREE, camera2);
|
|
60340
|
+
const liftToKeepInView = () => {
|
|
60341
|
+
if (!hold || !session || !("fov" in camera2)) return 0;
|
|
60342
|
+
const tanHalf = Math.tan((camera2.fov ?? 70) * PI16 / 360);
|
|
60343
|
+
eyeTurn.copy(camera2.quaternion).invert();
|
|
60344
|
+
const rest2 = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), 0);
|
|
60345
|
+
let needed = 0;
|
|
60346
|
+
for (const [anchor, pose] of [[hold.right, rest2.R], [hold.left, rest2.L]]) {
|
|
60347
|
+
if (!anchor || !pose || anchor.part !== session.model.root) continue;
|
|
60348
|
+
seen.set(...pose.position).sub(camera2.position).applyQuaternion(eyeTurn);
|
|
60349
|
+
if (-seen.z > 0.05) needed = Math.max(needed, HAND_IN_VIEW.floor * -seen.z * tanHalf - seen.y);
|
|
60350
|
+
}
|
|
60351
|
+
return Math.max(0, Math.min(HAND_IN_VIEW.max, needed));
|
|
60352
|
+
};
|
|
59728
60353
|
const place = (dt = 0) => {
|
|
59729
|
-
|
|
59730
|
-
const
|
|
59731
|
-
|
|
59732
|
-
hand.
|
|
59733
|
-
if (session && handDepth > 0.05 && "fov" in camera2) {
|
|
59734
|
-
const tanHalf = Math.tan((camera2.fov ?? 70) * PI16 / 360);
|
|
59735
|
-
const want2 = Math.max(0, Math.min(HAND_IN_VIEW.max, lift + (HAND_IN_VIEW.floor - handHeight) * tanHalf * handDepth));
|
|
59736
|
-
lift += (want2 - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
59737
|
-
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera2.quaternion), lift);
|
|
59738
|
-
}
|
|
60354
|
+
if (!session) return;
|
|
60355
|
+
const family = session.model.parts.family;
|
|
60356
|
+
session.holderScale = 1;
|
|
60357
|
+
placeOnBody({ THREE, hand, holder, frame: options.bodyHold?.() ?? eyeFrame(), rear: modelRear, lengthMetres: session.lengthMetres, handgun: family === "pistol" || family === "revolver", right: hold?.right ?? null, left: hold?.left ?? null });
|
|
59739
60358
|
hand.updateMatrixWorld(true);
|
|
59740
|
-
|
|
60359
|
+
lift += (liftToKeepInView() - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
60360
|
+
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera2.quaternion), lift);
|
|
60361
|
+
hand.updateMatrixWorld(true);
|
|
60362
|
+
if (!options.contact) return;
|
|
59741
60363
|
ahead.set(0, 0, -1).applyQuaternion(camera2.quaternion);
|
|
59742
60364
|
if (reach === 0) reach = Math.max(0.01, measureReach(session));
|
|
59743
60365
|
const free = options.contact(camera2.position, ahead, WALL_PULL.probe);
|
|
59744
|
-
const want = free === null ? 0 : Math.max(0, Math.min(WALL_PULL.max, reach + WALL_PULL.margin - free));
|
|
60366
|
+
const want = free === null ? 0 : Math.max(0, Math.min(Math.max(WALL_PULL.max, session.lengthMetres * session.holderScale), reach + WALL_PULL.margin - free));
|
|
59745
60367
|
pull += (want - pull) * Math.min(1, dt * WALL_PULL.rate);
|
|
59746
60368
|
if (pull < 1e-4) return;
|
|
59747
60369
|
hand.position.addScaledVector(ahead, -pull);
|
|
@@ -59851,12 +60473,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59851
60473
|
const targets = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), magazineHand, hold.works ? workWeights(hold.works, player?.running ?? []) : void 0);
|
|
59852
60474
|
const trigger = session.model.parts.trigger;
|
|
59853
60475
|
if (trigger) targets.fingers = { index: indexCurl(trigger.rotation.z - triggerRest, clock - lastShot) };
|
|
59854
|
-
|
|
59855
|
-
seen.set(...targets.R.position).sub(camera2.position).applyQuaternion(eyeTurn.copy(camera2.quaternion).invert());
|
|
59856
|
-
handDepth = -seen.z;
|
|
59857
|
-
handHeight = handDepth > 0.05 ? seen.y / handDepth / Math.tan((camera2.fov ?? 70) * PI16 / 360) : 0;
|
|
59858
|
-
}
|
|
59859
|
-
return targets;
|
|
60476
|
+
return limitSupportSpan(targets);
|
|
59860
60477
|
},
|
|
59861
60478
|
get inHand() {
|
|
59862
60479
|
return session?.id ?? null;
|
|
@@ -59875,6 +60492,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59875
60492
|
particles.dispose();
|
|
59876
60493
|
ownVfx?.dispose();
|
|
59877
60494
|
hand.removeFromParent();
|
|
60495
|
+
rocketLight.removeFromParent();
|
|
60496
|
+
rocketLight.dispose();
|
|
59878
60497
|
}
|
|
59879
60498
|
};
|
|
59880
60499
|
}
|
|
@@ -59899,6 +60518,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59899
60518
|
...options.attachmentModels ? { attachmentModels: options.attachmentModels } : {},
|
|
59900
60519
|
...options.raycast ? { raycast: options.raycast } : {},
|
|
59901
60520
|
...options.contact ? { contact: options.contact } : {},
|
|
60521
|
+
...options.bodyHold ? { bodyHold: options.bodyHold } : {},
|
|
59902
60522
|
...options.effects ? { effects: options.effects } : {},
|
|
59903
60523
|
...options.flyRocket === void 0 ? {} : { flyRocket: options.flyRocket }
|
|
59904
60524
|
});
|
|
@@ -60674,7 +61294,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60674
61294
|
var P13_VALVE_MESH = "valve";
|
|
60675
61295
|
var P13_VALVE_COLOR = 12095802;
|
|
60676
61296
|
var P13_WALL = 25e-4;
|
|
60677
|
-
var
|
|
61297
|
+
var SHAPES3 = {
|
|
60678
61298
|
fuel_barrel: { shape: { kind: "cylinder", radius: 0.3, height: 0.9 }, position: [0, 0.45, 0], color: 3104666 },
|
|
60679
61299
|
propane_p13: {
|
|
60680
61300
|
shape: { kind: "cylinder", radius: 0.176, height: 0.49 },
|
|
@@ -60685,7 +61305,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60685
61305
|
},
|
|
60686
61306
|
car_fuel_tank: { shape: { kind: "box", size: [0.9, 0.25, 0.5] }, position: [0, 0.4, 0], color: 4935509 }
|
|
60687
61307
|
};
|
|
60688
|
-
var WORLD_EXPLOSIVE_IDS = Object.keys(
|
|
61308
|
+
var WORLD_EXPLOSIVE_IDS = Object.keys(SHAPES3);
|
|
60689
61309
|
var SHELL = { thickness: 1e-3, fill: 0.03 };
|
|
60690
61310
|
var explosivePart = (def, shape, scale) => ({
|
|
60691
61311
|
id: "tank",
|
|
@@ -60731,7 +61351,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60731
61351
|
return [centre.x, centre.y, centre.z];
|
|
60732
61352
|
};
|
|
60733
61353
|
var worldExplosiveShell = (THREE, defId) => {
|
|
60734
|
-
const shape =
|
|
61354
|
+
const shape = SHAPES3[defId];
|
|
60735
61355
|
const model = shape ? modelOf(THREE, shape, 2) : void 0;
|
|
60736
61356
|
if (!model) return void 0;
|
|
60737
61357
|
model.traverse((node) => {
|
|
@@ -60744,7 +61364,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60744
61364
|
return model;
|
|
60745
61365
|
};
|
|
60746
61366
|
var worldExplosiveItem = (def, name, options = {}) => {
|
|
60747
|
-
const shape =
|
|
61367
|
+
const shape = SHAPES3[def.id];
|
|
60748
61368
|
if (!shape) return void 0;
|
|
60749
61369
|
const scale = () => options.scale?.() ?? 1;
|
|
60750
61370
|
const spec = () => ({ name, parts: [explosivePart(def, shape, scale())], joints: [], tags: ["explosive"] });
|
|
@@ -61179,6 +61799,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61179
61799
|
raycast: createLaserRaycast(lent.THREE, lent.scene, lent.camera),
|
|
61180
61800
|
// the weapon against a wall asks as often as the laser, on the clock of the simulation (so a fast-forwarded room measures as often per frame)
|
|
61181
61801
|
contact: createLaserRaycast(lent.THREE, lent.scene, lent.camera, { now: () => simulatedMs }),
|
|
61802
|
+
...lent.holdFrame ? { bodyHold: () => lent.holdFrame?.() ?? null } : {},
|
|
61182
61803
|
...lent.effects ? { effects: lent.effects } : {},
|
|
61183
61804
|
...options.attachmentModels ? { attachmentModels: options.attachmentModels } : {},
|
|
61184
61805
|
...options.condition ? { condition: options.condition } : {},
|
|
@@ -61991,7 +62612,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61991
62612
|
};
|
|
61992
62613
|
for (const { kind, item } of PUSH_KINDS) PUSH_LAYOUT[kind].forEach(([distance3, degrees]) => lay(kind, item, bearing(centre, distance3, degrees)));
|
|
61993
62614
|
if (mode === "grenade") {
|
|
61994
|
-
lay("zombie", "zombie_walker", bearing(centre, 3.4, -
|
|
62615
|
+
lay("zombie", "zombie_walker", bearing(centre, 3.4, -130));
|
|
61995
62616
|
lay("survivor", "person_police_officer", bearing(centre, 7.5, 78));
|
|
61996
62617
|
lay("camp_table", "camp_table", bearing(centre, 3, 90));
|
|
61997
62618
|
}
|
|
@@ -62034,7 +62655,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
62034
62655
|
const outward = ((now[0] - from[0]) * (from[0] - blast.point[0]) + (now[2] - from[2]) * (from[2] - blast.point[2])) / away;
|
|
62035
62656
|
return { ...body, distance: distance3, outward, moved: flat(now, from) };
|
|
62036
62657
|
});
|
|
62037
|
-
const torn = (row) => PUSH_KINDS.some((entry2) => entry2.kind === row.kind) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
|
|
62658
|
+
const torn = (row) => (PUSH_KINDS.some((entry2) => entry2.kind === row.kind) || HEAVY_ITEMS.includes(row.kind)) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
|
|
62038
62659
|
const reached = rows.filter((row) => row.distance < blast.radius * 0.85 && !torn(row));
|
|
62039
62660
|
const movedAway = reached.filter((row) => row.outward >= MOVED);
|
|
62040
62661
|
const least = reached.length === 0 ? 0 : Math.min(...reached.map((row) => row.outward));
|
|
@@ -62047,9 +62668,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
62047
62668
|
expect(near.outward > far.outward, fillText2(text.push_falloff, { what: things[kind] ?? kind, near: near.outward.toFixed(2), nearDistance: near.distance.toFixed(1), far: far.outward.toFixed(2), farDistance: far.distance.toFixed(1) }), { near: near.outward, far: far.outward });
|
|
62048
62669
|
}
|
|
62049
62670
|
for (const row of reached.filter((entry2) => entry2.kind === "zombie" || entry2.kind === "survivor")) {
|
|
62050
|
-
expect(row.outward >= MOVED, fillText2(text.push_person, { who: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), moved: row.outward.toFixed(2) }), { row });
|
|
62671
|
+
expect(row.outward >= MOVED, fillText2(text.push_person, { who: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), moved: row.outward.toFixed(2) }), { row: { kind: row.kind, distance: row.distance, outward: row.outward } });
|
|
62051
62672
|
}
|
|
62052
|
-
const table =
|
|
62673
|
+
const table = rows.find((row) => HEAVY_ITEMS.includes(row.kind) && row.distance < blast.radius * 0.85);
|
|
62053
62674
|
if (table) expect(table.moved >= MOVED, fillText2(text.push_heavy, { what: things[table.itemId] ?? table.itemId, moved: table.moved.toFixed(2) }), { table });
|
|
62054
62675
|
}
|
|
62055
62676
|
};
|