@adatechnology/game-destruction-three 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +17 -1
- package/dist/index.global.js +335 -79
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +318 -70
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.global.js
CHANGED
|
@@ -43,10 +43,12 @@ var GameDestructionThree = (() => {
|
|
|
43
43
|
FIRE_FALLOFF: () => FIRE_FALLOFF,
|
|
44
44
|
FIRE_LOOP: () => FIRE_LOOP,
|
|
45
45
|
FIRE_SOUNDS: () => FIRE_SOUNDS,
|
|
46
|
+
GRENADE_PIN_PULL: () => GRENADE_PIN_PULL,
|
|
46
47
|
IMPACT_FAMILY_TAGS: () => IMPACT_FAMILY_TAGS,
|
|
47
48
|
IMPACT_SOUNDS: () => IMPACT_SOUNDS,
|
|
48
49
|
IMPACT_SOUND_KEYS: () => IMPACT_SOUND_KEYS,
|
|
49
50
|
SCENE_SOUNDS: () => SCENE_SOUNDS,
|
|
51
|
+
SMOKE_HISS_LOOP: () => SMOKE_HISS_LOOP,
|
|
50
52
|
blastFireSampleBank: () => blastFireSampleBank,
|
|
51
53
|
breakSoundKeys: () => breakSoundKeys,
|
|
52
54
|
createDestructionRenderer: () => createDestructionRenderer,
|
|
@@ -244,7 +246,8 @@ var GameDestructionThree = (() => {
|
|
|
244
246
|
var EVENT_BUDGET = { particles: 400, debris: 40, blastLights: 4, fireLights: 2 };
|
|
245
247
|
var explosion_aliases_default = {
|
|
246
248
|
carl_gustaf_84mm: "at4_84mm",
|
|
247
|
-
vog_25: "m79_40mm"
|
|
249
|
+
vog_25: "m79_40mm",
|
|
250
|
+
semtex: "c4"
|
|
248
251
|
};
|
|
249
252
|
var explosions_default = {
|
|
250
253
|
m67: {
|
|
@@ -6221,8 +6224,8 @@ var GameDestructionThree = (() => {
|
|
|
6221
6224
|
id: "dust",
|
|
6222
6225
|
additive: false,
|
|
6223
6226
|
life: [
|
|
6224
|
-
|
|
6225
|
-
|
|
6227
|
+
2,
|
|
6228
|
+
5
|
|
6226
6229
|
],
|
|
6227
6230
|
size: [
|
|
6228
6231
|
0.08,
|
|
@@ -6289,8 +6292,8 @@ var GameDestructionThree = (() => {
|
|
|
6289
6292
|
id: "chips",
|
|
6290
6293
|
additive: false,
|
|
6291
6294
|
life: [
|
|
6292
|
-
|
|
6293
|
-
|
|
6295
|
+
2,
|
|
6296
|
+
4
|
|
6294
6297
|
],
|
|
6295
6298
|
size: [
|
|
6296
6299
|
0.025,
|
|
@@ -7737,7 +7740,7 @@ var GameDestructionThree = (() => {
|
|
|
7737
7740
|
};
|
|
7738
7741
|
var EFFECT_STEP_SECONDS = 1 / 50;
|
|
7739
7742
|
|
|
7740
|
-
// ../game-vfx-three/dist/chunk-
|
|
7743
|
+
// ../game-vfx-three/dist/chunk-IOGYK5RO.js
|
|
7741
7744
|
var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
|
|
7742
7745
|
void main(){vec3 R=vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]);vec3 Up=vec3(viewMatrix[0][1],viewMatrix[1][1],viewMatrix[2][1]);
|
|
7743
7746
|
vec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`;
|
|
@@ -7784,10 +7787,15 @@ void main(){
|
|
|
7784
7787
|
if(n<diss*1.08-.04)discard;
|
|
7785
7788
|
float lam=max(dot(N,uLight),0.);
|
|
7786
7789
|
float band=lam>.55?1.:lam>.15?.68:.42;
|
|
7787
|
-
|
|
7790
|
+
// soot: a dark smoke still scatters some light, so it never goes flat black, and its rim takes the sky so its volume reads (only the dark puffs; the rest as the Lab)
|
|
7791
|
+
float dark=1.-smoothstep(.02,.12,dot(vC,vec3(.2126,.7152,.0722)));
|
|
7792
|
+
vec3 alb=max(vC,vec3(.055)*dark);
|
|
7793
|
+
vec3 col=alb*(vec3(.5,.56,.7)*band*1.05+vec3(.2,.19,.18)+uGround*(.4-.4*N.y));
|
|
7794
|
+
float rim=pow(1.-clamp(abs(dot(N,normalize(cameraPosition-vWP))),0.,1.),2.);
|
|
7795
|
+
col+=dark*rim*(uSky*.9+alb);
|
|
7788
7796
|
vec3 tg=uGlowPos-vWP;float dg=length(tg)+.001;
|
|
7789
7797
|
float gi=uGlowI/(1.+dg*dg*.05)*(.3+.7*max(dot(N,tg/dg),0.));
|
|
7790
|
-
col+=uGlowCol*gi*
|
|
7798
|
+
col+=uGlowCol*gi*alb*1.6;
|
|
7791
7799
|
float h=heat*(.72+.56*n)+heat*(-N.y)*.18;
|
|
7792
7800
|
if(h>.05){
|
|
7793
7801
|
float hq=floor(h*5.+.5)/5.;
|
|
@@ -9939,7 +9947,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9939
9947
|
var SLAM_BOUNCE = 0.2;
|
|
9940
9948
|
var SLAM_FLOOR_NORMAL = 0.7;
|
|
9941
9949
|
var SUB_DT = 1 / 120;
|
|
9942
|
-
var
|
|
9950
|
+
var MU_STATIC = 0.7;
|
|
9951
|
+
var MU_DYNAMIC = 0.6;
|
|
9952
|
+
var SLIP_SPEED = 0.02;
|
|
9943
9953
|
var SLEEP_AFTER = 0.5;
|
|
9944
9954
|
var CALM_DRIFT = 0.01;
|
|
9945
9955
|
var CALM_TURN = 0.05;
|
|
@@ -9958,6 +9968,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9958
9968
|
var EDGE_POINTS = [-0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75];
|
|
9959
9969
|
var SUPPORT_CHECKS2 = 16;
|
|
9960
9970
|
var SUPPORT_GAP2 = 0.02;
|
|
9971
|
+
var WAKE_ABOVE_SPEED = 0.05;
|
|
9972
|
+
var WAKE_ABOVE_SPIN = 0.1;
|
|
9973
|
+
var topFace = 1;
|
|
9961
9974
|
var boxTopAt = (s, x, z) => {
|
|
9962
9975
|
const e = s.e;
|
|
9963
9976
|
const dx = x - s.c[0], dz = z - s.c[2];
|
|
@@ -9973,11 +9986,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9973
9986
|
let a = (-h - p) / d, b = (h - p) / d;
|
|
9974
9987
|
if (a > b) [a, b] = [b, a];
|
|
9975
9988
|
t0 = Math.max(t0, a);
|
|
9976
|
-
|
|
9989
|
+
if (b < t1) {
|
|
9990
|
+
t1 = b;
|
|
9991
|
+
topFace = k;
|
|
9992
|
+
}
|
|
9977
9993
|
if (t0 > t1) return -Infinity;
|
|
9978
9994
|
}
|
|
9979
9995
|
return s.c[1] + t1;
|
|
9980
9996
|
};
|
|
9997
|
+
var UP16 = [0, 1, 0];
|
|
9998
|
+
var faceNormal = (s, axis2) => {
|
|
9999
|
+
const e = s.e;
|
|
10000
|
+
const sign = e[axis2 * 4 + 1] < 0 ? -1 : 1;
|
|
10001
|
+
return [sign * e[axis2 * 4], sign * e[axis2 * 4 + 1], sign * e[axis2 * 4 + 2]];
|
|
10002
|
+
};
|
|
10003
|
+
var pairFriction = (a, b) => ({
|
|
10004
|
+
static: Math.sqrt((a?.static ?? MU_STATIC) * (b?.static ?? MU_STATIC)),
|
|
10005
|
+
dynamic: Math.sqrt((a?.dynamic ?? MU_DYNAMIC) * (b?.dynamic ?? MU_DYNAMIC))
|
|
10006
|
+
});
|
|
9981
10007
|
var PieceSystem = class {
|
|
9982
10008
|
constructor(THREE, o) {
|
|
9983
10009
|
__publicField(this, "THREE", THREE);
|
|
@@ -9991,6 +10017,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9991
10017
|
__publicField(this, "ax");
|
|
9992
10018
|
__publicField(this, "solids", []);
|
|
9993
10019
|
__publicField(this, "supportTurn", 0);
|
|
10020
|
+
/** The surface normal and the friction of what the last `supportAt` found under the point (the ground and fixed tops are level: up, and no piece). */
|
|
10021
|
+
__publicField(this, "supportNormal", UP16);
|
|
10022
|
+
__publicField(this, "supportFriction", null);
|
|
9994
10023
|
this.p = new THREE.Vector3();
|
|
9995
10024
|
this.q = new THREE.Quaternion();
|
|
9996
10025
|
this.dq = new THREE.Quaternion();
|
|
@@ -10096,8 +10125,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10096
10125
|
const lz = (e[8] * x + e[9] * y + e[10] * z) * iz;
|
|
10097
10126
|
return [e[0] * lx + e[4] * ly + e[8] * lz, e[1] * lx + e[5] * ly + e[9] * lz, e[2] * lx + e[6] * ly + e[10] * lz];
|
|
10098
10127
|
}
|
|
10099
|
-
/** The points of the box that touch the ground, in its own frame: the 8 corners, then (with `edges`) `EDGE_POINTS` along each of its 12 edges
|
|
10128
|
+
/** The points of the box that touch the ground, in its own frame: the 8 corners, then (with `edges`) `EDGE_POINTS` along each of its 12 edges; or the piece's own `contacts`. */
|
|
10100
10129
|
samples(b, edges) {
|
|
10130
|
+
if (b.contacts) return b.contacts;
|
|
10101
10131
|
const [hx, hy, hz2] = b.halfExtents;
|
|
10102
10132
|
const out = [];
|
|
10103
10133
|
for (let i = 0; i < 8; i++) out.push([i & 1 ? hx : -hx, i & 2 ? hy : -hy, i & 4 ? hz2 : -hz2]);
|
|
@@ -10162,6 +10192,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10162
10192
|
}
|
|
10163
10193
|
if (b.sleeping) continue;
|
|
10164
10194
|
this.integrate(b, dt);
|
|
10195
|
+
if (b.sleeping) continue;
|
|
10196
|
+
const v = b.velocity;
|
|
10197
|
+
const w = b.angularVelocity;
|
|
10198
|
+
if (Math.hypot(v[0], v[1], v[2]) > WAKE_ABOVE_SPEED || Math.hypot(w[0], w[1], w[2]) > WAKE_ABOVE_SPIN) this.wakeAbove(b);
|
|
10165
10199
|
}
|
|
10166
10200
|
this.separate(dt);
|
|
10167
10201
|
}
|
|
@@ -10192,15 +10226,48 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10192
10226
|
}
|
|
10193
10227
|
return false;
|
|
10194
10228
|
}
|
|
10229
|
+
/** The highest top of a piece (other than `self`) under `(x, z)` that a point at height `y` stands on (at most `step` above it), -Infinity when none; the boxes of the last update. */
|
|
10230
|
+
topAt(x, z, y, step, self) {
|
|
10231
|
+
let top = -Infinity;
|
|
10232
|
+
for (const s of this.solids) {
|
|
10233
|
+
if (s.body.objectId === self || s.body.held || Math.abs(x - s.c[0]) > s.r || Math.abs(z - s.c[2]) > s.r) continue;
|
|
10234
|
+
const t = boxTopAt(s, x, z);
|
|
10235
|
+
if (t > top && t <= y + step) top = t;
|
|
10236
|
+
}
|
|
10237
|
+
return top;
|
|
10238
|
+
}
|
|
10239
|
+
/** What lay asleep on `b` (a box over its top) wakes at once when `b` moves: a can on a table that goes over goes with it. */
|
|
10240
|
+
wakeAbove(b) {
|
|
10241
|
+
const below = this.solids.find((s) => s.body === b);
|
|
10242
|
+
if (!below) return;
|
|
10243
|
+
const top = below.c[1] + below.half[0] * Math.abs(below.e[1]) + below.half[1] * Math.abs(below.e[5]) + below.half[2] * Math.abs(below.e[9]);
|
|
10244
|
+
for (const s of this.solids) {
|
|
10245
|
+
const o = s.body;
|
|
10246
|
+
if (!o.sleeping || o.held || o === b) continue;
|
|
10247
|
+
if (s.c[1] < below.c[1] || s.c[1] - s.r > top + STEP || Math.hypot(s.c[0] - below.c[0], s.c[2] - below.c[2]) > s.r + below.r) continue;
|
|
10248
|
+
o.sleeping = false;
|
|
10249
|
+
o.still = 0;
|
|
10250
|
+
o.calmAt = null;
|
|
10251
|
+
}
|
|
10252
|
+
}
|
|
10195
10253
|
/** Where a corner of `self` at `(x, y, z)` lands: the ground, the top of another piece or of something fixed under it. */
|
|
10196
10254
|
supportAt(self, x, y, z, step) {
|
|
10197
10255
|
let top = this.o.groundAt(x, z);
|
|
10256
|
+
let on = null;
|
|
10257
|
+
let face = 1;
|
|
10198
10258
|
for (const s of this.solids) {
|
|
10199
10259
|
if (s.body === self || s.body.held || Math.abs(x - s.c[0]) > s.r || Math.abs(z - s.c[2]) > s.r) continue;
|
|
10200
10260
|
const t = boxTopAt(s, x, z);
|
|
10201
|
-
if (t > top && t <= y + step)
|
|
10261
|
+
if (t > top && t <= y + step) {
|
|
10262
|
+
top = t;
|
|
10263
|
+
on = s;
|
|
10264
|
+
face = topFace;
|
|
10265
|
+
}
|
|
10202
10266
|
}
|
|
10203
10267
|
const fixed = this.o.staticTopAt?.(x, z, y, step) ?? -Infinity;
|
|
10268
|
+
if (fixed >= top) on = null;
|
|
10269
|
+
this.supportNormal = on ? faceNormal(on, face) : UP16;
|
|
10270
|
+
this.supportFriction = on?.body.friction ?? null;
|
|
10204
10271
|
return Math.max(top, fixed);
|
|
10205
10272
|
}
|
|
10206
10273
|
/** Stops a piece that ran into something on its way from `start` to `pos` (`sweep`): moves it back to where it met it and takes the speed into it. */
|
|
@@ -10283,7 +10350,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10283
10350
|
const e = this.m.elements;
|
|
10284
10351
|
const rs = [];
|
|
10285
10352
|
const ids = [];
|
|
10353
|
+
const ns = [];
|
|
10354
|
+
const mus = [];
|
|
10286
10355
|
let maxPen = 0;
|
|
10356
|
+
let penNormal = UP16;
|
|
10287
10357
|
const rise = STEP + Math.max(0, -v[1]) * h * 2;
|
|
10288
10358
|
let side = null;
|
|
10289
10359
|
let lowest = Infinity;
|
|
@@ -10301,6 +10371,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10301
10371
|
if (floor2 !== void 0 && floor2 - cy > -SLOP && floor2 < raw - STEP) {
|
|
10302
10372
|
rs.push(r);
|
|
10303
10373
|
ids.push(i);
|
|
10374
|
+
ns.push(UP16);
|
|
10375
|
+
mus.push(pairFriction(b.friction, null));
|
|
10304
10376
|
maxPen = Math.max(maxPen, floor2 - cy);
|
|
10305
10377
|
}
|
|
10306
10378
|
continue;
|
|
@@ -10321,12 +10393,19 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10321
10393
|
}
|
|
10322
10394
|
continue;
|
|
10323
10395
|
}
|
|
10324
|
-
|
|
10396
|
+
this.supportNormal = UP16;
|
|
10397
|
+
this.supportFriction = null;
|
|
10398
|
+
const gy = i < 8 || b.contacts ? this.supportAt(b, px, cy, pz, rise) : Math.max(raw, this.o.staticTopAt?.(px, pz, cy, rise) ?? -Infinity);
|
|
10325
10399
|
const pen = gy - cy;
|
|
10326
10400
|
if (pen > -SLOP) {
|
|
10327
10401
|
rs.push(r);
|
|
10328
10402
|
ids.push(i);
|
|
10329
|
-
|
|
10403
|
+
ns.push(this.supportNormal);
|
|
10404
|
+
mus.push(pairFriction(b.friction, this.supportFriction));
|
|
10405
|
+
if (pen > maxPen) {
|
|
10406
|
+
maxPen = pen;
|
|
10407
|
+
penNormal = this.supportNormal;
|
|
10408
|
+
}
|
|
10330
10409
|
lowest = Math.min(lowest, gy);
|
|
10331
10410
|
}
|
|
10332
10411
|
}
|
|
@@ -10339,7 +10418,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10339
10418
|
contactedLast = rs.length > 0;
|
|
10340
10419
|
if (contactedLast) touched = true;
|
|
10341
10420
|
if (!contactedLast) continue;
|
|
10342
|
-
|
|
10421
|
+
const out = Math.max(0, maxPen - SLOP * 0.5) * penNormal[1];
|
|
10422
|
+
pos[0] += penNormal[0] * out;
|
|
10423
|
+
pos[1] += penNormal[1] * out;
|
|
10424
|
+
pos[2] += penNormal[2] * out;
|
|
10343
10425
|
let impact3 = 0;
|
|
10344
10426
|
for (const r of rs) impact3 = Math.max(impact3, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
10345
10427
|
const restitution = impact3 > 2 ? 0.25 : 0;
|
|
@@ -10355,76 +10437,72 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10355
10437
|
const lz = pos[2] + sz / rs.length;
|
|
10356
10438
|
landPoint = [lx, pos[1] - Math.max(...rs.map((r) => -r[1])), lz];
|
|
10357
10439
|
}
|
|
10358
|
-
const
|
|
10359
|
-
|
|
10360
|
-
|
|
10440
|
+
const pointV = (r) => [v[0] + w[1] * r[2] - w[2] * r[1], v[1] + w[2] * r[0] - w[0] * r[2], v[2] + w[0] * r[1] - w[1] * r[0]];
|
|
10441
|
+
const dot = (a, c2) => a[0] * c2[0] + a[1] * c2[1] + a[2] * c2[2];
|
|
10442
|
+
const goal = rs.map((r, k) => {
|
|
10443
|
+
const vn = dot(pointV(r), ns[k]);
|
|
10444
|
+
return vn < 0 ? -restitution * vn : 0;
|
|
10445
|
+
});
|
|
10446
|
+
const slipping = rs.map((r, k) => {
|
|
10447
|
+
const pv = pointV(r);
|
|
10448
|
+
const vn = dot(pv, ns[k]);
|
|
10449
|
+
return Math.hypot(pv[0] - vn * ns[k][0], pv[1] - vn * ns[k][1], pv[2] - vn * ns[k][2]) > SLIP_SPEED;
|
|
10361
10450
|
});
|
|
10362
10451
|
const vx0 = v[0];
|
|
10363
10452
|
const vz0 = v[2];
|
|
10364
|
-
if (!b.warm || b.warm.length !== WARM_SLOTS *
|
|
10453
|
+
if (!b.warm || b.warm.length !== WARM_SLOTS * 4) b.warm = new Float64Array(WARM_SLOTS * 4);
|
|
10365
10454
|
const warm = b.warm;
|
|
10366
|
-
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id *
|
|
10367
|
-
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id *
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
v[0] += fx * invM;
|
|
10374
|
-
v[1] += jn * invM;
|
|
10375
|
-
v[2] += fz * invM;
|
|
10376
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * fz - r[2] * jn, r[2] * fx - r[0] * fz, r[0] * jn - r[1] * fx);
|
|
10455
|
+
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id * 4] : 0);
|
|
10456
|
+
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id * 4 + 1], warm[id * 4 + 2], warm[id * 4 + 3]] : [0, 0, 0]);
|
|
10457
|
+
const push = (r, j) => {
|
|
10458
|
+
v[0] += j[0] * invM;
|
|
10459
|
+
v[1] += j[1] * invM;
|
|
10460
|
+
v[2] += j[2] * invM;
|
|
10461
|
+
const dw = this.mulInvInertiaWith(b, e, r[1] * j[2] - r[2] * j[1], r[2] * j[0] - r[0] * j[2], r[0] * j[1] - r[1] * j[0]);
|
|
10377
10462
|
w[0] += dw[0];
|
|
10378
10463
|
w[1] += dw[1];
|
|
10379
10464
|
w[2] += dw[2];
|
|
10465
|
+
};
|
|
10466
|
+
const reach = (r, d) => {
|
|
10467
|
+
const a = this.mulInvInertiaWith(b, e, r[1] * d[2] - r[2] * d[1], r[2] * d[0] - r[0] * d[2], r[0] * d[1] - r[1] * d[0]);
|
|
10468
|
+
return invM + (a[1] * r[2] - a[2] * r[1]) * d[0] + (a[2] * r[0] - a[0] * r[2]) * d[1] + (a[0] * r[1] - a[1] * r[0]) * d[2];
|
|
10469
|
+
};
|
|
10470
|
+
for (let k = 0; k < rs.length; k++) {
|
|
10471
|
+
const n = ns[k];
|
|
10472
|
+
const t = accT[k];
|
|
10473
|
+
if (accN[k] !== 0 || t[0] !== 0 || t[1] !== 0 || t[2] !== 0) push(rs[k], [n[0] * accN[k] + t[0], n[1] * accN[k] + t[1], n[2] * accN[k] + t[2]]);
|
|
10380
10474
|
}
|
|
10381
10475
|
for (let it = 0; it < SOLVER_PASSES; it++) {
|
|
10382
10476
|
for (let k = 0; k < rs.length; k++) {
|
|
10383
10477
|
const r = rs[k];
|
|
10384
|
-
const
|
|
10385
|
-
const
|
|
10386
|
-
const
|
|
10387
|
-
const total = Math.max(0, accN[k] + (goal[k] - vy2) / denom);
|
|
10478
|
+
const n = ns[k];
|
|
10479
|
+
const vn = dot(pointV(r), n);
|
|
10480
|
+
const total = Math.max(0, accN[k] + (goal[k] - vn) / reach(r, n));
|
|
10388
10481
|
const j = total - accN[k];
|
|
10389
10482
|
accN[k] = total;
|
|
10390
|
-
if (j !== 0)
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
if (
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
fx -= tx * jt;
|
|
10410
|
-
fz -= tz * jt;
|
|
10411
|
-
}
|
|
10412
|
-
const most = MU * accN[k];
|
|
10413
|
-
const f = Math.hypot(fx, fz);
|
|
10414
|
-
if (f > most) {
|
|
10415
|
-
fx *= most / f;
|
|
10416
|
-
fz *= most / f;
|
|
10483
|
+
if (j !== 0) push(r, [n[0] * j, n[1] * j, n[2] * j]);
|
|
10484
|
+
const pv = pointV(r);
|
|
10485
|
+
const pn = dot(pv, n);
|
|
10486
|
+
const vt = [pv[0] - pn * n[0], pv[1] - pn * n[1], pv[2] - pn * n[2]];
|
|
10487
|
+
const speed = Math.hypot(vt[0], vt[1], vt[2]);
|
|
10488
|
+
const f = [accT[k][0], accT[k][1], accT[k][2]];
|
|
10489
|
+
if (speed > 1e-6) {
|
|
10490
|
+
const d = [vt[0] / speed, vt[1] / speed, vt[2] / speed];
|
|
10491
|
+
const jt = speed / reach(r, d);
|
|
10492
|
+
f[0] -= d[0] * jt;
|
|
10493
|
+
f[1] -= d[1] * jt;
|
|
10494
|
+
f[2] -= d[2] * jt;
|
|
10495
|
+
} else if (f[0] === 0 && f[1] === 0 && f[2] === 0) continue;
|
|
10496
|
+
const most = (slipping[k] ? mus[k].dynamic : mus[k].static) * accN[k];
|
|
10497
|
+
const fl = Math.hypot(f[0], f[1], f[2]);
|
|
10498
|
+
if (fl > most) {
|
|
10499
|
+
f[0] *= most / fl;
|
|
10500
|
+
f[1] *= most / fl;
|
|
10501
|
+
f[2] *= most / fl;
|
|
10417
10502
|
}
|
|
10418
|
-
const
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
if (dx === 0 && dz === 0) continue;
|
|
10422
|
-
v[0] += dx * invM;
|
|
10423
|
-
v[2] += dz * invM;
|
|
10424
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * dz, r[2] * dx - r[0] * dz, -r[1] * dx);
|
|
10425
|
-
w[0] += dw[0];
|
|
10426
|
-
w[1] += dw[1];
|
|
10427
|
-
w[2] += dw[2];
|
|
10503
|
+
const dj = [f[0] - accT[k][0], f[1] - accT[k][1], f[2] - accT[k][2]];
|
|
10504
|
+
accT[k] = f;
|
|
10505
|
+
if (dj[0] !== 0 || dj[1] !== 0 || dj[2] !== 0) push(r, dj);
|
|
10428
10506
|
}
|
|
10429
10507
|
}
|
|
10430
10508
|
pos[0] += (v[0] - vx0) * h;
|
|
@@ -10433,9 +10511,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10433
10511
|
for (let k = 0; k < rs.length; k++) {
|
|
10434
10512
|
const id = ids[k];
|
|
10435
10513
|
if (id >= WARM_SLOTS) continue;
|
|
10436
|
-
warm[id *
|
|
10437
|
-
warm[id *
|
|
10438
|
-
warm[id *
|
|
10514
|
+
warm[id * 4] = accN[k];
|
|
10515
|
+
warm[id * 4 + 1] = accT[k][0];
|
|
10516
|
+
warm[id * 4 + 2] = accT[k][1];
|
|
10517
|
+
warm[id * 4 + 3] = accT[k][2];
|
|
10439
10518
|
}
|
|
10440
10519
|
let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0;
|
|
10441
10520
|
for (const r of rs) {
|
|
@@ -10507,6 +10586,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10507
10586
|
b.calm += dt;
|
|
10508
10587
|
return b.calm >= CALM_SLEEP_AFTER;
|
|
10509
10588
|
}
|
|
10589
|
+
/** Whether the centre `c` of `upper` is over the top of `lower`'s box (as it was at the start of the update). */
|
|
10590
|
+
onTopOf(upper, lower, c) {
|
|
10591
|
+
const s = this.solids.find((one) => one.body === lower);
|
|
10592
|
+
if (!s) return false;
|
|
10593
|
+
const top = boxTopAt(s, c.x, c.z);
|
|
10594
|
+
return top > -Infinity && c.y >= top - 0.01;
|
|
10595
|
+
}
|
|
10510
10596
|
/** Half the height of the box of a piece as it is turned now. */
|
|
10511
10597
|
halfHeight(b) {
|
|
10512
10598
|
const e = this.m.makeRotationFromQuaternion(b.root.quaternion).elements;
|
|
@@ -10532,6 +10618,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10532
10618
|
this.centerOf(b, cb);
|
|
10533
10619
|
if (Math.abs(ca.y - cb.y) > ra + rb) continue;
|
|
10534
10620
|
if (Math.abs(ca.y - cb.y) >= (this.halfHeight(a) + this.halfHeight(b)) * 0.9) continue;
|
|
10621
|
+
if (this.onTopOf(b, a, cb) || this.onTopOf(a, b, ca)) continue;
|
|
10535
10622
|
const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8;
|
|
10536
10623
|
let dx = cb.x - ca.x;
|
|
10537
10624
|
let dz = cb.z - ca.z;
|
|
@@ -13462,6 +13549,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13462
13549
|
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
13463
13550
|
var GLOW_FROM_C = 430;
|
|
13464
13551
|
var QUEUE_CAP = 4e3;
|
|
13552
|
+
var TRICKLE_SECONDS = [2, 5];
|
|
13553
|
+
var TRICKLE_EVERY = 0.25;
|
|
13554
|
+
var TRICKLE_MAX = 24;
|
|
13465
13555
|
var STRUCTURAL_EVENTS = /* @__PURE__ */ new Set([
|
|
13466
13556
|
"fracture",
|
|
13467
13557
|
"detach",
|
|
@@ -13485,6 +13575,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13485
13575
|
var FEET_BAND = 0.03;
|
|
13486
13576
|
var TIP_MIN_HEIGHT = 0.3;
|
|
13487
13577
|
var TIP_NARROW = 0.3;
|
|
13578
|
+
var CONTACT_MESHES = 12;
|
|
13579
|
+
var BROKEN_GAP = 0.03;
|
|
13580
|
+
var BROKEN_SAMPLES = [[0.5, 0.5], [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9]];
|
|
13581
|
+
var UNCHECKED_KINDS = /* @__PURE__ */ new Set(["structure", "room", "ground"]);
|
|
13488
13582
|
var FIRE_CEILING_SECONDS = 3600;
|
|
13489
13583
|
var GENERIC_REFERENCE_RADIUS = 15;
|
|
13490
13584
|
var SWEEP_CORE = 0.15;
|
|
@@ -13584,10 +13678,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13584
13678
|
__publicField(this, "staticRound", []);
|
|
13585
13679
|
/** Boxes of the pieces' meshes (as `staticBoxes`), with the time they were measured. */
|
|
13586
13680
|
__publicField(this, "pieceBoxes", /* @__PURE__ */ new WeakMap());
|
|
13681
|
+
/** Objects broken since the last update: `checkBroken` looks at what is left of them. */
|
|
13682
|
+
__publicField(this, "broken", /* @__PURE__ */ new Set());
|
|
13587
13683
|
__publicField(this, "staticAt", 0);
|
|
13588
13684
|
__publicField(this, "queue", []);
|
|
13589
13685
|
/** The cover of each body around a part of the world that went off, read as the event is emitted: before the blast tore or moved anything. */
|
|
13590
13686
|
__publicField(this, "coverWhenWentOff", /* @__PURE__ */ new WeakMap());
|
|
13687
|
+
/** Places a blow broke a hard face at: grit and dust keep coming off them for a few seconds, less and less (`TRICKLE_SECONDS`). */
|
|
13688
|
+
__publicField(this, "trickles", []);
|
|
13591
13689
|
/** Presentation work an event left for later frames (fragments still to spawn, loose groups still to split), run in order within the frame budget. */
|
|
13592
13690
|
__publicField(this, "slices", []);
|
|
13593
13691
|
/** When the work of this frame must stop; `Infinity` outside a drain, where a call finishes what it started. */
|
|
@@ -13930,10 +14028,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13930
14028
|
dt = clamp6(dt, 0, 0.1);
|
|
13931
14029
|
this.time += dt;
|
|
13932
14030
|
if (!this.attached) this.drainQueue();
|
|
14031
|
+
this.checkBroken();
|
|
13933
14032
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
13934
14033
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
13935
14034
|
this.updateActive(dt);
|
|
13936
14035
|
this.updateLeaks(dt);
|
|
14036
|
+
this.updateTrickles(dt);
|
|
13937
14037
|
this.pieceSys.update(dt);
|
|
13938
14038
|
this.fragments.update(dt);
|
|
13939
14039
|
this.ground.update(dt);
|
|
@@ -14132,6 +14232,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14132
14232
|
}
|
|
14133
14233
|
apply(ev) {
|
|
14134
14234
|
this.staticDirty.add(ev.object);
|
|
14235
|
+
if (ev.type === "fracture" || ev.type === "detach" || ev.type === "collapse" || ev.type === "burnout" || ev.type === "destroyed") this.broken.add(ev.object);
|
|
14135
14236
|
switch (ev.type) {
|
|
14136
14237
|
case "damage":
|
|
14137
14238
|
if (ev.damageType === "heat" || ev.damageType === "fire" || ev.damageType === "explosive") this.activate(ev.object, ev.part);
|
|
@@ -14456,9 +14557,34 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14456
14557
|
const core = def?.visual?.innerColor;
|
|
14457
14558
|
if (core !== void 0) this.vfx.burst("chips", ev.point, [normal.x, normal.y, normal.z], 5, 2.5, { color: core });
|
|
14458
14559
|
else this.playEvent({ kind: "spall", material: this.impactMaterial(def) }, ev.point);
|
|
14560
|
+
if (core !== void 0) this.trickleFrom(ev.point, [normal.x, normal.y, normal.z], core);
|
|
14459
14561
|
if (mv.mesh.userData.drawnSpalls?.includes(ev.part)) return;
|
|
14460
14562
|
this.visuals.addSpall(mv, wp, normal, Math.sqrt(ev.area / Math.PI), core ?? fallbackColor(pv.material, def));
|
|
14461
14563
|
}
|
|
14564
|
+
/** Grit and dust go on falling from where a blow broke a hard face (masonry, concrete) for `TRICKLE_SECONDS`, a pinch at a time and less and less. */
|
|
14565
|
+
trickleFrom(point, normal, color) {
|
|
14566
|
+
if (this.trickles.length >= TRICKLE_MAX) this.trickles.shift();
|
|
14567
|
+
const total = TRICKLE_SECONDS[0] + (TRICKLE_SECONDS[1] - TRICKLE_SECONDS[0]) * this.rng();
|
|
14568
|
+
this.trickles.push({ point: [point[0], point[1], point[2]], normal, color, left: total, total, next: TRICKLE_EVERY });
|
|
14569
|
+
}
|
|
14570
|
+
updateTrickles(dt) {
|
|
14571
|
+
for (let i = this.trickles.length - 1; i >= 0; i--) {
|
|
14572
|
+
const t = this.trickles[i];
|
|
14573
|
+
t.left -= dt;
|
|
14574
|
+
t.next -= dt;
|
|
14575
|
+
if (t.left <= 0) {
|
|
14576
|
+
this.trickles.splice(i, 1);
|
|
14577
|
+
continue;
|
|
14578
|
+
}
|
|
14579
|
+
if (t.next > 0) continue;
|
|
14580
|
+
const share = t.left / t.total;
|
|
14581
|
+
t.next = TRICKLE_EVERY / Math.max(share, 0.25);
|
|
14582
|
+
const out = [t.normal[0] * 0.2, -1, t.normal[2] * 0.2];
|
|
14583
|
+
const options = t.color !== void 0 ? { color: t.color } : void 0;
|
|
14584
|
+
this.vfx.burst("chips", t.point, out, this.low ? 1 : 2, 0.3, options);
|
|
14585
|
+
if (this.rng() < 0.5) this.vfx.burst("dust", t.point, out, 1, 0.2, options);
|
|
14586
|
+
}
|
|
14587
|
+
}
|
|
14462
14588
|
/**
|
|
14463
14589
|
* Stage 2 of a staged part: the unit-sized fragments come out and the part stays drawn (hiding it is what `fracture` does). The opening itself
|
|
14464
14590
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
@@ -14494,6 +14620,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14494
14620
|
const tone = fr.inner && fdef?.visual?.innerColor !== void 0 ? fdef.visual.innerColor : fdef === def ? color : fallbackColor(fr.material, fdef);
|
|
14495
14621
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, tone, false), ev.object, maxAlive);
|
|
14496
14622
|
}
|
|
14623
|
+
if (def?.visual?.innerColor !== void 0 || pv?.material === "concrete" || pv?.material === "brick") this.trickleFrom(ev.center, [0, 0, 0], color);
|
|
14497
14624
|
if (ev.torn) {
|
|
14498
14625
|
this.impactSound(pv?.material ?? "", ev.center, 0.5, ev.blow);
|
|
14499
14626
|
return;
|
|
@@ -14683,26 +14810,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14683
14810
|
this.o.onPiece?.(piece);
|
|
14684
14811
|
}
|
|
14685
14812
|
/**
|
|
14686
|
-
*
|
|
14687
|
-
* meshes at the bottom of the object as it was whole; while the ones left still span the same floor (a cupboard with a broken door,
|
|
14813
|
+
* What lost what it stood on falls (furniture, a stand, a crate, a car; not a structure): with every leg of a table broken the top is loose, and with the legs of one side
|
|
14814
|
+
* gone what is left topples. Feet are the meshes at the bottom of the object as it was whole; while the ones left still span the same floor (a cupboard with a broken door,
|
|
14815
|
+
* a table missing one leg) it stands. A piece that lost a part is the box of what is left, touching the ground by the corners of each part (`setContacts`).
|
|
14688
14816
|
*/
|
|
14689
14817
|
loosenIfFootless(objectId) {
|
|
14690
14818
|
const b = this.bounds.get(objectId);
|
|
14691
14819
|
if (!b || !this.autoSplit) return;
|
|
14692
14820
|
const left = this.meshesOf(b).filter((mv) => this.visuals.shown(mv));
|
|
14693
14821
|
if (left.length === 0) return;
|
|
14822
|
+
const structural = UNCHECKED_KINDS.has(this.kindOf(b));
|
|
14694
14823
|
const body = b.piece ?? this.pieceSys.get(objectId);
|
|
14695
14824
|
if (body) {
|
|
14696
14825
|
const { half, center } = this.measure(b.root, left);
|
|
14697
14826
|
body.halfExtents = half;
|
|
14698
14827
|
this.pieceSys.setCenter(body, center);
|
|
14699
14828
|
body.mass = Math.max(0.05, this.massOf(objectId));
|
|
14829
|
+
if (!structural) this.setContacts(b, body, left);
|
|
14700
14830
|
body.sleeping = false;
|
|
14701
14831
|
return;
|
|
14702
14832
|
}
|
|
14703
|
-
if (
|
|
14833
|
+
if (structural) return;
|
|
14704
14834
|
const boxOf = (mv) => this.visuals.worldBox(mv, new this.THREE.Box3());
|
|
14705
14835
|
const whole = [...b.parts.values()].flatMap((pv) => pv.meshes).map(boxOf).filter((one) => !one.isEmpty());
|
|
14836
|
+
if (!this.standsOn(objectId, whole.map((one) => [one.min.x, one.min.y, one.min.z, one.max.x, one.max.y, one.max.z]))) return;
|
|
14706
14837
|
const floor2 = Math.min(...whole.map((one) => one.min.y));
|
|
14707
14838
|
const feetOf = (boxes) => boxes.filter((one) => one.min.y < floor2 + FEET_BAND).reduce((feet, one) => feet.union(one), new this.THREE.Box3());
|
|
14708
14839
|
const was = feetOf(whole);
|
|
@@ -14718,9 +14849,116 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14718
14849
|
if (!narrow && centreX > now.min.x && centreX < now.max.x && centreZ > now.min.z && centreZ < now.max.z) return;
|
|
14719
14850
|
}
|
|
14720
14851
|
const piece = this.makePiece(objectId);
|
|
14852
|
+
this.setContacts(b, b.piece ?? piece, left);
|
|
14721
14853
|
this.tipIfTopHeavy(b);
|
|
14722
14854
|
this.o.onLoosened?.(objectId, piece, "apart");
|
|
14723
14855
|
}
|
|
14856
|
+
shownBoxes(objectId, partId) {
|
|
14857
|
+
const b = this.bounds.get(objectId);
|
|
14858
|
+
if (!b) return [];
|
|
14859
|
+
const box = new this.THREE.Box3();
|
|
14860
|
+
const out = [];
|
|
14861
|
+
const pv = partId === void 0 ? null : b.parts.get(partId);
|
|
14862
|
+
if (partId !== void 0 && (!pv || pv.gone !== "none")) return out;
|
|
14863
|
+
for (const mv of pv ? pv.meshes : this.meshesOf(b)) {
|
|
14864
|
+
if (!this.visuals.shown(mv)) continue;
|
|
14865
|
+
this.visuals.worldBox(mv, box);
|
|
14866
|
+
if (!box.isEmpty()) out.push([box.min.x, box.min.y, box.min.z, box.max.x, box.max.y, box.max.z]);
|
|
14867
|
+
}
|
|
14868
|
+
return out;
|
|
14869
|
+
}
|
|
14870
|
+
/**
|
|
14871
|
+
* What is left of each object broken since the last update stands on something, or falls: an object that is not a piece whose lowest drawn box has neither the ground, a
|
|
14872
|
+
* fixed top nor a piece within `BROKEN_GAP` under it is made a piece on the spot (see `onBrokenChecked`).
|
|
14873
|
+
*/
|
|
14874
|
+
checkBroken() {
|
|
14875
|
+
if (this.broken.size === 0) return;
|
|
14876
|
+
const ids = [...this.broken];
|
|
14877
|
+
this.broken.clear();
|
|
14878
|
+
for (const id of ids) {
|
|
14879
|
+
const released = this.releaseIfFloating(id);
|
|
14880
|
+
this.o.onBrokenChecked?.(id, released);
|
|
14881
|
+
}
|
|
14882
|
+
}
|
|
14883
|
+
releaseIfFloating(objectId) {
|
|
14884
|
+
const b = this.bounds.get(objectId);
|
|
14885
|
+
if (!b || b.piece || this.pieceSys.get(objectId) || !this.autoSplit || UNCHECKED_KINDS.has(this.kindOf(b))) return false;
|
|
14886
|
+
const boxes = this.shownBoxes(objectId);
|
|
14887
|
+
if (boxes.length === 0) return false;
|
|
14888
|
+
const whole = new this.THREE.Box3();
|
|
14889
|
+
const wholeBoxes = [];
|
|
14890
|
+
for (const pv of b.parts.values()) {
|
|
14891
|
+
for (const mv of pv.meshes) {
|
|
14892
|
+
this.visuals.worldBox(mv, whole);
|
|
14893
|
+
if (!whole.isEmpty()) wholeBoxes.push([whole.min.x, whole.min.y, whole.min.z, whole.max.x, whole.max.y, whole.max.z]);
|
|
14894
|
+
}
|
|
14895
|
+
}
|
|
14896
|
+
if (!this.standsOn(objectId, wholeBoxes) || this.standsOn(objectId, boxes)) return false;
|
|
14897
|
+
const piece = this.makePiece(objectId);
|
|
14898
|
+
this.setContacts(b, b.piece ?? piece, this.meshesOf(b).filter((mv) => this.visuals.shown(mv)));
|
|
14899
|
+
this.tipIfTopHeavy(b);
|
|
14900
|
+
this.o.onLoosened?.(objectId, piece, "apart");
|
|
14901
|
+
return true;
|
|
14902
|
+
}
|
|
14903
|
+
/** Whether the lowest of `boxes` (world boxes of the object `objectId`) has the ground, a fixed top or a piece within `BROKEN_GAP` under it. */
|
|
14904
|
+
standsOn(objectId, boxes) {
|
|
14905
|
+
if (boxes.length === 0) return false;
|
|
14906
|
+
const low = Math.min(...boxes.map((one) => one[1]));
|
|
14907
|
+
for (const [x0, y0, z0, x1, , z1] of boxes) {
|
|
14908
|
+
if (y0 > low + FEET_BAND) continue;
|
|
14909
|
+
for (const [fx, fz] of BROKEN_SAMPLES) {
|
|
14910
|
+
const x = x0 + (x1 - x0) * fx;
|
|
14911
|
+
const z = z0 + (z1 - z0) * fz;
|
|
14912
|
+
const under = Math.max(this.groundAt(x, z), this.staticTopAtBut(objectId, x, z, y0, BROKEN_GAP), this.pieceSys.topAt(x, z, y0, BROKEN_GAP, objectId));
|
|
14913
|
+
if (under >= y0 - BROKEN_GAP) return true;
|
|
14914
|
+
}
|
|
14915
|
+
}
|
|
14916
|
+
return false;
|
|
14917
|
+
}
|
|
14918
|
+
/** The kind of kit piece a bound object is, by the mark on its root or the nearest one above it (a house binds a group inside its marked root); '' when unmarked. */
|
|
14919
|
+
kindOf(b) {
|
|
14920
|
+
for (let o = b.root; o; o = o.parent) {
|
|
14921
|
+
const kind = kitOriginOf(o)?.kind;
|
|
14922
|
+
if (kind) return kind;
|
|
14923
|
+
}
|
|
14924
|
+
return "";
|
|
14925
|
+
}
|
|
14926
|
+
/** `staticTopAt` without the boxes of `objectId` itself. */
|
|
14927
|
+
staticTopAtBut(objectId, x, z, y, step) {
|
|
14928
|
+
let top = -Infinity;
|
|
14929
|
+
for (const [id, a] of this.staticNow()) {
|
|
14930
|
+
if (id === objectId || a.length === 0 || x < a[0] || x > a[3] || z < a[2] || z > a[5]) continue;
|
|
14931
|
+
for (let i = 6; i < a.length; i += 6) {
|
|
14932
|
+
if (x < a[i] || x > a[i + 3] || z < a[i + 2] || z > a[i + 5]) continue;
|
|
14933
|
+
const t = a[i + 4];
|
|
14934
|
+
if (t > top && t <= y + step) top = t;
|
|
14935
|
+
}
|
|
14936
|
+
}
|
|
14937
|
+
return top;
|
|
14938
|
+
}
|
|
14939
|
+
/**
|
|
14940
|
+
* The piece touches the ground by the corners of each part left of it, not by the box around them all: on one leg, or on the legs of one side, its centre of mass is off
|
|
14941
|
+
* what holds it and it goes over until the top meets the floor. Past `CONTACT_MESHES` meshes it keeps the corners of its box.
|
|
14942
|
+
*/
|
|
14943
|
+
setContacts(b, body, meshes) {
|
|
14944
|
+
if (meshes.length > CONTACT_MESHES) {
|
|
14945
|
+
body.contacts = void 0;
|
|
14946
|
+
return;
|
|
14947
|
+
}
|
|
14948
|
+
const inv = specFrameInverse(this.THREE, b.root);
|
|
14949
|
+
const m4 = new this.THREE.Matrix4();
|
|
14950
|
+
const lb = new this.THREE.Box3();
|
|
14951
|
+
const [cx, cy, cz] = body.localCenter;
|
|
14952
|
+
const points = [];
|
|
14953
|
+
for (const mv of meshes) {
|
|
14954
|
+
mv.mesh.updateWorldMatrix(true, false);
|
|
14955
|
+
this.visuals.localBox(mv, lb);
|
|
14956
|
+
if (lb.isEmpty()) continue;
|
|
14957
|
+
lb.applyMatrix4(m4.multiplyMatrices(inv, mv.mesh.matrixWorld));
|
|
14958
|
+
for (let i = 0; i < 8; i++) points.push([(i & 1 ? lb.max.x : lb.min.x) - cx, (i & 2 ? lb.max.y : lb.min.y) - cy, (i & 4 ? lb.max.z : lb.min.z) - cz]);
|
|
14959
|
+
}
|
|
14960
|
+
body.contacts = points.length > 0 ? points : void 0;
|
|
14961
|
+
}
|
|
14724
14962
|
/**
|
|
14725
14963
|
* A piece of furniture standing on feet too close for its height (a board on two legs in a line, a lone leg) does not stay up: the box of a piece hides that its weight is up
|
|
14726
14964
|
* high and its feet are thin, so it gets the start of the fall, across the line of its feet.
|
|
@@ -15012,6 +15250,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15012
15250
|
this.active.clear();
|
|
15013
15251
|
for (const key of [...this.fires.keys()]) this.stopFire(key);
|
|
15014
15252
|
this.leaks.length = 0;
|
|
15253
|
+
this.trickles.length = 0;
|
|
15015
15254
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
15016
15255
|
for (const b of [...this.bounds.values()]) {
|
|
15017
15256
|
if (b.isSplit) {
|
|
@@ -15147,7 +15386,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15147
15386
|
return { id: options.id, samples };
|
|
15148
15387
|
}
|
|
15149
15388
|
|
|
15150
|
-
// ../game-audio/dist/chunk-
|
|
15389
|
+
// ../game-audio/dist/chunk-BUYBYP4L.js
|
|
15151
15390
|
var DISTANCE_CURVES = {
|
|
15152
15391
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
15153
15392
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -15325,6 +15564,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15325
15564
|
["revolver_38_357", 2, "sfx_pistol"],
|
|
15326
15565
|
["revolver_44_50ae", 3, "sfx_pistol"],
|
|
15327
15566
|
["smg_9mm_auto", 1, "sfx_pistol"],
|
|
15567
|
+
["launcher_40mm", 2],
|
|
15568
|
+
["rocket_launch", 2],
|
|
15328
15569
|
["shotgun_12", 1, "sfx_shotgun"],
|
|
15329
15570
|
["rifle_5_56", 1, "sfx_rifle"],
|
|
15330
15571
|
["rifle_7_62x39", 1, "sfx_rifle"],
|
|
@@ -16790,6 +17031,21 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
16790
17031
|
volume: 0,
|
|
16791
17032
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
16792
17033
|
};
|
|
17034
|
+
var SMOKE_HISS_LOOP = {
|
|
17035
|
+
id: "smoke_grenade_hiss",
|
|
17036
|
+
bus: "world",
|
|
17037
|
+
order: "chain_loop",
|
|
17038
|
+
source: { kind: "noise", loop: true },
|
|
17039
|
+
filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 1400 }, q: 0.7 }, { type: "lowpass", frequency: { set: 7e3 }, q: 0.5 }] },
|
|
17040
|
+
volume: 0,
|
|
17041
|
+
sources: ["sdk: a smoke canister venting (the gas through its holes, steady for its whole burn)"]
|
|
17042
|
+
};
|
|
17043
|
+
var GRENADE_PIN_PULL = blast(
|
|
17044
|
+
"grenade_pin_pull",
|
|
17045
|
+
[sceneNoise({ type: "highpass", f: [3200, 4200], d: 0.025, vol: 0.5 }), sceneNoise({ type: "bandpass", f: [2200, 2800], q: 2, d: 0.04, vol: 0.4, delay: 0.18 })],
|
|
17046
|
+
["sdk: the ring pulled from the fuse, then the spoon flying off"],
|
|
17047
|
+
FIRE_FALLOFF
|
|
17048
|
+
);
|
|
16793
17049
|
var FIRE_FUEL_OF = {
|
|
16794
17050
|
wood_soft: "wood",
|
|
16795
17051
|
wood_hard: "wood",
|
|
@@ -16979,7 +17235,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
16979
17235
|
impact2("plastic_crack", [sceneNoise({ type: "bandpass", f: 1800, q: 2, d: 0.06, vol: 0.75 }), sceneTone({ f: [450, 550], f2: 250, d: 0.05, vol: 0.22 })]),
|
|
16980
17236
|
impact2("mdf_crumble", [sceneNoise({ type: "lowpass", f: 700, d: 0.25, vol: 0.75, wet: 0.2 }), ...sceneMany(4, () => sceneNoise({ type: "lowpass", f: 500, d: 0.05, vol: 0.32, delay: [0.05, 0.3] }))])
|
|
16981
17237
|
];
|
|
16982
|
-
var DESTRUCTION_SOUND_BANK = { id: "destruction_three", sounds: [...SCENE_SOUNDS, ...DRAG_SOUNDS, ...LEAF_SOUNDS, ...IMPACT_SOUNDS, EXPLOSION_BOOM, ...BLAST_SOUNDS, ...FIRE_SOUNDS], loops: [BURN_LOOP, FIRE_LOOP, ...FIRE_FUEL_LOOPS] };
|
|
17238
|
+
var DESTRUCTION_SOUND_BANK = { id: "destruction_three", sounds: [...SCENE_SOUNDS, ...DRAG_SOUNDS, ...LEAF_SOUNDS, ...IMPACT_SOUNDS, EXPLOSION_BOOM, ...BLAST_SOUNDS, ...FIRE_SOUNDS, GRENADE_PIN_PULL], loops: [BURN_LOOP, FIRE_LOOP, ...FIRE_FUEL_LOOPS, SMOKE_HISS_LOOP] };
|
|
16983
17239
|
|
|
16984
17240
|
// src/audio/sound.ts
|
|
16985
17241
|
var IMPACT_SOUND_KEYS = {
|