@adatechnology/game-destruction-three 0.17.3 → 0.18.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/dist/index.d.ts +17 -1
- package/dist/index.global.js +338 -79
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +323 -71
- 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,
|
|
@@ -6222,8 +6224,8 @@ var GameDestructionThree = (() => {
|
|
|
6222
6224
|
id: "dust",
|
|
6223
6225
|
additive: false,
|
|
6224
6226
|
life: [
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
+
2,
|
|
6228
|
+
5
|
|
6227
6229
|
],
|
|
6228
6230
|
size: [
|
|
6229
6231
|
0.08,
|
|
@@ -6290,8 +6292,8 @@ var GameDestructionThree = (() => {
|
|
|
6290
6292
|
id: "chips",
|
|
6291
6293
|
additive: false,
|
|
6292
6294
|
life: [
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
+
2,
|
|
6296
|
+
4
|
|
6295
6297
|
],
|
|
6296
6298
|
size: [
|
|
6297
6299
|
0.025,
|
|
@@ -7738,7 +7740,7 @@ var GameDestructionThree = (() => {
|
|
|
7738
7740
|
};
|
|
7739
7741
|
var EFFECT_STEP_SECONDS = 1 / 50;
|
|
7740
7742
|
|
|
7741
|
-
// ../game-vfx-three/dist/chunk-
|
|
7743
|
+
// ../game-vfx-three/dist/chunk-IOGYK5RO.js
|
|
7742
7744
|
var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
|
|
7743
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]);
|
|
7744
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.);}`;
|
|
@@ -7785,10 +7787,15 @@ void main(){
|
|
|
7785
7787
|
if(n<diss*1.08-.04)discard;
|
|
7786
7788
|
float lam=max(dot(N,uLight),0.);
|
|
7787
7789
|
float band=lam>.55?1.:lam>.15?.68:.42;
|
|
7788
|
-
|
|
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);
|
|
7789
7796
|
vec3 tg=uGlowPos-vWP;float dg=length(tg)+.001;
|
|
7790
7797
|
float gi=uGlowI/(1.+dg*dg*.05)*(.3+.7*max(dot(N,tg/dg),0.));
|
|
7791
|
-
col+=uGlowCol*gi*
|
|
7798
|
+
col+=uGlowCol*gi*alb*1.6;
|
|
7792
7799
|
float h=heat*(.72+.56*n)+heat*(-N.y)*.18;
|
|
7793
7800
|
if(h>.05){
|
|
7794
7801
|
float hq=floor(h*5.+.5)/5.;
|
|
@@ -9940,7 +9947,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9940
9947
|
var SLAM_BOUNCE = 0.2;
|
|
9941
9948
|
var SLAM_FLOOR_NORMAL = 0.7;
|
|
9942
9949
|
var SUB_DT = 1 / 120;
|
|
9943
|
-
var
|
|
9950
|
+
var MU_STATIC = 0.7;
|
|
9951
|
+
var MU_DYNAMIC = 0.6;
|
|
9952
|
+
var SLIP_SPEED = 0.02;
|
|
9944
9953
|
var SLEEP_AFTER = 0.5;
|
|
9945
9954
|
var CALM_DRIFT = 0.01;
|
|
9946
9955
|
var CALM_TURN = 0.05;
|
|
@@ -9959,6 +9968,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9959
9968
|
var EDGE_POINTS = [-0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75];
|
|
9960
9969
|
var SUPPORT_CHECKS2 = 16;
|
|
9961
9970
|
var SUPPORT_GAP2 = 0.02;
|
|
9971
|
+
var WAKE_ABOVE_SPEED = 0.05;
|
|
9972
|
+
var WAKE_ABOVE_SPIN = 0.1;
|
|
9973
|
+
var topFace = 1;
|
|
9962
9974
|
var boxTopAt = (s, x, z) => {
|
|
9963
9975
|
const e = s.e;
|
|
9964
9976
|
const dx = x - s.c[0], dz = z - s.c[2];
|
|
@@ -9974,11 +9986,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9974
9986
|
let a = (-h - p) / d, b = (h - p) / d;
|
|
9975
9987
|
if (a > b) [a, b] = [b, a];
|
|
9976
9988
|
t0 = Math.max(t0, a);
|
|
9977
|
-
|
|
9989
|
+
if (b < t1) {
|
|
9990
|
+
t1 = b;
|
|
9991
|
+
topFace = k;
|
|
9992
|
+
}
|
|
9978
9993
|
if (t0 > t1) return -Infinity;
|
|
9979
9994
|
}
|
|
9980
9995
|
return s.c[1] + t1;
|
|
9981
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
|
+
});
|
|
9982
10007
|
var PieceSystem = class {
|
|
9983
10008
|
constructor(THREE, o) {
|
|
9984
10009
|
__publicField(this, "THREE", THREE);
|
|
@@ -9992,6 +10017,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9992
10017
|
__publicField(this, "ax");
|
|
9993
10018
|
__publicField(this, "solids", []);
|
|
9994
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);
|
|
9995
10023
|
this.p = new THREE.Vector3();
|
|
9996
10024
|
this.q = new THREE.Quaternion();
|
|
9997
10025
|
this.dq = new THREE.Quaternion();
|
|
@@ -10097,8 +10125,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10097
10125
|
const lz = (e[8] * x + e[9] * y + e[10] * z) * iz;
|
|
10098
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];
|
|
10099
10127
|
}
|
|
10100
|
-
/** 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`. */
|
|
10101
10129
|
samples(b, edges) {
|
|
10130
|
+
if (b.contacts) return b.contacts;
|
|
10102
10131
|
const [hx, hy, hz2] = b.halfExtents;
|
|
10103
10132
|
const out = [];
|
|
10104
10133
|
for (let i = 0; i < 8; i++) out.push([i & 1 ? hx : -hx, i & 2 ? hy : -hy, i & 4 ? hz2 : -hz2]);
|
|
@@ -10163,6 +10192,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10163
10192
|
}
|
|
10164
10193
|
if (b.sleeping) continue;
|
|
10165
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);
|
|
10166
10199
|
}
|
|
10167
10200
|
this.separate(dt);
|
|
10168
10201
|
}
|
|
@@ -10193,15 +10226,48 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10193
10226
|
}
|
|
10194
10227
|
return false;
|
|
10195
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
|
+
}
|
|
10196
10253
|
/** Where a corner of `self` at `(x, y, z)` lands: the ground, the top of another piece or of something fixed under it. */
|
|
10197
10254
|
supportAt(self, x, y, z, step) {
|
|
10198
10255
|
let top = this.o.groundAt(x, z);
|
|
10256
|
+
let on = null;
|
|
10257
|
+
let face = 1;
|
|
10199
10258
|
for (const s of this.solids) {
|
|
10200
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;
|
|
10201
10260
|
const t = boxTopAt(s, x, z);
|
|
10202
|
-
if (t > top && t <= y + step)
|
|
10261
|
+
if (t > top && t <= y + step) {
|
|
10262
|
+
top = t;
|
|
10263
|
+
on = s;
|
|
10264
|
+
face = topFace;
|
|
10265
|
+
}
|
|
10203
10266
|
}
|
|
10204
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;
|
|
10205
10271
|
return Math.max(top, fixed);
|
|
10206
10272
|
}
|
|
10207
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. */
|
|
@@ -10284,7 +10350,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10284
10350
|
const e = this.m.elements;
|
|
10285
10351
|
const rs = [];
|
|
10286
10352
|
const ids = [];
|
|
10353
|
+
const ns = [];
|
|
10354
|
+
const mus = [];
|
|
10287
10355
|
let maxPen = 0;
|
|
10356
|
+
let penNormal = UP16;
|
|
10288
10357
|
const rise = STEP + Math.max(0, -v[1]) * h * 2;
|
|
10289
10358
|
let side = null;
|
|
10290
10359
|
let lowest = Infinity;
|
|
@@ -10302,6 +10371,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10302
10371
|
if (floor2 !== void 0 && floor2 - cy > -SLOP && floor2 < raw - STEP) {
|
|
10303
10372
|
rs.push(r);
|
|
10304
10373
|
ids.push(i);
|
|
10374
|
+
ns.push(UP16);
|
|
10375
|
+
mus.push(pairFriction(b.friction, null));
|
|
10305
10376
|
maxPen = Math.max(maxPen, floor2 - cy);
|
|
10306
10377
|
}
|
|
10307
10378
|
continue;
|
|
@@ -10322,12 +10393,19 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10322
10393
|
}
|
|
10323
10394
|
continue;
|
|
10324
10395
|
}
|
|
10325
|
-
|
|
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);
|
|
10326
10399
|
const pen = gy - cy;
|
|
10327
10400
|
if (pen > -SLOP) {
|
|
10328
10401
|
rs.push(r);
|
|
10329
10402
|
ids.push(i);
|
|
10330
|
-
|
|
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
|
+
}
|
|
10331
10409
|
lowest = Math.min(lowest, gy);
|
|
10332
10410
|
}
|
|
10333
10411
|
}
|
|
@@ -10340,7 +10418,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10340
10418
|
contactedLast = rs.length > 0;
|
|
10341
10419
|
if (contactedLast) touched = true;
|
|
10342
10420
|
if (!contactedLast) continue;
|
|
10343
|
-
|
|
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;
|
|
10344
10425
|
let impact3 = 0;
|
|
10345
10426
|
for (const r of rs) impact3 = Math.max(impact3, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
10346
10427
|
const restitution = impact3 > 2 ? 0.25 : 0;
|
|
@@ -10356,76 +10437,72 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10356
10437
|
const lz = pos[2] + sz / rs.length;
|
|
10357
10438
|
landPoint = [lx, pos[1] - Math.max(...rs.map((r) => -r[1])), lz];
|
|
10358
10439
|
}
|
|
10359
|
-
const
|
|
10360
|
-
|
|
10361
|
-
|
|
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;
|
|
10362
10450
|
});
|
|
10363
10451
|
const vx0 = v[0];
|
|
10364
10452
|
const vz0 = v[2];
|
|
10365
|
-
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);
|
|
10366
10454
|
const warm = b.warm;
|
|
10367
|
-
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id *
|
|
10368
|
-
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id *
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
|
|
10374
|
-
v[0] += fx * invM;
|
|
10375
|
-
v[1] += jn * invM;
|
|
10376
|
-
v[2] += fz * invM;
|
|
10377
|
-
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]);
|
|
10378
10462
|
w[0] += dw[0];
|
|
10379
10463
|
w[1] += dw[1];
|
|
10380
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]]);
|
|
10381
10474
|
}
|
|
10382
10475
|
for (let it = 0; it < SOLVER_PASSES; it++) {
|
|
10383
10476
|
for (let k = 0; k < rs.length; k++) {
|
|
10384
10477
|
const r = rs[k];
|
|
10385
|
-
const
|
|
10386
|
-
const
|
|
10387
|
-
const
|
|
10388
|
-
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));
|
|
10389
10481
|
const j = total - accN[k];
|
|
10390
10482
|
accN[k] = total;
|
|
10391
|
-
if (j !== 0)
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
if (
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
fx -= tx * jt;
|
|
10411
|
-
fz -= tz * jt;
|
|
10412
|
-
}
|
|
10413
|
-
const most = MU * accN[k];
|
|
10414
|
-
const f = Math.hypot(fx, fz);
|
|
10415
|
-
if (f > most) {
|
|
10416
|
-
fx *= most / f;
|
|
10417
|
-
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;
|
|
10418
10502
|
}
|
|
10419
|
-
const
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
if (dx === 0 && dz === 0) continue;
|
|
10423
|
-
v[0] += dx * invM;
|
|
10424
|
-
v[2] += dz * invM;
|
|
10425
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * dz, r[2] * dx - r[0] * dz, -r[1] * dx);
|
|
10426
|
-
w[0] += dw[0];
|
|
10427
|
-
w[1] += dw[1];
|
|
10428
|
-
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);
|
|
10429
10506
|
}
|
|
10430
10507
|
}
|
|
10431
10508
|
pos[0] += (v[0] - vx0) * h;
|
|
@@ -10434,9 +10511,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10434
10511
|
for (let k = 0; k < rs.length; k++) {
|
|
10435
10512
|
const id = ids[k];
|
|
10436
10513
|
if (id >= WARM_SLOTS) continue;
|
|
10437
|
-
warm[id *
|
|
10438
|
-
warm[id *
|
|
10439
|
-
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];
|
|
10440
10518
|
}
|
|
10441
10519
|
let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0;
|
|
10442
10520
|
for (const r of rs) {
|
|
@@ -10508,6 +10586,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10508
10586
|
b.calm += dt;
|
|
10509
10587
|
return b.calm >= CALM_SLEEP_AFTER;
|
|
10510
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
|
+
}
|
|
10511
10596
|
/** Half the height of the box of a piece as it is turned now. */
|
|
10512
10597
|
halfHeight(b) {
|
|
10513
10598
|
const e = this.m.makeRotationFromQuaternion(b.root.quaternion).elements;
|
|
@@ -10533,6 +10618,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10533
10618
|
this.centerOf(b, cb);
|
|
10534
10619
|
if (Math.abs(ca.y - cb.y) > ra + rb) continue;
|
|
10535
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;
|
|
10536
10622
|
const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8;
|
|
10537
10623
|
let dx = cb.x - ca.x;
|
|
10538
10624
|
let dz = cb.z - ca.z;
|
|
@@ -13463,6 +13549,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13463
13549
|
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
13464
13550
|
var GLOW_FROM_C = 430;
|
|
13465
13551
|
var QUEUE_CAP = 4e3;
|
|
13552
|
+
var TRICKLE_SECONDS = [2, 5];
|
|
13553
|
+
var TRICKLE_EVERY = 0.25;
|
|
13554
|
+
var TRICKLE_MAX = 24;
|
|
13466
13555
|
var STRUCTURAL_EVENTS = /* @__PURE__ */ new Set([
|
|
13467
13556
|
"fracture",
|
|
13468
13557
|
"detach",
|
|
@@ -13486,6 +13575,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13486
13575
|
var FEET_BAND = 0.03;
|
|
13487
13576
|
var TIP_MIN_HEIGHT = 0.3;
|
|
13488
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"]);
|
|
13489
13582
|
var FIRE_CEILING_SECONDS = 3600;
|
|
13490
13583
|
var GENERIC_REFERENCE_RADIUS = 15;
|
|
13491
13584
|
var SWEEP_CORE = 0.15;
|
|
@@ -13585,10 +13678,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13585
13678
|
__publicField(this, "staticRound", []);
|
|
13586
13679
|
/** Boxes of the pieces' meshes (as `staticBoxes`), with the time they were measured. */
|
|
13587
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());
|
|
13588
13683
|
__publicField(this, "staticAt", 0);
|
|
13589
13684
|
__publicField(this, "queue", []);
|
|
13590
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. */
|
|
13591
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", []);
|
|
13592
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. */
|
|
13593
13690
|
__publicField(this, "slices", []);
|
|
13594
13691
|
/** When the work of this frame must stop; `Infinity` outside a drain, where a call finishes what it started. */
|
|
@@ -13931,10 +14028,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13931
14028
|
dt = clamp6(dt, 0, 0.1);
|
|
13932
14029
|
this.time += dt;
|
|
13933
14030
|
if (!this.attached) this.drainQueue();
|
|
14031
|
+
this.checkBroken();
|
|
13934
14032
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
13935
14033
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
13936
14034
|
this.updateActive(dt);
|
|
13937
14035
|
this.updateLeaks(dt);
|
|
14036
|
+
this.updateTrickles(dt);
|
|
13938
14037
|
this.pieceSys.update(dt);
|
|
13939
14038
|
this.fragments.update(dt);
|
|
13940
14039
|
this.ground.update(dt);
|
|
@@ -14133,6 +14232,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14133
14232
|
}
|
|
14134
14233
|
apply(ev) {
|
|
14135
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);
|
|
14136
14236
|
switch (ev.type) {
|
|
14137
14237
|
case "damage":
|
|
14138
14238
|
if (ev.damageType === "heat" || ev.damageType === "fire" || ev.damageType === "explosive") this.activate(ev.object, ev.part);
|
|
@@ -14457,9 +14557,34 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14457
14557
|
const core = def?.visual?.innerColor;
|
|
14458
14558
|
if (core !== void 0) this.vfx.burst("chips", ev.point, [normal.x, normal.y, normal.z], 5, 2.5, { color: core });
|
|
14459
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);
|
|
14460
14561
|
if (mv.mesh.userData.drawnSpalls?.includes(ev.part)) return;
|
|
14461
14562
|
this.visuals.addSpall(mv, wp, normal, Math.sqrt(ev.area / Math.PI), core ?? fallbackColor(pv.material, def));
|
|
14462
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
|
+
}
|
|
14463
14588
|
/**
|
|
14464
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
|
|
14465
14590
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
@@ -14495,6 +14620,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14495
14620
|
const tone = fr.inner && fdef?.visual?.innerColor !== void 0 ? fdef.visual.innerColor : fdef === def ? color : fallbackColor(fr.material, fdef);
|
|
14496
14621
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, tone, false), ev.object, maxAlive);
|
|
14497
14622
|
}
|
|
14623
|
+
if (def?.visual?.innerColor !== void 0 || pv?.material === "concrete" || pv?.material === "brick") this.trickleFrom(ev.center, [0, 0, 0], color);
|
|
14498
14624
|
if (ev.torn) {
|
|
14499
14625
|
this.impactSound(pv?.material ?? "", ev.center, 0.5, ev.blow);
|
|
14500
14626
|
return;
|
|
@@ -14684,26 +14810,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14684
14810
|
this.o.onPiece?.(piece);
|
|
14685
14811
|
}
|
|
14686
14812
|
/**
|
|
14687
|
-
*
|
|
14688
|
-
* 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`).
|
|
14689
14816
|
*/
|
|
14690
14817
|
loosenIfFootless(objectId) {
|
|
14691
14818
|
const b = this.bounds.get(objectId);
|
|
14692
14819
|
if (!b || !this.autoSplit) return;
|
|
14693
14820
|
const left = this.meshesOf(b).filter((mv) => this.visuals.shown(mv));
|
|
14694
14821
|
if (left.length === 0) return;
|
|
14822
|
+
const structural = UNCHECKED_KINDS.has(this.kindOf(b));
|
|
14695
14823
|
const body = b.piece ?? this.pieceSys.get(objectId);
|
|
14696
14824
|
if (body) {
|
|
14697
14825
|
const { half, center } = this.measure(b.root, left);
|
|
14698
14826
|
body.halfExtents = half;
|
|
14699
14827
|
this.pieceSys.setCenter(body, center);
|
|
14700
14828
|
body.mass = Math.max(0.05, this.massOf(objectId));
|
|
14829
|
+
if (!structural) this.setContacts(b, body, left);
|
|
14701
14830
|
body.sleeping = false;
|
|
14702
14831
|
return;
|
|
14703
14832
|
}
|
|
14704
|
-
if (
|
|
14833
|
+
if (structural) return;
|
|
14705
14834
|
const boxOf = (mv) => this.visuals.worldBox(mv, new this.THREE.Box3());
|
|
14706
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;
|
|
14707
14837
|
const floor2 = Math.min(...whole.map((one) => one.min.y));
|
|
14708
14838
|
const feetOf = (boxes) => boxes.filter((one) => one.min.y < floor2 + FEET_BAND).reduce((feet, one) => feet.union(one), new this.THREE.Box3());
|
|
14709
14839
|
const was = feetOf(whole);
|
|
@@ -14719,9 +14849,116 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14719
14849
|
if (!narrow && centreX > now.min.x && centreX < now.max.x && centreZ > now.min.z && centreZ < now.max.z) return;
|
|
14720
14850
|
}
|
|
14721
14851
|
const piece = this.makePiece(objectId);
|
|
14852
|
+
this.setContacts(b, b.piece ?? piece, left);
|
|
14722
14853
|
this.tipIfTopHeavy(b);
|
|
14723
14854
|
this.o.onLoosened?.(objectId, piece, "apart");
|
|
14724
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
|
+
}
|
|
14725
14962
|
/**
|
|
14726
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
|
|
14727
14964
|
* high and its feet are thin, so it gets the start of the fall, across the line of its feet.
|
|
@@ -15013,6 +15250,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15013
15250
|
this.active.clear();
|
|
15014
15251
|
for (const key of [...this.fires.keys()]) this.stopFire(key);
|
|
15015
15252
|
this.leaks.length = 0;
|
|
15253
|
+
this.trickles.length = 0;
|
|
15016
15254
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
15017
15255
|
for (const b of [...this.bounds.values()]) {
|
|
15018
15256
|
if (b.isSplit) {
|
|
@@ -15148,7 +15386,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15148
15386
|
return { id: options.id, samples };
|
|
15149
15387
|
}
|
|
15150
15388
|
|
|
15151
|
-
// ../game-audio/dist/chunk-
|
|
15389
|
+
// ../game-audio/dist/chunk-BUYBYP4L.js
|
|
15152
15390
|
var DISTANCE_CURVES = {
|
|
15153
15391
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
15154
15392
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -15326,6 +15564,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15326
15564
|
["revolver_38_357", 2, "sfx_pistol"],
|
|
15327
15565
|
["revolver_44_50ae", 3, "sfx_pistol"],
|
|
15328
15566
|
["smg_9mm_auto", 1, "sfx_pistol"],
|
|
15567
|
+
["launcher_40mm", 2],
|
|
15568
|
+
["rocket_launch", 2],
|
|
15329
15569
|
["shotgun_12", 1, "sfx_shotgun"],
|
|
15330
15570
|
["rifle_5_56", 1, "sfx_rifle"],
|
|
15331
15571
|
["rifle_7_62x39", 1, "sfx_rifle"],
|
|
@@ -16791,6 +17031,21 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
16791
17031
|
volume: 0,
|
|
16792
17032
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
16793
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
|
+
);
|
|
16794
17049
|
var FIRE_FUEL_OF = {
|
|
16795
17050
|
wood_soft: "wood",
|
|
16796
17051
|
wood_hard: "wood",
|
|
@@ -16980,7 +17235,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
16980
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 })]),
|
|
16981
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] }))])
|
|
16982
17237
|
];
|
|
16983
|
-
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] };
|
|
16984
17239
|
|
|
16985
17240
|
// src/audio/sound.ts
|
|
16986
17241
|
var IMPACT_SOUND_KEYS = {
|
|
@@ -17281,7 +17536,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
17281
17536
|
// each fuel has its own roar: a gas jet (burners and torches), a pool of fuel, plastic and rubber sizzling under the flames
|
|
17282
17537
|
fire_loop_gas: { bus: "world", gain: 6, loop: { crossfade: 1 } },
|
|
17283
17538
|
fire_loop_liquid: { bus: "world", gain: 3.5, loop: { crossfade: 1 } },
|
|
17284
|
-
fire_loop_plastic: { bus: "world", gain: 1, loop: { crossfade: 0.6 } }
|
|
17539
|
+
fire_loop_plastic: { bus: "world", gain: 1, loop: { crossfade: 0.6 } },
|
|
17540
|
+
// a smoke canister venting: a gas hiss chained like the fire roar (the level comes from the room, by how near the burning canisters are)
|
|
17541
|
+
smoke_grenade_hiss: { bus: "world", gain: 1, loop: { crossfade: 0.8 } },
|
|
17542
|
+
// the ring and the spoon of a hand grenade: a small metal latch
|
|
17543
|
+
grenade_pin_pull: fire(0.5)
|
|
17285
17544
|
};
|
|
17286
17545
|
function blastFireSampleBank(manifest, options = {}) {
|
|
17287
17546
|
const sounds = options.sounds ?? BLAST_FIRE_SAMPLES;
|