@adatechnology/game-destruction-three 0.17.3 → 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 +333 -78
- 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.js
CHANGED
|
@@ -764,7 +764,9 @@ var SLAM_MIN_SPEED = 2;
|
|
|
764
764
|
var SLAM_BOUNCE = 0.2;
|
|
765
765
|
var SLAM_FLOOR_NORMAL = 0.7;
|
|
766
766
|
var SUB_DT = 1 / 120;
|
|
767
|
-
var
|
|
767
|
+
var MU_STATIC = 0.7;
|
|
768
|
+
var MU_DYNAMIC = 0.6;
|
|
769
|
+
var SLIP_SPEED = 0.02;
|
|
768
770
|
var SLEEP_AFTER = 0.5;
|
|
769
771
|
var CALM_DRIFT = 0.01;
|
|
770
772
|
var CALM_TURN = 0.05;
|
|
@@ -783,6 +785,9 @@ var SIDE_BOUNCE = 0.1;
|
|
|
783
785
|
var EDGE_POINTS = [-0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75];
|
|
784
786
|
var SUPPORT_CHECKS2 = 16;
|
|
785
787
|
var SUPPORT_GAP2 = 0.02;
|
|
788
|
+
var WAKE_ABOVE_SPEED = 0.05;
|
|
789
|
+
var WAKE_ABOVE_SPIN = 0.1;
|
|
790
|
+
var topFace = 1;
|
|
786
791
|
var boxTopAt = (s, x, z) => {
|
|
787
792
|
const e = s.e;
|
|
788
793
|
const dx = x - s.c[0], dz = z - s.c[2];
|
|
@@ -798,11 +803,24 @@ var boxTopAt = (s, x, z) => {
|
|
|
798
803
|
let a = (-h - p) / d, b = (h - p) / d;
|
|
799
804
|
if (a > b) [a, b] = [b, a];
|
|
800
805
|
t0 = Math.max(t0, a);
|
|
801
|
-
|
|
806
|
+
if (b < t1) {
|
|
807
|
+
t1 = b;
|
|
808
|
+
topFace = k;
|
|
809
|
+
}
|
|
802
810
|
if (t0 > t1) return -Infinity;
|
|
803
811
|
}
|
|
804
812
|
return s.c[1] + t1;
|
|
805
813
|
};
|
|
814
|
+
var UP = [0, 1, 0];
|
|
815
|
+
var faceNormal = (s, axis) => {
|
|
816
|
+
const e = s.e;
|
|
817
|
+
const sign = e[axis * 4 + 1] < 0 ? -1 : 1;
|
|
818
|
+
return [sign * e[axis * 4], sign * e[axis * 4 + 1], sign * e[axis * 4 + 2]];
|
|
819
|
+
};
|
|
820
|
+
var pairFriction = (a, b) => ({
|
|
821
|
+
static: Math.sqrt((a?.static ?? MU_STATIC) * (b?.static ?? MU_STATIC)),
|
|
822
|
+
dynamic: Math.sqrt((a?.dynamic ?? MU_DYNAMIC) * (b?.dynamic ?? MU_DYNAMIC))
|
|
823
|
+
});
|
|
806
824
|
var PieceSystem = class {
|
|
807
825
|
constructor(THREE, o) {
|
|
808
826
|
__publicField(this, "THREE", THREE);
|
|
@@ -816,6 +834,9 @@ var PieceSystem = class {
|
|
|
816
834
|
__publicField(this, "ax");
|
|
817
835
|
__publicField(this, "solids", []);
|
|
818
836
|
__publicField(this, "supportTurn", 0);
|
|
837
|
+
/** 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). */
|
|
838
|
+
__publicField(this, "supportNormal", UP);
|
|
839
|
+
__publicField(this, "supportFriction", null);
|
|
819
840
|
this.p = new THREE.Vector3();
|
|
820
841
|
this.q = new THREE.Quaternion();
|
|
821
842
|
this.dq = new THREE.Quaternion();
|
|
@@ -921,8 +942,9 @@ var PieceSystem = class {
|
|
|
921
942
|
const lz = (e[8] * x + e[9] * y + e[10] * z) * iz;
|
|
922
943
|
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];
|
|
923
944
|
}
|
|
924
|
-
/** 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
|
|
945
|
+
/** 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`. */
|
|
925
946
|
samples(b, edges) {
|
|
947
|
+
if (b.contacts) return b.contacts;
|
|
926
948
|
const [hx, hy, hz] = b.halfExtents;
|
|
927
949
|
const out = [];
|
|
928
950
|
for (let i = 0; i < 8; i++) out.push([i & 1 ? hx : -hx, i & 2 ? hy : -hy, i & 4 ? hz : -hz]);
|
|
@@ -987,6 +1009,10 @@ var PieceSystem = class {
|
|
|
987
1009
|
}
|
|
988
1010
|
if (b.sleeping) continue;
|
|
989
1011
|
this.integrate(b, dt);
|
|
1012
|
+
if (b.sleeping) continue;
|
|
1013
|
+
const v = b.velocity;
|
|
1014
|
+
const w = b.angularVelocity;
|
|
1015
|
+
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);
|
|
990
1016
|
}
|
|
991
1017
|
this.separate(dt);
|
|
992
1018
|
}
|
|
@@ -1017,15 +1043,48 @@ var PieceSystem = class {
|
|
|
1017
1043
|
}
|
|
1018
1044
|
return false;
|
|
1019
1045
|
}
|
|
1046
|
+
/** 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. */
|
|
1047
|
+
topAt(x, z, y, step, self) {
|
|
1048
|
+
let top = -Infinity;
|
|
1049
|
+
for (const s of this.solids) {
|
|
1050
|
+
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;
|
|
1051
|
+
const t = boxTopAt(s, x, z);
|
|
1052
|
+
if (t > top && t <= y + step) top = t;
|
|
1053
|
+
}
|
|
1054
|
+
return top;
|
|
1055
|
+
}
|
|
1056
|
+
/** 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. */
|
|
1057
|
+
wakeAbove(b) {
|
|
1058
|
+
const below = this.solids.find((s) => s.body === b);
|
|
1059
|
+
if (!below) return;
|
|
1060
|
+
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]);
|
|
1061
|
+
for (const s of this.solids) {
|
|
1062
|
+
const o = s.body;
|
|
1063
|
+
if (!o.sleeping || o.held || o === b) continue;
|
|
1064
|
+
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;
|
|
1065
|
+
o.sleeping = false;
|
|
1066
|
+
o.still = 0;
|
|
1067
|
+
o.calmAt = null;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1020
1070
|
/** Where a corner of `self` at `(x, y, z)` lands: the ground, the top of another piece or of something fixed under it. */
|
|
1021
1071
|
supportAt(self, x, y, z, step) {
|
|
1022
1072
|
let top = this.o.groundAt(x, z);
|
|
1073
|
+
let on = null;
|
|
1074
|
+
let face = 1;
|
|
1023
1075
|
for (const s of this.solids) {
|
|
1024
1076
|
if (s.body === self || s.body.held || Math.abs(x - s.c[0]) > s.r || Math.abs(z - s.c[2]) > s.r) continue;
|
|
1025
1077
|
const t = boxTopAt(s, x, z);
|
|
1026
|
-
if (t > top && t <= y + step)
|
|
1078
|
+
if (t > top && t <= y + step) {
|
|
1079
|
+
top = t;
|
|
1080
|
+
on = s;
|
|
1081
|
+
face = topFace;
|
|
1082
|
+
}
|
|
1027
1083
|
}
|
|
1028
1084
|
const fixed = this.o.staticTopAt?.(x, z, y, step) ?? -Infinity;
|
|
1085
|
+
if (fixed >= top) on = null;
|
|
1086
|
+
this.supportNormal = on ? faceNormal(on, face) : UP;
|
|
1087
|
+
this.supportFriction = on?.body.friction ?? null;
|
|
1029
1088
|
return Math.max(top, fixed);
|
|
1030
1089
|
}
|
|
1031
1090
|
/** 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. */
|
|
@@ -1108,7 +1167,10 @@ var PieceSystem = class {
|
|
|
1108
1167
|
const e = this.m.elements;
|
|
1109
1168
|
const rs = [];
|
|
1110
1169
|
const ids = [];
|
|
1170
|
+
const ns = [];
|
|
1171
|
+
const mus = [];
|
|
1111
1172
|
let maxPen = 0;
|
|
1173
|
+
let penNormal = UP;
|
|
1112
1174
|
const rise = STEP + Math.max(0, -v[1]) * h * 2;
|
|
1113
1175
|
let side = null;
|
|
1114
1176
|
let lowest = Infinity;
|
|
@@ -1126,6 +1188,8 @@ var PieceSystem = class {
|
|
|
1126
1188
|
if (floor !== void 0 && floor - cy > -SLOP && floor < raw - STEP) {
|
|
1127
1189
|
rs.push(r);
|
|
1128
1190
|
ids.push(i);
|
|
1191
|
+
ns.push(UP);
|
|
1192
|
+
mus.push(pairFriction(b.friction, null));
|
|
1129
1193
|
maxPen = Math.max(maxPen, floor - cy);
|
|
1130
1194
|
}
|
|
1131
1195
|
continue;
|
|
@@ -1146,12 +1210,19 @@ var PieceSystem = class {
|
|
|
1146
1210
|
}
|
|
1147
1211
|
continue;
|
|
1148
1212
|
}
|
|
1149
|
-
|
|
1213
|
+
this.supportNormal = UP;
|
|
1214
|
+
this.supportFriction = null;
|
|
1215
|
+
const gy = i < 8 || b.contacts ? this.supportAt(b, px, cy, pz, rise) : Math.max(raw, this.o.staticTopAt?.(px, pz, cy, rise) ?? -Infinity);
|
|
1150
1216
|
const pen = gy - cy;
|
|
1151
1217
|
if (pen > -SLOP) {
|
|
1152
1218
|
rs.push(r);
|
|
1153
1219
|
ids.push(i);
|
|
1154
|
-
|
|
1220
|
+
ns.push(this.supportNormal);
|
|
1221
|
+
mus.push(pairFriction(b.friction, this.supportFriction));
|
|
1222
|
+
if (pen > maxPen) {
|
|
1223
|
+
maxPen = pen;
|
|
1224
|
+
penNormal = this.supportNormal;
|
|
1225
|
+
}
|
|
1155
1226
|
lowest = Math.min(lowest, gy);
|
|
1156
1227
|
}
|
|
1157
1228
|
}
|
|
@@ -1164,7 +1235,10 @@ var PieceSystem = class {
|
|
|
1164
1235
|
contactedLast = rs.length > 0;
|
|
1165
1236
|
if (contactedLast) touched = true;
|
|
1166
1237
|
if (!contactedLast) continue;
|
|
1167
|
-
|
|
1238
|
+
const out = Math.max(0, maxPen - SLOP * 0.5) * penNormal[1];
|
|
1239
|
+
pos[0] += penNormal[0] * out;
|
|
1240
|
+
pos[1] += penNormal[1] * out;
|
|
1241
|
+
pos[2] += penNormal[2] * out;
|
|
1168
1242
|
let impact2 = 0;
|
|
1169
1243
|
for (const r of rs) impact2 = Math.max(impact2, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
1170
1244
|
const restitution = impact2 > 2 ? 0.25 : 0;
|
|
@@ -1180,76 +1254,72 @@ var PieceSystem = class {
|
|
|
1180
1254
|
const lz = pos[2] + sz / rs.length;
|
|
1181
1255
|
landPoint = [lx, pos[1] - Math.max(...rs.map((r) => -r[1])), lz];
|
|
1182
1256
|
}
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1257
|
+
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]];
|
|
1258
|
+
const dot = (a, c2) => a[0] * c2[0] + a[1] * c2[1] + a[2] * c2[2];
|
|
1259
|
+
const goal = rs.map((r, k) => {
|
|
1260
|
+
const vn = dot(pointV(r), ns[k]);
|
|
1261
|
+
return vn < 0 ? -restitution * vn : 0;
|
|
1262
|
+
});
|
|
1263
|
+
const slipping = rs.map((r, k) => {
|
|
1264
|
+
const pv = pointV(r);
|
|
1265
|
+
const vn = dot(pv, ns[k]);
|
|
1266
|
+
return Math.hypot(pv[0] - vn * ns[k][0], pv[1] - vn * ns[k][1], pv[2] - vn * ns[k][2]) > SLIP_SPEED;
|
|
1186
1267
|
});
|
|
1187
1268
|
const vx0 = v[0];
|
|
1188
1269
|
const vz0 = v[2];
|
|
1189
|
-
if (!b.warm || b.warm.length !== WARM_SLOTS *
|
|
1270
|
+
if (!b.warm || b.warm.length !== WARM_SLOTS * 4) b.warm = new Float64Array(WARM_SLOTS * 4);
|
|
1190
1271
|
const warm = b.warm;
|
|
1191
|
-
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id *
|
|
1192
|
-
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id *
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
v[0] += fx * invM;
|
|
1199
|
-
v[1] += jn * invM;
|
|
1200
|
-
v[2] += fz * invM;
|
|
1201
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * fz - r[2] * jn, r[2] * fx - r[0] * fz, r[0] * jn - r[1] * fx);
|
|
1272
|
+
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id * 4] : 0);
|
|
1273
|
+
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id * 4 + 1], warm[id * 4 + 2], warm[id * 4 + 3]] : [0, 0, 0]);
|
|
1274
|
+
const push = (r, j) => {
|
|
1275
|
+
v[0] += j[0] * invM;
|
|
1276
|
+
v[1] += j[1] * invM;
|
|
1277
|
+
v[2] += j[2] * invM;
|
|
1278
|
+
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]);
|
|
1202
1279
|
w[0] += dw[0];
|
|
1203
1280
|
w[1] += dw[1];
|
|
1204
1281
|
w[2] += dw[2];
|
|
1282
|
+
};
|
|
1283
|
+
const reach = (r, d) => {
|
|
1284
|
+
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]);
|
|
1285
|
+
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];
|
|
1286
|
+
};
|
|
1287
|
+
for (let k = 0; k < rs.length; k++) {
|
|
1288
|
+
const n = ns[k];
|
|
1289
|
+
const t = accT[k];
|
|
1290
|
+
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]]);
|
|
1205
1291
|
}
|
|
1206
1292
|
for (let it = 0; it < SOLVER_PASSES; it++) {
|
|
1207
1293
|
for (let k = 0; k < rs.length; k++) {
|
|
1208
1294
|
const r = rs[k];
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1212
|
-
const total = Math.max(0, accN[k] + (goal[k] - vy) / denom);
|
|
1295
|
+
const n = ns[k];
|
|
1296
|
+
const vn = dot(pointV(r), n);
|
|
1297
|
+
const total = Math.max(0, accN[k] + (goal[k] - vn) / reach(r, n));
|
|
1213
1298
|
const j = total - accN[k];
|
|
1214
1299
|
accN[k] = total;
|
|
1215
|
-
if (j !== 0)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
if (
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
fx -= tx * jt;
|
|
1235
|
-
fz -= tz * jt;
|
|
1300
|
+
if (j !== 0) push(r, [n[0] * j, n[1] * j, n[2] * j]);
|
|
1301
|
+
const pv = pointV(r);
|
|
1302
|
+
const pn = dot(pv, n);
|
|
1303
|
+
const vt = [pv[0] - pn * n[0], pv[1] - pn * n[1], pv[2] - pn * n[2]];
|
|
1304
|
+
const speed = Math.hypot(vt[0], vt[1], vt[2]);
|
|
1305
|
+
const f = [accT[k][0], accT[k][1], accT[k][2]];
|
|
1306
|
+
if (speed > 1e-6) {
|
|
1307
|
+
const d = [vt[0] / speed, vt[1] / speed, vt[2] / speed];
|
|
1308
|
+
const jt = speed / reach(r, d);
|
|
1309
|
+
f[0] -= d[0] * jt;
|
|
1310
|
+
f[1] -= d[1] * jt;
|
|
1311
|
+
f[2] -= d[2] * jt;
|
|
1312
|
+
} else if (f[0] === 0 && f[1] === 0 && f[2] === 0) continue;
|
|
1313
|
+
const most = (slipping[k] ? mus[k].dynamic : mus[k].static) * accN[k];
|
|
1314
|
+
const fl = Math.hypot(f[0], f[1], f[2]);
|
|
1315
|
+
if (fl > most) {
|
|
1316
|
+
f[0] *= most / fl;
|
|
1317
|
+
f[1] *= most / fl;
|
|
1318
|
+
f[2] *= most / fl;
|
|
1236
1319
|
}
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
if (
|
|
1240
|
-
fx *= most / f;
|
|
1241
|
-
fz *= most / f;
|
|
1242
|
-
}
|
|
1243
|
-
const dx = fx - accT[k][0];
|
|
1244
|
-
const dz = fz - accT[k][1];
|
|
1245
|
-
accT[k] = [fx, fz];
|
|
1246
|
-
if (dx === 0 && dz === 0) continue;
|
|
1247
|
-
v[0] += dx * invM;
|
|
1248
|
-
v[2] += dz * invM;
|
|
1249
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * dz, r[2] * dx - r[0] * dz, -r[1] * dx);
|
|
1250
|
-
w[0] += dw[0];
|
|
1251
|
-
w[1] += dw[1];
|
|
1252
|
-
w[2] += dw[2];
|
|
1320
|
+
const dj = [f[0] - accT[k][0], f[1] - accT[k][1], f[2] - accT[k][2]];
|
|
1321
|
+
accT[k] = f;
|
|
1322
|
+
if (dj[0] !== 0 || dj[1] !== 0 || dj[2] !== 0) push(r, dj);
|
|
1253
1323
|
}
|
|
1254
1324
|
}
|
|
1255
1325
|
pos[0] += (v[0] - vx0) * h;
|
|
@@ -1258,9 +1328,10 @@ var PieceSystem = class {
|
|
|
1258
1328
|
for (let k = 0; k < rs.length; k++) {
|
|
1259
1329
|
const id = ids[k];
|
|
1260
1330
|
if (id >= WARM_SLOTS) continue;
|
|
1261
|
-
warm[id *
|
|
1262
|
-
warm[id *
|
|
1263
|
-
warm[id *
|
|
1331
|
+
warm[id * 4] = accN[k];
|
|
1332
|
+
warm[id * 4 + 1] = accT[k][0];
|
|
1333
|
+
warm[id * 4 + 2] = accT[k][1];
|
|
1334
|
+
warm[id * 4 + 3] = accT[k][2];
|
|
1264
1335
|
}
|
|
1265
1336
|
let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0;
|
|
1266
1337
|
for (const r of rs) {
|
|
@@ -1332,6 +1403,13 @@ var PieceSystem = class {
|
|
|
1332
1403
|
b.calm += dt;
|
|
1333
1404
|
return b.calm >= CALM_SLEEP_AFTER;
|
|
1334
1405
|
}
|
|
1406
|
+
/** Whether the centre `c` of `upper` is over the top of `lower`'s box (as it was at the start of the update). */
|
|
1407
|
+
onTopOf(upper, lower, c) {
|
|
1408
|
+
const s = this.solids.find((one) => one.body === lower);
|
|
1409
|
+
if (!s) return false;
|
|
1410
|
+
const top = boxTopAt(s, c.x, c.z);
|
|
1411
|
+
return top > -Infinity && c.y >= top - 0.01;
|
|
1412
|
+
}
|
|
1335
1413
|
/** Half the height of the box of a piece as it is turned now. */
|
|
1336
1414
|
halfHeight(b) {
|
|
1337
1415
|
const e = this.m.makeRotationFromQuaternion(b.root.quaternion).elements;
|
|
@@ -1357,6 +1435,7 @@ var PieceSystem = class {
|
|
|
1357
1435
|
this.centerOf(b, cb);
|
|
1358
1436
|
if (Math.abs(ca.y - cb.y) > ra + rb) continue;
|
|
1359
1437
|
if (Math.abs(ca.y - cb.y) >= (this.halfHeight(a) + this.halfHeight(b)) * 0.9) continue;
|
|
1438
|
+
if (this.onTopOf(b, a, cb) || this.onTopOf(a, b, ca)) continue;
|
|
1360
1439
|
const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8;
|
|
1361
1440
|
let dx = cb.x - ca.x;
|
|
1362
1441
|
let dz = cb.z - ca.z;
|
|
@@ -2764,6 +2843,9 @@ var WOOD_RIM = 1.5;
|
|
|
2764
2843
|
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
2765
2844
|
var GLOW_FROM_C = 430;
|
|
2766
2845
|
var QUEUE_CAP = 4e3;
|
|
2846
|
+
var TRICKLE_SECONDS = [2, 5];
|
|
2847
|
+
var TRICKLE_EVERY = 0.25;
|
|
2848
|
+
var TRICKLE_MAX = 24;
|
|
2767
2849
|
var STRUCTURAL_EVENTS = /* @__PURE__ */ new Set([
|
|
2768
2850
|
"fracture",
|
|
2769
2851
|
"detach",
|
|
@@ -2787,6 +2869,10 @@ var STATIC_SPAN = 3;
|
|
|
2787
2869
|
var FEET_BAND = 0.03;
|
|
2788
2870
|
var TIP_MIN_HEIGHT = 0.3;
|
|
2789
2871
|
var TIP_NARROW = 0.3;
|
|
2872
|
+
var CONTACT_MESHES = 12;
|
|
2873
|
+
var BROKEN_GAP = 0.03;
|
|
2874
|
+
var BROKEN_SAMPLES = [[0.5, 0.5], [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9]];
|
|
2875
|
+
var UNCHECKED_KINDS = /* @__PURE__ */ new Set(["structure", "room", "ground"]);
|
|
2790
2876
|
var FIRE_CEILING_SECONDS = 3600;
|
|
2791
2877
|
var GENERIC_REFERENCE_RADIUS = 15;
|
|
2792
2878
|
var SWEEP_CORE = 0.15;
|
|
@@ -2886,10 +2972,14 @@ var Renderer = class {
|
|
|
2886
2972
|
__publicField(this, "staticRound", []);
|
|
2887
2973
|
/** Boxes of the pieces' meshes (as `staticBoxes`), with the time they were measured. */
|
|
2888
2974
|
__publicField(this, "pieceBoxes", /* @__PURE__ */ new WeakMap());
|
|
2975
|
+
/** Objects broken since the last update: `checkBroken` looks at what is left of them. */
|
|
2976
|
+
__publicField(this, "broken", /* @__PURE__ */ new Set());
|
|
2889
2977
|
__publicField(this, "staticAt", 0);
|
|
2890
2978
|
__publicField(this, "queue", []);
|
|
2891
2979
|
/** 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. */
|
|
2892
2980
|
__publicField(this, "coverWhenWentOff", /* @__PURE__ */ new WeakMap());
|
|
2981
|
+
/** Places a blow broke a hard face at: grit and dust keep coming off them for a few seconds, less and less (`TRICKLE_SECONDS`). */
|
|
2982
|
+
__publicField(this, "trickles", []);
|
|
2893
2983
|
/** Presentation work an event left for later frames (fragments still to spawn, loose groups still to split), run in order within the frame budget. */
|
|
2894
2984
|
__publicField(this, "slices", []);
|
|
2895
2985
|
/** When the work of this frame must stop; `Infinity` outside a drain, where a call finishes what it started. */
|
|
@@ -3232,10 +3322,12 @@ var Renderer = class {
|
|
|
3232
3322
|
dt = clamp(dt, 0, 0.1);
|
|
3233
3323
|
this.time += dt;
|
|
3234
3324
|
if (!this.attached) this.drainQueue();
|
|
3325
|
+
this.checkBroken();
|
|
3235
3326
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
3236
3327
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
3237
3328
|
this.updateActive(dt);
|
|
3238
3329
|
this.updateLeaks(dt);
|
|
3330
|
+
this.updateTrickles(dt);
|
|
3239
3331
|
this.pieceSys.update(dt);
|
|
3240
3332
|
this.fragments.update(dt);
|
|
3241
3333
|
this.ground.update(dt);
|
|
@@ -3434,6 +3526,7 @@ var Renderer = class {
|
|
|
3434
3526
|
}
|
|
3435
3527
|
apply(ev) {
|
|
3436
3528
|
this.staticDirty.add(ev.object);
|
|
3529
|
+
if (ev.type === "fracture" || ev.type === "detach" || ev.type === "collapse" || ev.type === "burnout" || ev.type === "destroyed") this.broken.add(ev.object);
|
|
3437
3530
|
switch (ev.type) {
|
|
3438
3531
|
case "damage":
|
|
3439
3532
|
if (ev.damageType === "heat" || ev.damageType === "fire" || ev.damageType === "explosive") this.activate(ev.object, ev.part);
|
|
@@ -3758,9 +3851,34 @@ var Renderer = class {
|
|
|
3758
3851
|
const core = def?.visual?.innerColor;
|
|
3759
3852
|
if (core !== void 0) this.vfx.burst("chips", ev.point, [normal.x, normal.y, normal.z], 5, 2.5, { color: core });
|
|
3760
3853
|
else this.playEvent({ kind: "spall", material: this.impactMaterial(def) }, ev.point);
|
|
3854
|
+
if (core !== void 0) this.trickleFrom(ev.point, [normal.x, normal.y, normal.z], core);
|
|
3761
3855
|
if (mv.mesh.userData.drawnSpalls?.includes(ev.part)) return;
|
|
3762
3856
|
this.visuals.addSpall(mv, wp, normal, Math.sqrt(ev.area / Math.PI), core ?? fallbackColor(pv.material, def));
|
|
3763
3857
|
}
|
|
3858
|
+
/** 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. */
|
|
3859
|
+
trickleFrom(point, normal, color) {
|
|
3860
|
+
if (this.trickles.length >= TRICKLE_MAX) this.trickles.shift();
|
|
3861
|
+
const total = TRICKLE_SECONDS[0] + (TRICKLE_SECONDS[1] - TRICKLE_SECONDS[0]) * this.rng();
|
|
3862
|
+
this.trickles.push({ point: [point[0], point[1], point[2]], normal, color, left: total, total, next: TRICKLE_EVERY });
|
|
3863
|
+
}
|
|
3864
|
+
updateTrickles(dt) {
|
|
3865
|
+
for (let i = this.trickles.length - 1; i >= 0; i--) {
|
|
3866
|
+
const t = this.trickles[i];
|
|
3867
|
+
t.left -= dt;
|
|
3868
|
+
t.next -= dt;
|
|
3869
|
+
if (t.left <= 0) {
|
|
3870
|
+
this.trickles.splice(i, 1);
|
|
3871
|
+
continue;
|
|
3872
|
+
}
|
|
3873
|
+
if (t.next > 0) continue;
|
|
3874
|
+
const share = t.left / t.total;
|
|
3875
|
+
t.next = TRICKLE_EVERY / Math.max(share, 0.25);
|
|
3876
|
+
const out = [t.normal[0] * 0.2, -1, t.normal[2] * 0.2];
|
|
3877
|
+
const options = t.color !== void 0 ? { color: t.color } : void 0;
|
|
3878
|
+
this.vfx.burst("chips", t.point, out, this.low ? 1 : 2, 0.3, options);
|
|
3879
|
+
if (this.rng() < 0.5) this.vfx.burst("dust", t.point, out, 1, 0.2, options);
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3764
3882
|
/**
|
|
3765
3883
|
* 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
|
|
3766
3884
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
@@ -3796,6 +3914,7 @@ var Renderer = class {
|
|
|
3796
3914
|
const tone3 = fr.inner && fdef?.visual?.innerColor !== void 0 ? fdef.visual.innerColor : fdef === def ? color : fallbackColor(fr.material, fdef);
|
|
3797
3915
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, tone3, false), ev.object, maxAlive);
|
|
3798
3916
|
}
|
|
3917
|
+
if (def?.visual?.innerColor !== void 0 || pv?.material === "concrete" || pv?.material === "brick") this.trickleFrom(ev.center, [0, 0, 0], color);
|
|
3799
3918
|
if (ev.torn) {
|
|
3800
3919
|
this.impactSound(pv?.material ?? "", ev.center, 0.5, ev.blow);
|
|
3801
3920
|
return;
|
|
@@ -3985,26 +4104,30 @@ var Renderer = class {
|
|
|
3985
4104
|
this.o.onPiece?.(piece);
|
|
3986
4105
|
}
|
|
3987
4106
|
/**
|
|
3988
|
-
*
|
|
3989
|
-
* 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,
|
|
4107
|
+
* 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
|
|
4108
|
+
* 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,
|
|
4109
|
+
* 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`).
|
|
3990
4110
|
*/
|
|
3991
4111
|
loosenIfFootless(objectId) {
|
|
3992
4112
|
const b = this.bounds.get(objectId);
|
|
3993
4113
|
if (!b || !this.autoSplit) return;
|
|
3994
4114
|
const left = this.meshesOf(b).filter((mv) => this.visuals.shown(mv));
|
|
3995
4115
|
if (left.length === 0) return;
|
|
4116
|
+
const structural = UNCHECKED_KINDS.has(this.kindOf(b));
|
|
3996
4117
|
const body = b.piece ?? this.pieceSys.get(objectId);
|
|
3997
4118
|
if (body) {
|
|
3998
4119
|
const { half, center } = this.measure(b.root, left);
|
|
3999
4120
|
body.halfExtents = half;
|
|
4000
4121
|
this.pieceSys.setCenter(body, center);
|
|
4001
4122
|
body.mass = Math.max(0.05, this.massOf(objectId));
|
|
4123
|
+
if (!structural) this.setContacts(b, body, left);
|
|
4002
4124
|
body.sleeping = false;
|
|
4003
4125
|
return;
|
|
4004
4126
|
}
|
|
4005
|
-
if (
|
|
4127
|
+
if (structural) return;
|
|
4006
4128
|
const boxOf = (mv) => this.visuals.worldBox(mv, new this.THREE.Box3());
|
|
4007
4129
|
const whole = [...b.parts.values()].flatMap((pv) => pv.meshes).map(boxOf).filter((one) => !one.isEmpty());
|
|
4130
|
+
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;
|
|
4008
4131
|
const floor = Math.min(...whole.map((one) => one.min.y));
|
|
4009
4132
|
const feetOf = (boxes) => boxes.filter((one) => one.min.y < floor + FEET_BAND).reduce((feet, one) => feet.union(one), new this.THREE.Box3());
|
|
4010
4133
|
const was = feetOf(whole);
|
|
@@ -4020,9 +4143,116 @@ var Renderer = class {
|
|
|
4020
4143
|
if (!narrow && centreX > now.min.x && centreX < now.max.x && centreZ > now.min.z && centreZ < now.max.z) return;
|
|
4021
4144
|
}
|
|
4022
4145
|
const piece = this.makePiece(objectId);
|
|
4146
|
+
this.setContacts(b, b.piece ?? piece, left);
|
|
4023
4147
|
this.tipIfTopHeavy(b);
|
|
4024
4148
|
this.o.onLoosened?.(objectId, piece, "apart");
|
|
4025
4149
|
}
|
|
4150
|
+
shownBoxes(objectId, partId) {
|
|
4151
|
+
const b = this.bounds.get(objectId);
|
|
4152
|
+
if (!b) return [];
|
|
4153
|
+
const box = new this.THREE.Box3();
|
|
4154
|
+
const out = [];
|
|
4155
|
+
const pv = partId === void 0 ? null : b.parts.get(partId);
|
|
4156
|
+
if (partId !== void 0 && (!pv || pv.gone !== "none")) return out;
|
|
4157
|
+
for (const mv of pv ? pv.meshes : this.meshesOf(b)) {
|
|
4158
|
+
if (!this.visuals.shown(mv)) continue;
|
|
4159
|
+
this.visuals.worldBox(mv, box);
|
|
4160
|
+
if (!box.isEmpty()) out.push([box.min.x, box.min.y, box.min.z, box.max.x, box.max.y, box.max.z]);
|
|
4161
|
+
}
|
|
4162
|
+
return out;
|
|
4163
|
+
}
|
|
4164
|
+
/**
|
|
4165
|
+
* 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
|
|
4166
|
+
* fixed top nor a piece within `BROKEN_GAP` under it is made a piece on the spot (see `onBrokenChecked`).
|
|
4167
|
+
*/
|
|
4168
|
+
checkBroken() {
|
|
4169
|
+
if (this.broken.size === 0) return;
|
|
4170
|
+
const ids = [...this.broken];
|
|
4171
|
+
this.broken.clear();
|
|
4172
|
+
for (const id of ids) {
|
|
4173
|
+
const released = this.releaseIfFloating(id);
|
|
4174
|
+
this.o.onBrokenChecked?.(id, released);
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
releaseIfFloating(objectId) {
|
|
4178
|
+
const b = this.bounds.get(objectId);
|
|
4179
|
+
if (!b || b.piece || this.pieceSys.get(objectId) || !this.autoSplit || UNCHECKED_KINDS.has(this.kindOf(b))) return false;
|
|
4180
|
+
const boxes = this.shownBoxes(objectId);
|
|
4181
|
+
if (boxes.length === 0) return false;
|
|
4182
|
+
const whole = new this.THREE.Box3();
|
|
4183
|
+
const wholeBoxes = [];
|
|
4184
|
+
for (const pv of b.parts.values()) {
|
|
4185
|
+
for (const mv of pv.meshes) {
|
|
4186
|
+
this.visuals.worldBox(mv, whole);
|
|
4187
|
+
if (!whole.isEmpty()) wholeBoxes.push([whole.min.x, whole.min.y, whole.min.z, whole.max.x, whole.max.y, whole.max.z]);
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
if (!this.standsOn(objectId, wholeBoxes) || this.standsOn(objectId, boxes)) return false;
|
|
4191
|
+
const piece = this.makePiece(objectId);
|
|
4192
|
+
this.setContacts(b, b.piece ?? piece, this.meshesOf(b).filter((mv) => this.visuals.shown(mv)));
|
|
4193
|
+
this.tipIfTopHeavy(b);
|
|
4194
|
+
this.o.onLoosened?.(objectId, piece, "apart");
|
|
4195
|
+
return true;
|
|
4196
|
+
}
|
|
4197
|
+
/** 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. */
|
|
4198
|
+
standsOn(objectId, boxes) {
|
|
4199
|
+
if (boxes.length === 0) return false;
|
|
4200
|
+
const low = Math.min(...boxes.map((one) => one[1]));
|
|
4201
|
+
for (const [x0, y0, z0, x1, , z1] of boxes) {
|
|
4202
|
+
if (y0 > low + FEET_BAND) continue;
|
|
4203
|
+
for (const [fx, fz] of BROKEN_SAMPLES) {
|
|
4204
|
+
const x = x0 + (x1 - x0) * fx;
|
|
4205
|
+
const z = z0 + (z1 - z0) * fz;
|
|
4206
|
+
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));
|
|
4207
|
+
if (under >= y0 - BROKEN_GAP) return true;
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
return false;
|
|
4211
|
+
}
|
|
4212
|
+
/** 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. */
|
|
4213
|
+
kindOf(b) {
|
|
4214
|
+
for (let o = b.root; o; o = o.parent) {
|
|
4215
|
+
const kind = kitOriginOf(o)?.kind;
|
|
4216
|
+
if (kind) return kind;
|
|
4217
|
+
}
|
|
4218
|
+
return "";
|
|
4219
|
+
}
|
|
4220
|
+
/** `staticTopAt` without the boxes of `objectId` itself. */
|
|
4221
|
+
staticTopAtBut(objectId, x, z, y, step) {
|
|
4222
|
+
let top = -Infinity;
|
|
4223
|
+
for (const [id, a] of this.staticNow()) {
|
|
4224
|
+
if (id === objectId || a.length === 0 || x < a[0] || x > a[3] || z < a[2] || z > a[5]) continue;
|
|
4225
|
+
for (let i = 6; i < a.length; i += 6) {
|
|
4226
|
+
if (x < a[i] || x > a[i + 3] || z < a[i + 2] || z > a[i + 5]) continue;
|
|
4227
|
+
const t = a[i + 4];
|
|
4228
|
+
if (t > top && t <= y + step) top = t;
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
return top;
|
|
4232
|
+
}
|
|
4233
|
+
/**
|
|
4234
|
+
* 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
|
|
4235
|
+
* what holds it and it goes over until the top meets the floor. Past `CONTACT_MESHES` meshes it keeps the corners of its box.
|
|
4236
|
+
*/
|
|
4237
|
+
setContacts(b, body, meshes) {
|
|
4238
|
+
if (meshes.length > CONTACT_MESHES) {
|
|
4239
|
+
body.contacts = void 0;
|
|
4240
|
+
return;
|
|
4241
|
+
}
|
|
4242
|
+
const inv = specFrameInverse(this.THREE, b.root);
|
|
4243
|
+
const m4 = new this.THREE.Matrix4();
|
|
4244
|
+
const lb = new this.THREE.Box3();
|
|
4245
|
+
const [cx, cy, cz] = body.localCenter;
|
|
4246
|
+
const points = [];
|
|
4247
|
+
for (const mv of meshes) {
|
|
4248
|
+
mv.mesh.updateWorldMatrix(true, false);
|
|
4249
|
+
this.visuals.localBox(mv, lb);
|
|
4250
|
+
if (lb.isEmpty()) continue;
|
|
4251
|
+
lb.applyMatrix4(m4.multiplyMatrices(inv, mv.mesh.matrixWorld));
|
|
4252
|
+
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]);
|
|
4253
|
+
}
|
|
4254
|
+
body.contacts = points.length > 0 ? points : void 0;
|
|
4255
|
+
}
|
|
4026
4256
|
/**
|
|
4027
4257
|
* 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
|
|
4028
4258
|
* high and its feet are thin, so it gets the start of the fall, across the line of its feet.
|
|
@@ -4314,6 +4544,7 @@ var Renderer = class {
|
|
|
4314
4544
|
this.active.clear();
|
|
4315
4545
|
for (const key of [...this.fires.keys()]) this.stopFire(key);
|
|
4316
4546
|
this.leaks.length = 0;
|
|
4547
|
+
this.trickles.length = 0;
|
|
4317
4548
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
4318
4549
|
for (const b of [...this.bounds.values()]) {
|
|
4319
4550
|
if (b.isSplit) {
|
|
@@ -4582,6 +4813,21 @@ var FIRE_LOOP = {
|
|
|
4582
4813
|
volume: 0,
|
|
4583
4814
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
4584
4815
|
};
|
|
4816
|
+
var SMOKE_HISS_LOOP = {
|
|
4817
|
+
id: "smoke_grenade_hiss",
|
|
4818
|
+
bus: "world",
|
|
4819
|
+
order: "chain_loop",
|
|
4820
|
+
source: { kind: "noise", loop: true },
|
|
4821
|
+
filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 1400 }, q: 0.7 }, { type: "lowpass", frequency: { set: 7e3 }, q: 0.5 }] },
|
|
4822
|
+
volume: 0,
|
|
4823
|
+
sources: ["sdk: a smoke canister venting (the gas through its holes, steady for its whole burn)"]
|
|
4824
|
+
};
|
|
4825
|
+
var GRENADE_PIN_PULL = blast(
|
|
4826
|
+
"grenade_pin_pull",
|
|
4827
|
+
[noise({ type: "highpass", f: [3200, 4200], d: 0.025, vol: 0.5 }), noise({ type: "bandpass", f: [2200, 2800], q: 2, d: 0.04, vol: 0.4, delay: 0.18 })],
|
|
4828
|
+
["sdk: the ring pulled from the fuse, then the spoon flying off"],
|
|
4829
|
+
FIRE_FALLOFF
|
|
4830
|
+
);
|
|
4585
4831
|
var FIRE_FUEL_OF = {
|
|
4586
4832
|
wood_soft: "wood",
|
|
4587
4833
|
wood_hard: "wood",
|
|
@@ -4771,7 +5017,7 @@ var IMPACT_SOUNDS = [
|
|
|
4771
5017
|
impact("plastic_crack", [noise2({ type: "bandpass", f: 1800, q: 2, d: 0.06, vol: 0.75 }), tone2({ f: [450, 550], f2: 250, d: 0.05, vol: 0.22 })]),
|
|
4772
5018
|
impact("mdf_crumble", [noise2({ type: "lowpass", f: 700, d: 0.25, vol: 0.75, wet: 0.2 }), ...many2(4, () => noise2({ type: "lowpass", f: 500, d: 0.05, vol: 0.32, delay: [0.05, 0.3] }))])
|
|
4773
5019
|
];
|
|
4774
|
-
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] };
|
|
5020
|
+
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] };
|
|
4775
5021
|
|
|
4776
5022
|
// src/audio/sound.ts
|
|
4777
5023
|
import { FOLEY_BANK, GAME_SFX_BANK } from "@adatechnology/game-audio";
|
|
@@ -5106,10 +5352,12 @@ export {
|
|
|
5106
5352
|
FIRE_FALLOFF,
|
|
5107
5353
|
FIRE_LOOP,
|
|
5108
5354
|
FIRE_SOUNDS,
|
|
5355
|
+
GRENADE_PIN_PULL,
|
|
5109
5356
|
IMPACT_FAMILY_TAGS,
|
|
5110
5357
|
IMPACT_SOUNDS,
|
|
5111
5358
|
IMPACT_SOUND_KEYS,
|
|
5112
5359
|
SCENE_SOUNDS,
|
|
5360
|
+
SMOKE_HISS_LOOP,
|
|
5113
5361
|
blastFireSampleBank,
|
|
5114
5362
|
breakSoundKeys,
|
|
5115
5363
|
createDestructionRenderer,
|