@adatechnology/game-weapons-three 0.9.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.global.js +486 -69
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +183 -3
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.global.js
CHANGED
|
@@ -19681,6 +19681,62 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19681
19681
|
return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
|
|
19682
19682
|
};
|
|
19683
19683
|
var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
19684
|
+
var splinterBox = (inp, box5, limit = inp.maxFragments) => {
|
|
19685
|
+
const { rnd: rnd2 } = inp;
|
|
19686
|
+
const dims = box5.s;
|
|
19687
|
+
const [fmin, fmax] = inp.fragmentSize;
|
|
19688
|
+
const g = inp.grain ?? [0, 1, 2].sort((a2, b2) => dims[b2] - dims[a2])[0];
|
|
19689
|
+
const [a, b] = [0, 1, 2].filter((i) => i !== g);
|
|
19690
|
+
const w = Math.max(fmin * 1.5, 0.01);
|
|
19691
|
+
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
|
|
19692
|
+
const maxLen = fmax * 4;
|
|
19693
|
+
const cells = bsp(box5, c, [a, b], rnd2);
|
|
19694
|
+
const pieces = [];
|
|
19695
|
+
for (const cell of cells) {
|
|
19696
|
+
const extra = rnd2() < 0.4 ? 1 : 0;
|
|
19697
|
+
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
19698
|
+
for (const p of bsp(cell, k, [g], rnd2)) pieces.push(p);
|
|
19699
|
+
}
|
|
19700
|
+
return pieces.slice(0, limit).map((p) => ({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: "splinter", weight: p.s[0] * p.s[1] * p.s[2] }));
|
|
19701
|
+
};
|
|
19702
|
+
var severAt = (inp, root, at3, gap) => {
|
|
19703
|
+
const dims = root.s;
|
|
19704
|
+
const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
|
|
19705
|
+
const half = dims[L] / 2;
|
|
19706
|
+
const lo = clamp(at3 - gap / 2, -half, half), hi = clamp(at3 + gap / 2, -half, half);
|
|
19707
|
+
const out = [];
|
|
19708
|
+
const stub = (from, to) => {
|
|
19709
|
+
if (to - from < 0.02) return;
|
|
19710
|
+
const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
|
|
19711
|
+
c[L] = (from + to) / 2;
|
|
19712
|
+
sz[L] = to - from;
|
|
19713
|
+
out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight: sz[0] * sz[1] * sz[2] });
|
|
19714
|
+
};
|
|
19715
|
+
stub(-half, lo);
|
|
19716
|
+
stub(hi, half);
|
|
19717
|
+
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
19718
|
+
band.c[L] = (lo + hi) / 2;
|
|
19719
|
+
band.s[L] = Math.max(hi - lo, 0.01);
|
|
19720
|
+
for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
19721
|
+
return out;
|
|
19722
|
+
};
|
|
19723
|
+
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
19724
|
+
const { dims, rnd: rnd2, maxFragments } = inp;
|
|
19725
|
+
const layers = clamp(Math.ceil(dims[t] / STACK_LAYER), 2, 4);
|
|
19726
|
+
const per = clamp(n, 3, Math.max(3, Math.floor(maxFragments / layers)));
|
|
19727
|
+
const out = [];
|
|
19728
|
+
for (let k = 0; k < layers; k++) {
|
|
19729
|
+
const off = -dims[t] / 2 + (k + 0.5) * dims[t] / layers;
|
|
19730
|
+
for (const b of bsp(root, per, [u, v], rnd2)) {
|
|
19731
|
+
if (out.length >= maxFragments) return out;
|
|
19732
|
+
const c = [b.c[0], b.c[1], b.c[2]];
|
|
19733
|
+
c[t] = off;
|
|
19734
|
+
out.push({ center: c, size: [b.s[u], b.s[v], sheet], q: frame(u, v, t, (rnd2() - 0.5) * 0.8), shape: "scrap", weight: b.s[u] * b.s[v], inner: out.length % 4 !== 0 });
|
|
19735
|
+
}
|
|
19736
|
+
}
|
|
19737
|
+
return out;
|
|
19738
|
+
};
|
|
19739
|
+
var STACK_LAYER = 0.012;
|
|
19684
19740
|
var planFragments = (inp) => {
|
|
19685
19741
|
const { style, dims, rnd: rnd2, maxFragments } = inp;
|
|
19686
19742
|
const [fmin, fmax] = inp.fragmentSize;
|
|
@@ -19702,27 +19758,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19702
19758
|
return out;
|
|
19703
19759
|
}
|
|
19704
19760
|
if (style === "splinter") {
|
|
19705
|
-
|
|
19706
|
-
const
|
|
19707
|
-
const w = Math.max(fmin * 1.5, 0.01);
|
|
19708
|
-
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(maxFragments / 2)));
|
|
19709
|
-
const maxLen = fmax * 4;
|
|
19710
|
-
const cells = bsp(root, c, [a, b], rnd2);
|
|
19711
|
-
const pieces = [];
|
|
19712
|
-
for (const cell of cells) {
|
|
19713
|
-
let list2 = [cell];
|
|
19714
|
-
const extra = rnd2() < 0.4 ? 1 : 0;
|
|
19715
|
-
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
19716
|
-
list2 = bsp(cell, k, [g], rnd2);
|
|
19717
|
-
for (const p of list2) pieces.push(p);
|
|
19718
|
-
}
|
|
19719
|
-
for (const p of pieces.slice(0, maxFragments)) {
|
|
19720
|
-
out.push({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: "splinter", weight: p.s[0] * p.s[1] * p.s[2] });
|
|
19721
|
-
}
|
|
19761
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
19762
|
+
for (const p of splinterBox(inp, root)) out.push(p);
|
|
19722
19763
|
return out;
|
|
19723
19764
|
}
|
|
19724
19765
|
if (style === "tear") {
|
|
19725
19766
|
const n2 = clamp(Math.round(dims[u] * dims[v] / (mean * mean)), 3, maxFragments);
|
|
19767
|
+
const sheet = inp.sheet ?? 0;
|
|
19768
|
+
if (sheet > 0 && dims[t] > sheet * 4) return tearStack(inp, root, t, u, v, n2, sheet);
|
|
19726
19769
|
const th2 = Math.min(inp.thickness, dims[t]);
|
|
19727
19770
|
for (const b of bsp(root, n2, [u, v], rnd2)) {
|
|
19728
19771
|
out.push({ center: [b.c[0], b.c[1], b.c[2]], size: [b.s[u], b.s[v], th2], q: frame(u, v, t), shape: "scrap", weight: b.s[u] * b.s[v] });
|
|
@@ -21305,6 +21348,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21305
21348
|
0.03,
|
|
21306
21349
|
0.15
|
|
21307
21350
|
],
|
|
21351
|
+
sheet: 1e-3,
|
|
21308
21352
|
damageScale: {
|
|
21309
21353
|
slash: 3
|
|
21310
21354
|
},
|
|
@@ -21320,7 +21364,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21320
21364
|
smoke: "grey"
|
|
21321
21365
|
},
|
|
21322
21366
|
visual: {
|
|
21323
|
-
charColor: 1709072
|
|
21367
|
+
charColor: 1709072,
|
|
21368
|
+
innerColor: 15524048
|
|
21324
21369
|
},
|
|
21325
21370
|
tags: [
|
|
21326
21371
|
"fabric",
|
|
@@ -21702,9 +21747,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21702
21747
|
var BREACH_AREA = 0.25;
|
|
21703
21748
|
var BREACH_BLAST_REACH = 0.12;
|
|
21704
21749
|
var RESIDUAL_CAPACITY = 0.15;
|
|
21750
|
+
var SEVER_BAND = 0.1;
|
|
21751
|
+
var SEVER_ASPECT = 3;
|
|
21752
|
+
var SEVER_BALLISTIC = 2;
|
|
21705
21753
|
var SUPPORT_TOLERANCE = 0.05;
|
|
21706
21754
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
21707
21755
|
var BREACH_GRID_CAP = 4096;
|
|
21756
|
+
var CHIP_ENERGY = 200;
|
|
21757
|
+
var CHIP_SPLASH = 0.5;
|
|
21708
21758
|
var CUT_SLICE = 1;
|
|
21709
21759
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
21710
21760
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -21748,6 +21798,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21748
21798
|
let windCfg = windOpts ? resolveWind(windOpts) : null;
|
|
21749
21799
|
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
21750
21800
|
const breachBlastFactor = options.breach?.blastFactor ?? BREACH_BLAST_FACTOR;
|
|
21801
|
+
const chipEnergy = options.breach?.chipEnergy ?? CHIP_ENERGY;
|
|
21751
21802
|
const rng2 = createRng2(options.seed ?? 1);
|
|
21752
21803
|
const rnd2 = () => rng2.next();
|
|
21753
21804
|
let time = 0;
|
|
@@ -22121,41 +22172,68 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22121
22172
|
markDamaged(p);
|
|
22122
22173
|
if (p.rt.hinge >= flexure.failRotationDeg * Math.PI / 180) hingeDrop(o, p, src);
|
|
22123
22174
|
};
|
|
22175
|
+
const freeSpan = (p, supports) => {
|
|
22176
|
+
if (supports.length === 0) return Infinity;
|
|
22177
|
+
const d = p.dims;
|
|
22178
|
+
const L = [0, 1, 2].sort((a, b) => d[b] - d[a])[0];
|
|
22179
|
+
const spans = supports.map((k) => contactBox(p, k)).map(([lo, hi]) => [lo[L], hi[L]]);
|
|
22180
|
+
let far = 0;
|
|
22181
|
+
for (let i = 0; i <= 16; i++) {
|
|
22182
|
+
const x = (i / 16 - 0.5) * d[L];
|
|
22183
|
+
let near = Infinity;
|
|
22184
|
+
for (const [lo, hi] of spans) near = Math.min(near, Math.max(lo - x, x - hi, 0));
|
|
22185
|
+
far = Math.max(far, near);
|
|
22186
|
+
}
|
|
22187
|
+
return 2 * far;
|
|
22188
|
+
};
|
|
22124
22189
|
const solveStructure = (o) => {
|
|
22125
22190
|
o.maxStress = 0;
|
|
22126
22191
|
if (!o.hasStructure) return;
|
|
22127
22192
|
const parts = o.parts;
|
|
22128
22193
|
const n = parts.length;
|
|
22129
22194
|
const dist2 = new Array(n).fill(-1);
|
|
22130
|
-
const queue = [];
|
|
22131
22195
|
const ok = (i) => parts[i].structural && !isLost(parts[i]);
|
|
22132
22196
|
const isPartition = (i) => parts[i].spec.structure?.role === "partition";
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
if (
|
|
22140
|
-
|
|
22141
|
-
|
|
22142
|
-
|
|
22143
|
-
|
|
22144
|
-
|
|
22145
|
-
|
|
22197
|
+
const out = /* @__PURE__ */ new Set();
|
|
22198
|
+
let queue = [];
|
|
22199
|
+
let lowerOf = /* @__PURE__ */ new Map();
|
|
22200
|
+
for (; ; ) {
|
|
22201
|
+
dist2.fill(-1);
|
|
22202
|
+
queue = [];
|
|
22203
|
+
for (let i = 0; i < n; i++) if (ok(i) && !out.has(i) && parts[i].anchored) {
|
|
22204
|
+
dist2[i] = 0;
|
|
22205
|
+
queue.push(i);
|
|
22206
|
+
}
|
|
22207
|
+
for (let h = 0; h < queue.length; h++) {
|
|
22208
|
+
const s = queue[h];
|
|
22209
|
+
if (isPartition(s)) continue;
|
|
22210
|
+
for (const j of o.byPart.get(parts[s].spec.id)) {
|
|
22211
|
+
if (j.st.open || !j.bears) continue;
|
|
22212
|
+
const r = j.ia === s ? j.ib : j.ia;
|
|
22213
|
+
if (dist2[r] >= 0 || !ok(r) || out.has(r) || restsOn(o, j, r) !== s) continue;
|
|
22214
|
+
dist2[r] = dist2[s] + 1;
|
|
22215
|
+
queue.push(r);
|
|
22216
|
+
}
|
|
22146
22217
|
}
|
|
22147
|
-
|
|
22148
|
-
|
|
22149
|
-
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22218
|
+
lowerOf = /* @__PURE__ */ new Map();
|
|
22219
|
+
let dropped = false;
|
|
22220
|
+
for (const i of queue) {
|
|
22221
|
+
const lower = [];
|
|
22222
|
+
for (const j of o.byPart.get(parts[i].spec.id)) {
|
|
22223
|
+
if (j.st.open || !j.bears) continue;
|
|
22224
|
+
const k = restsOn(o, j, i);
|
|
22225
|
+
if (k >= 0 && dist2[k] >= 0 && dist2[k] < dist2[i] && !isPartition(k)) lower.push(k);
|
|
22226
|
+
}
|
|
22227
|
+
lowerOf.set(i, lower);
|
|
22228
|
+
const max2 = parts[i].spec.structure?.maxSpan;
|
|
22229
|
+
if (max2 !== void 0 && !parts[i].anchored && freeSpan(parts[i], lower.map((k) => parts[k])) > max2) {
|
|
22230
|
+
out.add(i);
|
|
22231
|
+
dropped = true;
|
|
22232
|
+
}
|
|
22156
22233
|
}
|
|
22157
|
-
|
|
22234
|
+
if (!dropped) break;
|
|
22158
22235
|
}
|
|
22236
|
+
const incoming = new Array(n).fill(0);
|
|
22159
22237
|
const bending = new Array(n).fill(0);
|
|
22160
22238
|
const through = parts.map(() => []);
|
|
22161
22239
|
const pass = (k, at3, mass) => {
|
|
@@ -22667,6 +22745,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22667
22745
|
const r = rayPart(o, p, origin, d);
|
|
22668
22746
|
if (!r || r.t0 < 0 || r.t0 > maxDist) continue;
|
|
22669
22747
|
if (p.st.opening && insideOpening(o, p, vec.madd(origin, d, r.t0))) continue;
|
|
22748
|
+
if (p.st.chipped && insideChip(p, objLocal(o, vec.madd(origin, d, r.t0)))) continue;
|
|
22670
22749
|
if (!best || r.t0 < best.distance) best = { object: o.id, part: p.spec.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 };
|
|
22671
22750
|
}
|
|
22672
22751
|
}
|
|
@@ -22930,6 +23009,62 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22930
23009
|
if (total < (p.spec.breach?.area ?? BREACH_AREA)) return;
|
|
22931
23010
|
openBreach(o, p, [sum2[0] / total, sum2[1] / total, sum2[2] / total], total, energy, dir, src);
|
|
22932
23011
|
};
|
|
23012
|
+
const unitGrids = /* @__PURE__ */ new WeakMap();
|
|
23013
|
+
const gridOf = (p) => {
|
|
23014
|
+
let grid = unitGrids.get(p);
|
|
23015
|
+
if (!grid) unitGrids.set(p, grid = unitGrid(p.dims, p.thin, unitsOf2(p), BREACH_GRID_CAP));
|
|
23016
|
+
return grid;
|
|
23017
|
+
};
|
|
23018
|
+
const insideChip = (p, objectPoint) => {
|
|
23019
|
+
const cells = p.st.chipped;
|
|
23020
|
+
if (!cells?.length) return false;
|
|
23021
|
+
const [u, v] = planeAxes(p.thin);
|
|
23022
|
+
const lp = toLocal(p.pos, p.q, objectPoint);
|
|
23023
|
+
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);
|
|
23024
|
+
};
|
|
23025
|
+
const chipUnits = (o, p, wp, energy, dir, radius, src) => {
|
|
23026
|
+
var _a, _b;
|
|
23027
|
+
if (!p.spec.units || p.spec.reinforcement || !p.staged || chipEnergy <= 0 || energy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
23028
|
+
const [u, v] = planeAxes(p.thin);
|
|
23029
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
23030
|
+
const grid = gridOf(p);
|
|
23031
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
23032
|
+
const out = [];
|
|
23033
|
+
for (let i = 0; i < grid.length; i++) {
|
|
23034
|
+
const cell = grid[i];
|
|
23035
|
+
if (worn[i] === -1) continue;
|
|
23036
|
+
const gap = Math.hypot(Math.max(0, Math.abs(lp[u] - cell.center[u]) - cell.size[u] / 2), Math.max(0, Math.abs(lp[v] - cell.center[v]) - cell.size[v] / 2));
|
|
23037
|
+
if (gap > radius) continue;
|
|
23038
|
+
const share = gap === 0 ? 1 : CHIP_SPLASH * (1 - gap / radius);
|
|
23039
|
+
const total = (worn[i] ?? 0) + energy * share;
|
|
23040
|
+
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
23041
|
+
if (total < chipEnergy * mass) {
|
|
23042
|
+
worn[i] = total;
|
|
23043
|
+
continue;
|
|
23044
|
+
}
|
|
23045
|
+
worn[i] = -1;
|
|
23046
|
+
out.push(cell);
|
|
23047
|
+
}
|
|
23048
|
+
if (out.length === 0) return;
|
|
23049
|
+
const { c, q } = partXf(o, p);
|
|
23050
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
23051
|
+
const holes = localHolesOf(p);
|
|
23052
|
+
for (const cell of out) {
|
|
23053
|
+
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
23054
|
+
const center = toWorld(c, q, cell.center);
|
|
23055
|
+
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
23056
|
+
const fragments = cellFragments(o, p, [cell], wp, dir, speed, mass, `c${chipped.length}_`);
|
|
23057
|
+
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
23058
|
+
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
23059
|
+
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
23060
|
+
p.rt.removed = (p.rt.removed ?? 0) + mass;
|
|
23061
|
+
markDamaged(p);
|
|
23062
|
+
setStage(o, p, 1, {}, src);
|
|
23063
|
+
emit2({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
23064
|
+
debris2(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
23065
|
+
}
|
|
23066
|
+
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
23067
|
+
};
|
|
22933
23068
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
22934
23069
|
const [u, v] = planeAxes(p.thin);
|
|
22935
23070
|
const radius = clamp2(Math.sqrt(total / Math.PI), Math.sqrt((p.spec.breach?.area ?? BREACH_AREA) / Math.PI), breachRadius);
|
|
@@ -22941,7 +23076,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22941
23076
|
failPart(o, p, worldCenter, dir, energy, 12, src);
|
|
22942
23077
|
return;
|
|
22943
23078
|
}
|
|
22944
|
-
const
|
|
23079
|
+
const worn = p.rt.chips;
|
|
23080
|
+
const cells = cellsWithin(gridOf(p).filter((_, i) => worn?.[i] !== -1), p.thin, lc, radius);
|
|
22945
23081
|
const removed = p.st.mass * cells.reduce((sum2, cell) => sum2 + cell.size[u] * cell.size[v], 0) / (p.dims[u] * p.dims[v]);
|
|
22946
23082
|
const speed = clamp2(Math.sqrt(2 * energy * 0.1 / Math.max(removed, 0.05)), 0.5, 8);
|
|
22947
23083
|
const fragments = cellFragments(o, p, cells, worldCenter, dir, speed, removed, "b");
|
|
@@ -23025,6 +23161,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23025
23161
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
23026
23162
|
const hitLocal = toLocal(c, q, hitW);
|
|
23027
23163
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
23164
|
+
const axis2 = style === "splinter" ? severAxis(p) : -1;
|
|
23165
|
+
const cutAt2 = axis2 < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis2] : void 0);
|
|
23028
23166
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
23029
23167
|
style,
|
|
23030
23168
|
dims: p.dims,
|
|
@@ -23033,7 +23171,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23033
23171
|
hit: hitLocal,
|
|
23034
23172
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
23035
23173
|
maxFragments,
|
|
23036
|
-
rnd: rnd2
|
|
23174
|
+
rnd: rnd2,
|
|
23175
|
+
sheet: p.mat.sheet,
|
|
23176
|
+
...cutAt2 !== void 0 ? { cut: { at: cutAt2, gap: SEVER_BAND } } : {}
|
|
23037
23177
|
});
|
|
23038
23178
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
23039
23179
|
const mass = p.st.mass;
|
|
@@ -23057,7 +23197,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23057
23197
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd2(), dv[2] * k],
|
|
23058
23198
|
angularVelocity: [(rnd2() - 0.5) * 12, (rnd2() - 0.5) * 12, (rnd2() - 0.5) * 12],
|
|
23059
23199
|
mass: mass * f.weight / totW,
|
|
23060
|
-
...marks.length > 0 ? { marks } : {}
|
|
23200
|
+
...marks.length > 0 ? { marks } : {},
|
|
23201
|
+
...f.inner ? { inner: true } : {}
|
|
23061
23202
|
};
|
|
23062
23203
|
});
|
|
23063
23204
|
const reinforcement = p.spec.reinforcement;
|
|
@@ -23091,10 +23232,33 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23091
23232
|
emit2({ ...base(o, src), type: "primed", part: p.spec.id, point: partXf(o, p).c, trigger, delay });
|
|
23092
23233
|
};
|
|
23093
23234
|
const CHAIN_TRIGGER_OF = { ballistic: "shot", explosive: "explosion" };
|
|
23235
|
+
const severAxis = (p) => {
|
|
23236
|
+
if (p.mat.fracture !== "splinter" || p.spec.shape.kind !== "box") return -1;
|
|
23237
|
+
const d = p.dims;
|
|
23238
|
+
const order = [0, 1, 2].sort((a, b) => d[b] - d[a]);
|
|
23239
|
+
return d[order[0]] >= SEVER_ASPECT * d[order[1]] && d[order[0]] >= 3 * SEVER_BAND ? order[0] : -1;
|
|
23240
|
+
};
|
|
23241
|
+
const bandOf = (o, p, axis2, origin) => {
|
|
23242
|
+
const { c, q } = partXf(o, p);
|
|
23243
|
+
const along = toLocal(c, q, origin)[axis2];
|
|
23244
|
+
const L = p.dims[axis2];
|
|
23245
|
+
return clamp2(Math.floor((along + L / 2) / SEVER_BAND), 0, Math.ceil(L / SEVER_BAND) - 1);
|
|
23246
|
+
};
|
|
23094
23247
|
const applyDamage = (o, p, amount, type, origin, dir, fragEnergy, cap, src) => {
|
|
23248
|
+
var _a;
|
|
23095
23249
|
if (isLost(p) || amount <= 0) return 0;
|
|
23096
23250
|
const prev = p.st.hp;
|
|
23097
23251
|
p.st.hp -= amount;
|
|
23252
|
+
const axis2 = origin && type !== "fire" && type !== "heat" ? severAxis(p) : -1;
|
|
23253
|
+
if (axis2 >= 0 && p.st.hp > 0) {
|
|
23254
|
+
const band = bandOf(o, p, axis2, origin);
|
|
23255
|
+
const cuts = (_a = p.rt).cuts ?? (_a.cuts = {});
|
|
23256
|
+
cuts[band] = (cuts[band] ?? 0) + amount * (type === "ballistic" ? SEVER_BALLISTIC : 1);
|
|
23257
|
+
if (cuts[band] >= p.st.maxHp * SEVER_BAND / p.dims[axis2]) {
|
|
23258
|
+
p.rt.cutAt = -p.dims[axis2] / 2 + (band + 0.5) * SEVER_BAND;
|
|
23259
|
+
p.st.hp = 0;
|
|
23260
|
+
}
|
|
23261
|
+
}
|
|
23098
23262
|
markDamaged(p);
|
|
23099
23263
|
const trigger = CHAIN_TRIGGER_OF[type];
|
|
23100
23264
|
const chained = trigger !== void 0 && chainDelayOf(p.spec, trigger) !== void 0;
|
|
@@ -23133,7 +23297,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23133
23297
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round5 = {}) => {
|
|
23134
23298
|
const mat = p.mat;
|
|
23135
23299
|
const piercing = round5.piercing ?? 1;
|
|
23136
|
-
if (p.st.stripped || p.st.opening && insideOpening(o, p, objLocal(o, entry2))) {
|
|
23300
|
+
if (p.st.stripped || p.st.opening && insideOpening(o, p, objLocal(o, entry2)) || insideChip(p, objLocal(o, entry2))) {
|
|
23137
23301
|
return { penetrated: true, exitEnergy: E, exitPoint: vec.madd(entry2, d, Math.min(pathLen, p.thickness)), damage: 0 };
|
|
23138
23302
|
}
|
|
23139
23303
|
const sin2 = Math.min(1, Math.abs(vec.dot(d, n)));
|
|
@@ -23192,6 +23356,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23192
23356
|
debris2(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
23193
23357
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
23194
23358
|
const dmg = applyDamage(o, p, E * DMG_PER_J * scale, "ballistic", entry2, d, E * 0.1, 12, src);
|
|
23359
|
+
chipUnits(o, p, entry2, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
23195
23360
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
23196
23361
|
};
|
|
23197
23362
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -23219,6 +23384,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
23219
23384
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
23220
23385
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
23221
23386
|
addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src);
|
|
23387
|
+
if (kind === "blunt") chipUnits(o, p, point, E, d, Math.max(contactR, 0.03), src);
|
|
23222
23388
|
return done;
|
|
23223
23389
|
};
|
|
23224
23390
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -33237,12 +33403,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33237
33403
|
A.invalidate = () => {
|
|
33238
33404
|
for (let i = 0; i < NS; i++) PL[i].shadow.needsUpdate = true;
|
|
33239
33405
|
};
|
|
33240
|
-
A.update = function(
|
|
33406
|
+
A.update = function(houses2, focus, op) {
|
|
33241
33407
|
op = op || {};
|
|
33242
|
-
|
|
33408
|
+
houses2 = (Array.isArray(houses2) ? houses2 : [houses2]).filter(Boolean);
|
|
33243
33409
|
_f.set(focus.x, focus.y, focus.z);
|
|
33244
33410
|
const rooms = [], exterior = [];
|
|
33245
|
-
for (const house of
|
|
33411
|
+
for (const house of houses2) {
|
|
33246
33412
|
if (house.group && !house.group.visible) continue;
|
|
33247
33413
|
house.house.updateMatrixWorld(true);
|
|
33248
33414
|
const M = house.house.matrixWorld, by = /* @__PURE__ */ new Map();
|
|
@@ -33621,7 +33787,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33621
33787
|
}
|
|
33622
33788
|
return null;
|
|
33623
33789
|
};
|
|
33624
|
-
|
|
33790
|
+
const local2 = (p) => {
|
|
33791
|
+
house.updateMatrixWorld(true);
|
|
33792
|
+
const v = house.worldToLocal(tmp.p.set(p[0], p[1], p[2]));
|
|
33793
|
+
return [v.x, v.y, v.z];
|
|
33794
|
+
};
|
|
33795
|
+
function courses(part, units, opening, chips2, paint2) {
|
|
33625
33796
|
const [u0, v0, u1, v1] = part.aabb;
|
|
33626
33797
|
const alongU = u1 - u0 >= v1 - v0;
|
|
33627
33798
|
const len = alongU ? u1 - u0 : v1 - v0;
|
|
@@ -33645,23 +33816,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33645
33816
|
const x = alongU ? u0 + mid : (u0 + u1) / 2;
|
|
33646
33817
|
const z = alongU ? (v0 + v1) / 2 : v0 + mid;
|
|
33647
33818
|
const y = part.y0 + (r + 0.5) * rowH;
|
|
33648
|
-
const d = Math.hypot(x - opening.center[0], y - opening.center[1], z - opening.center[2]);
|
|
33649
|
-
if (d < opening.radius) continue;
|
|
33650
|
-
|
|
33819
|
+
const d = opening ? Math.hypot(x - opening.center[0], y - opening.center[1], z - opening.center[2]) : Infinity;
|
|
33820
|
+
if (opening && d < opening.radius) continue;
|
|
33821
|
+
const gone = (c) => Math.abs(c[1] - y) <= rowH / 2 && (alongU ? c[0] >= u0 + a && c[0] <= u0 + b : c[2] >= v0 + a && c[2] <= v0 + b);
|
|
33822
|
+
if (chips2.some(gone)) continue;
|
|
33823
|
+
const near = opening && d < opening.radius + 0.25 || chips2.some((c) => Math.hypot(c[0] - x, c[1] - y, c[2] - z) < 0.15);
|
|
33824
|
+
list2.push({ x, y, z, sx: alongU ? b - a : thick, sy: rowH, sz: alongU ? thick : b - a, colour: near || paint2 === null ? edge : paint2 });
|
|
33651
33825
|
}
|
|
33652
33826
|
}
|
|
33653
33827
|
return list2;
|
|
33654
33828
|
}
|
|
33655
|
-
function
|
|
33656
|
-
if (lod !== 0
|
|
33657
|
-
const part = structure.byId.get(
|
|
33658
|
-
const spec = opts.spec()?.parts?.find((q) => q.id ===
|
|
33829
|
+
function draw2(partId, change) {
|
|
33830
|
+
if (lod !== 0) return false;
|
|
33831
|
+
const part = structure.byId.get(partId);
|
|
33832
|
+
const spec = opts.spec()?.parts?.find((q) => q.id === partId);
|
|
33659
33833
|
if (!part || !spec || !spec.units) return false;
|
|
33660
|
-
|
|
33661
|
-
if (!
|
|
33662
|
-
|
|
33834
|
+
let view = views.get(partId);
|
|
33835
|
+
if (!view) {
|
|
33836
|
+
const items = structure.meshesOf(partId).filter((it) => it.mesh);
|
|
33837
|
+
if (!items.length) return false;
|
|
33838
|
+
view = { mesh: null, saved: null, items, paint: wallColour(items), units: 0, opening: null, chips: [] };
|
|
33839
|
+
}
|
|
33840
|
+
if (change.opening) {
|
|
33841
|
+
if (view.opening) return false;
|
|
33842
|
+
view.opening = { center: local2(change.opening.center), radius: change.opening.radius };
|
|
33843
|
+
}
|
|
33844
|
+
if (change.chip) view.chips.push(local2(change.chip));
|
|
33845
|
+
const list2 = courses(part, spec.units, view.opening, view.chips, view.paint);
|
|
33846
|
+
if (view.mesh) {
|
|
33847
|
+
view.mesh.removeFromParent();
|
|
33848
|
+
view.mesh.dispose();
|
|
33849
|
+
}
|
|
33663
33850
|
const mesh3 = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
33664
|
-
mesh3.name = "breach:" +
|
|
33851
|
+
mesh3.name = "breach:" + partId;
|
|
33665
33852
|
list2.forEach((u, i) => {
|
|
33666
33853
|
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
33667
33854
|
mesh3.setMatrixAt(i, tmp.m);
|
|
@@ -33673,8 +33860,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33673
33860
|
mesh3.castShadow = true;
|
|
33674
33861
|
mesh3.receiveShadow = true;
|
|
33675
33862
|
house.add(mesh3);
|
|
33676
|
-
|
|
33677
|
-
|
|
33863
|
+
if (!view.saved) view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
33864
|
+
view.mesh = mesh3;
|
|
33865
|
+
view.units = list2.length;
|
|
33866
|
+
views.set(partId, view);
|
|
33678
33867
|
invalidate();
|
|
33679
33868
|
invalidateShadow();
|
|
33680
33869
|
return true;
|
|
@@ -33688,7 +33877,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33688
33877
|
return {
|
|
33689
33878
|
/** Handles a `stage` event of the house's object. Returns true when the wall was redrawn as courses. */
|
|
33690
33879
|
stage(event) {
|
|
33691
|
-
return event.stage === 2 ?
|
|
33880
|
+
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
33881
|
+
},
|
|
33882
|
+
/** Handles a `chip` event: the unit that came out leaves a gap in the courses. Returns true when the wall was redrawn. */
|
|
33883
|
+
chip(event) {
|
|
33884
|
+
return draw2(event.part, { chip: event.center });
|
|
33692
33885
|
},
|
|
33693
33886
|
/** The part broke or fell: the courses go (the plain range stays collapsed, the renderer hides the part). */
|
|
33694
33887
|
drop(partId) {
|
|
@@ -33918,6 +34111,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33918
34111
|
case "stage":
|
|
33919
34112
|
breach.stage(e);
|
|
33920
34113
|
break;
|
|
34114
|
+
case "chip":
|
|
34115
|
+
breach.chip(e);
|
|
34116
|
+
break;
|
|
33921
34117
|
case "collapse":
|
|
33922
34118
|
for (const id of e.parts) {
|
|
33923
34119
|
breach.drop(id);
|
|
@@ -33972,6 +34168,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
33972
34168
|
if (D.active && D.world.has(D.id)) D.world.remove(D.id);
|
|
33973
34169
|
D.active = false;
|
|
33974
34170
|
};
|
|
34171
|
+
D.follow = (world, id, spec) => {
|
|
34172
|
+
D.spec = spec;
|
|
34173
|
+
const offFollow = world.on("*", (e) => {
|
|
34174
|
+
if (e.object !== id) return;
|
|
34175
|
+
switch (e.type) {
|
|
34176
|
+
case "stage":
|
|
34177
|
+
breach.stage(e);
|
|
34178
|
+
break;
|
|
34179
|
+
case "chip":
|
|
34180
|
+
breach.chip(e);
|
|
34181
|
+
break;
|
|
34182
|
+
case "fracture":
|
|
34183
|
+
breach.drop(e.part);
|
|
34184
|
+
break;
|
|
34185
|
+
case "collapse":
|
|
34186
|
+
for (const p of e.parts) breach.drop(p);
|
|
34187
|
+
break;
|
|
34188
|
+
default:
|
|
34189
|
+
break;
|
|
34190
|
+
}
|
|
34191
|
+
});
|
|
34192
|
+
return { restore: () => breach.restore(), dispose: () => {
|
|
34193
|
+
offFollow();
|
|
34194
|
+
breach.restore();
|
|
34195
|
+
} };
|
|
34196
|
+
};
|
|
33975
34197
|
D.piece = (id) => D.active ? D.world.part(D.id, id) : null;
|
|
33976
34198
|
D.state = () => D.active ? D.world.state(D.id) : null;
|
|
33977
34199
|
D.reset = () => {
|
|
@@ -34694,6 +34916,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34694
34916
|
return buildApi(K, shared);
|
|
34695
34917
|
}
|
|
34696
34918
|
var WOOD_THIN = 0.035;
|
|
34919
|
+
var SHELF_SPAN = 1.5;
|
|
34920
|
+
var SHELVING = /shelf|bookcase|rack/;
|
|
34921
|
+
function carcass(parts, drawn) {
|
|
34922
|
+
const all = [...drawn.values()];
|
|
34923
|
+
const width = Math.max(...all.map((p) => p.max[0])) - Math.min(...all.map((p) => p.min[0]));
|
|
34924
|
+
for (const part of parts) {
|
|
34925
|
+
if (LOOSE.has(part.material) || part.shape.kind !== "box" || part.rotation) continue;
|
|
34926
|
+
const [x, y, z] = part.shape.size;
|
|
34927
|
+
if (y >= x && y >= z) {
|
|
34928
|
+
if (z < x && x >= 0.6 * width) continue;
|
|
34929
|
+
part.structure = { role: "column" };
|
|
34930
|
+
} else if (y <= x && y <= z) part.structure = { role: "beam", maxSpan: SHELF_SPAN * Math.max(x, z) };
|
|
34931
|
+
}
|
|
34932
|
+
}
|
|
34697
34933
|
var FLOOR_TOUCH = 5e-3;
|
|
34698
34934
|
var LOOSE = /* @__PURE__ */ new Set(["paper"]);
|
|
34699
34935
|
var BASE_MATERIAL = {
|
|
@@ -34809,6 +35045,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34809
35045
|
if (area3 < 0.6 * footprint || p.max[1] < 0.2) continue;
|
|
34810
35046
|
if (!top || p.max[1] > top.y + 1e-6) top = { part: p.id, y: p.max[1] };
|
|
34811
35047
|
}
|
|
35048
|
+
if (opts.carcass) carcass(parts, byId);
|
|
34812
35049
|
const spec = { name, parts, joints, tags: ["furniture"] };
|
|
34813
35050
|
const colors = new Map(drawn.map((p) => [p.id, p.color]));
|
|
34814
35051
|
const build = (T3) => {
|
|
@@ -34852,7 +35089,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34852
35089
|
if (w === void 0 || d === void 0 || h === void 0) throw new Error(`game-floorplan-three: furniture type "${type}" is not in the catalogue, so give w, d and h`);
|
|
34853
35090
|
const o = opts.options ?? {};
|
|
34854
35091
|
const seed = Math.round(opts.seed ?? 1);
|
|
34855
|
-
const plain2 =
|
|
35092
|
+
const plain2 = SHELVING.test(type) ? "paper" : "plastic";
|
|
34856
35093
|
const root = { x: seed, z: 0, ry: 0, w, d, h, y: 0, base: 0, palette: o.palette, seed: `furniture_${type}_${seed}` };
|
|
34857
35094
|
const { K, shared } = renderer.internals;
|
|
34858
35095
|
const before = K.PARTS.length;
|
|
@@ -34871,7 +35108,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
34871
35108
|
shared.ctx = saved.ctx;
|
|
34872
35109
|
}
|
|
34873
35110
|
},
|
|
34874
|
-
{ materials: { plain: plain2, ...o.materials }, chairs: o.chairs }
|
|
35111
|
+
{ materials: { plain: plain2, ...o.materials }, chairs: o.chairs, carcass: SHELVING.test(type) }
|
|
34875
35112
|
);
|
|
34876
35113
|
const spouts = K.PARTS.slice(before).filter((p) => p.type === "water_tap").map((p) => [p.pos[0] - root.x, p.pos[1], p.pos[2] - root.z]);
|
|
34877
35114
|
K.PARTS.length = before;
|
|
@@ -37811,6 +38048,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
37811
38048
|
/** Closure of the fingers on a grip and on a forend (0 open, 1 closed). */
|
|
37812
38049
|
closed: 0.9,
|
|
37813
38050
|
forend: 0.6,
|
|
38051
|
+
/** How far the shooting hand slides on its grip so the thumb reaches the safety lever. */
|
|
38052
|
+
thumbReach: 0.02,
|
|
37814
38053
|
/** How fast the supporting hand goes to the magazine and back, per second. */
|
|
37815
38054
|
magRate: 7,
|
|
37816
38055
|
/** The hands shrink with the tool: a tool drawn at scale `s` takes hands at `s ** handScaleExponent`. */
|
|
@@ -48497,9 +48736,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
48497
48736
|
);
|
|
48498
48737
|
return [misfire, ...stopped];
|
|
48499
48738
|
}
|
|
48739
|
+
var SAFETY_MOTION = { cant: 0.22, lift: 0.012, in: 0.14, flickAt: 0.16, flick: 0.1, outAt: 0.32, out: 0.16, lever: 0.6 };
|
|
48500
48740
|
function safetyClips(context) {
|
|
48501
|
-
|
|
48502
|
-
|
|
48741
|
+
const { cant, lift, in: rise, flickAt, flick, outAt, out, lever } = SAFETY_MOTION;
|
|
48742
|
+
const carry = () => [turn(ROOT, "x", 0, rise, 0, cant), move(ROOT, "y", 0, rise, 0, lift), turn(ROOT, "x", outAt, out, cant, 0), move(ROOT, "y", outAt, out, lift, 0)];
|
|
48743
|
+
const flickTo = (from, to) => context.parts.safety ? [turn("safety", "z", flickAt, flick, from, to)] : [];
|
|
48744
|
+
const clips = [composeClip("safety_on", [...carry(), ...flickTo(0, -lever)]), composeClip("safety_off", [...carry(), ...flickTo(-lever, 0)])];
|
|
48745
|
+
if (context.parts.safety) clips.push(composeClip("mode", [turn("safety", "z", 0, 0.07, 0, 0.4), turn("safety", "z", 0.07, 0.07, 0.4, 0)]));
|
|
48746
|
+
return clips;
|
|
48503
48747
|
}
|
|
48504
48748
|
var drawClip = () => composeClip("draw", [move(ROOT, "y", 0, 0.35, -0.1, 0), turn(ROOT, "z", 0, 0.35, -0.6, 0)]);
|
|
48505
48749
|
|
|
@@ -58270,8 +58514,169 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58270
58514
|
hand: "L",
|
|
58271
58515
|
anchor: "cylinder_side",
|
|
58272
58516
|
merge: 1
|
|
58517
|
+
},
|
|
58518
|
+
{
|
|
58519
|
+
family: "pistol",
|
|
58520
|
+
clips: [
|
|
58521
|
+
"safety_on",
|
|
58522
|
+
"safety_off"
|
|
58523
|
+
],
|
|
58524
|
+
hand: "R",
|
|
58525
|
+
anchor: "thumb_safety",
|
|
58526
|
+
whole: true,
|
|
58527
|
+
lead: 0.3,
|
|
58528
|
+
tail: 0.3
|
|
58529
|
+
},
|
|
58530
|
+
{
|
|
58531
|
+
family: "revolver",
|
|
58532
|
+
clips: [
|
|
58533
|
+
"safety_on",
|
|
58534
|
+
"safety_off"
|
|
58535
|
+
],
|
|
58536
|
+
hand: "R",
|
|
58537
|
+
anchor: "thumb_safety",
|
|
58538
|
+
whole: true,
|
|
58539
|
+
lead: 0.3,
|
|
58540
|
+
tail: 0.3
|
|
58541
|
+
},
|
|
58542
|
+
{
|
|
58543
|
+
family: "rifle",
|
|
58544
|
+
clips: [
|
|
58545
|
+
"safety_on",
|
|
58546
|
+
"safety_off"
|
|
58547
|
+
],
|
|
58548
|
+
hand: "R",
|
|
58549
|
+
anchor: "thumb_safety",
|
|
58550
|
+
whole: true,
|
|
58551
|
+
lead: 0.3,
|
|
58552
|
+
tail: 0.3,
|
|
58553
|
+
style: "ar"
|
|
58554
|
+
},
|
|
58555
|
+
{
|
|
58556
|
+
family: "p90",
|
|
58557
|
+
clips: [
|
|
58558
|
+
"safety_on",
|
|
58559
|
+
"safety_off"
|
|
58560
|
+
],
|
|
58561
|
+
hand: "R",
|
|
58562
|
+
anchor: "thumb_safety",
|
|
58563
|
+
whole: true,
|
|
58564
|
+
lead: 0.3,
|
|
58565
|
+
tail: 0.3
|
|
58566
|
+
},
|
|
58567
|
+
{
|
|
58568
|
+
family: "pump",
|
|
58569
|
+
clips: [
|
|
58570
|
+
"safety_on",
|
|
58571
|
+
"safety_off"
|
|
58572
|
+
],
|
|
58573
|
+
hand: "R",
|
|
58574
|
+
anchor: "thumb_safety",
|
|
58575
|
+
whole: true,
|
|
58576
|
+
lead: 0.3,
|
|
58577
|
+
tail: 0.3
|
|
58578
|
+
},
|
|
58579
|
+
{
|
|
58580
|
+
family: "semi12",
|
|
58581
|
+
clips: [
|
|
58582
|
+
"safety_on",
|
|
58583
|
+
"safety_off"
|
|
58584
|
+
],
|
|
58585
|
+
hand: "R",
|
|
58586
|
+
anchor: "thumb_safety",
|
|
58587
|
+
whole: true,
|
|
58588
|
+
lead: 0.3,
|
|
58589
|
+
tail: 0.3
|
|
58590
|
+
},
|
|
58591
|
+
{
|
|
58592
|
+
family: "semiint",
|
|
58593
|
+
clips: [
|
|
58594
|
+
"safety_on",
|
|
58595
|
+
"safety_off"
|
|
58596
|
+
],
|
|
58597
|
+
hand: "R",
|
|
58598
|
+
anchor: "thumb_safety",
|
|
58599
|
+
whole: true,
|
|
58600
|
+
lead: 0.3,
|
|
58601
|
+
tail: 0.3
|
|
58602
|
+
},
|
|
58603
|
+
{
|
|
58604
|
+
family: "break",
|
|
58605
|
+
clips: [
|
|
58606
|
+
"safety_on",
|
|
58607
|
+
"safety_off"
|
|
58608
|
+
],
|
|
58609
|
+
hand: "R",
|
|
58610
|
+
anchor: "thumb_safety",
|
|
58611
|
+
whole: true,
|
|
58612
|
+
lead: 0.3,
|
|
58613
|
+
tail: 0.3
|
|
58614
|
+
},
|
|
58615
|
+
{
|
|
58616
|
+
family: "bolt",
|
|
58617
|
+
clips: [
|
|
58618
|
+
"safety_on",
|
|
58619
|
+
"safety_off"
|
|
58620
|
+
],
|
|
58621
|
+
hand: "R",
|
|
58622
|
+
anchor: "thumb_safety",
|
|
58623
|
+
whole: true,
|
|
58624
|
+
lead: 0.3,
|
|
58625
|
+
tail: 0.3
|
|
58626
|
+
},
|
|
58627
|
+
{
|
|
58628
|
+
family: "boltmag",
|
|
58629
|
+
clips: [
|
|
58630
|
+
"safety_on",
|
|
58631
|
+
"safety_off"
|
|
58632
|
+
],
|
|
58633
|
+
hand: "R",
|
|
58634
|
+
anchor: "thumb_safety",
|
|
58635
|
+
whole: true,
|
|
58636
|
+
lead: 0.3,
|
|
58637
|
+
tail: 0.3
|
|
58638
|
+
},
|
|
58639
|
+
{
|
|
58640
|
+
family: "lever",
|
|
58641
|
+
clips: [
|
|
58642
|
+
"safety_on",
|
|
58643
|
+
"safety_off"
|
|
58644
|
+
],
|
|
58645
|
+
hand: "R",
|
|
58646
|
+
anchor: "thumb_safety",
|
|
58647
|
+
whole: true,
|
|
58648
|
+
lead: 0.3,
|
|
58649
|
+
tail: 0.3
|
|
58650
|
+
},
|
|
58651
|
+
{
|
|
58652
|
+
family: "belt",
|
|
58653
|
+
clips: [
|
|
58654
|
+
"safety_on",
|
|
58655
|
+
"safety_off"
|
|
58656
|
+
],
|
|
58657
|
+
hand: "R",
|
|
58658
|
+
anchor: "thumb_safety",
|
|
58659
|
+
whole: true,
|
|
58660
|
+
lead: 0.3,
|
|
58661
|
+
tail: 0.3
|
|
58662
|
+
},
|
|
58663
|
+
{
|
|
58664
|
+
family: "rifle",
|
|
58665
|
+
clips: [
|
|
58666
|
+
"safety_on",
|
|
58667
|
+
"safety_off"
|
|
58668
|
+
],
|
|
58669
|
+
hand: "L",
|
|
58670
|
+
anchor: "lever_right",
|
|
58671
|
+
style: "ak",
|
|
58672
|
+
whole: true,
|
|
58673
|
+
lead: 0.3,
|
|
58674
|
+
tail: 0.3
|
|
58273
58675
|
}
|
|
58274
|
-
]
|
|
58676
|
+
],
|
|
58677
|
+
safety: {
|
|
58678
|
+
comment: "Safety on and off (B): the shooting hand slides up its grip so the thumb flicks the lever while the clip cants the weapon; an AK-style rifle (akCharge) has its big lever on the right, pushed by the support hand over the top."
|
|
58679
|
+
}
|
|
58275
58680
|
};
|
|
58276
58681
|
|
|
58277
58682
|
// src/fx/hand-work.ts
|
|
@@ -58299,6 +58704,18 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
58299
58704
|
if (!right || !parts.slide) return null;
|
|
58300
58705
|
return { part: right.part, offset: right.offset.clone().add(V2(THREE, 0, 0.012, 0)), quaternion: right.quaternion };
|
|
58301
58706
|
}
|
|
58707
|
+
if (kind === "thumb_safety") {
|
|
58708
|
+
if (!right) return null;
|
|
58709
|
+
const toward2 = parts.safety ? right.part.worldToLocal(parts.safety.getWorldPosition(V2(THREE, 0, 0, 0))).sub(right.offset) : V2(THREE, 0, HAND_HOLD.thumbReach, 0);
|
|
58710
|
+
return { part: right.part, offset: right.offset.clone().add(toward2.clampLength(0, HAND_HOLD.thumbReach)), quaternion: right.quaternion };
|
|
58711
|
+
}
|
|
58712
|
+
if (kind === "lever_right") {
|
|
58713
|
+
const receiver = parts.bolt ?? parts.boltHandle;
|
|
58714
|
+
if (!receiver) return null;
|
|
58715
|
+
const b2 = boxIn(THREE, root, receiver);
|
|
58716
|
+
const x = parts.trigger ? root.worldToLocal(parts.trigger.getWorldPosition(V2(THREE, 0, 0, 0))).x : b2.min.x;
|
|
58717
|
+
return anchorAt(receiver, V2(THREE, x, b2.getCenter(V2(THREE, 0, 0, 0)).y, b2.max.z + palm), handFrame(THREE, V2(THREE, 1, 0, 0), V2(THREE, 0, 0, 1)));
|
|
58718
|
+
}
|
|
58302
58719
|
if (!part) return null;
|
|
58303
58720
|
const b = boxIn(THREE, root, part);
|
|
58304
58721
|
const c = b.getCenter(V2(THREE, 0, 0, 0));
|