@adatechnology/game-weapons-three 0.10.3 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.global.js +938 -206
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +153 -46
- 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. */
|
|
@@ -39701,6 +40154,55 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39701
40154
|
ends: 22e-5
|
|
39702
40155
|
}
|
|
39703
40156
|
}
|
|
40157
|
+
},
|
|
40158
|
+
plastic_bottle: {
|
|
40159
|
+
models: [
|
|
40160
|
+
"water_bottle",
|
|
40161
|
+
"water_jug_5l",
|
|
40162
|
+
"dirty_water",
|
|
40163
|
+
"sports_drink",
|
|
40164
|
+
"coconut_water",
|
|
40165
|
+
"cooking_oil",
|
|
40166
|
+
"alcohol_70",
|
|
40167
|
+
"fuel_jerrycan"
|
|
40168
|
+
],
|
|
40169
|
+
rule: {
|
|
40170
|
+
hollow: true,
|
|
40171
|
+
welded: true,
|
|
40172
|
+
skinTags: [
|
|
40173
|
+
"paper",
|
|
40174
|
+
"food"
|
|
40175
|
+
],
|
|
40176
|
+
materials: {
|
|
40177
|
+
glass: "plastic"
|
|
40178
|
+
}
|
|
40179
|
+
}
|
|
40180
|
+
},
|
|
40181
|
+
glass_bottle: {
|
|
40182
|
+
models: [
|
|
40183
|
+
"cachaca",
|
|
40184
|
+
"communion_wine",
|
|
40185
|
+
"honey_jar",
|
|
40186
|
+
"home_preserves"
|
|
40187
|
+
],
|
|
40188
|
+
rule: {
|
|
40189
|
+
hollow: true,
|
|
40190
|
+
welded: true,
|
|
40191
|
+
skinTags: [
|
|
40192
|
+
"paper",
|
|
40193
|
+
"food"
|
|
40194
|
+
]
|
|
40195
|
+
}
|
|
40196
|
+
},
|
|
40197
|
+
drink_carton: {
|
|
40198
|
+
models: [
|
|
40199
|
+
"juice_box",
|
|
40200
|
+
"milk_carton"
|
|
40201
|
+
],
|
|
40202
|
+
rule: {
|
|
40203
|
+
hollow: true,
|
|
40204
|
+
welded: true
|
|
40205
|
+
}
|
|
39704
40206
|
}
|
|
39705
40207
|
},
|
|
39706
40208
|
contents: {
|
|
@@ -39716,6 +40218,67 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39716
40218
|
liquid: "soda",
|
|
39717
40219
|
fill: 0.95
|
|
39718
40220
|
},
|
|
40221
|
+
water_bottle: {
|
|
40222
|
+
liquid: "water",
|
|
40223
|
+
fill: 0.95
|
|
40224
|
+
},
|
|
40225
|
+
water_jug_5l: {
|
|
40226
|
+
liquid: "water",
|
|
40227
|
+
fill: 0.95,
|
|
40228
|
+
box: true
|
|
40229
|
+
},
|
|
40230
|
+
dirty_water: {
|
|
40231
|
+
liquid: "water",
|
|
40232
|
+
fill: 0.9
|
|
40233
|
+
},
|
|
40234
|
+
sports_drink: {
|
|
40235
|
+
liquid: "juice",
|
|
40236
|
+
fill: 0.95
|
|
40237
|
+
},
|
|
40238
|
+
coconut_water: {
|
|
40239
|
+
liquid: "water",
|
|
40240
|
+
fill: 0.9,
|
|
40241
|
+
box: true
|
|
40242
|
+
},
|
|
40243
|
+
juice_box: {
|
|
40244
|
+
liquid: "juice",
|
|
40245
|
+
fill: 0.95,
|
|
40246
|
+
box: true
|
|
40247
|
+
},
|
|
40248
|
+
milk_carton: {
|
|
40249
|
+
liquid: "milk",
|
|
40250
|
+
fill: 0.95,
|
|
40251
|
+
box: true
|
|
40252
|
+
},
|
|
40253
|
+
cachaca: {
|
|
40254
|
+
liquid: "spirits",
|
|
40255
|
+
fill: 0.9
|
|
40256
|
+
},
|
|
40257
|
+
communion_wine: {
|
|
40258
|
+
liquid: "wine",
|
|
40259
|
+
fill: 0.9
|
|
40260
|
+
},
|
|
40261
|
+
cooking_oil: {
|
|
40262
|
+
liquid: "cooking_oil",
|
|
40263
|
+
fill: 0.9
|
|
40264
|
+
},
|
|
40265
|
+
alcohol_70: {
|
|
40266
|
+
liquid: "spirits",
|
|
40267
|
+
fill: 0.9
|
|
40268
|
+
},
|
|
40269
|
+
honey_jar: {
|
|
40270
|
+
liquid: "honey",
|
|
40271
|
+
fill: 0.85
|
|
40272
|
+
},
|
|
40273
|
+
home_preserves: {
|
|
40274
|
+
liquid: "water",
|
|
40275
|
+
fill: 0.5
|
|
40276
|
+
},
|
|
40277
|
+
fuel_jerrycan: {
|
|
40278
|
+
liquid: "fuel",
|
|
40279
|
+
fill: 0.85,
|
|
40280
|
+
box: true
|
|
40281
|
+
},
|
|
39719
40282
|
canned_beans: {
|
|
39720
40283
|
liquid: "juice",
|
|
39721
40284
|
fill: 0.4
|
|
@@ -39737,6 +40300,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39737
40300
|
liquid: "honey",
|
|
39738
40301
|
fill: 0.9
|
|
39739
40302
|
}
|
|
40303
|
+
},
|
|
40304
|
+
shapes: {
|
|
40305
|
+
box: {
|
|
40306
|
+
hollow: true,
|
|
40307
|
+
welded: true
|
|
40308
|
+
}
|
|
39740
40309
|
}
|
|
39741
40310
|
};
|
|
39742
40311
|
var DESTRUCTION_MATERIAL_BY_TAG = {
|
|
@@ -39750,6 +40319,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39750
40319
|
};
|
|
39751
40320
|
var FAMILIES = destruction_default.families;
|
|
39752
40321
|
var CONTENTS = destruction_default.contents;
|
|
40322
|
+
var SHAPES2 = destruction_default.shapes;
|
|
40323
|
+
var shapeDestruction = (shapeId) => SHAPES2[shapeId];
|
|
39753
40324
|
var FAMILY_OF_MODEL = new Map(Object.entries(FAMILIES).flatMap(([family, entry2]) => entry2.models.map((model) => [model, family])));
|
|
39754
40325
|
var itemDestruction = (modelId) => {
|
|
39755
40326
|
const family = FAMILY_OF_MODEL.get(modelId);
|
|
@@ -39863,9 +40434,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39863
40434
|
root.userData.tessellationTriangles = added;
|
|
39864
40435
|
}
|
|
39865
40436
|
function tagForDestruction(root, options = {}) {
|
|
39866
|
-
const { modelId, weight, size, three } = options;
|
|
40437
|
+
const { modelId, shapeId, weight, size, powder, three } = options;
|
|
39867
40438
|
if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
|
|
39868
|
-
const rule = modelId
|
|
40439
|
+
const rule = modelId !== void 0 ? itemDestruction(modelId) : shapeId !== void 0 ? shapeDestruction(shapeId) : void 0;
|
|
39869
40440
|
if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
|
|
39870
40441
|
const solid2 = [];
|
|
39871
40442
|
root.traverse((object2) => {
|
|
@@ -39880,6 +40451,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
39880
40451
|
return;
|
|
39881
40452
|
}
|
|
39882
40453
|
if (rule?.hollow) mesh3.userData.hollow = true;
|
|
40454
|
+
if (powder) mesh3.userData.destructionPowder = { color: powder.color };
|
|
39883
40455
|
solid2.push(mesh3);
|
|
39884
40456
|
});
|
|
39885
40457
|
if (!rule?.welded || solid2.length < 2) return;
|
|
@@ -42357,10 +42929,41 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42357
42929
|
const m = metal2(9277590);
|
|
42358
42930
|
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]));
|
|
42359
42931
|
};
|
|
42360
|
-
var
|
|
42932
|
+
var opening_default = {
|
|
42933
|
+
families: {
|
|
42934
|
+
drink_can: { style: "pull_tab" },
|
|
42935
|
+
food_tin: { style: "cut_lid" }
|
|
42936
|
+
},
|
|
42937
|
+
models: {
|
|
42938
|
+
empty_can: { style: "none" },
|
|
42939
|
+
canned_beans: { food: "#7a2e1c" },
|
|
42940
|
+
canned_corn_and_peas: { food: "#c7b03c" },
|
|
42941
|
+
canned_sausages: { food: "#c98d5c" },
|
|
42942
|
+
canned_sardines: { style: "peel_lid", food: "#8e979c" },
|
|
42943
|
+
condensed_milk: { food: "#e3cf9c" },
|
|
42944
|
+
energy_drink: { style: "pull_tab" },
|
|
42945
|
+
honey_jar: { style: "screw_cap", food: "#d99a1c" },
|
|
42946
|
+
home_preserves: { style: "screw_cap", food: "#8e1f3a" },
|
|
42947
|
+
cooking_oil: { style: "screw_cap", food: "#e3b830" },
|
|
42948
|
+
water_bottle: { style: "screw_cap" },
|
|
42949
|
+
water_jug_5l: { style: "screw_cap" },
|
|
42950
|
+
dirty_water: { style: "screw_cap" },
|
|
42951
|
+
sports_drink: { style: "screw_cap" },
|
|
42952
|
+
coconut_water: { style: "screw_cap" },
|
|
42953
|
+
cachaca: { style: "screw_cap" },
|
|
42954
|
+
communion_wine: { style: "screw_cap" }
|
|
42955
|
+
}
|
|
42956
|
+
};
|
|
42957
|
+
var FAMILIES2 = opening_default.families;
|
|
42958
|
+
var MODELS = opening_default.models;
|
|
42959
|
+
var asLid = (mesh3) => {
|
|
42960
|
+
mesh3.userData.itemLid = true;
|
|
42961
|
+
return mesh3;
|
|
42962
|
+
};
|
|
42963
|
+
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]));
|
|
42361
42964
|
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]));
|
|
42362
42965
|
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]));
|
|
42363
|
-
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]));
|
|
42966
|
+
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]));
|
|
42364
42967
|
var firstAidKit = ({ mesh: mesh3, bx, torus: torus2, G, plastic }) => {
|
|
42365
42968
|
const r = plastic(11680300);
|
|
42366
42969
|
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]));
|
|
@@ -42520,7 +43123,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42520
43123
|
mesh3(cyl(0.41, 0.41, 0.86, 12), food(9314106), [0, -0.2, 0]),
|
|
42521
43124
|
mesh3(cyl(0.34, 0.445, 0.12, 12), glass(13625070, 0.5), [0, 0.39, 0]),
|
|
42522
43125
|
mesh3(cyl(0.34, 0.34, 0.05, 12), glass(13625070, 0.5), [0, 0.475, 0]),
|
|
42523
|
-
mesh3(cyl(0.38, 0.38, 0.15, 12), metal2(12163644), [0, 0.575, 0]),
|
|
43126
|
+
asLid(mesh3(cyl(0.38, 0.38, 0.15, 12), metal2(12163644), [0, 0.575, 0])),
|
|
42524
43127
|
mesh3(cyl(0.45, 0.45, 0.4, 12), paper2(15327172), [0, -0.2, 0]),
|
|
42525
43128
|
mesh3(cyl(0.452, 0.452, 0.08, 12), cloth2(10172986), [0, -0.2, 0])
|
|
42526
43129
|
);
|
|
@@ -42695,7 +43298,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42695
43298
|
mesh3(cyl(0.395, 0.395, 0.68, 12), glass(14925936, 0.55), [0, -0.16, 0]),
|
|
42696
43299
|
mesh3(cyl(0.37, 0.37, 0.6, 12), food(14260764), [0, -0.2, 0]),
|
|
42697
43300
|
mesh3(cyl(0.3, 0.395, 0.14, 12), glass(14925936, 0.55), [0, 0.25, 0]),
|
|
42698
|
-
mesh3(cyl(0.3, 0.3, 0.18, 12), metal2(7031324), [0, 0.41, 0]),
|
|
43301
|
+
asLid(mesh3(cyl(0.3, 0.3, 0.18, 12), metal2(7031324), [0, 0.41, 0])),
|
|
42699
43302
|
mesh3(cyl(0.4, 0.4, 0.34, 12), paper2(15853512), [0, -0.2, 0]),
|
|
42700
43303
|
mesh3(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.1, 0]),
|
|
42701
43304
|
mesh3(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.3, 0])
|
|
@@ -42781,7 +43384,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42781
43384
|
mesh3(cyl(0.345, 0.345, 1.7, 12), glass(14923824, 0.65), [0, -0.45, 0]),
|
|
42782
43385
|
mesh3(cyl(0.12, 0.345, 0.5, 12), glass(14923824, 0.65), [0, 0.65, 0]),
|
|
42783
43386
|
mesh3(cyl(0.12, 0.12, 0.25, 12), glass(14923824, 0.65), [0, 1.025, 0]),
|
|
42784
|
-
mesh3(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0]),
|
|
43387
|
+
asLid(mesh3(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0])),
|
|
42785
43388
|
mesh3(cyl(0.35, 0.35, 0.8, 12), paper2(15853512), [0, -0.5, 0]),
|
|
42786
43389
|
mesh3(cyl(0.352, 0.352, 0.2, 12), paper2(4160042), [0, -0.5, 0])
|
|
42787
43390
|
);
|
|
@@ -42813,7 +43416,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42813
43416
|
const body = mesh3(bx(1.5, 2.3, 1.5), glass(10473710, 0.75), [0, -0.5, 0]);
|
|
42814
43417
|
const shoulder = mesh3(bx(1.2, 0.45, 1.2), glass(10473710, 0.75), [0, 0.875, 0]);
|
|
42815
43418
|
const neck = mesh3(cyl(0.34, 0.34, 0.3, 12), dark, [0, 1.25, 0]);
|
|
42816
|
-
const cap = mesh3(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]);
|
|
43419
|
+
const cap = asLid(mesh3(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]));
|
|
42817
43420
|
const grip = mesh3(bx(0.16, 0.9, 0.3), dark, [1.07, 0.6, 0]);
|
|
42818
43421
|
const upperBar = mesh3(bx(0.45, 0.16, 0.3), dark, [0.925, 0.97, 0]);
|
|
42819
43422
|
const lowerBar = mesh3(bx(0.45, 0.16, 0.3), dark, [0.925, 0.23, 0]);
|
|
@@ -42826,7 +43429,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42826
43429
|
const label = mesh3(cyl(0.332, 0.332, 0.7, 12), paper2(1448738), [0, -0.12, 0]);
|
|
42827
43430
|
const stripe = mesh3(cyl(0.332, 0.332, 0.1, 12), paper2(15753232), [0, 0.28, 0]);
|
|
42828
43431
|
const collar = mesh3(cyl(0.2, 0.2, 0.04, 12), plastic(15263976), [0, 0.705, 0]);
|
|
42829
|
-
const cap = mesh3(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]);
|
|
43432
|
+
const cap = asLid(mesh3(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]));
|
|
42830
43433
|
return G(bottle2, label, stripe, collar, cap);
|
|
42831
43434
|
};
|
|
42832
43435
|
var milkCarton = ({ mesh: mesh3, bx, cyl, extrude: extrude2, G, paper: paper2, plastic }) => {
|
|
@@ -42865,7 +43468,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42865
43468
|
const goldBand = mesh3(cyl(0.382, 0.382, 0.05, 12), metal2(11896880), [0, 0.075, 0]);
|
|
42866
43469
|
const crossUpright = mesh3(bx(0.06, 0.26, 0.02), paper2(8003371), [0, -0.35, 0.375]);
|
|
42867
43470
|
const crossArm = mesh3(bx(0.18, 0.06, 0.02), paper2(8003371), [0, -0.3, 0.375]);
|
|
42868
|
-
const foil = mesh3(cyl(0.15, 0.15, 0.46, 12), metal2(11896880), [0, 1.27, 0]);
|
|
43471
|
+
const foil = asLid(mesh3(cyl(0.15, 0.15, 0.46, 12), metal2(11896880), [0, 1.27, 0]));
|
|
42869
43472
|
return G(bottle2, label, goldBand, crossUpright, crossArm, foil);
|
|
42870
43473
|
};
|
|
42871
43474
|
var coconutWater = ({ mesh: mesh3, bx, cyl, lathe: lathe2, G, plastic, paper: paper2 }) => {
|
|
@@ -42874,7 +43477,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42874
43477
|
const nut = mesh3(cyl(0.11, 0.11, 0.03, 10), paper2(7031339), [0, -0.12, 0.285], [Math.PI / 2, 0, 0]);
|
|
42875
43478
|
const leafLeft = mesh3(bx(0.2, 0.05, 0.01), paper2(15135448), [-0.12, 0.08, 0.292], [0, 0, 0.5]);
|
|
42876
43479
|
const leafRight = mesh3(bx(0.2, 0.05, 0.01), paper2(15135448), [0.12, 0.08, 0.292], [0, 0, -0.5]);
|
|
42877
|
-
const cap = mesh3(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]);
|
|
43480
|
+
const cap = asLid(mesh3(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]));
|
|
42878
43481
|
return G(bottle2, label, nut, leafLeft, leafRight, cap);
|
|
42879
43482
|
};
|
|
42880
43483
|
var dirtyWater = ({ mesh: mesh3, cyl, lathe: lathe2, G, plastic, paper: paper2, glass, food }) => {
|
|
@@ -42882,7 +43485,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
42882
43485
|
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));
|
|
42883
43486
|
const sediment = mesh3(cyl(0.31, 0.27, 0.12, 8), food(4865314), [0, -0.82, 0]);
|
|
42884
43487
|
const label = mesh3(cyl(0.332, 0.332, 0.22, 12), paper2(13617588), [0, 0.38, 0]);
|
|
42885
|
-
const cap = mesh3(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]);
|
|
43488
|
+
const cap = asLid(mesh3(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]));
|
|
42886
43489
|
return G(bottle2, murk, sediment, label, cap);
|
|
42887
43490
|
};
|
|
42888
43491
|
var painkillers = ({ mesh: mesh3, bx, cyl, G, paper: paper2, metal: metal2, glass }) => {
|
|
@@ -45498,7 +46101,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
45498
46101
|
const materials = kit.materials.slice(first);
|
|
45499
46102
|
if (options.wear) setWear(materials, options.wear);
|
|
45500
46103
|
root.userData.wear = options.wear ?? 0;
|
|
45501
|
-
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 });
|
|
46104
|
+
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 });
|
|
45502
46105
|
markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
|
|
45503
46106
|
return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
|
|
45504
46107
|
}
|
|
@@ -48785,7 +49388,6 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48785
49388
|
var MAX_CASINGS = 120;
|
|
48786
49389
|
var MAX_MAGAZINES = 12;
|
|
48787
49390
|
var LIFETIME_S = 25;
|
|
48788
|
-
var HAND_OFFSET = [0.2, -0.2, -0.42];
|
|
48789
49391
|
var GRAVITY2 = 9.81;
|
|
48790
49392
|
function createBoxFx(options) {
|
|
48791
49393
|
const { THREE, scene, camera: camera2, arsenal, groundAt } = options;
|
|
@@ -48807,6 +49409,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48807
49409
|
let dip = 0;
|
|
48808
49410
|
let reloading = false;
|
|
48809
49411
|
const v = new THREE.Vector3();
|
|
49412
|
+
const v2 = new THREE.Vector3();
|
|
49413
|
+
const eyeFrame = createEyeFrame(THREE, camera2);
|
|
49414
|
+
let rear = 0;
|
|
49415
|
+
let lengthMetres = 0;
|
|
48810
49416
|
const q = new THREE.Quaternion();
|
|
48811
49417
|
const q2 = new THREE.Quaternion();
|
|
48812
49418
|
const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
|
|
@@ -48818,13 +49424,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48818
49424
|
if (!def || BODY_ACTIONS.has(def.id)) return;
|
|
48819
49425
|
model = buildWeaponModel(THREE, def);
|
|
48820
49426
|
const length2 = new THREE.Box3().setFromObject(model).getSize(v).x;
|
|
48821
|
-
|
|
49427
|
+
rear = new THREE.Box3().setFromObject(model).min.x;
|
|
49428
|
+
lengthMetres = length2;
|
|
48822
49429
|
model.quaternion.copy(pointForward);
|
|
48823
49430
|
hand.add(model);
|
|
48824
49431
|
inHand = def.id;
|
|
48825
|
-
hold = holdOfProp(THREE, model, def.category,
|
|
49432
|
+
hold = holdOfProp(THREE, model, def.category, 1);
|
|
49433
|
+
};
|
|
49434
|
+
const handPoint = (x, y, z, out) => {
|
|
49435
|
+
if (!model) return out.set(x, y, z).applyQuaternion(camera2.quaternion).add(camera2.position);
|
|
49436
|
+
return out.set(x, y, z).applyQuaternion(camera2.quaternion).add(model.getWorldPosition(v2));
|
|
48826
49437
|
};
|
|
48827
|
-
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);
|
|
48828
49438
|
const throwBody = (kind, mesh3, from, velocity, radius) => {
|
|
48829
49439
|
if (!(kind === "casing" ? layers.casings : layers.magazines)) {
|
|
48830
49440
|
mesh3.geometry.dispose();
|
|
@@ -48935,10 +49545,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48935
49545
|
update(dt) {
|
|
48936
49546
|
kick *= Math.exp(-dt * 14);
|
|
48937
49547
|
dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8);
|
|
48938
|
-
if (inHand) {
|
|
48939
|
-
|
|
49548
|
+
if (inHand && model) {
|
|
49549
|
+
const handgun = def?.category === "pistol" || def?.category === "revolver";
|
|
49550
|
+
placeOnBody({ THREE, hand, holder: model, frame: options.bodyHold?.() ?? eyeFrame(), rear, lengthMetres, handgun, right: hold?.right ?? null, left: hold?.left ?? null });
|
|
49551
|
+
hand.position.add(v.set(0, -0.12 * dip, 0.05 * kick).applyQuaternion(camera2.quaternion));
|
|
48940
49552
|
q.setFromEuler(new THREE.Euler(0.14 * kick - 0.25 * dip, 0, 0.6 * dip));
|
|
48941
|
-
hand.quaternion.
|
|
49553
|
+
hand.quaternion.multiply(q);
|
|
48942
49554
|
}
|
|
48943
49555
|
for (const body of [...bodies]) {
|
|
48944
49556
|
stepBody(body, dt);
|
|
@@ -57659,6 +58271,11 @@ void main(){
|
|
|
57659
58271
|
var MAX_CONFORM_STEPS = 48;
|
|
57660
58272
|
var MIN_CONFORM_STEPS = 16;
|
|
57661
58273
|
var BLOOD_GROW_RATE = 1.2;
|
|
58274
|
+
var BLOOD_START_GROWTH = 0.05;
|
|
58275
|
+
var DEFAULT_MAX_GROUND_MARKS = 64;
|
|
58276
|
+
var DEFAULT_MAX_ROCKS = 512;
|
|
58277
|
+
var DEFAULT_MAX_BLOOD = 256;
|
|
58278
|
+
var MAX_SPARE_GROUND_GEOMETRIES = 16;
|
|
57662
58279
|
function createMarkLayer(THREE, options) {
|
|
57663
58280
|
const group = new THREE.Group();
|
|
57664
58281
|
const textures = createMarkTextures(THREE, options.createCanvas);
|
|
@@ -57682,110 +58299,168 @@ void main(){
|
|
|
57682
58299
|
const bloodMaterial = new THREE.MeshStandardMaterial({ color: 6031882, roughness: 0.15, metalness: 0.1, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 });
|
|
57683
58300
|
const bloodGeometry = new THREE.CircleGeometry(0.5, 9).rotateX(-Math.PI / 2);
|
|
57684
58301
|
owned.push(bloodMaterial, bloodGeometry);
|
|
57685
|
-
const
|
|
57686
|
-
const
|
|
57687
|
-
|
|
57688
|
-
|
|
57689
|
-
|
|
57690
|
-
|
|
57691
|
-
material = new THREE.MeshStandardMaterial({ color: color2, roughness: 0.9, metalness: 0, flatShading: true });
|
|
57692
|
-
rockMaterials.set(color2, material);
|
|
57693
|
-
owned.push(material);
|
|
58302
|
+
const maxGroundMarks = options.maxGroundMarks ?? DEFAULT_MAX_GROUND_MARKS;
|
|
58303
|
+
const instanced = {};
|
|
58304
|
+
function rockMaterial() {
|
|
58305
|
+
if (!instanced.rockMaterial) {
|
|
58306
|
+
instanced.rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.9, metalness: 0, flatShading: true });
|
|
58307
|
+
owned.push(instanced.rockMaterial);
|
|
57694
58308
|
}
|
|
57695
|
-
return
|
|
58309
|
+
return instanced.rockMaterial;
|
|
58310
|
+
}
|
|
58311
|
+
function bloodMesh() {
|
|
58312
|
+
return instanced.blood ?? (instanced.blood = attach(createInstanced(bloodGeometry, bloodMaterial, options.maxBlood ?? DEFAULT_MAX_BLOOD)));
|
|
58313
|
+
}
|
|
58314
|
+
function rockMesh() {
|
|
58315
|
+
return instanced.rock ?? (instanced.rock = attach(createInstanced(options.rockGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
58316
|
+
}
|
|
58317
|
+
function plateMesh() {
|
|
58318
|
+
return instanced.plate ?? (instanced.plate = attach(createInstanced(options.plateGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
58319
|
+
}
|
|
58320
|
+
function attach(mesh3, castShadow = false) {
|
|
58321
|
+
mesh3.castShadow = castShadow;
|
|
58322
|
+
group.add(mesh3);
|
|
58323
|
+
return mesh3;
|
|
58324
|
+
}
|
|
58325
|
+
const groundMarks = [];
|
|
58326
|
+
const spareGeometries = [];
|
|
58327
|
+
const blood = [];
|
|
58328
|
+
const placed = { blood: 0, rock: 0, plate: 0 };
|
|
58329
|
+
const matrix = new THREE.Matrix4();
|
|
58330
|
+
const position = new THREE.Vector3();
|
|
58331
|
+
const quaternion2 = new THREE.Quaternion();
|
|
58332
|
+
const euler = new THREE.Euler();
|
|
58333
|
+
const scale = new THREE.Vector3();
|
|
58334
|
+
const color2 = new THREE.Color();
|
|
58335
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
58336
|
+
function createInstanced(geometry, material, capacity) {
|
|
58337
|
+
const mesh3 = new THREE.InstancedMesh(geometry, material, capacity);
|
|
58338
|
+
mesh3.count = 0;
|
|
58339
|
+
mesh3.frustumCulled = false;
|
|
58340
|
+
mesh3.receiveShadow = true;
|
|
58341
|
+
return mesh3;
|
|
58342
|
+
}
|
|
58343
|
+
function takeSpare(vertexCount) {
|
|
58344
|
+
const found = spareGeometries.findIndex((geometry) => geometry.getAttribute("position").count === vertexCount);
|
|
58345
|
+
return found < 0 ? void 0 : spareGeometries.splice(found, 1)[0];
|
|
58346
|
+
}
|
|
58347
|
+
function evictOldestGroundMark() {
|
|
58348
|
+
const oldest = groundMarks.shift();
|
|
58349
|
+
group.remove(oldest.mesh);
|
|
58350
|
+
spareGeometries.push(oldest.mesh.geometry);
|
|
58351
|
+
if (spareGeometries.length > MAX_SPARE_GROUND_GEOMETRIES) spareGeometries.shift().dispose();
|
|
57696
58352
|
}
|
|
57697
58353
|
function conform(mark, material) {
|
|
57698
58354
|
const radius = mark.radius;
|
|
57699
58355
|
const steps = Math.max(MIN_CONFORM_STEPS, Math.min(MAX_CONFORM_STEPS, Math.round(radius * 8)));
|
|
57700
|
-
|
|
57701
|
-
const
|
|
58356
|
+
if (groundMarks.length >= maxGroundMarks) evictOldestGroundMark();
|
|
58357
|
+
const geometry = takeSpare((steps + 1) * (steps + 1)) ?? new THREE.PlaneGeometry(2, 2, steps, steps).rotateX(-Math.PI / 2);
|
|
58358
|
+
const grid = geometry.attributes.position;
|
|
58359
|
+
const cell = 2 / steps;
|
|
57702
58360
|
const cos2 = Math.cos(mark.rotation);
|
|
57703
58361
|
const sin2 = Math.sin(mark.rotation);
|
|
57704
|
-
for (let index = 0; index <
|
|
57705
|
-
const localX =
|
|
57706
|
-
const localZ =
|
|
58362
|
+
for (let index = 0; index < grid.count; index++) {
|
|
58363
|
+
const localX = Math.fround(index % (steps + 1) * cell - 1) * radius;
|
|
58364
|
+
const localZ = Math.fround(Math.floor(index / (steps + 1)) * cell - 1) * radius;
|
|
57707
58365
|
const worldX = mark.x + localX * cos2 - localZ * sin2;
|
|
57708
58366
|
const worldZ = mark.z + localX * sin2 + localZ * cos2;
|
|
57709
|
-
|
|
58367
|
+
grid.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
|
|
57710
58368
|
}
|
|
58369
|
+
grid.needsUpdate = true;
|
|
57711
58370
|
geometry.computeVertexNormals();
|
|
58371
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
58372
|
+
geometry.boundingSphere = null;
|
|
58373
|
+
geometry.boundingBox = null;
|
|
57712
58374
|
const mesh3 = new THREE.Mesh(geometry, material);
|
|
57713
58375
|
mesh3.receiveShadow = true;
|
|
57714
|
-
|
|
58376
|
+
group.add(mesh3);
|
|
58377
|
+
groundMarks.push({ mesh: mesh3, steps });
|
|
57715
58378
|
}
|
|
57716
58379
|
function addRock(mark) {
|
|
57717
|
-
const mesh3 =
|
|
57718
|
-
|
|
57719
|
-
|
|
57720
|
-
|
|
57721
|
-
mesh3.
|
|
57722
|
-
mesh3.
|
|
57723
|
-
|
|
58380
|
+
const mesh3 = mark.plate ? plateMesh() : rockMesh();
|
|
58381
|
+
const slot = mark.plate ? "plate" : "rock";
|
|
58382
|
+
const index = placed[slot]++ % mesh3.instanceMatrix.count;
|
|
58383
|
+
quaternion2.setFromEuler(euler.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]));
|
|
58384
|
+
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])));
|
|
58385
|
+
mesh3.setColorAt(index, color2.set(mark.color));
|
|
58386
|
+
mesh3.count = Math.min(placed[slot], mesh3.instanceMatrix.count);
|
|
58387
|
+
mesh3.instanceMatrix.needsUpdate = true;
|
|
58388
|
+
mesh3.instanceColor.needsUpdate = true;
|
|
58389
|
+
}
|
|
58390
|
+
function writeBlood(index, drawn, growth) {
|
|
58391
|
+
quaternion2.setFromAxisAngle(up, drawn.rotation);
|
|
58392
|
+
const mesh3 = bloodMesh();
|
|
58393
|
+
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)));
|
|
58394
|
+
mesh3.instanceMatrix.needsUpdate = true;
|
|
57724
58395
|
}
|
|
57725
58396
|
function addBlood(mark) {
|
|
57726
|
-
const
|
|
57727
|
-
|
|
57728
|
-
|
|
57729
|
-
|
|
57730
|
-
|
|
57731
|
-
mesh3.scale.multiplyScalar(0.05);
|
|
57732
|
-
blood.push({ mesh: mesh3, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
|
|
57733
|
-
return mesh3;
|
|
58397
|
+
const index = placed.blood++ % bloodMesh().instanceMatrix.count;
|
|
58398
|
+
const drawn = { position: [mark.x, mark.y, mark.z], rotation: mark.rotation, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 };
|
|
58399
|
+
blood[index] = drawn;
|
|
58400
|
+
writeBlood(index, drawn, BLOOD_START_GROWTH);
|
|
58401
|
+
bloodMesh().count = Math.min(placed.blood, bloodMesh().instanceMatrix.count);
|
|
57734
58402
|
}
|
|
57735
58403
|
function build(mark) {
|
|
57736
|
-
if (mark.kind === "scorch")
|
|
57737
|
-
if (mark.kind === "dirt")
|
|
57738
|
-
if (mark.kind === "rock")
|
|
57739
|
-
|
|
57740
|
-
}
|
|
57741
|
-
function
|
|
57742
|
-
for (const
|
|
57743
|
-
|
|
57744
|
-
|
|
57745
|
-
|
|
57746
|
-
|
|
58404
|
+
if (mark.kind === "scorch") conform(mark, scorchMaterials[mark.variant]);
|
|
58405
|
+
else if (mark.kind === "dirt") conform(mark, dirtMaterial);
|
|
58406
|
+
else if (mark.kind === "rock") addRock(mark);
|
|
58407
|
+
else addBlood(mark);
|
|
58408
|
+
}
|
|
58409
|
+
function clearMarks() {
|
|
58410
|
+
for (const drawn of groundMarks) group.remove(drawn.mesh);
|
|
58411
|
+
for (const drawn of groundMarks) drawn.mesh.geometry.dispose();
|
|
58412
|
+
groundMarks.length = 0;
|
|
58413
|
+
for (const geometry of spareGeometries) geometry.dispose();
|
|
58414
|
+
spareGeometries.length = 0;
|
|
57747
58415
|
blood.length = 0;
|
|
57748
|
-
|
|
58416
|
+
placed.blood = placed.rock = placed.plate = 0;
|
|
58417
|
+
for (const mesh3 of [instanced.blood, instanced.rock, instanced.plate]) if (mesh3) mesh3.count = 0;
|
|
57749
58418
|
}
|
|
57750
58419
|
return {
|
|
57751
58420
|
group,
|
|
57752
58421
|
handle(event) {
|
|
57753
|
-
if (event.type === "clear")
|
|
57754
|
-
|
|
57755
|
-
|
|
57756
|
-
|
|
57757
|
-
|
|
57758
|
-
|
|
58422
|
+
if (event.type === "clear") clearMarks();
|
|
58423
|
+
else build(event.mark);
|
|
58424
|
+
},
|
|
58425
|
+
prepare() {
|
|
58426
|
+
bloodMesh();
|
|
58427
|
+
rockMesh();
|
|
58428
|
+
plateMesh();
|
|
57759
58429
|
},
|
|
57760
58430
|
update(dt) {
|
|
57761
|
-
for (
|
|
57762
|
-
|
|
58431
|
+
for (let index = 0; index < blood.length; index++) {
|
|
58432
|
+
const drawn = blood[index];
|
|
58433
|
+
if (!drawn || drawn.age > 1) continue;
|
|
57763
58434
|
drawn.age += dt * BLOOD_GROW_RATE;
|
|
57764
|
-
|
|
57765
|
-
drawn.mesh.scale.set(drawn.scaleX * Math.max(0.05, ease3), 1, drawn.scaleZ * Math.max(0.05, ease3));
|
|
58435
|
+
writeBlood(index, drawn, Math.max(BLOOD_START_GROWTH, 1 - Math.pow(1 - Math.min(1, drawn.age), 2.5)));
|
|
57766
58436
|
}
|
|
57767
58437
|
},
|
|
57768
|
-
count: () =>
|
|
58438
|
+
count: () => groundMarks.length + [instanced.blood, instanced.rock, instanced.plate].filter((mesh3) => mesh3 && mesh3.count > 0).length,
|
|
57769
58439
|
dispose() {
|
|
57770
|
-
|
|
58440
|
+
clearMarks();
|
|
58441
|
+
for (const mesh3 of [instanced.blood, instanced.rock, instanced.plate]) mesh3?.dispose();
|
|
57771
58442
|
for (const item of owned) item.dispose();
|
|
57772
58443
|
}
|
|
57773
58444
|
};
|
|
57774
58445
|
}
|
|
57775
58446
|
var SPHERE_DEFAULT_WIDTH = 8;
|
|
57776
58447
|
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
58448
|
+
var MAX_PARKED_MESHES_PER_KIND = 256;
|
|
57777
58449
|
function geometryOf(THREE, particle) {
|
|
57778
58450
|
switch (particle.shape) {
|
|
57779
58451
|
case "cone":
|
|
57780
|
-
return new THREE.ConeGeometry(
|
|
58452
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, false);
|
|
57781
58453
|
case "cone_open":
|
|
57782
|
-
return new THREE.ConeGeometry(
|
|
58454
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, true);
|
|
57783
58455
|
case "sphere":
|
|
57784
|
-
return new THREE.SphereGeometry(
|
|
58456
|
+
return new THREE.SphereGeometry(1, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
57785
58457
|
case "icosahedron":
|
|
57786
|
-
return new THREE.IcosahedronGeometry(
|
|
58458
|
+
return new THREE.IcosahedronGeometry(1, particle.segments);
|
|
57787
58459
|
}
|
|
57788
58460
|
}
|
|
58461
|
+
function isCone(particle) {
|
|
58462
|
+
return particle.shape === "cone" || particle.shape === "cone_open";
|
|
58463
|
+
}
|
|
57789
58464
|
function materialOf(THREE, particle) {
|
|
57790
58465
|
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
57791
58466
|
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
@@ -57796,51 +58471,82 @@ void main(){
|
|
|
57796
58471
|
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
57797
58472
|
);
|
|
57798
58473
|
}
|
|
58474
|
+
function geometryKeyOf(particle) {
|
|
58475
|
+
return `${particle.shape}:${particle.segments}:${particle.segments2}`;
|
|
58476
|
+
}
|
|
57799
58477
|
function createMeshLayer(THREE, pool) {
|
|
57800
58478
|
const group = new THREE.Group();
|
|
57801
58479
|
group.name = "workshop-meshes";
|
|
57802
58480
|
const drawn = /* @__PURE__ */ new Map();
|
|
58481
|
+
const geometries = /* @__PURE__ */ new Map();
|
|
58482
|
+
const parked = /* @__PURE__ */ new Map();
|
|
57803
58483
|
const frame2 = new THREE.Quaternion();
|
|
57804
58484
|
const euler = new THREE.Euler();
|
|
57805
58485
|
const turn2 = new THREE.Quaternion();
|
|
57806
58486
|
const offset = new THREE.Vector3();
|
|
57807
|
-
|
|
57808
|
-
|
|
57809
|
-
|
|
57810
|
-
|
|
58487
|
+
let stamp = 0;
|
|
58488
|
+
function geometryFor(particle) {
|
|
58489
|
+
const key2 = geometryKeyOf(particle);
|
|
58490
|
+
let geometry = geometries.get(key2);
|
|
58491
|
+
if (!geometry) {
|
|
58492
|
+
geometry = geometryOf(THREE, particle);
|
|
58493
|
+
geometries.set(key2, geometry);
|
|
58494
|
+
}
|
|
58495
|
+
return geometry;
|
|
58496
|
+
}
|
|
58497
|
+
function acquire(particle) {
|
|
58498
|
+
const parkingKey = `${geometryKeyOf(particle)}|${particle.lit}|${particle.additive}`;
|
|
58499
|
+
const mesh3 = parked.get(parkingKey)?.pop() ?? new THREE.Mesh(geometryFor(particle), materialOf(THREE, particle));
|
|
58500
|
+
mesh3.material.color.set(particle.color);
|
|
58501
|
+
mesh3.frustumCulled = false;
|
|
58502
|
+
mesh3.renderOrder = particle.additive ? 5 : 3;
|
|
58503
|
+
group.add(mesh3);
|
|
58504
|
+
return { mesh: mesh3, parkingKey, stamp };
|
|
58505
|
+
}
|
|
58506
|
+
function park(entry2) {
|
|
58507
|
+
entry2.mesh.removeFromParent();
|
|
58508
|
+
let free = parked.get(entry2.parkingKey);
|
|
58509
|
+
if (!free) {
|
|
58510
|
+
free = [];
|
|
58511
|
+
parked.set(entry2.parkingKey, free);
|
|
58512
|
+
}
|
|
58513
|
+
if (free.length < MAX_PARKED_MESHES_PER_KIND) free.push(entry2.mesh);
|
|
58514
|
+
else entry2.mesh.material.dispose();
|
|
57811
58515
|
}
|
|
57812
58516
|
return {
|
|
57813
58517
|
group,
|
|
57814
58518
|
sync() {
|
|
57815
|
-
|
|
58519
|
+
stamp++;
|
|
57816
58520
|
for (const particle of pool.particles) {
|
|
57817
|
-
|
|
57818
|
-
|
|
57819
|
-
|
|
57820
|
-
|
|
57821
|
-
mesh3.frustumCulled = false;
|
|
57822
|
-
mesh3.renderOrder = particle.additive ? 5 : 3;
|
|
57823
|
-
drawn.set(particle.id, mesh3);
|
|
57824
|
-
group.add(mesh3);
|
|
58521
|
+
let entry2 = drawn.get(particle.id);
|
|
58522
|
+
if (!entry2) {
|
|
58523
|
+
entry2 = acquire(particle);
|
|
58524
|
+
drawn.set(particle.id, entry2);
|
|
57825
58525
|
}
|
|
58526
|
+
entry2.stamp = stamp;
|
|
58527
|
+
const mesh3 = entry2.mesh;
|
|
57826
58528
|
frame2.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
57827
58529
|
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame2);
|
|
57828
58530
|
mesh3.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
57829
58531
|
turn2.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
57830
58532
|
mesh3.quaternion.copy(frame2).multiply(turn2);
|
|
57831
|
-
mesh3.scale.
|
|
58533
|
+
mesh3.scale.set(particle.radius * particle.scale, (isCone(particle) ? particle.height : particle.radius) * particle.scale, particle.radius * particle.scale);
|
|
57832
58534
|
mesh3.material.opacity = particle.opacity;
|
|
57833
58535
|
}
|
|
57834
|
-
for (const [id,
|
|
57835
|
-
if (
|
|
57836
|
-
|
|
58536
|
+
for (const [id, entry2] of drawn) {
|
|
58537
|
+
if (entry2.stamp === stamp) continue;
|
|
58538
|
+
park(entry2);
|
|
57837
58539
|
drawn.delete(id);
|
|
57838
58540
|
}
|
|
57839
58541
|
return pool.count;
|
|
57840
58542
|
},
|
|
57841
58543
|
dispose() {
|
|
57842
|
-
for (const
|
|
58544
|
+
for (const entry2 of drawn.values()) park(entry2);
|
|
57843
58545
|
drawn.clear();
|
|
58546
|
+
for (const free of parked.values()) for (const mesh3 of free) mesh3.material.dispose();
|
|
58547
|
+
parked.clear();
|
|
58548
|
+
for (const geometry of geometries.values()) geometry.dispose();
|
|
58549
|
+
geometries.clear();
|
|
57844
58550
|
group.removeFromParent();
|
|
57845
58551
|
}
|
|
57846
58552
|
};
|
|
@@ -58408,7 +59114,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58408
59114
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
58409
59115
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
58410
59116
|
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
58411
|
-
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry });
|
|
59117
|
+
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
58412
59118
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
58413
59119
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
58414
59120
|
const overlay = options.overlay ? createScreenOverlay(options.overlay) : void 0;
|
|
@@ -58434,6 +59140,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58434
59140
|
const position = options.camera.position;
|
|
58435
59141
|
world.setCamera([position.x, position.y, position.z]);
|
|
58436
59142
|
}
|
|
59143
|
+
function attachWorkshopLayers() {
|
|
59144
|
+
const engine = world.peekWorkshop();
|
|
59145
|
+
if (!engine || engine === workshopEngine) return;
|
|
59146
|
+
workshopLayers?.dispose();
|
|
59147
|
+
if (workshopLayers) root.remove(workshopLayers.group);
|
|
59148
|
+
workshopEngine = engine;
|
|
59149
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
59150
|
+
root.add(workshopLayers.group);
|
|
59151
|
+
applyLayers();
|
|
59152
|
+
}
|
|
58437
59153
|
function syncAll() {
|
|
58438
59154
|
lastDrawn.puffs = puffs.sync();
|
|
58439
59155
|
lastDrawn.sparks = sparks.sync();
|
|
@@ -58442,15 +59158,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58442
59158
|
lastDrawn.shocks = shocks.sync();
|
|
58443
59159
|
lastDrawn.sprites = sprites.sync();
|
|
58444
59160
|
lights.sync();
|
|
58445
|
-
|
|
58446
|
-
if (engine && engine !== workshopEngine) {
|
|
58447
|
-
workshopLayers?.dispose();
|
|
58448
|
-
if (workshopLayers) root.remove(workshopLayers.group);
|
|
58449
|
-
workshopEngine = engine;
|
|
58450
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
58451
|
-
root.add(workshopLayers.group);
|
|
58452
|
-
applyLayers();
|
|
58453
|
-
}
|
|
59161
|
+
attachWorkshopLayers();
|
|
58454
59162
|
const workshopCalls = workshopLayers?.sync() ?? 0;
|
|
58455
59163
|
const glow = pools.lights.glow;
|
|
58456
59164
|
uniforms.uGlowPos.value.set(glow.x, glow.y, glow.z);
|
|
@@ -58503,6 +59211,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58503
59211
|
},
|
|
58504
59212
|
warmUpProbes() {
|
|
58505
59213
|
releaseAnchors();
|
|
59214
|
+
marks.prepare();
|
|
59215
|
+
world.workshop();
|
|
59216
|
+
attachWorkshopLayers();
|
|
58506
59217
|
const meshes = createMeshLayerProbes(THREE);
|
|
58507
59218
|
for (const mesh3 of meshes) mesh3.frustumCulled = false;
|
|
58508
59219
|
const group = new THREE.Group();
|
|
@@ -58569,6 +59280,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58569
59280
|
|
|
58570
59281
|
// src/fx/heavy.ts
|
|
58571
59282
|
var PI14 = Math.PI;
|
|
59283
|
+
var ROCKET_LIGHT_NAME = "rocket-light";
|
|
59284
|
+
var rocketsInFlight = /* @__PURE__ */ new WeakMap();
|
|
59285
|
+
function createRocketLight(THREE) {
|
|
59286
|
+
const light = new THREE.PointLight(16751168, 0, 2.5);
|
|
59287
|
+
light.name = ROCKET_LIGHT_NAME;
|
|
59288
|
+
light.position.set(0, -50, 0);
|
|
59289
|
+
return light;
|
|
59290
|
+
}
|
|
58572
59291
|
var flameMaterials = (kit) => {
|
|
58573
59292
|
const { THREE } = kit;
|
|
58574
59293
|
return [
|
|
@@ -58598,8 +59317,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58598
59317
|
exhaust.rotation.z = PI14 / 2;
|
|
58599
59318
|
exhaust.position.x = -0.08;
|
|
58600
59319
|
rocket.add(exhaust);
|
|
58601
|
-
const light =
|
|
58602
|
-
|
|
59320
|
+
const light = context.rocketLight;
|
|
59321
|
+
light.intensity = 3;
|
|
59322
|
+
rocketsInFlight.set(light, (rocketsInFlight.get(light) ?? 0) + 1);
|
|
58603
59323
|
context.every(
|
|
58604
59324
|
1.6,
|
|
58605
59325
|
(progress, dt) => {
|
|
@@ -58607,6 +59327,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58607
59327
|
const distance3 = 1.2 * flight + (flight > 0.25 ? 9 * (flight - 0.25) ** 2 : 0);
|
|
58608
59328
|
rocket.position.copy(muzzle2).addScaledVector(forward, distance3);
|
|
58609
59329
|
rocket.rotation.x += 0.25 * dt * 60;
|
|
59330
|
+
light.position.copy(rocket.position);
|
|
58610
59331
|
exhaust.scale.set(1, 0.6 + random() * 0.8, 1);
|
|
58611
59332
|
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);
|
|
58612
59333
|
},
|
|
@@ -58614,7 +59335,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58614
59335
|
rocket.removeFromParent();
|
|
58615
59336
|
exhaust.geometry.dispose();
|
|
58616
59337
|
exhaust.material.dispose();
|
|
58617
|
-
|
|
59338
|
+
const remaining = (rocketsInFlight.get(light) ?? 1) - 1;
|
|
59339
|
+
rocketsInFlight.set(light, remaining);
|
|
59340
|
+
if (remaining > 0) return;
|
|
59341
|
+
light.intensity = 0;
|
|
59342
|
+
light.position.set(0, -50, 0);
|
|
58618
59343
|
}
|
|
58619
59344
|
);
|
|
58620
59345
|
}
|
|
@@ -59429,11 +60154,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59429
60154
|
|
|
59430
60155
|
// src/fx/viewmodel.ts
|
|
59431
60156
|
var PI16 = Math.PI;
|
|
59432
|
-
var HAND_OFFSET2 = [0.2, -0.2, -0.42];
|
|
59433
|
-
var HANDGUN_OFFSET = [0.1, -0.12, -0.44];
|
|
59434
60157
|
var HEAT_DISPLAY_MAX_FALLBACK = 120;
|
|
59435
|
-
var WALL_PULL = { margin: 0.03, max:
|
|
59436
|
-
var HAND_IN_VIEW = { floor: -0.88, max: 0.
|
|
60158
|
+
var WALL_PULL = { margin: 0.03, max: 1.2, rate: 14, probe: 3 };
|
|
60159
|
+
var HAND_IN_VIEW = { floor: -0.88, max: 0.3, rate: 10 };
|
|
59437
60160
|
var LAUNCH_VOLUME = 0.6;
|
|
59438
60161
|
var fittedOf2 = (parts) => parts.map((part) => ({ id: part.id, mount: part.mount, ...part.integral ? { integral: true } : {}, ...part.on === void 0 ? {} : { on: part.on } }));
|
|
59439
60162
|
var collapsedStockPose = (move2) => {
|
|
@@ -59463,6 +60186,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59463
60186
|
const hand = new THREE.Group();
|
|
59464
60187
|
hand.name = VIEWMODEL_NODE_NAME;
|
|
59465
60188
|
scene.add(hand);
|
|
60189
|
+
const rocketLight = createRocketLight(THREE);
|
|
60190
|
+
scene.add(rocketLight);
|
|
59466
60191
|
const holder = new THREE.Group();
|
|
59467
60192
|
holder.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), PI16 / 2);
|
|
59468
60193
|
hand.add(holder);
|
|
@@ -59519,7 +60244,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59519
60244
|
positionOf: (part) => worldPositionOf(current, part, new THREE.Vector3()),
|
|
59520
60245
|
orientation: () => current.model.root.getWorldQuaternion(new THREE.Quaternion()),
|
|
59521
60246
|
every: (seconds, step, done) => void tasks.push({ delay: 0, seconds, elapsed: 0, step, done }),
|
|
59522
|
-
groundAt
|
|
60247
|
+
groundAt,
|
|
60248
|
+
rocketLight
|
|
59523
60249
|
});
|
|
59524
60250
|
const equip = (weapon) => {
|
|
59525
60251
|
const def = arsenal.weapons.get(weapon);
|
|
@@ -59532,14 +60258,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59532
60258
|
reach = 0;
|
|
59533
60259
|
pull = 0;
|
|
59534
60260
|
lift = 0;
|
|
59535
|
-
handHeight = 0;
|
|
59536
|
-
handDepth = 0;
|
|
59537
60261
|
rigStock(THREE, model, def);
|
|
59538
60262
|
rigUnderbarrel(THREE, model, def);
|
|
59539
|
-
const
|
|
59540
|
-
const
|
|
59541
|
-
|
|
59542
|
-
holder.position.set(0, 0, Math.min(0.25, 0.25 * length2 * scale));
|
|
60263
|
+
const modelBox = new THREE.Box3().setFromObject(model.root);
|
|
60264
|
+
const length2 = modelBox.getSize(new THREE.Vector3()).x;
|
|
60265
|
+
modelRear = modelBox.min.x;
|
|
59543
60266
|
holder.add(model.root);
|
|
59544
60267
|
const caliber = caliberOf(arsenal, def);
|
|
59545
60268
|
const dims = caliber?.cartridge;
|
|
@@ -59551,7 +60274,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59551
60274
|
def,
|
|
59552
60275
|
caliber,
|
|
59553
60276
|
model,
|
|
59554
|
-
holderScale:
|
|
60277
|
+
holderScale: 1,
|
|
59555
60278
|
lengthMetres: length2,
|
|
59556
60279
|
caseType: caliber?.caseType ?? "rifle",
|
|
59557
60280
|
soundProfile: soundProfileOf(weapon, model.parts.family),
|
|
@@ -59581,7 +60304,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59581
60304
|
applyCondition(kit, model, condition);
|
|
59582
60305
|
lastAmmo = options.tool?.().ammo ?? 0;
|
|
59583
60306
|
hand.updateMatrixWorld(true);
|
|
59584
|
-
hold = holdOfModel(THREE, model,
|
|
60307
|
+
hold = holdOfModel(THREE, model, 1);
|
|
59585
60308
|
hold = { ...hold, works: handWorkOf(THREE, model, weapon, hold.right) };
|
|
59586
60309
|
triggerRest = model.parts.trigger?.rotation.z ?? 0;
|
|
59587
60310
|
lastShot = -Infinity;
|
|
@@ -59601,8 +60324,6 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59601
60324
|
let reach = 0;
|
|
59602
60325
|
let pull = 0;
|
|
59603
60326
|
let lift = 0;
|
|
59604
|
-
let handHeight = 0;
|
|
59605
|
-
let handDepth = 0;
|
|
59606
60327
|
const ahead = new THREE.Vector3();
|
|
59607
60328
|
const up = new THREE.Vector3();
|
|
59608
60329
|
const seen = new THREE.Vector3();
|
|
@@ -59615,19 +60336,31 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59615
60336
|
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));
|
|
59616
60337
|
return farthest;
|
|
59617
60338
|
};
|
|
60339
|
+
let modelRear = 0;
|
|
60340
|
+
const eyeFrame = createEyeFrame(THREE, camera2);
|
|
60341
|
+
const liftToKeepInView = () => {
|
|
60342
|
+
if (!hold || !session || !("fov" in camera2)) return 0;
|
|
60343
|
+
const tanHalf = Math.tan((camera2.fov ?? 70) * PI16 / 360);
|
|
60344
|
+
eyeTurn.copy(camera2.quaternion).invert();
|
|
60345
|
+
const rest2 = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), 0);
|
|
60346
|
+
let needed = 0;
|
|
60347
|
+
for (const [anchor, pose] of [[hold.right, rest2.R], [hold.left, rest2.L]]) {
|
|
60348
|
+
if (!anchor || !pose || anchor.part !== session.model.root) continue;
|
|
60349
|
+
seen.set(...pose.position).sub(camera2.position).applyQuaternion(eyeTurn);
|
|
60350
|
+
if (-seen.z > 0.05) needed = Math.max(needed, HAND_IN_VIEW.floor * -seen.z * tanHalf - seen.y);
|
|
60351
|
+
}
|
|
60352
|
+
return Math.max(0, Math.min(HAND_IN_VIEW.max, needed));
|
|
60353
|
+
};
|
|
59618
60354
|
const place = (dt = 0) => {
|
|
59619
|
-
|
|
59620
|
-
const
|
|
59621
|
-
|
|
59622
|
-
hand.
|
|
59623
|
-
|
|
59624
|
-
|
|
59625
|
-
|
|
59626
|
-
lift += (want2 - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
59627
|
-
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera2.quaternion), lift);
|
|
59628
|
-
}
|
|
60355
|
+
if (!session) return;
|
|
60356
|
+
const family = session.model.parts.family;
|
|
60357
|
+
session.holderScale = 1;
|
|
60358
|
+
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 });
|
|
60359
|
+
hand.updateMatrixWorld(true);
|
|
60360
|
+
lift += (liftToKeepInView() - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
60361
|
+
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera2.quaternion), lift);
|
|
59629
60362
|
hand.updateMatrixWorld(true);
|
|
59630
|
-
if (!options.contact
|
|
60363
|
+
if (!options.contact) return;
|
|
59631
60364
|
ahead.set(0, 0, -1).applyQuaternion(camera2.quaternion);
|
|
59632
60365
|
if (reach === 0) reach = Math.max(0.01, measureReach(session));
|
|
59633
60366
|
const free = options.contact(camera2.position, ahead, WALL_PULL.probe);
|
|
@@ -59741,12 +60474,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59741
60474
|
const targets = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), magazineHand, hold.works ? workWeights(hold.works, player?.running ?? []) : void 0);
|
|
59742
60475
|
const trigger = session.model.parts.trigger;
|
|
59743
60476
|
if (trigger) targets.fingers = { index: indexCurl(trigger.rotation.z - triggerRest, clock - lastShot) };
|
|
59744
|
-
|
|
59745
|
-
seen.set(...targets.R.position).sub(camera2.position).applyQuaternion(eyeTurn.copy(camera2.quaternion).invert());
|
|
59746
|
-
handDepth = -seen.z;
|
|
59747
|
-
handHeight = handDepth > 0.05 ? seen.y / handDepth / Math.tan((camera2.fov ?? 70) * PI16 / 360) : 0;
|
|
59748
|
-
}
|
|
59749
|
-
return targets;
|
|
60477
|
+
return limitSupportSpan(targets);
|
|
59750
60478
|
},
|
|
59751
60479
|
get inHand() {
|
|
59752
60480
|
return session?.id ?? null;
|
|
@@ -59765,6 +60493,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59765
60493
|
particles.dispose();
|
|
59766
60494
|
ownVfx?.dispose();
|
|
59767
60495
|
hand.removeFromParent();
|
|
60496
|
+
rocketLight.removeFromParent();
|
|
60497
|
+
rocketLight.dispose();
|
|
59768
60498
|
}
|
|
59769
60499
|
};
|
|
59770
60500
|
}
|
|
@@ -59789,6 +60519,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
59789
60519
|
...options.attachmentModels ? { attachmentModels: options.attachmentModels } : {},
|
|
59790
60520
|
...options.raycast ? { raycast: options.raycast } : {},
|
|
59791
60521
|
...options.contact ? { contact: options.contact } : {},
|
|
60522
|
+
...options.bodyHold ? { bodyHold: options.bodyHold } : {},
|
|
59792
60523
|
...options.effects ? { effects: options.effects } : {},
|
|
59793
60524
|
...options.flyRocket === void 0 ? {} : { flyRocket: options.flyRocket }
|
|
59794
60525
|
});
|
|
@@ -60564,7 +61295,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60564
61295
|
var P13_VALVE_MESH = "valve";
|
|
60565
61296
|
var P13_VALVE_COLOR = 12095802;
|
|
60566
61297
|
var P13_WALL = 25e-4;
|
|
60567
|
-
var
|
|
61298
|
+
var SHAPES3 = {
|
|
60568
61299
|
fuel_barrel: { shape: { kind: "cylinder", radius: 0.3, height: 0.9 }, position: [0, 0.45, 0], color: 3104666 },
|
|
60569
61300
|
propane_p13: {
|
|
60570
61301
|
shape: { kind: "cylinder", radius: 0.176, height: 0.49 },
|
|
@@ -60575,7 +61306,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60575
61306
|
},
|
|
60576
61307
|
car_fuel_tank: { shape: { kind: "box", size: [0.9, 0.25, 0.5] }, position: [0, 0.4, 0], color: 4935509 }
|
|
60577
61308
|
};
|
|
60578
|
-
var WORLD_EXPLOSIVE_IDS = Object.keys(
|
|
61309
|
+
var WORLD_EXPLOSIVE_IDS = Object.keys(SHAPES3);
|
|
60579
61310
|
var SHELL = { thickness: 1e-3, fill: 0.03 };
|
|
60580
61311
|
var explosivePart = (def, shape, scale) => ({
|
|
60581
61312
|
id: "tank",
|
|
@@ -60621,7 +61352,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60621
61352
|
return [centre.x, centre.y, centre.z];
|
|
60622
61353
|
};
|
|
60623
61354
|
var worldExplosiveShell = (THREE, defId) => {
|
|
60624
|
-
const shape =
|
|
61355
|
+
const shape = SHAPES3[defId];
|
|
60625
61356
|
const model = shape ? modelOf(THREE, shape, 2) : void 0;
|
|
60626
61357
|
if (!model) return void 0;
|
|
60627
61358
|
model.traverse((node) => {
|
|
@@ -60634,7 +61365,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
60634
61365
|
return model;
|
|
60635
61366
|
};
|
|
60636
61367
|
var worldExplosiveItem = (def, name, options = {}) => {
|
|
60637
|
-
const shape =
|
|
61368
|
+
const shape = SHAPES3[def.id];
|
|
60638
61369
|
if (!shape) return void 0;
|
|
60639
61370
|
const scale = () => options.scale?.() ?? 1;
|
|
60640
61371
|
const spec = () => ({ name, parts: [explosivePart(def, shape, scale())], joints: [], tags: ["explosive"] });
|
|
@@ -61069,6 +61800,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61069
61800
|
raycast: createLaserRaycast(lent.THREE, lent.scene, lent.camera),
|
|
61070
61801
|
// 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)
|
|
61071
61802
|
contact: createLaserRaycast(lent.THREE, lent.scene, lent.camera, { now: () => simulatedMs }),
|
|
61803
|
+
...lent.holdFrame ? { bodyHold: () => lent.holdFrame?.() ?? null } : {},
|
|
61072
61804
|
...lent.effects ? { effects: lent.effects } : {},
|
|
61073
61805
|
...options.attachmentModels ? { attachmentModels: options.attachmentModels } : {},
|
|
61074
61806
|
...options.condition ? { condition: options.condition } : {},
|
|
@@ -61881,7 +62613,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61881
62613
|
};
|
|
61882
62614
|
for (const { kind, item } of PUSH_KINDS) PUSH_LAYOUT[kind].forEach(([distance3, degrees]) => lay(kind, item, bearing(centre, distance3, degrees)));
|
|
61883
62615
|
if (mode === "grenade") {
|
|
61884
|
-
lay("zombie", "zombie_walker", bearing(centre, 3.4, -
|
|
62616
|
+
lay("zombie", "zombie_walker", bearing(centre, 3.4, -130));
|
|
61885
62617
|
lay("survivor", "person_police_officer", bearing(centre, 7.5, 78));
|
|
61886
62618
|
lay("camp_table", "camp_table", bearing(centre, 3, 90));
|
|
61887
62619
|
}
|
|
@@ -61924,7 +62656,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61924
62656
|
const outward = ((now[0] - from[0]) * (from[0] - blast.point[0]) + (now[2] - from[2]) * (from[2] - blast.point[2])) / away;
|
|
61925
62657
|
return { ...body, distance: distance3, outward, moved: flat(now, from) };
|
|
61926
62658
|
});
|
|
61927
|
-
const torn = (row) => PUSH_KINDS.some((entry2) => entry2.kind === row.kind) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
|
|
62659
|
+
const torn = (row) => (PUSH_KINDS.some((entry2) => entry2.kind === row.kind) || HEAVY_ITEMS.includes(row.kind)) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
|
|
61928
62660
|
const reached = rows.filter((row) => row.distance < blast.radius * 0.85 && !torn(row));
|
|
61929
62661
|
const movedAway = reached.filter((row) => row.outward >= MOVED);
|
|
61930
62662
|
const least = reached.length === 0 ? 0 : Math.min(...reached.map((row) => row.outward));
|
|
@@ -61937,9 +62669,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
61937
62669
|
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 });
|
|
61938
62670
|
}
|
|
61939
62671
|
for (const row of reached.filter((entry2) => entry2.kind === "zombie" || entry2.kind === "survivor")) {
|
|
61940
|
-
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 });
|
|
62672
|
+
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 } });
|
|
61941
62673
|
}
|
|
61942
|
-
const table =
|
|
62674
|
+
const table = rows.find((row) => HEAVY_ITEMS.includes(row.kind) && row.distance < blast.radius * 0.85);
|
|
61943
62675
|
if (table) expect(table.moved >= MOVED, fillText2(text.push_heavy, { what: things[table.itemId] ?? table.itemId, moved: table.moved.toFixed(2) }), { table });
|
|
61944
62676
|
}
|
|
61945
62677
|
};
|