@adatechnology/game-test-room 0.29.0 → 0.29.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.global.js +451 -92
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +52 -5
- package/dist/index.js.map +1 -1
- package/package.json +15 -15
package/dist/index.global.js
CHANGED
|
@@ -3722,23 +3722,42 @@ var GameTestRoom = (() => {
|
|
|
3722
3722
|
const v = table[mat.penetrationClass ?? mat.id] ?? table[mat.id];
|
|
3723
3723
|
return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
|
|
3724
3724
|
};
|
|
3725
|
-
var
|
|
3725
|
+
var BURIED_BLAST_REACH = 0.5;
|
|
3726
|
+
var GIVE_AT_CUT_HP = 0.5;
|
|
3727
|
+
var capacityScale = (p, borne, through) => {
|
|
3726
3728
|
const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
|
|
3727
3729
|
if (p.st.stripped) return residual;
|
|
3728
|
-
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1,
|
|
3730
|
+
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, sectionKept(p, residual, borne, through));
|
|
3729
3731
|
return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
|
|
3730
3732
|
};
|
|
3731
|
-
var
|
|
3733
|
+
var sectionKept = (p, residual, borne, through) => {
|
|
3734
|
+
let kept = 1;
|
|
3735
|
+
forEachSection(p, borne, (c, along, gone) => {
|
|
3736
|
+
const share = through?.(c.center, along) ?? 1;
|
|
3737
|
+
if (share <= 0) return;
|
|
3738
|
+
kept = Math.min(kept, Math.max(p.spec.reinforcement ? residual : 0, 1 - gone) / share);
|
|
3739
|
+
});
|
|
3740
|
+
return kept;
|
|
3741
|
+
};
|
|
3742
|
+
var sectionLoss = (p, borne) => {
|
|
3743
|
+
let worst = 0;
|
|
3744
|
+
forEachSection(p, borne, (_c, _along, gone) => {
|
|
3745
|
+
worst = Math.max(worst, gone);
|
|
3746
|
+
});
|
|
3747
|
+
return Math.min(1, worst);
|
|
3748
|
+
};
|
|
3749
|
+
var forEachSection = (p, borne, visit) => {
|
|
3732
3750
|
const cells = p.st.chipped;
|
|
3733
|
-
if (!cells?.length) return
|
|
3751
|
+
if (!cells?.length) return;
|
|
3734
3752
|
const [u, v] = planeAxes(p.thin);
|
|
3735
3753
|
const role = p.spec.structure?.role;
|
|
3736
3754
|
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
3737
3755
|
const upright = Math.abs(up[1]) > 0.7 ? v : Math.abs(rotateVec(p.q, [0, 0, 0].map((_, k) => k === u ? 1 : 0))[1]) > 0.7 ? u : -1;
|
|
3738
3756
|
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
3739
3757
|
const across = along === u ? v : u;
|
|
3740
|
-
|
|
3758
|
+
const lying = along !== upright || upright < 0;
|
|
3741
3759
|
for (const c of cells) {
|
|
3760
|
+
if (lying && borne?.(c.center, along)) continue;
|
|
3742
3761
|
const spans = [];
|
|
3743
3762
|
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) spans.push([d.center[across] - d.size[across] / 2, d.center[across] + d.size[across] / 2]);
|
|
3744
3763
|
spans.sort((a, b) => a[0] - b[0]);
|
|
@@ -3748,9 +3767,8 @@ var GameTestRoom = (() => {
|
|
|
3748
3767
|
gone += b - Math.max(a, end3);
|
|
3749
3768
|
end3 = b;
|
|
3750
3769
|
}
|
|
3751
|
-
|
|
3770
|
+
visit(c, along, Math.min(1, gone / Math.max(p.dims[across], 1e-6)));
|
|
3752
3771
|
}
|
|
3753
|
-
return Math.min(1, worst);
|
|
3754
3772
|
};
|
|
3755
3773
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
3756
3774
|
var clone = (v) => structuredClone(v);
|
|
@@ -4061,6 +4079,26 @@ var GameTestRoom = (() => {
|
|
|
4061
4079
|
byTouching.set(support, { version: poseVersion, box: box2 });
|
|
4062
4080
|
return box2;
|
|
4063
4081
|
};
|
|
4082
|
+
const throughOf = (p) => {
|
|
4083
|
+
const load = p.st.load ?? 0;
|
|
4084
|
+
if (load <= 0) return void 0;
|
|
4085
|
+
const above2 = Math.max(0, load - p.st.mass);
|
|
4086
|
+
const up = rotateVec(p.q, [0, 1, 0]);
|
|
4087
|
+
return (local2, along) => {
|
|
4088
|
+
const axis2 = [0, 0, 0];
|
|
4089
|
+
axis2[along] = 1;
|
|
4090
|
+
const sign = Math.sign(rotateVec(p.q, axis2)[1]);
|
|
4091
|
+
if (Math.abs(up[1]) < 0.7 && Math.abs(rotateVec(p.q, axis2)[1]) < 0.7) return 1;
|
|
4092
|
+
const half = p.dims[along] / 2;
|
|
4093
|
+
const share = clamp2((half - sign * local2[along]) / Math.max(p.dims[along], 1e-6), 0, 1);
|
|
4094
|
+
return (above2 + p.st.mass * share) / load;
|
|
4095
|
+
};
|
|
4096
|
+
};
|
|
4097
|
+
const borneOn = (p, supports) => {
|
|
4098
|
+
if (supports.length === 0) return void 0;
|
|
4099
|
+
const boxes = supports.map((support) => contactBox(p, support));
|
|
4100
|
+
return (local2, along) => boxes.some(([lo, hi]) => local2[along] >= lo[along] - SUPPORT_TOLERANCE && local2[along] <= hi[along] + SUPPORT_TOLERANCE);
|
|
4101
|
+
};
|
|
4064
4102
|
const measureContact = (p, support) => {
|
|
4065
4103
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
4066
4104
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
@@ -4268,7 +4306,7 @@ var GameTestRoom = (() => {
|
|
|
4268
4306
|
o.unsup[i] = o.anyAnchor && !isLost(p) && !p.orphan;
|
|
4269
4307
|
continue;
|
|
4270
4308
|
}
|
|
4271
|
-
p.st.stress = p.st.load * structureSafety / (p.cap * capacityScale(p));
|
|
4309
|
+
p.st.stress = p.st.load * structureSafety / (p.cap * capacityScale(p, borneOn(p, (lowerOf.get(i) ?? []).map((k) => parts[k])), throughOf(p)));
|
|
4272
4310
|
if (p.bend) {
|
|
4273
4311
|
p.bendLoad = p.st.mass + bending[i];
|
|
4274
4312
|
const bent = restFlexure(o, p, lowerOf.get(i) ?? []);
|
|
@@ -5358,6 +5396,27 @@ var GameTestRoom = (() => {
|
|
|
5358
5396
|
debris3(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
|
|
5359
5397
|
return loose.length;
|
|
5360
5398
|
};
|
|
5399
|
+
const giveAtCut = (o, p, src) => {
|
|
5400
|
+
if (!p.spec.reinforcement || !p.spec.units || !p.st.chipped?.length || sectionLoss(p) <= 0) return false;
|
|
5401
|
+
const opened = [];
|
|
5402
|
+
for (const j of o.byPart.get(p.spec.id) ?? []) {
|
|
5403
|
+
if (j.st.open) continue;
|
|
5404
|
+
const onIt = j.bears === "a_on_b" && j.spec.b === p.spec.id || j.bears === "b_on_a" && j.spec.a === p.spec.id;
|
|
5405
|
+
if (!onIt) continue;
|
|
5406
|
+
j.st.open = true;
|
|
5407
|
+
j.st.openedBy = "break";
|
|
5408
|
+
opened.push(j);
|
|
5409
|
+
}
|
|
5410
|
+
if (opened.length === 0) return false;
|
|
5411
|
+
p.st.hp = Math.max(p.st.hp, p.st.maxHp * GIVE_AT_CUT_HP);
|
|
5412
|
+
markDamaged(p);
|
|
5413
|
+
solveStructure(o);
|
|
5414
|
+
recomputeGroups(o);
|
|
5415
|
+
detachEvents(o, opened, "break", src);
|
|
5416
|
+
exposeRebar(o, p, src);
|
|
5417
|
+
collapseCheck(o, src);
|
|
5418
|
+
return true;
|
|
5419
|
+
};
|
|
5361
5420
|
const stripPart = (o, p, reinforcement, src) => {
|
|
5362
5421
|
const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
|
|
5363
5422
|
p.st.stripped = true;
|
|
@@ -5415,6 +5474,7 @@ var GameTestRoom = (() => {
|
|
|
5415
5474
|
hingeDrop(o, p, src);
|
|
5416
5475
|
return;
|
|
5417
5476
|
}
|
|
5477
|
+
if (!force && giveAtCut(o, p, src)) return;
|
|
5418
5478
|
const ex = p.spec.explodes;
|
|
5419
5479
|
const style = force ?? (p.snap ? "snap" : p.mat.fracture === "none" ? "snap" : p.mat.fracture);
|
|
5420
5480
|
const { c, q } = partXf(o, p);
|
|
@@ -5761,7 +5821,9 @@ var GameTestRoom = (() => {
|
|
|
5761
5821
|
const dx = local2[0] - p.pos[0];
|
|
5762
5822
|
const dy = local2[1] - p.pos[1];
|
|
5763
5823
|
const dz = local2[2] - p.pos[2];
|
|
5764
|
-
if (dx * dx + dy * dy + dz * dz
|
|
5824
|
+
if (dx * dx + dy * dy + dz * dz >= reach * reach) continue;
|
|
5825
|
+
if (p.spec.anchored && p.spec.structure?.role === "foundation" && distToPart(o, p, point) > BURIED_BLAST_REACH) continue;
|
|
5826
|
+
near.push(p);
|
|
5765
5827
|
}
|
|
5766
5828
|
blastProbe.partsTested += o.parts.length;
|
|
5767
5829
|
blastProbe.partsNear += near.length;
|
|
@@ -7270,7 +7332,7 @@ var GameTestRoom = (() => {
|
|
|
7270
7332
|
return bank;
|
|
7271
7333
|
}
|
|
7272
7334
|
|
|
7273
|
-
// ../game-audio/dist/chunk-
|
|
7335
|
+
// ../game-audio/dist/chunk-BUYBYP4L.js
|
|
7274
7336
|
var DISTANCE_CURVES = {
|
|
7275
7337
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
7276
7338
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -9665,6 +9727,8 @@ var GameTestRoom = (() => {
|
|
|
9665
9727
|
["revolver_38_357", 2, "sfx_pistol"],
|
|
9666
9728
|
["revolver_44_50ae", 3, "sfx_pistol"],
|
|
9667
9729
|
["smg_9mm_auto", 1, "sfx_pistol"],
|
|
9730
|
+
["launcher_40mm", 2],
|
|
9731
|
+
["rocket_launch", 2],
|
|
9668
9732
|
["shotgun_12", 1, "sfx_shotgun"],
|
|
9669
9733
|
["rifle_5_56", 1, "sfx_rifle"],
|
|
9670
9734
|
["rifle_7_62x39", 1, "sfx_rifle"],
|
|
@@ -17204,8 +17268,8 @@ var GameTestRoom = (() => {
|
|
|
17204
17268
|
id: "dust",
|
|
17205
17269
|
additive: false,
|
|
17206
17270
|
life: [
|
|
17207
|
-
|
|
17208
|
-
|
|
17271
|
+
2,
|
|
17272
|
+
5
|
|
17209
17273
|
],
|
|
17210
17274
|
size: [
|
|
17211
17275
|
0.08,
|
|
@@ -17272,8 +17336,8 @@ var GameTestRoom = (() => {
|
|
|
17272
17336
|
id: "chips",
|
|
17273
17337
|
additive: false,
|
|
17274
17338
|
life: [
|
|
17275
|
-
|
|
17276
|
-
|
|
17339
|
+
2,
|
|
17340
|
+
4
|
|
17277
17341
|
],
|
|
17278
17342
|
size: [
|
|
17279
17343
|
0.025,
|
|
@@ -19915,7 +19979,7 @@ var GameTestRoom = (() => {
|
|
|
19915
19979
|
return sources;
|
|
19916
19980
|
}
|
|
19917
19981
|
|
|
19918
|
-
// ../game-vfx-three/dist/chunk-
|
|
19982
|
+
// ../game-vfx-three/dist/chunk-IOGYK5RO.js
|
|
19919
19983
|
var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
|
|
19920
19984
|
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]);
|
|
19921
19985
|
vec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`;
|
|
@@ -19962,10 +20026,15 @@ void main(){
|
|
|
19962
20026
|
if(n<diss*1.08-.04)discard;
|
|
19963
20027
|
float lam=max(dot(N,uLight),0.);
|
|
19964
20028
|
float band=lam>.55?1.:lam>.15?.68:.42;
|
|
19965
|
-
|
|
20029
|
+
// 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)
|
|
20030
|
+
float dark=1.-smoothstep(.02,.12,dot(vC,vec3(.2126,.7152,.0722)));
|
|
20031
|
+
vec3 alb=max(vC,vec3(.055)*dark);
|
|
20032
|
+
vec3 col=alb*(vec3(.5,.56,.7)*band*1.05+vec3(.2,.19,.18)+uGround*(.4-.4*N.y));
|
|
20033
|
+
float rim=pow(1.-clamp(abs(dot(N,normalize(cameraPosition-vWP))),0.,1.),2.);
|
|
20034
|
+
col+=dark*rim*(uSky*.9+alb);
|
|
19966
20035
|
vec3 tg=uGlowPos-vWP;float dg=length(tg)+.001;
|
|
19967
20036
|
float gi=uGlowI/(1.+dg*dg*.05)*(.3+.7*max(dot(N,tg/dg),0.));
|
|
19968
|
-
col+=uGlowCol*gi*
|
|
20037
|
+
col+=uGlowCol*gi*alb*1.6;
|
|
19969
20038
|
float h=heat*(.72+.56*n)+heat*(-N.y)*.18;
|
|
19970
20039
|
if(h>.05){
|
|
19971
20040
|
float hq=floor(h*5.+.5)/5.;
|
|
@@ -22143,7 +22212,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22143
22212
|
var SLAM_BOUNCE = 0.2;
|
|
22144
22213
|
var SLAM_FLOOR_NORMAL = 0.7;
|
|
22145
22214
|
var SUB_DT = 1 / 120;
|
|
22146
|
-
var
|
|
22215
|
+
var MU_STATIC = 0.7;
|
|
22216
|
+
var MU_DYNAMIC = 0.6;
|
|
22217
|
+
var SLIP_SPEED = 0.02;
|
|
22147
22218
|
var SLEEP_AFTER = 0.5;
|
|
22148
22219
|
var CALM_DRIFT = 0.01;
|
|
22149
22220
|
var CALM_TURN = 0.05;
|
|
@@ -22162,6 +22233,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22162
22233
|
var EDGE_POINTS = [-0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75];
|
|
22163
22234
|
var SUPPORT_CHECKS2 = 16;
|
|
22164
22235
|
var SUPPORT_GAP2 = 0.02;
|
|
22236
|
+
var WAKE_ABOVE_SPEED = 0.05;
|
|
22237
|
+
var WAKE_ABOVE_SPIN = 0.1;
|
|
22238
|
+
var topFace = 1;
|
|
22165
22239
|
var boxTopAt = (s, x, z) => {
|
|
22166
22240
|
const e = s.e;
|
|
22167
22241
|
const dx = x - s.c[0], dz = z - s.c[2];
|
|
@@ -22177,11 +22251,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22177
22251
|
let a = (-h - p) / d, b = (h - p) / d;
|
|
22178
22252
|
if (a > b) [a, b] = [b, a];
|
|
22179
22253
|
t0 = Math.max(t0, a);
|
|
22180
|
-
|
|
22254
|
+
if (b < t1) {
|
|
22255
|
+
t1 = b;
|
|
22256
|
+
topFace = k;
|
|
22257
|
+
}
|
|
22181
22258
|
if (t0 > t1) return -Infinity;
|
|
22182
22259
|
}
|
|
22183
22260
|
return s.c[1] + t1;
|
|
22184
22261
|
};
|
|
22262
|
+
var UP16 = [0, 1, 0];
|
|
22263
|
+
var faceNormal = (s, axis2) => {
|
|
22264
|
+
const e = s.e;
|
|
22265
|
+
const sign = e[axis2 * 4 + 1] < 0 ? -1 : 1;
|
|
22266
|
+
return [sign * e[axis2 * 4], sign * e[axis2 * 4 + 1], sign * e[axis2 * 4 + 2]];
|
|
22267
|
+
};
|
|
22268
|
+
var pairFriction = (a, b) => ({
|
|
22269
|
+
static: Math.sqrt((a?.static ?? MU_STATIC) * (b?.static ?? MU_STATIC)),
|
|
22270
|
+
dynamic: Math.sqrt((a?.dynamic ?? MU_DYNAMIC) * (b?.dynamic ?? MU_DYNAMIC))
|
|
22271
|
+
});
|
|
22185
22272
|
var PieceSystem = class {
|
|
22186
22273
|
constructor(THREE, o) {
|
|
22187
22274
|
__publicField3(this, "THREE", THREE);
|
|
@@ -22195,6 +22282,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22195
22282
|
__publicField3(this, "ax");
|
|
22196
22283
|
__publicField3(this, "solids", []);
|
|
22197
22284
|
__publicField3(this, "supportTurn", 0);
|
|
22285
|
+
__publicField3(this, "supportNormal", UP16);
|
|
22286
|
+
__publicField3(this, "supportFriction", null);
|
|
22198
22287
|
this.p = new THREE.Vector3();
|
|
22199
22288
|
this.q = new THREE.Quaternion();
|
|
22200
22289
|
this.dq = new THREE.Quaternion();
|
|
@@ -22300,8 +22389,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22300
22389
|
const lz = (e[8] * x + e[9] * y + e[10] * z) * iz;
|
|
22301
22390
|
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];
|
|
22302
22391
|
}
|
|
22303
|
-
/** 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
|
|
22392
|
+
/** 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`. */
|
|
22304
22393
|
samples(b, edges) {
|
|
22394
|
+
if (b.contacts) return b.contacts;
|
|
22305
22395
|
const [hx, hy, hz2] = b.halfExtents;
|
|
22306
22396
|
const out = [];
|
|
22307
22397
|
for (let i = 0; i < 8; i++) out.push([i & 1 ? hx : -hx, i & 2 ? hy : -hy, i & 4 ? hz2 : -hz2]);
|
|
@@ -22366,6 +22456,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22366
22456
|
}
|
|
22367
22457
|
if (b.sleeping) continue;
|
|
22368
22458
|
this.integrate(b, dt);
|
|
22459
|
+
if (b.sleeping) continue;
|
|
22460
|
+
const v = b.velocity;
|
|
22461
|
+
const w = b.angularVelocity;
|
|
22462
|
+
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);
|
|
22369
22463
|
}
|
|
22370
22464
|
this.separate(dt);
|
|
22371
22465
|
}
|
|
@@ -22396,15 +22490,48 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22396
22490
|
}
|
|
22397
22491
|
return false;
|
|
22398
22492
|
}
|
|
22493
|
+
/** 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. */
|
|
22494
|
+
topAt(x, z, y, step, self) {
|
|
22495
|
+
let top = -Infinity;
|
|
22496
|
+
for (const s of this.solids) {
|
|
22497
|
+
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;
|
|
22498
|
+
const t = boxTopAt(s, x, z);
|
|
22499
|
+
if (t > top && t <= y + step) top = t;
|
|
22500
|
+
}
|
|
22501
|
+
return top;
|
|
22502
|
+
}
|
|
22503
|
+
/** 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. */
|
|
22504
|
+
wakeAbove(b) {
|
|
22505
|
+
const below = this.solids.find((s) => s.body === b);
|
|
22506
|
+
if (!below) return;
|
|
22507
|
+
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]);
|
|
22508
|
+
for (const s of this.solids) {
|
|
22509
|
+
const o = s.body;
|
|
22510
|
+
if (!o.sleeping || o.held || o === b) continue;
|
|
22511
|
+
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;
|
|
22512
|
+
o.sleeping = false;
|
|
22513
|
+
o.still = 0;
|
|
22514
|
+
o.calmAt = null;
|
|
22515
|
+
}
|
|
22516
|
+
}
|
|
22399
22517
|
/** Where a corner of `self` at `(x, y, z)` lands: the ground, the top of another piece or of something fixed under it. */
|
|
22400
22518
|
supportAt(self, x, y, z, step) {
|
|
22401
22519
|
let top = this.o.groundAt(x, z);
|
|
22520
|
+
let on = null;
|
|
22521
|
+
let face = 1;
|
|
22402
22522
|
for (const s of this.solids) {
|
|
22403
22523
|
if (s.body === self || s.body.held || Math.abs(x - s.c[0]) > s.r || Math.abs(z - s.c[2]) > s.r) continue;
|
|
22404
22524
|
const t = boxTopAt(s, x, z);
|
|
22405
|
-
if (t > top && t <= y + step)
|
|
22525
|
+
if (t > top && t <= y + step) {
|
|
22526
|
+
top = t;
|
|
22527
|
+
on = s;
|
|
22528
|
+
face = topFace;
|
|
22529
|
+
}
|
|
22406
22530
|
}
|
|
22407
22531
|
const fixed2 = this.o.staticTopAt?.(x, z, y, step) ?? -Infinity;
|
|
22532
|
+
if (fixed2 >= top) on = null;
|
|
22533
|
+
this.supportNormal = on ? faceNormal(on, face) : UP16;
|
|
22534
|
+
this.supportFriction = on?.body.friction ?? null;
|
|
22408
22535
|
return Math.max(top, fixed2);
|
|
22409
22536
|
}
|
|
22410
22537
|
/** 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. */
|
|
@@ -22487,7 +22614,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22487
22614
|
const e = this.m.elements;
|
|
22488
22615
|
const rs = [];
|
|
22489
22616
|
const ids = [];
|
|
22617
|
+
const ns = [];
|
|
22618
|
+
const mus = [];
|
|
22490
22619
|
let maxPen = 0;
|
|
22620
|
+
let penNormal = UP16;
|
|
22491
22621
|
const rise = STEP + Math.max(0, -v[1]) * h * 2;
|
|
22492
22622
|
let side = null;
|
|
22493
22623
|
let lowest = Infinity;
|
|
@@ -22505,6 +22635,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22505
22635
|
if (floor2 !== void 0 && floor2 - cy > -SLOP && floor2 < raw - STEP) {
|
|
22506
22636
|
rs.push(r);
|
|
22507
22637
|
ids.push(i);
|
|
22638
|
+
ns.push(UP16);
|
|
22639
|
+
mus.push(pairFriction(b.friction, null));
|
|
22508
22640
|
maxPen = Math.max(maxPen, floor2 - cy);
|
|
22509
22641
|
}
|
|
22510
22642
|
continue;
|
|
@@ -22525,12 +22657,19 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22525
22657
|
}
|
|
22526
22658
|
continue;
|
|
22527
22659
|
}
|
|
22528
|
-
|
|
22660
|
+
this.supportNormal = UP16;
|
|
22661
|
+
this.supportFriction = null;
|
|
22662
|
+
const gy = i < 8 || b.contacts ? this.supportAt(b, px, cy, pz, rise) : Math.max(raw, this.o.staticTopAt?.(px, pz, cy, rise) ?? -Infinity);
|
|
22529
22663
|
const pen = gy - cy;
|
|
22530
22664
|
if (pen > -SLOP) {
|
|
22531
22665
|
rs.push(r);
|
|
22532
22666
|
ids.push(i);
|
|
22533
|
-
|
|
22667
|
+
ns.push(this.supportNormal);
|
|
22668
|
+
mus.push(pairFriction(b.friction, this.supportFriction));
|
|
22669
|
+
if (pen > maxPen) {
|
|
22670
|
+
maxPen = pen;
|
|
22671
|
+
penNormal = this.supportNormal;
|
|
22672
|
+
}
|
|
22534
22673
|
lowest = Math.min(lowest, gy);
|
|
22535
22674
|
}
|
|
22536
22675
|
}
|
|
@@ -22543,7 +22682,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22543
22682
|
contactedLast = rs.length > 0;
|
|
22544
22683
|
if (contactedLast) touched = true;
|
|
22545
22684
|
if (!contactedLast) continue;
|
|
22546
|
-
|
|
22685
|
+
const out = Math.max(0, maxPen - SLOP * 0.5) * penNormal[1];
|
|
22686
|
+
pos[0] += penNormal[0] * out;
|
|
22687
|
+
pos[1] += penNormal[1] * out;
|
|
22688
|
+
pos[2] += penNormal[2] * out;
|
|
22547
22689
|
let impact22 = 0;
|
|
22548
22690
|
for (const r of rs) impact22 = Math.max(impact22, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
22549
22691
|
const restitution = impact22 > 2 ? 0.25 : 0;
|
|
@@ -22559,76 +22701,72 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22559
22701
|
const lz = pos[2] + sz / rs.length;
|
|
22560
22702
|
landPoint = [lx, pos[1] - Math.max(...rs.map((r) => -r[1])), lz];
|
|
22561
22703
|
}
|
|
22562
|
-
const
|
|
22563
|
-
|
|
22564
|
-
|
|
22704
|
+
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]];
|
|
22705
|
+
const dot5 = (a, c2) => a[0] * c2[0] + a[1] * c2[1] + a[2] * c2[2];
|
|
22706
|
+
const goal = rs.map((r, k) => {
|
|
22707
|
+
const vn = dot5(pointV(r), ns[k]);
|
|
22708
|
+
return vn < 0 ? -restitution * vn : 0;
|
|
22709
|
+
});
|
|
22710
|
+
const slipping = rs.map((r, k) => {
|
|
22711
|
+
const pv = pointV(r);
|
|
22712
|
+
const vn = dot5(pv, ns[k]);
|
|
22713
|
+
return Math.hypot(pv[0] - vn * ns[k][0], pv[1] - vn * ns[k][1], pv[2] - vn * ns[k][2]) > SLIP_SPEED;
|
|
22565
22714
|
});
|
|
22566
22715
|
const vx0 = v[0];
|
|
22567
22716
|
const vz0 = v[2];
|
|
22568
|
-
if (!b.warm || b.warm.length !== WARM_SLOTS *
|
|
22717
|
+
if (!b.warm || b.warm.length !== WARM_SLOTS * 4) b.warm = new Float64Array(WARM_SLOTS * 4);
|
|
22569
22718
|
const warm = b.warm;
|
|
22570
|
-
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id *
|
|
22571
|
-
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id *
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
|
|
22575
|
-
|
|
22576
|
-
|
|
22577
|
-
v[0] += fx * invM;
|
|
22578
|
-
v[1] += jn * invM;
|
|
22579
|
-
v[2] += fz * invM;
|
|
22580
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * fz - r[2] * jn, r[2] * fx - r[0] * fz, r[0] * jn - r[1] * fx);
|
|
22719
|
+
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id * 4] : 0);
|
|
22720
|
+
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id * 4 + 1], warm[id * 4 + 2], warm[id * 4 + 3]] : [0, 0, 0]);
|
|
22721
|
+
const push = (r, j) => {
|
|
22722
|
+
v[0] += j[0] * invM;
|
|
22723
|
+
v[1] += j[1] * invM;
|
|
22724
|
+
v[2] += j[2] * invM;
|
|
22725
|
+
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]);
|
|
22581
22726
|
w[0] += dw[0];
|
|
22582
22727
|
w[1] += dw[1];
|
|
22583
22728
|
w[2] += dw[2];
|
|
22729
|
+
};
|
|
22730
|
+
const reach = (r, d) => {
|
|
22731
|
+
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]);
|
|
22732
|
+
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];
|
|
22733
|
+
};
|
|
22734
|
+
for (let k = 0; k < rs.length; k++) {
|
|
22735
|
+
const n = ns[k];
|
|
22736
|
+
const t = accT[k];
|
|
22737
|
+
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]]);
|
|
22584
22738
|
}
|
|
22585
22739
|
for (let it = 0; it < SOLVER_PASSES; it++) {
|
|
22586
22740
|
for (let k = 0; k < rs.length; k++) {
|
|
22587
22741
|
const r = rs[k];
|
|
22588
|
-
const
|
|
22589
|
-
const
|
|
22590
|
-
const
|
|
22591
|
-
const total = Math.max(0, accN[k] + (goal[k] - vy2) / denom);
|
|
22742
|
+
const n = ns[k];
|
|
22743
|
+
const vn = dot5(pointV(r), n);
|
|
22744
|
+
const total = Math.max(0, accN[k] + (goal[k] - vn) / reach(r, n));
|
|
22592
22745
|
const j = total - accN[k];
|
|
22593
22746
|
accN[k] = total;
|
|
22594
|
-
if (j !== 0)
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22747
|
+
if (j !== 0) push(r, [n[0] * j, n[1] * j, n[2] * j]);
|
|
22748
|
+
const pv = pointV(r);
|
|
22749
|
+
const pn = dot5(pv, n);
|
|
22750
|
+
const vt = [pv[0] - pn * n[0], pv[1] - pn * n[1], pv[2] - pn * n[2]];
|
|
22751
|
+
const speed = Math.hypot(vt[0], vt[1], vt[2]);
|
|
22752
|
+
const f = [accT[k][0], accT[k][1], accT[k][2]];
|
|
22753
|
+
if (speed > 1e-6) {
|
|
22754
|
+
const d = [vt[0] / speed, vt[1] / speed, vt[2] / speed];
|
|
22755
|
+
const jt = speed / reach(r, d);
|
|
22756
|
+
f[0] -= d[0] * jt;
|
|
22757
|
+
f[1] -= d[1] * jt;
|
|
22758
|
+
f[2] -= d[2] * jt;
|
|
22759
|
+
} else if (f[0] === 0 && f[1] === 0 && f[2] === 0) continue;
|
|
22760
|
+
const most = (slipping[k] ? mus[k].dynamic : mus[k].static) * accN[k];
|
|
22761
|
+
const fl = Math.hypot(f[0], f[1], f[2]);
|
|
22762
|
+
if (fl > most) {
|
|
22763
|
+
f[0] *= most / fl;
|
|
22764
|
+
f[1] *= most / fl;
|
|
22765
|
+
f[2] *= most / fl;
|
|
22599
22766
|
}
|
|
22600
|
-
const
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
if (vt < 1e-6 && accT[k][0] === 0 && accT[k][1] === 0) continue;
|
|
22604
|
-
let fx = accT[k][0];
|
|
22605
|
-
let fz = accT[k][1];
|
|
22606
|
-
if (vt > 1e-6) {
|
|
22607
|
-
const tx = vx2 / vt;
|
|
22608
|
-
const tz = vz2 / vt;
|
|
22609
|
-
const at3 = this.mulInvInertiaWith(b, e, r[1] * tz, r[2] * tx - r[0] * tz, -r[1] * tx);
|
|
22610
|
-
const cx = at3[1] * r[2] - at3[2] * r[1];
|
|
22611
|
-
const cz = at3[0] * r[1] - at3[1] * r[0];
|
|
22612
|
-
const jt = vt / (invM + cx * tx + cz * tz);
|
|
22613
|
-
fx -= tx * jt;
|
|
22614
|
-
fz -= tz * jt;
|
|
22615
|
-
}
|
|
22616
|
-
const most = MU * accN[k];
|
|
22617
|
-
const f = Math.hypot(fx, fz);
|
|
22618
|
-
if (f > most) {
|
|
22619
|
-
fx *= most / f;
|
|
22620
|
-
fz *= most / f;
|
|
22621
|
-
}
|
|
22622
|
-
const dx = fx - accT[k][0];
|
|
22623
|
-
const dz = fz - accT[k][1];
|
|
22624
|
-
accT[k] = [fx, fz];
|
|
22625
|
-
if (dx === 0 && dz === 0) continue;
|
|
22626
|
-
v[0] += dx * invM;
|
|
22627
|
-
v[2] += dz * invM;
|
|
22628
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * dz, r[2] * dx - r[0] * dz, -r[1] * dx);
|
|
22629
|
-
w[0] += dw[0];
|
|
22630
|
-
w[1] += dw[1];
|
|
22631
|
-
w[2] += dw[2];
|
|
22767
|
+
const dj = [f[0] - accT[k][0], f[1] - accT[k][1], f[2] - accT[k][2]];
|
|
22768
|
+
accT[k] = f;
|
|
22769
|
+
if (dj[0] !== 0 || dj[1] !== 0 || dj[2] !== 0) push(r, dj);
|
|
22632
22770
|
}
|
|
22633
22771
|
}
|
|
22634
22772
|
pos[0] += (v[0] - vx0) * h;
|
|
@@ -22637,9 +22775,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22637
22775
|
for (let k = 0; k < rs.length; k++) {
|
|
22638
22776
|
const id = ids[k];
|
|
22639
22777
|
if (id >= WARM_SLOTS) continue;
|
|
22640
|
-
warm[id *
|
|
22641
|
-
warm[id *
|
|
22642
|
-
warm[id *
|
|
22778
|
+
warm[id * 4] = accN[k];
|
|
22779
|
+
warm[id * 4 + 1] = accT[k][0];
|
|
22780
|
+
warm[id * 4 + 2] = accT[k][1];
|
|
22781
|
+
warm[id * 4 + 3] = accT[k][2];
|
|
22643
22782
|
}
|
|
22644
22783
|
let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0;
|
|
22645
22784
|
for (const r of rs) {
|
|
@@ -22711,6 +22850,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22711
22850
|
b.calm += dt;
|
|
22712
22851
|
return b.calm >= CALM_SLEEP_AFTER;
|
|
22713
22852
|
}
|
|
22853
|
+
/** Whether the centre `c` of `upper` is over the top of `lower`'s box (as it was at the start of the update). */
|
|
22854
|
+
onTopOf(upper, lower, c) {
|
|
22855
|
+
const s = this.solids.find((one) => one.body === lower);
|
|
22856
|
+
if (!s) return false;
|
|
22857
|
+
const top = boxTopAt(s, c.x, c.z);
|
|
22858
|
+
return top > -Infinity && c.y >= top - 0.01;
|
|
22859
|
+
}
|
|
22714
22860
|
/** Half the height of the box of a piece as it is turned now. */
|
|
22715
22861
|
halfHeight(b) {
|
|
22716
22862
|
const e = this.m.makeRotationFromQuaternion(b.root.quaternion).elements;
|
|
@@ -22736,6 +22882,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22736
22882
|
this.centerOf(b, cb);
|
|
22737
22883
|
if (Math.abs(ca.y - cb.y) > ra + rb) continue;
|
|
22738
22884
|
if (Math.abs(ca.y - cb.y) >= (this.halfHeight(a) + this.halfHeight(b)) * 0.9) continue;
|
|
22885
|
+
if (this.onTopOf(b, a, cb) || this.onTopOf(a, b, ca)) continue;
|
|
22739
22886
|
const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8;
|
|
22740
22887
|
let dx = cb.x - ca.x;
|
|
22741
22888
|
let dz = cb.z - ca.z;
|
|
@@ -24126,6 +24273,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24126
24273
|
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
24127
24274
|
var GLOW_FROM_C = 430;
|
|
24128
24275
|
var QUEUE_CAP = 4e3;
|
|
24276
|
+
var TRICKLE_SECONDS = [2, 5];
|
|
24277
|
+
var TRICKLE_EVERY = 0.25;
|
|
24278
|
+
var TRICKLE_MAX = 24;
|
|
24129
24279
|
var STRUCTURAL_EVENTS = /* @__PURE__ */ new Set([
|
|
24130
24280
|
"fracture",
|
|
24131
24281
|
"detach",
|
|
@@ -24149,6 +24299,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24149
24299
|
var FEET_BAND = 0.03;
|
|
24150
24300
|
var TIP_MIN_HEIGHT = 0.3;
|
|
24151
24301
|
var TIP_NARROW = 0.3;
|
|
24302
|
+
var CONTACT_MESHES = 12;
|
|
24303
|
+
var BROKEN_GAP = 0.03;
|
|
24304
|
+
var BROKEN_SAMPLES = [[0.5, 0.5], [0.1, 0.1], [0.9, 0.1], [0.1, 0.9], [0.9, 0.9]];
|
|
24305
|
+
var UNCHECKED_KINDS = /* @__PURE__ */ new Set(["structure", "room", "ground"]);
|
|
24152
24306
|
var FIRE_CEILING_SECONDS = 3600;
|
|
24153
24307
|
var GENERIC_REFERENCE_RADIUS = 15;
|
|
24154
24308
|
var SWEEP_CORE = 0.15;
|
|
@@ -24244,9 +24398,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24244
24398
|
__publicField3(this, "staticAll", true);
|
|
24245
24399
|
__publicField3(this, "staticRound", []);
|
|
24246
24400
|
__publicField3(this, "pieceBoxes", /* @__PURE__ */ new WeakMap());
|
|
24401
|
+
__publicField3(this, "broken", /* @__PURE__ */ new Set());
|
|
24247
24402
|
__publicField3(this, "staticAt", 0);
|
|
24248
24403
|
__publicField3(this, "queue", []);
|
|
24249
24404
|
__publicField3(this, "coverWhenWentOff", /* @__PURE__ */ new WeakMap());
|
|
24405
|
+
__publicField3(this, "trickles", []);
|
|
24250
24406
|
__publicField3(this, "slices", []);
|
|
24251
24407
|
__publicField3(this, "sliceDeadline", Infinity);
|
|
24252
24408
|
__publicField3(this, "drainClock", () => performance.now());
|
|
@@ -24581,10 +24737,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24581
24737
|
dt = clamp6(dt, 0, 0.1);
|
|
24582
24738
|
this.time += dt;
|
|
24583
24739
|
if (!this.attached) this.drainQueue();
|
|
24740
|
+
this.checkBroken();
|
|
24584
24741
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
24585
24742
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
24586
24743
|
this.updateActive(dt);
|
|
24587
24744
|
this.updateLeaks(dt);
|
|
24745
|
+
this.updateTrickles(dt);
|
|
24588
24746
|
this.pieceSys.update(dt);
|
|
24589
24747
|
this.fragments.update(dt);
|
|
24590
24748
|
this.ground.update(dt);
|
|
@@ -24783,6 +24941,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24783
24941
|
}
|
|
24784
24942
|
apply(ev) {
|
|
24785
24943
|
this.staticDirty.add(ev.object);
|
|
24944
|
+
if (ev.type === "fracture" || ev.type === "detach" || ev.type === "collapse" || ev.type === "burnout" || ev.type === "destroyed") this.broken.add(ev.object);
|
|
24786
24945
|
switch (ev.type) {
|
|
24787
24946
|
case "damage":
|
|
24788
24947
|
if (ev.damageType === "heat" || ev.damageType === "fire" || ev.damageType === "explosive") this.activate(ev.object, ev.part);
|
|
@@ -25107,9 +25266,34 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25107
25266
|
const core = def?.visual?.innerColor;
|
|
25108
25267
|
if (core !== void 0) this.vfx.burst("chips", ev.point, [normal.x, normal.y, normal.z], 5, 2.5, { color: core });
|
|
25109
25268
|
else this.playEvent({ kind: "spall", material: this.impactMaterial(def) }, ev.point);
|
|
25269
|
+
if (core !== void 0) this.trickleFrom(ev.point, [normal.x, normal.y, normal.z], core);
|
|
25110
25270
|
if (mv.mesh.userData.drawnSpalls?.includes(ev.part)) return;
|
|
25111
25271
|
this.visuals.addSpall(mv, wp, normal, Math.sqrt(ev.area / Math.PI), core ?? fallbackColor(pv.material, def));
|
|
25112
25272
|
}
|
|
25273
|
+
/** 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. */
|
|
25274
|
+
trickleFrom(point, normal, color) {
|
|
25275
|
+
if (this.trickles.length >= TRICKLE_MAX) this.trickles.shift();
|
|
25276
|
+
const total = TRICKLE_SECONDS[0] + (TRICKLE_SECONDS[1] - TRICKLE_SECONDS[0]) * this.rng();
|
|
25277
|
+
this.trickles.push({ point: [point[0], point[1], point[2]], normal, color, left: total, total, next: TRICKLE_EVERY });
|
|
25278
|
+
}
|
|
25279
|
+
updateTrickles(dt) {
|
|
25280
|
+
for (let i = this.trickles.length - 1; i >= 0; i--) {
|
|
25281
|
+
const t = this.trickles[i];
|
|
25282
|
+
t.left -= dt;
|
|
25283
|
+
t.next -= dt;
|
|
25284
|
+
if (t.left <= 0) {
|
|
25285
|
+
this.trickles.splice(i, 1);
|
|
25286
|
+
continue;
|
|
25287
|
+
}
|
|
25288
|
+
if (t.next > 0) continue;
|
|
25289
|
+
const share = t.left / t.total;
|
|
25290
|
+
t.next = TRICKLE_EVERY / Math.max(share, 0.25);
|
|
25291
|
+
const out = [t.normal[0] * 0.2, -1, t.normal[2] * 0.2];
|
|
25292
|
+
const options = t.color !== void 0 ? { color: t.color } : void 0;
|
|
25293
|
+
this.vfx.burst("chips", t.point, out, this.low ? 1 : 2, 0.3, options);
|
|
25294
|
+
if (this.rng() < 0.5) this.vfx.burst("dust", t.point, out, 1, 0.2, options);
|
|
25295
|
+
}
|
|
25296
|
+
}
|
|
25113
25297
|
/**
|
|
25114
25298
|
* 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
|
|
25115
25299
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
@@ -25145,6 +25329,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25145
25329
|
const tone3 = fr.inner && fdef?.visual?.innerColor !== void 0 ? fdef.visual.innerColor : fdef === def ? color : fallbackColor(fr.material, fdef);
|
|
25146
25330
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, tone3, false), ev.object, maxAlive);
|
|
25147
25331
|
}
|
|
25332
|
+
if (def?.visual?.innerColor !== void 0 || pv?.material === "concrete" || pv?.material === "brick") this.trickleFrom(ev.center, [0, 0, 0], color);
|
|
25148
25333
|
if (ev.torn) {
|
|
25149
25334
|
this.impactSound(pv?.material ?? "", ev.center, 0.5, ev.blow);
|
|
25150
25335
|
return;
|
|
@@ -25334,26 +25519,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25334
25519
|
this.o.onPiece?.(piece);
|
|
25335
25520
|
}
|
|
25336
25521
|
/**
|
|
25337
|
-
*
|
|
25338
|
-
* 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,
|
|
25522
|
+
* 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
|
|
25523
|
+
* 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,
|
|
25524
|
+
* 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`).
|
|
25339
25525
|
*/
|
|
25340
25526
|
loosenIfFootless(objectId) {
|
|
25341
25527
|
const b = this.bounds.get(objectId);
|
|
25342
25528
|
if (!b || !this.autoSplit) return;
|
|
25343
25529
|
const left = this.meshesOf(b).filter((mv) => this.visuals.shown(mv));
|
|
25344
25530
|
if (left.length === 0) return;
|
|
25531
|
+
const structural = UNCHECKED_KINDS.has(this.kindOf(b));
|
|
25345
25532
|
const body = b.piece ?? this.pieceSys.get(objectId);
|
|
25346
25533
|
if (body) {
|
|
25347
25534
|
const { half, center } = this.measure(b.root, left);
|
|
25348
25535
|
body.halfExtents = half;
|
|
25349
25536
|
this.pieceSys.setCenter(body, center);
|
|
25350
25537
|
body.mass = Math.max(0.05, this.massOf(objectId));
|
|
25538
|
+
if (!structural) this.setContacts(b, body, left);
|
|
25351
25539
|
body.sleeping = false;
|
|
25352
25540
|
return;
|
|
25353
25541
|
}
|
|
25354
|
-
if (
|
|
25542
|
+
if (structural) return;
|
|
25355
25543
|
const boxOf = (mv) => this.visuals.worldBox(mv, new this.THREE.Box3());
|
|
25356
25544
|
const whole = [...b.parts.values()].flatMap((pv) => pv.meshes).map(boxOf).filter((one) => !one.isEmpty());
|
|
25545
|
+
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;
|
|
25357
25546
|
const floor2 = Math.min(...whole.map((one) => one.min.y));
|
|
25358
25547
|
const feetOf = (boxes) => boxes.filter((one) => one.min.y < floor2 + FEET_BAND).reduce((feet, one) => feet.union(one), new this.THREE.Box3());
|
|
25359
25548
|
const was = feetOf(whole);
|
|
@@ -25369,9 +25558,116 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25369
25558
|
if (!narrow && centreX > now.min.x && centreX < now.max.x && centreZ > now.min.z && centreZ < now.max.z) return;
|
|
25370
25559
|
}
|
|
25371
25560
|
const piece = this.makePiece(objectId);
|
|
25561
|
+
this.setContacts(b, b.piece ?? piece, left);
|
|
25372
25562
|
this.tipIfTopHeavy(b);
|
|
25373
25563
|
this.o.onLoosened?.(objectId, piece, "apart");
|
|
25374
25564
|
}
|
|
25565
|
+
shownBoxes(objectId, partId) {
|
|
25566
|
+
const b = this.bounds.get(objectId);
|
|
25567
|
+
if (!b) return [];
|
|
25568
|
+
const box2 = new this.THREE.Box3();
|
|
25569
|
+
const out = [];
|
|
25570
|
+
const pv = partId === void 0 ? null : b.parts.get(partId);
|
|
25571
|
+
if (partId !== void 0 && (!pv || pv.gone !== "none")) return out;
|
|
25572
|
+
for (const mv of pv ? pv.meshes : this.meshesOf(b)) {
|
|
25573
|
+
if (!this.visuals.shown(mv)) continue;
|
|
25574
|
+
this.visuals.worldBox(mv, box2);
|
|
25575
|
+
if (!box2.isEmpty()) out.push([box2.min.x, box2.min.y, box2.min.z, box2.max.x, box2.max.y, box2.max.z]);
|
|
25576
|
+
}
|
|
25577
|
+
return out;
|
|
25578
|
+
}
|
|
25579
|
+
/**
|
|
25580
|
+
* 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
|
|
25581
|
+
* fixed top nor a piece within `BROKEN_GAP` under it is made a piece on the spot (see `onBrokenChecked`).
|
|
25582
|
+
*/
|
|
25583
|
+
checkBroken() {
|
|
25584
|
+
if (this.broken.size === 0) return;
|
|
25585
|
+
const ids = [...this.broken];
|
|
25586
|
+
this.broken.clear();
|
|
25587
|
+
for (const id of ids) {
|
|
25588
|
+
const released = this.releaseIfFloating(id);
|
|
25589
|
+
this.o.onBrokenChecked?.(id, released);
|
|
25590
|
+
}
|
|
25591
|
+
}
|
|
25592
|
+
releaseIfFloating(objectId) {
|
|
25593
|
+
const b = this.bounds.get(objectId);
|
|
25594
|
+
if (!b || b.piece || this.pieceSys.get(objectId) || !this.autoSplit || UNCHECKED_KINDS.has(this.kindOf(b))) return false;
|
|
25595
|
+
const boxes = this.shownBoxes(objectId);
|
|
25596
|
+
if (boxes.length === 0) return false;
|
|
25597
|
+
const whole = new this.THREE.Box3();
|
|
25598
|
+
const wholeBoxes = [];
|
|
25599
|
+
for (const pv of b.parts.values()) {
|
|
25600
|
+
for (const mv of pv.meshes) {
|
|
25601
|
+
this.visuals.worldBox(mv, whole);
|
|
25602
|
+
if (!whole.isEmpty()) wholeBoxes.push([whole.min.x, whole.min.y, whole.min.z, whole.max.x, whole.max.y, whole.max.z]);
|
|
25603
|
+
}
|
|
25604
|
+
}
|
|
25605
|
+
if (!this.standsOn(objectId, wholeBoxes) || this.standsOn(objectId, boxes)) return false;
|
|
25606
|
+
const piece = this.makePiece(objectId);
|
|
25607
|
+
this.setContacts(b, b.piece ?? piece, this.meshesOf(b).filter((mv) => this.visuals.shown(mv)));
|
|
25608
|
+
this.tipIfTopHeavy(b);
|
|
25609
|
+
this.o.onLoosened?.(objectId, piece, "apart");
|
|
25610
|
+
return true;
|
|
25611
|
+
}
|
|
25612
|
+
/** 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. */
|
|
25613
|
+
standsOn(objectId, boxes) {
|
|
25614
|
+
if (boxes.length === 0) return false;
|
|
25615
|
+
const low = Math.min(...boxes.map((one) => one[1]));
|
|
25616
|
+
for (const [x0, y0, z0, x1, , z1] of boxes) {
|
|
25617
|
+
if (y0 > low + FEET_BAND) continue;
|
|
25618
|
+
for (const [fx, fz] of BROKEN_SAMPLES) {
|
|
25619
|
+
const x = x0 + (x1 - x0) * fx;
|
|
25620
|
+
const z = z0 + (z1 - z0) * fz;
|
|
25621
|
+
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));
|
|
25622
|
+
if (under >= y0 - BROKEN_GAP) return true;
|
|
25623
|
+
}
|
|
25624
|
+
}
|
|
25625
|
+
return false;
|
|
25626
|
+
}
|
|
25627
|
+
/** 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. */
|
|
25628
|
+
kindOf(b) {
|
|
25629
|
+
for (let o = b.root; o; o = o.parent) {
|
|
25630
|
+
const kind = kitOriginOf(o)?.kind;
|
|
25631
|
+
if (kind) return kind;
|
|
25632
|
+
}
|
|
25633
|
+
return "";
|
|
25634
|
+
}
|
|
25635
|
+
/** `staticTopAt` without the boxes of `objectId` itself. */
|
|
25636
|
+
staticTopAtBut(objectId, x, z, y, step) {
|
|
25637
|
+
let top = -Infinity;
|
|
25638
|
+
for (const [id, a] of this.staticNow()) {
|
|
25639
|
+
if (id === objectId || a.length === 0 || x < a[0] || x > a[3] || z < a[2] || z > a[5]) continue;
|
|
25640
|
+
for (let i = 6; i < a.length; i += 6) {
|
|
25641
|
+
if (x < a[i] || x > a[i + 3] || z < a[i + 2] || z > a[i + 5]) continue;
|
|
25642
|
+
const t = a[i + 4];
|
|
25643
|
+
if (t > top && t <= y + step) top = t;
|
|
25644
|
+
}
|
|
25645
|
+
}
|
|
25646
|
+
return top;
|
|
25647
|
+
}
|
|
25648
|
+
/**
|
|
25649
|
+
* 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
|
|
25650
|
+
* what holds it and it goes over until the top meets the floor. Past `CONTACT_MESHES` meshes it keeps the corners of its box.
|
|
25651
|
+
*/
|
|
25652
|
+
setContacts(b, body, meshes) {
|
|
25653
|
+
if (meshes.length > CONTACT_MESHES) {
|
|
25654
|
+
body.contacts = void 0;
|
|
25655
|
+
return;
|
|
25656
|
+
}
|
|
25657
|
+
const inv = specFrameInverse(this.THREE, b.root);
|
|
25658
|
+
const m4 = new this.THREE.Matrix4();
|
|
25659
|
+
const lb = new this.THREE.Box3();
|
|
25660
|
+
const [cx, cy, cz] = body.localCenter;
|
|
25661
|
+
const points = [];
|
|
25662
|
+
for (const mv of meshes) {
|
|
25663
|
+
mv.mesh.updateWorldMatrix(true, false);
|
|
25664
|
+
this.visuals.localBox(mv, lb);
|
|
25665
|
+
if (lb.isEmpty()) continue;
|
|
25666
|
+
lb.applyMatrix4(m4.multiplyMatrices(inv, mv.mesh.matrixWorld));
|
|
25667
|
+
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]);
|
|
25668
|
+
}
|
|
25669
|
+
body.contacts = points.length > 0 ? points : void 0;
|
|
25670
|
+
}
|
|
25375
25671
|
/**
|
|
25376
25672
|
* 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
|
|
25377
25673
|
* high and its feet are thin, so it gets the start of the fall, across the line of its feet.
|
|
@@ -25663,6 +25959,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25663
25959
|
this.active.clear();
|
|
25664
25960
|
for (const key2 of [...this.fires.keys()]) this.stopFire(key2);
|
|
25665
25961
|
this.leaks.length = 0;
|
|
25962
|
+
this.trickles.length = 0;
|
|
25666
25963
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
25667
25964
|
for (const b of [...this.bounds.values()]) {
|
|
25668
25965
|
if (b.isSplit) {
|
|
@@ -25924,6 +26221,21 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25924
26221
|
volume: 0,
|
|
25925
26222
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
25926
26223
|
};
|
|
26224
|
+
var SMOKE_HISS_LOOP = {
|
|
26225
|
+
id: "smoke_grenade_hiss",
|
|
26226
|
+
bus: "world",
|
|
26227
|
+
order: "chain_loop",
|
|
26228
|
+
source: { kind: "noise", loop: true },
|
|
26229
|
+
filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 1400 }, q: 0.7 }, { type: "lowpass", frequency: { set: 7e3 }, q: 0.5 }] },
|
|
26230
|
+
volume: 0,
|
|
26231
|
+
sources: ["sdk: a smoke canister venting (the gas through its holes, steady for its whole burn)"]
|
|
26232
|
+
};
|
|
26233
|
+
var GRENADE_PIN_PULL = blast(
|
|
26234
|
+
"grenade_pin_pull",
|
|
26235
|
+
[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 })],
|
|
26236
|
+
["sdk: the ring pulled from the fuse, then the spoon flying off"],
|
|
26237
|
+
FIRE_FALLOFF
|
|
26238
|
+
);
|
|
25927
26239
|
var FIRE_FUEL_OF = {
|
|
25928
26240
|
wood_soft: "wood",
|
|
25929
26241
|
wood_hard: "wood",
|
|
@@ -26111,7 +26423,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
26111
26423
|
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 })]),
|
|
26112
26424
|
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] }))])
|
|
26113
26425
|
];
|
|
26114
|
-
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] };
|
|
26426
|
+
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] };
|
|
26115
26427
|
var IMPACT_SOUND_KEYS = {
|
|
26116
26428
|
impact_metal_ballistic: { sound: "metal_bullet" },
|
|
26117
26429
|
impact_metal_blunt: { sound: "metal_blunt" },
|
|
@@ -48938,6 +49250,8 @@ void main(){
|
|
|
48938
49250
|
var SMOKE_PUFF_SPEED = 1.7;
|
|
48939
49251
|
var SMOKE_PUFF_SIZE = 5;
|
|
48940
49252
|
var SMOKE_PUFF_ALPHA = 1.5;
|
|
49253
|
+
var SMOKE_HISS = { loop: "smoke_grenade_hiss", volume: 0.7, halfAt: 6, max: 1 };
|
|
49254
|
+
var GRENADE_PIN_SOUND = "grenade_pin_pull";
|
|
48941
49255
|
var CONE_BLAST_RADIUS = 0.5;
|
|
48942
49256
|
var summarizeBlast = (results) => {
|
|
48943
49257
|
const reports = results.flatMap((r) => r.blast ? [r.blast] : []);
|
|
@@ -48957,12 +49271,31 @@ void main(){
|
|
|
48957
49271
|
const random = context.random ?? Math.random;
|
|
48958
49272
|
const meshes = [];
|
|
48959
49273
|
const up = new THREE.Vector3(0, 1, 0);
|
|
49274
|
+
let smokeHiss = null;
|
|
49275
|
+
const hissSmoke = () => {
|
|
49276
|
+
if (!context.audio) return;
|
|
49277
|
+
if (smokeSources.length === 0) {
|
|
49278
|
+
smokeHiss?.stop();
|
|
49279
|
+
smokeHiss = null;
|
|
49280
|
+
return;
|
|
49281
|
+
}
|
|
49282
|
+
const at3 = camera2.position;
|
|
49283
|
+
let level = 0;
|
|
49284
|
+
for (const source of smokeSources) {
|
|
49285
|
+
const distance4 = Math.hypot(source.point[0] - at3.x, source.point[1] - at3.y, source.point[2] - at3.z);
|
|
49286
|
+
level += SMOKE_HISS.volume / (1 + (distance4 / SMOKE_HISS.halfAt) ** 2);
|
|
49287
|
+
}
|
|
49288
|
+
smokeHiss ?? (smokeHiss = context.audio.loop(SMOKE_HISS.loop));
|
|
49289
|
+
smokeHiss.set({ volume: Math.min(SMOKE_HISS.max, level) });
|
|
49290
|
+
};
|
|
48960
49291
|
const drop = (objects) => {
|
|
48961
49292
|
for (const object of objects) object.removeFromParent();
|
|
48962
49293
|
releaseObject3D(objects, { keep: scene2 });
|
|
48963
49294
|
};
|
|
48964
49295
|
const clear = () => {
|
|
48965
49296
|
smokeSources.length = 0;
|
|
49297
|
+
smokeHiss?.stop();
|
|
49298
|
+
smokeHiss = null;
|
|
48966
49299
|
projectiles.length = 0;
|
|
48967
49300
|
drop(meshes);
|
|
48968
49301
|
meshes.length = 0;
|
|
@@ -49016,6 +49349,7 @@ void main(){
|
|
|
49016
49349
|
age: 0,
|
|
49017
49350
|
...use.explosive !== void 0 ? { body: createExplosiveBody(use, arsenal.explosives, arsenal.rules.explosives), aligned: isRocket } : {}
|
|
49018
49351
|
});
|
|
49352
|
+
if (!isRocket && !isMolotov && use.explosive !== void 0) context.audio?.play(GRENADE_PIN_SOUND, { position: use.origin });
|
|
49019
49353
|
const key2 = isRocket ? "act.launch" : isMolotov ? "act.throw.molotov" : "act.throw.grenade";
|
|
49020
49354
|
action(isRocket ? "launch_projectile" : "throw_projectile", translate(key2, { weapon: context.toolName(), fuse: format(use.fuse, 1), arm: format(use.armDistance ?? 0, 0) }), {
|
|
49021
49355
|
data: { weapon: context.toolId(), fuse: use.fuse, explosive: use.explosive }
|
|
@@ -49269,6 +49603,7 @@ void main(){
|
|
|
49269
49603
|
}
|
|
49270
49604
|
if (source.left <= 0) smokeSources.splice(index, 1);
|
|
49271
49605
|
}
|
|
49606
|
+
hissSmoke();
|
|
49272
49607
|
};
|
|
49273
49608
|
const update = (dt) => {
|
|
49274
49609
|
burnSmoke(dt);
|
|
@@ -55687,6 +56022,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
55687
56022
|
"act.player_thrown_wounded": "The blast threw the player at {speed} m/s, {distance} m from it: badly wounded, stunned for {stun} s ({reach}% got through the cover)",
|
|
55688
56023
|
"act.player_stunned": "The blast stunned the player for {stun} s, {distance} m from it: dizzy, ears ringing",
|
|
55689
56024
|
"act.came_apart": "{name} came apart: what stayed is loose and falls",
|
|
56025
|
+
"check.broken_floating": "ERROR: what was left of {name} stood on nothing after the break; it was let go to fall",
|
|
55690
56026
|
"act.shoved": "{name} stands free: the shots moved it",
|
|
55691
56027
|
"act.impact": "{name} hit {where} at {speed} m/s: {energy} J of blunt damage",
|
|
55692
56028
|
"act.use_item": "{name}: {use}",
|
|
@@ -56065,6 +56401,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
56065
56401
|
"act.player_thrown_wounded": "A explos\xE3o arremessou o jogador a {speed} m/s, a {distance} m dela: ferido gravemente, atordoado por {stun} s ({reach}% passou pela cobertura)",
|
|
56066
56402
|
"act.player_stunned": "A explos\xE3o atordoou o jogador por {stun} s, a {distance} m dela: tonto, com os ouvidos zumbindo",
|
|
56067
56403
|
"act.came_apart": "{name} se desmontou: o que ficou est\xE1 solto e cai",
|
|
56404
|
+
"check.broken_floating": "ERRO: o que sobrou de {name} ficou sem apoio depois de quebrar; foi solto para cair",
|
|
56068
56405
|
"act.shoved": "{name} estava solto no ch\xE3o: os tiros o moveram",
|
|
56069
56406
|
"act.impact": "{name} bateu em {where} a {speed} m/s: {energy} J de dano de impacto",
|
|
56070
56407
|
"act.use_item": "{name}: {use}",
|
|
@@ -57795,6 +58132,14 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57795
58132
|
rendererPieces.set(objectId, piece);
|
|
57796
58133
|
action("loosened", t(cause === "blast" ? "act.loosened" : "act.came_apart", { name: e.name }), { objectId, itemId: e.so.itemId });
|
|
57797
58134
|
},
|
|
58135
|
+
// after a break the collision of the object is measured again from what is still drawn; what stood on nothing was let go, which is a bug the room says aloud
|
|
58136
|
+
onBrokenChecked: (objectId, released) => {
|
|
58137
|
+
bodyBoxes.delete(objectId);
|
|
58138
|
+
if (standingIds.includes(objectId)) leaveFootprint(objectId);
|
|
58139
|
+
if (!released) return;
|
|
58140
|
+
const e = entries2.get(objectId);
|
|
58141
|
+
log.add({ channel: "check", type: "fail", text: t("check.broken_floating", { name: e?.name ?? objectId }), objectId, ...e ? { itemId: e.so.itemId } : {}, data: { invariant: "broken_floating" } });
|
|
58142
|
+
},
|
|
57798
58143
|
onPiece: (piece) => {
|
|
57799
58144
|
rendererPieces.set(piece.objectId, piece);
|
|
57800
58145
|
loose.push(piece);
|
|
@@ -58458,9 +58803,14 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58458
58803
|
bodyBoxes.delete(id);
|
|
58459
58804
|
return null;
|
|
58460
58805
|
}
|
|
58461
|
-
|
|
58462
|
-
if (
|
|
58463
|
-
|
|
58806
|
+
const parts = renderer.shownBoxes(id);
|
|
58807
|
+
if (parts.length === 0) {
|
|
58808
|
+
bodyBoxes.delete(id);
|
|
58809
|
+
return null;
|
|
58810
|
+
}
|
|
58811
|
+
bodyBounds.makeEmpty();
|
|
58812
|
+
for (const p of parts) bodyBounds.expandByPoint(tmpV2.set(p[0], p[1], p[2])).expandByPoint(tmpV2.set(p[3], p[4], p[5]));
|
|
58813
|
+
const box2 = { minX: bodyBounds.min.x, maxX: bodyBounds.max.x, minZ: bodyBounds.min.z, maxZ: bodyBounds.max.z, base: bodyBounds.min.y, top: bodyBounds.max.y, stamp: frameStamp, parts };
|
|
58464
58814
|
bodyBoxes.set(id, box2);
|
|
58465
58815
|
return box2;
|
|
58466
58816
|
};
|
|
@@ -58477,7 +58827,15 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58477
58827
|
const reach = c.radius + r;
|
|
58478
58828
|
return (x - footPos.x) ** 2 + (z - footPos.z) ** 2 < reach * reach ? { ...b, top: Math.min(b.top, footPos.y + (c.height ?? b.top - footPos.y)) } : null;
|
|
58479
58829
|
}
|
|
58480
|
-
|
|
58830
|
+
if (!(x > b.minX - r && x < b.maxX + r && z > b.minZ - r && z < b.maxZ + r)) return null;
|
|
58831
|
+
let base = Infinity;
|
|
58832
|
+
let top = -Infinity;
|
|
58833
|
+
for (const p of b.parts) {
|
|
58834
|
+
if (!(x > p[0] - r && x < p[3] + r && z > p[2] - r && z < p[5] + r)) continue;
|
|
58835
|
+
base = Math.min(base, p[1]);
|
|
58836
|
+
top = Math.max(top, p[4]);
|
|
58837
|
+
}
|
|
58838
|
+
return top > base ? { ...b, base, top } : null;
|
|
58481
58839
|
};
|
|
58482
58840
|
blockers.add((x, z, r) => {
|
|
58483
58841
|
for (const e of entries2.values()) {
|
|
@@ -59535,7 +59893,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
59535
59893
|
emitHost,
|
|
59536
59894
|
startFire,
|
|
59537
59895
|
say,
|
|
59538
|
-
projectileModel: options.projectileModel
|
|
59896
|
+
projectileModel: options.projectileModel,
|
|
59897
|
+
audio
|
|
59539
59898
|
});
|
|
59540
59899
|
const toolOf = (hand) => hand === "L" ? offTool && offToolId ? { id: offToolId, state: offTool } : null : { id: toolId, state: tool };
|
|
59541
59900
|
const dualReload = createDualReload(
|