@adatechnology/game-test-room 0.28.2 → 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/README.md +2 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.global.js +2692 -183
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +449 -88
- 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"],
|
|
@@ -11226,7 +11290,8 @@ var GameTestRoom = (() => {
|
|
|
11226
11290
|
var EVENT_BUDGET = { particles: 400, debris: 40, blastLights: 4, fireLights: 2 };
|
|
11227
11291
|
var explosion_aliases_default = {
|
|
11228
11292
|
carl_gustaf_84mm: "at4_84mm",
|
|
11229
|
-
vog_25: "m79_40mm"
|
|
11293
|
+
vog_25: "m79_40mm",
|
|
11294
|
+
semtex: "c4"
|
|
11230
11295
|
};
|
|
11231
11296
|
var explosions_default = {
|
|
11232
11297
|
m67: {
|
|
@@ -17203,8 +17268,8 @@ var GameTestRoom = (() => {
|
|
|
17203
17268
|
id: "dust",
|
|
17204
17269
|
additive: false,
|
|
17205
17270
|
life: [
|
|
17206
|
-
|
|
17207
|
-
|
|
17271
|
+
2,
|
|
17272
|
+
5
|
|
17208
17273
|
],
|
|
17209
17274
|
size: [
|
|
17210
17275
|
0.08,
|
|
@@ -17271,8 +17336,8 @@ var GameTestRoom = (() => {
|
|
|
17271
17336
|
id: "chips",
|
|
17272
17337
|
additive: false,
|
|
17273
17338
|
life: [
|
|
17274
|
-
|
|
17275
|
-
|
|
17339
|
+
2,
|
|
17340
|
+
4
|
|
17276
17341
|
],
|
|
17277
17342
|
size: [
|
|
17278
17343
|
0.025,
|
|
@@ -19914,7 +19979,7 @@ var GameTestRoom = (() => {
|
|
|
19914
19979
|
return sources;
|
|
19915
19980
|
}
|
|
19916
19981
|
|
|
19917
|
-
// ../game-vfx-three/dist/chunk-
|
|
19982
|
+
// ../game-vfx-three/dist/chunk-IOGYK5RO.js
|
|
19918
19983
|
var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
|
|
19919
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]);
|
|
19920
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.);}`;
|
|
@@ -19961,10 +20026,15 @@ void main(){
|
|
|
19961
20026
|
if(n<diss*1.08-.04)discard;
|
|
19962
20027
|
float lam=max(dot(N,uLight),0.);
|
|
19963
20028
|
float band=lam>.55?1.:lam>.15?.68:.42;
|
|
19964
|
-
|
|
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);
|
|
19965
20035
|
vec3 tg=uGlowPos-vWP;float dg=length(tg)+.001;
|
|
19966
20036
|
float gi=uGlowI/(1.+dg*dg*.05)*(.3+.7*max(dot(N,tg/dg),0.));
|
|
19967
|
-
col+=uGlowCol*gi*
|
|
20037
|
+
col+=uGlowCol*gi*alb*1.6;
|
|
19968
20038
|
float h=heat*(.72+.56*n)+heat*(-N.y)*.18;
|
|
19969
20039
|
if(h>.05){
|
|
19970
20040
|
float hq=floor(h*5.+.5)/5.;
|
|
@@ -22142,7 +22212,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22142
22212
|
var SLAM_BOUNCE = 0.2;
|
|
22143
22213
|
var SLAM_FLOOR_NORMAL = 0.7;
|
|
22144
22214
|
var SUB_DT = 1 / 120;
|
|
22145
|
-
var
|
|
22215
|
+
var MU_STATIC = 0.7;
|
|
22216
|
+
var MU_DYNAMIC = 0.6;
|
|
22217
|
+
var SLIP_SPEED = 0.02;
|
|
22146
22218
|
var SLEEP_AFTER = 0.5;
|
|
22147
22219
|
var CALM_DRIFT = 0.01;
|
|
22148
22220
|
var CALM_TURN = 0.05;
|
|
@@ -22161,6 +22233,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22161
22233
|
var EDGE_POINTS = [-0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75];
|
|
22162
22234
|
var SUPPORT_CHECKS2 = 16;
|
|
22163
22235
|
var SUPPORT_GAP2 = 0.02;
|
|
22236
|
+
var WAKE_ABOVE_SPEED = 0.05;
|
|
22237
|
+
var WAKE_ABOVE_SPIN = 0.1;
|
|
22238
|
+
var topFace = 1;
|
|
22164
22239
|
var boxTopAt = (s, x, z) => {
|
|
22165
22240
|
const e = s.e;
|
|
22166
22241
|
const dx = x - s.c[0], dz = z - s.c[2];
|
|
@@ -22176,11 +22251,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22176
22251
|
let a = (-h - p) / d, b = (h - p) / d;
|
|
22177
22252
|
if (a > b) [a, b] = [b, a];
|
|
22178
22253
|
t0 = Math.max(t0, a);
|
|
22179
|
-
|
|
22254
|
+
if (b < t1) {
|
|
22255
|
+
t1 = b;
|
|
22256
|
+
topFace = k;
|
|
22257
|
+
}
|
|
22180
22258
|
if (t0 > t1) return -Infinity;
|
|
22181
22259
|
}
|
|
22182
22260
|
return s.c[1] + t1;
|
|
22183
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
|
+
});
|
|
22184
22272
|
var PieceSystem = class {
|
|
22185
22273
|
constructor(THREE, o) {
|
|
22186
22274
|
__publicField3(this, "THREE", THREE);
|
|
@@ -22194,6 +22282,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22194
22282
|
__publicField3(this, "ax");
|
|
22195
22283
|
__publicField3(this, "solids", []);
|
|
22196
22284
|
__publicField3(this, "supportTurn", 0);
|
|
22285
|
+
__publicField3(this, "supportNormal", UP16);
|
|
22286
|
+
__publicField3(this, "supportFriction", null);
|
|
22197
22287
|
this.p = new THREE.Vector3();
|
|
22198
22288
|
this.q = new THREE.Quaternion();
|
|
22199
22289
|
this.dq = new THREE.Quaternion();
|
|
@@ -22299,8 +22389,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22299
22389
|
const lz = (e[8] * x + e[9] * y + e[10] * z) * iz;
|
|
22300
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];
|
|
22301
22391
|
}
|
|
22302
|
-
/** 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`. */
|
|
22303
22393
|
samples(b, edges) {
|
|
22394
|
+
if (b.contacts) return b.contacts;
|
|
22304
22395
|
const [hx, hy, hz2] = b.halfExtents;
|
|
22305
22396
|
const out = [];
|
|
22306
22397
|
for (let i = 0; i < 8; i++) out.push([i & 1 ? hx : -hx, i & 2 ? hy : -hy, i & 4 ? hz2 : -hz2]);
|
|
@@ -22365,6 +22456,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22365
22456
|
}
|
|
22366
22457
|
if (b.sleeping) continue;
|
|
22367
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);
|
|
22368
22463
|
}
|
|
22369
22464
|
this.separate(dt);
|
|
22370
22465
|
}
|
|
@@ -22395,15 +22490,48 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22395
22490
|
}
|
|
22396
22491
|
return false;
|
|
22397
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
|
+
}
|
|
22398
22517
|
/** Where a corner of `self` at `(x, y, z)` lands: the ground, the top of another piece or of something fixed under it. */
|
|
22399
22518
|
supportAt(self, x, y, z, step) {
|
|
22400
22519
|
let top = this.o.groundAt(x, z);
|
|
22520
|
+
let on = null;
|
|
22521
|
+
let face = 1;
|
|
22401
22522
|
for (const s of this.solids) {
|
|
22402
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;
|
|
22403
22524
|
const t = boxTopAt(s, x, z);
|
|
22404
|
-
if (t > top && t <= y + step)
|
|
22525
|
+
if (t > top && t <= y + step) {
|
|
22526
|
+
top = t;
|
|
22527
|
+
on = s;
|
|
22528
|
+
face = topFace;
|
|
22529
|
+
}
|
|
22405
22530
|
}
|
|
22406
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;
|
|
22407
22535
|
return Math.max(top, fixed2);
|
|
22408
22536
|
}
|
|
22409
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. */
|
|
@@ -22486,7 +22614,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22486
22614
|
const e = this.m.elements;
|
|
22487
22615
|
const rs = [];
|
|
22488
22616
|
const ids = [];
|
|
22617
|
+
const ns = [];
|
|
22618
|
+
const mus = [];
|
|
22489
22619
|
let maxPen = 0;
|
|
22620
|
+
let penNormal = UP16;
|
|
22490
22621
|
const rise = STEP + Math.max(0, -v[1]) * h * 2;
|
|
22491
22622
|
let side = null;
|
|
22492
22623
|
let lowest = Infinity;
|
|
@@ -22504,6 +22635,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22504
22635
|
if (floor2 !== void 0 && floor2 - cy > -SLOP && floor2 < raw - STEP) {
|
|
22505
22636
|
rs.push(r);
|
|
22506
22637
|
ids.push(i);
|
|
22638
|
+
ns.push(UP16);
|
|
22639
|
+
mus.push(pairFriction(b.friction, null));
|
|
22507
22640
|
maxPen = Math.max(maxPen, floor2 - cy);
|
|
22508
22641
|
}
|
|
22509
22642
|
continue;
|
|
@@ -22524,12 +22657,19 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22524
22657
|
}
|
|
22525
22658
|
continue;
|
|
22526
22659
|
}
|
|
22527
|
-
|
|
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);
|
|
22528
22663
|
const pen = gy - cy;
|
|
22529
22664
|
if (pen > -SLOP) {
|
|
22530
22665
|
rs.push(r);
|
|
22531
22666
|
ids.push(i);
|
|
22532
|
-
|
|
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
|
+
}
|
|
22533
22673
|
lowest = Math.min(lowest, gy);
|
|
22534
22674
|
}
|
|
22535
22675
|
}
|
|
@@ -22542,7 +22682,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22542
22682
|
contactedLast = rs.length > 0;
|
|
22543
22683
|
if (contactedLast) touched = true;
|
|
22544
22684
|
if (!contactedLast) continue;
|
|
22545
|
-
|
|
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;
|
|
22546
22689
|
let impact22 = 0;
|
|
22547
22690
|
for (const r of rs) impact22 = Math.max(impact22, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
22548
22691
|
const restitution = impact22 > 2 ? 0.25 : 0;
|
|
@@ -22558,76 +22701,72 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22558
22701
|
const lz = pos[2] + sz / rs.length;
|
|
22559
22702
|
landPoint = [lx, pos[1] - Math.max(...rs.map((r) => -r[1])), lz];
|
|
22560
22703
|
}
|
|
22561
|
-
const
|
|
22562
|
-
|
|
22563
|
-
|
|
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;
|
|
22564
22714
|
});
|
|
22565
22715
|
const vx0 = v[0];
|
|
22566
22716
|
const vz0 = v[2];
|
|
22567
|
-
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);
|
|
22568
22718
|
const warm = b.warm;
|
|
22569
|
-
const accN = ids.map((id) => id < WARM_SLOTS ? warm[id *
|
|
22570
|
-
const accT = ids.map((id) => id < WARM_SLOTS ? [warm[id *
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
|
|
22575
|
-
|
|
22576
|
-
v[0] += fx * invM;
|
|
22577
|
-
v[1] += jn * invM;
|
|
22578
|
-
v[2] += fz * invM;
|
|
22579
|
-
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]);
|
|
22580
22726
|
w[0] += dw[0];
|
|
22581
22727
|
w[1] += dw[1];
|
|
22582
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]]);
|
|
22583
22738
|
}
|
|
22584
22739
|
for (let it = 0; it < SOLVER_PASSES; it++) {
|
|
22585
22740
|
for (let k = 0; k < rs.length; k++) {
|
|
22586
22741
|
const r = rs[k];
|
|
22587
|
-
const
|
|
22588
|
-
const
|
|
22589
|
-
const
|
|
22590
|
-
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));
|
|
22591
22745
|
const j = total - accN[k];
|
|
22592
22746
|
accN[k] = total;
|
|
22593
|
-
if (j !== 0)
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
if (
|
|
22606
|
-
|
|
22607
|
-
|
|
22608
|
-
|
|
22609
|
-
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
fx -= tx * jt;
|
|
22613
|
-
fz -= tz * jt;
|
|
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;
|
|
22614
22766
|
}
|
|
22615
|
-
const
|
|
22616
|
-
|
|
22617
|
-
if (
|
|
22618
|
-
fx *= most / f;
|
|
22619
|
-
fz *= most / f;
|
|
22620
|
-
}
|
|
22621
|
-
const dx = fx - accT[k][0];
|
|
22622
|
-
const dz = fz - accT[k][1];
|
|
22623
|
-
accT[k] = [fx, fz];
|
|
22624
|
-
if (dx === 0 && dz === 0) continue;
|
|
22625
|
-
v[0] += dx * invM;
|
|
22626
|
-
v[2] += dz * invM;
|
|
22627
|
-
const dw = this.mulInvInertiaWith(b, e, r[1] * dz, r[2] * dx - r[0] * dz, -r[1] * dx);
|
|
22628
|
-
w[0] += dw[0];
|
|
22629
|
-
w[1] += dw[1];
|
|
22630
|
-
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);
|
|
22631
22770
|
}
|
|
22632
22771
|
}
|
|
22633
22772
|
pos[0] += (v[0] - vx0) * h;
|
|
@@ -22636,9 +22775,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22636
22775
|
for (let k = 0; k < rs.length; k++) {
|
|
22637
22776
|
const id = ids[k];
|
|
22638
22777
|
if (id >= WARM_SLOTS) continue;
|
|
22639
|
-
warm[id *
|
|
22640
|
-
warm[id *
|
|
22641
|
-
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];
|
|
22642
22782
|
}
|
|
22643
22783
|
let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0;
|
|
22644
22784
|
for (const r of rs) {
|
|
@@ -22710,6 +22850,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22710
22850
|
b.calm += dt;
|
|
22711
22851
|
return b.calm >= CALM_SLEEP_AFTER;
|
|
22712
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
|
+
}
|
|
22713
22860
|
/** Half the height of the box of a piece as it is turned now. */
|
|
22714
22861
|
halfHeight(b) {
|
|
22715
22862
|
const e = this.m.makeRotationFromQuaternion(b.root.quaternion).elements;
|
|
@@ -22735,6 +22882,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
22735
22882
|
this.centerOf(b, cb);
|
|
22736
22883
|
if (Math.abs(ca.y - cb.y) > ra + rb) continue;
|
|
22737
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;
|
|
22738
22886
|
const rh = (Math.max(a.halfExtents[0], a.halfExtents[2]) + Math.max(b.halfExtents[0], b.halfExtents[2])) * 0.8;
|
|
22739
22887
|
let dx = cb.x - ca.x;
|
|
22740
22888
|
let dz = cb.z - ca.z;
|
|
@@ -24125,6 +24273,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24125
24273
|
var IMPACT_TAGS = ["metal", "glass", "wood"];
|
|
24126
24274
|
var GLOW_FROM_C = 430;
|
|
24127
24275
|
var QUEUE_CAP = 4e3;
|
|
24276
|
+
var TRICKLE_SECONDS = [2, 5];
|
|
24277
|
+
var TRICKLE_EVERY = 0.25;
|
|
24278
|
+
var TRICKLE_MAX = 24;
|
|
24128
24279
|
var STRUCTURAL_EVENTS = /* @__PURE__ */ new Set([
|
|
24129
24280
|
"fracture",
|
|
24130
24281
|
"detach",
|
|
@@ -24148,6 +24299,10 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24148
24299
|
var FEET_BAND = 0.03;
|
|
24149
24300
|
var TIP_MIN_HEIGHT = 0.3;
|
|
24150
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"]);
|
|
24151
24306
|
var FIRE_CEILING_SECONDS = 3600;
|
|
24152
24307
|
var GENERIC_REFERENCE_RADIUS = 15;
|
|
24153
24308
|
var SWEEP_CORE = 0.15;
|
|
@@ -24243,9 +24398,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24243
24398
|
__publicField3(this, "staticAll", true);
|
|
24244
24399
|
__publicField3(this, "staticRound", []);
|
|
24245
24400
|
__publicField3(this, "pieceBoxes", /* @__PURE__ */ new WeakMap());
|
|
24401
|
+
__publicField3(this, "broken", /* @__PURE__ */ new Set());
|
|
24246
24402
|
__publicField3(this, "staticAt", 0);
|
|
24247
24403
|
__publicField3(this, "queue", []);
|
|
24248
24404
|
__publicField3(this, "coverWhenWentOff", /* @__PURE__ */ new WeakMap());
|
|
24405
|
+
__publicField3(this, "trickles", []);
|
|
24249
24406
|
__publicField3(this, "slices", []);
|
|
24250
24407
|
__publicField3(this, "sliceDeadline", Infinity);
|
|
24251
24408
|
__publicField3(this, "drainClock", () => performance.now());
|
|
@@ -24580,10 +24737,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24580
24737
|
dt = clamp6(dt, 0, 0.1);
|
|
24581
24738
|
this.time += dt;
|
|
24582
24739
|
if (!this.attached) this.drainQueue();
|
|
24740
|
+
this.checkBroken();
|
|
24583
24741
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
24584
24742
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
24585
24743
|
this.updateActive(dt);
|
|
24586
24744
|
this.updateLeaks(dt);
|
|
24745
|
+
this.updateTrickles(dt);
|
|
24587
24746
|
this.pieceSys.update(dt);
|
|
24588
24747
|
this.fragments.update(dt);
|
|
24589
24748
|
this.ground.update(dt);
|
|
@@ -24782,6 +24941,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
24782
24941
|
}
|
|
24783
24942
|
apply(ev) {
|
|
24784
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);
|
|
24785
24945
|
switch (ev.type) {
|
|
24786
24946
|
case "damage":
|
|
24787
24947
|
if (ev.damageType === "heat" || ev.damageType === "fire" || ev.damageType === "explosive") this.activate(ev.object, ev.part);
|
|
@@ -25106,9 +25266,34 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25106
25266
|
const core = def?.visual?.innerColor;
|
|
25107
25267
|
if (core !== void 0) this.vfx.burst("chips", ev.point, [normal.x, normal.y, normal.z], 5, 2.5, { color: core });
|
|
25108
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);
|
|
25109
25270
|
if (mv.mesh.userData.drawnSpalls?.includes(ev.part)) return;
|
|
25110
25271
|
this.visuals.addSpall(mv, wp, normal, Math.sqrt(ev.area / Math.PI), core ?? fallbackColor(pv.material, def));
|
|
25111
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
|
+
}
|
|
25112
25297
|
/**
|
|
25113
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
|
|
25114
25299
|
* (cutting the wall's faces) is not drawn yet: it needs the per-vertex binding of the house renderer.
|
|
@@ -25144,6 +25329,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25144
25329
|
const tone3 = fr.inner && fdef?.visual?.innerColor !== void 0 ? fdef.visual.innerColor : fdef === def ? color : fallbackColor(fr.material, fdef);
|
|
25145
25330
|
this.fragments.spawn(fr, this.fragments.materialFor(fr.material, tone3, false), ev.object, maxAlive);
|
|
25146
25331
|
}
|
|
25332
|
+
if (def?.visual?.innerColor !== void 0 || pv?.material === "concrete" || pv?.material === "brick") this.trickleFrom(ev.center, [0, 0, 0], color);
|
|
25147
25333
|
if (ev.torn) {
|
|
25148
25334
|
this.impactSound(pv?.material ?? "", ev.center, 0.5, ev.blow);
|
|
25149
25335
|
return;
|
|
@@ -25333,26 +25519,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25333
25519
|
this.o.onPiece?.(piece);
|
|
25334
25520
|
}
|
|
25335
25521
|
/**
|
|
25336
|
-
*
|
|
25337
|
-
* 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`).
|
|
25338
25525
|
*/
|
|
25339
25526
|
loosenIfFootless(objectId) {
|
|
25340
25527
|
const b = this.bounds.get(objectId);
|
|
25341
25528
|
if (!b || !this.autoSplit) return;
|
|
25342
25529
|
const left = this.meshesOf(b).filter((mv) => this.visuals.shown(mv));
|
|
25343
25530
|
if (left.length === 0) return;
|
|
25531
|
+
const structural = UNCHECKED_KINDS.has(this.kindOf(b));
|
|
25344
25532
|
const body = b.piece ?? this.pieceSys.get(objectId);
|
|
25345
25533
|
if (body) {
|
|
25346
25534
|
const { half, center } = this.measure(b.root, left);
|
|
25347
25535
|
body.halfExtents = half;
|
|
25348
25536
|
this.pieceSys.setCenter(body, center);
|
|
25349
25537
|
body.mass = Math.max(0.05, this.massOf(objectId));
|
|
25538
|
+
if (!structural) this.setContacts(b, body, left);
|
|
25350
25539
|
body.sleeping = false;
|
|
25351
25540
|
return;
|
|
25352
25541
|
}
|
|
25353
|
-
if (
|
|
25542
|
+
if (structural) return;
|
|
25354
25543
|
const boxOf = (mv) => this.visuals.worldBox(mv, new this.THREE.Box3());
|
|
25355
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;
|
|
25356
25546
|
const floor2 = Math.min(...whole.map((one) => one.min.y));
|
|
25357
25547
|
const feetOf = (boxes) => boxes.filter((one) => one.min.y < floor2 + FEET_BAND).reduce((feet, one) => feet.union(one), new this.THREE.Box3());
|
|
25358
25548
|
const was = feetOf(whole);
|
|
@@ -25368,8 +25558,115 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25368
25558
|
if (!narrow && centreX > now.min.x && centreX < now.max.x && centreZ > now.min.z && centreZ < now.max.z) return;
|
|
25369
25559
|
}
|
|
25370
25560
|
const piece = this.makePiece(objectId);
|
|
25561
|
+
this.setContacts(b, b.piece ?? piece, left);
|
|
25562
|
+
this.tipIfTopHeavy(b);
|
|
25563
|
+
this.o.onLoosened?.(objectId, piece, "apart");
|
|
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)));
|
|
25371
25608
|
this.tipIfTopHeavy(b);
|
|
25372
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;
|
|
25373
25670
|
}
|
|
25374
25671
|
/**
|
|
25375
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
|
|
@@ -25662,6 +25959,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25662
25959
|
this.active.clear();
|
|
25663
25960
|
for (const key2 of [...this.fires.keys()]) this.stopFire(key2);
|
|
25664
25961
|
this.leaks.length = 0;
|
|
25962
|
+
this.trickles.length = 0;
|
|
25665
25963
|
for (const id of [...this.bounds.keys()]) if (this.bounds.has(id)) this.restore(id);
|
|
25666
25964
|
for (const b of [...this.bounds.values()]) {
|
|
25667
25965
|
if (b.isSplit) {
|
|
@@ -25923,6 +26221,21 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
25923
26221
|
volume: 0,
|
|
25924
26222
|
sources: ["sdk: the roar under the crackles of a fire"]
|
|
25925
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
|
+
);
|
|
25926
26239
|
var FIRE_FUEL_OF = {
|
|
25927
26240
|
wood_soft: "wood",
|
|
25928
26241
|
wood_hard: "wood",
|
|
@@ -26110,7 +26423,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
26110
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 })]),
|
|
26111
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] }))])
|
|
26112
26425
|
];
|
|
26113
|
-
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] };
|
|
26114
26427
|
var IMPACT_SOUND_KEYS = {
|
|
26115
26428
|
impact_metal_ballistic: { sound: "metal_bullet" },
|
|
26116
26429
|
impact_metal_blunt: { sound: "metal_blunt" },
|
|
@@ -26417,6 +26730,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
26417
26730
|
kick: "kick",
|
|
26418
26731
|
shove: "shove",
|
|
26419
26732
|
knife: "blade",
|
|
26733
|
+
kabar: "blade",
|
|
26734
|
+
katana: "blade",
|
|
26735
|
+
brass_knuckles: "punch",
|
|
26420
26736
|
machete: "blade",
|
|
26421
26737
|
utility_knife: "blade",
|
|
26422
26738
|
axe: "chop",
|
|
@@ -27468,6 +27784,60 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
27468
27784
|
sources: [
|
|
27469
27785
|
"Estimated from muzzle energy, bullet construction and the published anchors of this file (see _notes); refine with a datasheet."
|
|
27470
27786
|
]
|
|
27787
|
+
},
|
|
27788
|
+
{
|
|
27789
|
+
id: "7_62x25:fmj",
|
|
27790
|
+
caliber: "7_62x25",
|
|
27791
|
+
kind: "fmj",
|
|
27792
|
+
label: "7.62x25 Tokarev FMJ",
|
|
27793
|
+
core: "lead",
|
|
27794
|
+
jacket: "steel",
|
|
27795
|
+
penetration: {
|
|
27796
|
+
steel: 3.5,
|
|
27797
|
+
concrete: 30,
|
|
27798
|
+
brick: 60,
|
|
27799
|
+
wood: 100
|
|
27800
|
+
},
|
|
27801
|
+
basis: "estimated",
|
|
27802
|
+
sources: [
|
|
27803
|
+
"Estimated from muzzle energy, bullet construction and the published anchors of this file (see _notes); refine with a datasheet."
|
|
27804
|
+
]
|
|
27805
|
+
},
|
|
27806
|
+
{
|
|
27807
|
+
id: "7_92x57:fmj",
|
|
27808
|
+
caliber: "7_92x57",
|
|
27809
|
+
kind: "fmj",
|
|
27810
|
+
label: "7.92x57 s.S. ball",
|
|
27811
|
+
core: "lead",
|
|
27812
|
+
jacket: "gilding_metal",
|
|
27813
|
+
penetration: {
|
|
27814
|
+
steel: 5.5,
|
|
27815
|
+
concrete: 125,
|
|
27816
|
+
brick: 250,
|
|
27817
|
+
wood: 330
|
|
27818
|
+
},
|
|
27819
|
+
basis: "estimated",
|
|
27820
|
+
sources: [
|
|
27821
|
+
"Estimated from muzzle energy, bullet construction and the published anchors of this file (see _notes); refine with a datasheet."
|
|
27822
|
+
]
|
|
27823
|
+
},
|
|
27824
|
+
{
|
|
27825
|
+
id: "9x39:fmj",
|
|
27826
|
+
caliber: "9x39",
|
|
27827
|
+
kind: "fmj",
|
|
27828
|
+
label: "9x39 SP-5",
|
|
27829
|
+
core: "lead",
|
|
27830
|
+
jacket: "gilding_metal",
|
|
27831
|
+
penetration: {
|
|
27832
|
+
steel: 3,
|
|
27833
|
+
concrete: 30,
|
|
27834
|
+
brick: 70,
|
|
27835
|
+
wood: 130
|
|
27836
|
+
},
|
|
27837
|
+
basis: "estimated",
|
|
27838
|
+
sources: [
|
|
27839
|
+
"Estimated from muzzle energy, bullet construction and the published anchors of this file (see _notes); refine with a datasheet."
|
|
27840
|
+
]
|
|
27471
27841
|
}
|
|
27472
27842
|
]
|
|
27473
27843
|
};
|
|
@@ -30140,6 +30510,72 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
30140
30510
|
sources: [
|
|
30141
30511
|
"sdk"
|
|
30142
30512
|
]
|
|
30513
|
+
},
|
|
30514
|
+
{
|
|
30515
|
+
id: "7_62x25",
|
|
30516
|
+
label: "7.62\xD725 mm Tokarev",
|
|
30517
|
+
diameter: 7.85,
|
|
30518
|
+
bulletGrams: 5.5,
|
|
30519
|
+
muzzleSpeed: 488,
|
|
30520
|
+
dragPer100m: 0.12,
|
|
30521
|
+
kind: "fmj",
|
|
30522
|
+
noise: 300,
|
|
30523
|
+
caseType: "pistol",
|
|
30524
|
+
rim: "rimless",
|
|
30525
|
+
cartridge: {
|
|
30526
|
+
caseDiameter: 9.95,
|
|
30527
|
+
overallLength: 35.2,
|
|
30528
|
+
caseLength: 25,
|
|
30529
|
+
projectileDiameter: 7.85
|
|
30530
|
+
},
|
|
30531
|
+
sources: [
|
|
30532
|
+
"reference",
|
|
30533
|
+
"derived"
|
|
30534
|
+
]
|
|
30535
|
+
},
|
|
30536
|
+
{
|
|
30537
|
+
id: "7_92x57",
|
|
30538
|
+
label: "7.92\xD757 mm Mauser",
|
|
30539
|
+
diameter: 8.22,
|
|
30540
|
+
bulletGrams: 11.7,
|
|
30541
|
+
muzzleSpeed: 760,
|
|
30542
|
+
dragPer100m: 0.08,
|
|
30543
|
+
kind: "fmj",
|
|
30544
|
+
noise: 420,
|
|
30545
|
+
caseType: "rifle",
|
|
30546
|
+
rim: "rimless",
|
|
30547
|
+
cartridge: {
|
|
30548
|
+
caseDiameter: 11.95,
|
|
30549
|
+
overallLength: 82,
|
|
30550
|
+
caseLength: 57,
|
|
30551
|
+
projectileDiameter: 8.22
|
|
30552
|
+
},
|
|
30553
|
+
sources: [
|
|
30554
|
+
"reference",
|
|
30555
|
+
"derived"
|
|
30556
|
+
]
|
|
30557
|
+
},
|
|
30558
|
+
{
|
|
30559
|
+
id: "9x39",
|
|
30560
|
+
label: "9\xD739 mm SP-5",
|
|
30561
|
+
diameter: 9.27,
|
|
30562
|
+
bulletGrams: 16.1,
|
|
30563
|
+
muzzleSpeed: 290,
|
|
30564
|
+
dragPer100m: 0.2,
|
|
30565
|
+
kind: "fmj",
|
|
30566
|
+
noise: 300,
|
|
30567
|
+
caseType: "rifle",
|
|
30568
|
+
rim: "rimless",
|
|
30569
|
+
cartridge: {
|
|
30570
|
+
caseDiameter: 11.33,
|
|
30571
|
+
overallLength: 56,
|
|
30572
|
+
caseLength: 38.7,
|
|
30573
|
+
projectileDiameter: 9.27
|
|
30574
|
+
},
|
|
30575
|
+
sources: [
|
|
30576
|
+
"reference",
|
|
30577
|
+
"derived"
|
|
30578
|
+
]
|
|
30143
30579
|
}
|
|
30144
30580
|
];
|
|
30145
30581
|
var explosives_default = [
|
|
@@ -30594,6 +31030,59 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
30594
31030
|
"lab",
|
|
30595
31031
|
"derived"
|
|
30596
31032
|
]
|
|
31033
|
+
},
|
|
31034
|
+
{
|
|
31035
|
+
id: "semtex",
|
|
31036
|
+
name: "Semtex charge",
|
|
31037
|
+
kind: "blast",
|
|
31038
|
+
lethalRadius: 6,
|
|
31039
|
+
injuryRadius: 18,
|
|
31040
|
+
energy: 377400,
|
|
31041
|
+
tnt: 0.83,
|
|
31042
|
+
destructionLevel: 3,
|
|
31043
|
+
noise: 700,
|
|
31044
|
+
fuseLength: 0.23,
|
|
31045
|
+
burnRate: 131,
|
|
31046
|
+
burnTolerance: 0.1,
|
|
31047
|
+
triggers: [
|
|
31048
|
+
"remote",
|
|
31049
|
+
"timer"
|
|
31050
|
+
],
|
|
31051
|
+
mass: 0.5,
|
|
31052
|
+
sources: [
|
|
31053
|
+
"lab",
|
|
31054
|
+
"reference",
|
|
31055
|
+
"derived"
|
|
31056
|
+
]
|
|
31057
|
+
},
|
|
31058
|
+
{
|
|
31059
|
+
id: "smoke_grenade",
|
|
31060
|
+
name: "M18 colored smoke grenade",
|
|
31061
|
+
kind: "smoke",
|
|
31062
|
+
lethalRadius: 0,
|
|
31063
|
+
injuryRadius: 0,
|
|
31064
|
+
energy: 0,
|
|
31065
|
+
destructionLevel: 0,
|
|
31066
|
+
noise: 30,
|
|
31067
|
+
fuseRange: [
|
|
31068
|
+
1,
|
|
31069
|
+
2.3
|
|
31070
|
+
],
|
|
31071
|
+
smoke: {
|
|
31072
|
+
color: 4177738,
|
|
31073
|
+
seconds: [
|
|
31074
|
+
50,
|
|
31075
|
+
90
|
|
31076
|
+
]
|
|
31077
|
+
},
|
|
31078
|
+
triggers: [
|
|
31079
|
+
"timer"
|
|
31080
|
+
],
|
|
31081
|
+
mass: 0.54,
|
|
31082
|
+
sources: [
|
|
31083
|
+
"reference",
|
|
31084
|
+
"derived"
|
|
31085
|
+
]
|
|
30597
31086
|
}
|
|
30598
31087
|
];
|
|
30599
31088
|
var explosive_weapons_default = [
|
|
@@ -30695,6 +31184,54 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
30695
31184
|
"lab",
|
|
30696
31185
|
"reference"
|
|
30697
31186
|
]
|
|
31187
|
+
},
|
|
31188
|
+
{
|
|
31189
|
+
id: "semtex",
|
|
31190
|
+
name: "Semtex charge",
|
|
31191
|
+
category: "throwable",
|
|
31192
|
+
action: {
|
|
31193
|
+
kind: "throwable",
|
|
31194
|
+
explosive: "semtex",
|
|
31195
|
+
energy: 0,
|
|
31196
|
+
radius: 0,
|
|
31197
|
+
fuse: 0,
|
|
31198
|
+
throwSpeed: 0,
|
|
31199
|
+
noise: 0
|
|
31200
|
+
},
|
|
31201
|
+
mass: 0.5,
|
|
31202
|
+
tags: [
|
|
31203
|
+
"explosive",
|
|
31204
|
+
"placed"
|
|
31205
|
+
],
|
|
31206
|
+
archetype: "frag_grenade",
|
|
31207
|
+
sources: [
|
|
31208
|
+
"reference",
|
|
31209
|
+
"derived"
|
|
31210
|
+
]
|
|
31211
|
+
},
|
|
31212
|
+
{
|
|
31213
|
+
id: "smoke_grenade",
|
|
31214
|
+
name: "M18 colored smoke grenade",
|
|
31215
|
+
category: "throwable",
|
|
31216
|
+
action: {
|
|
31217
|
+
kind: "throwable",
|
|
31218
|
+
explosive: "smoke_grenade",
|
|
31219
|
+
energy: 0,
|
|
31220
|
+
radius: 0,
|
|
31221
|
+
fuse: 0,
|
|
31222
|
+
throwSpeed: 15,
|
|
31223
|
+
noise: 0
|
|
31224
|
+
},
|
|
31225
|
+
mass: 0.54,
|
|
31226
|
+
tags: [
|
|
31227
|
+
"explosive",
|
|
31228
|
+
"smoke"
|
|
31229
|
+
],
|
|
31230
|
+
archetype: "frag_grenade",
|
|
31231
|
+
sources: [
|
|
31232
|
+
"reference",
|
|
31233
|
+
"derived"
|
|
31234
|
+
]
|
|
30698
31235
|
}
|
|
30699
31236
|
];
|
|
30700
31237
|
var firearms_default = [
|
|
@@ -38216,6 +38753,1477 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
38216
38753
|
}
|
|
38217
38754
|
],
|
|
38218
38755
|
fitted: []
|
|
38756
|
+
},
|
|
38757
|
+
{
|
|
38758
|
+
id: "aks_74u",
|
|
38759
|
+
name: "AKS-74U",
|
|
38760
|
+
category: "rifle",
|
|
38761
|
+
action: {
|
|
38762
|
+
kind: "firearm",
|
|
38763
|
+
caliber: "5_45x39",
|
|
38764
|
+
rpm: 700,
|
|
38765
|
+
modes: [
|
|
38766
|
+
"auto",
|
|
38767
|
+
"semi"
|
|
38768
|
+
],
|
|
38769
|
+
magazine: 30,
|
|
38770
|
+
reload: 2.6,
|
|
38771
|
+
muzzleSpeed: 735,
|
|
38772
|
+
spreadDeg: 0.6,
|
|
38773
|
+
bloomDeg: 0.25,
|
|
38774
|
+
recoveryDegPerS: 5,
|
|
38775
|
+
recoil: [
|
|
38776
|
+
0.66,
|
|
38777
|
+
0.4
|
|
38778
|
+
],
|
|
38779
|
+
range: 400,
|
|
38780
|
+
family: "magazine",
|
|
38781
|
+
timings: {
|
|
38782
|
+
draw: 0.35,
|
|
38783
|
+
reload: 2.6,
|
|
38784
|
+
reloadEmpty: 3.4,
|
|
38785
|
+
clear: 1.2
|
|
38786
|
+
}
|
|
38787
|
+
},
|
|
38788
|
+
mass: 2.7,
|
|
38789
|
+
dimensions: {
|
|
38790
|
+
length: 0.73,
|
|
38791
|
+
barrel: 0.206,
|
|
38792
|
+
emptyMass: 2.7,
|
|
38793
|
+
lengthCollapsed: 0.49
|
|
38794
|
+
},
|
|
38795
|
+
archetype: "rifle_5_56",
|
|
38796
|
+
variantOf: "ak_74",
|
|
38797
|
+
sources: [
|
|
38798
|
+
"reference",
|
|
38799
|
+
"derived",
|
|
38800
|
+
"sdk"
|
|
38801
|
+
],
|
|
38802
|
+
mounts: [
|
|
38803
|
+
{
|
|
38804
|
+
kind: "muzzle",
|
|
38805
|
+
interfaces: [
|
|
38806
|
+
"fixed_muzzle_device"
|
|
38807
|
+
],
|
|
38808
|
+
sources: [
|
|
38809
|
+
"derived"
|
|
38810
|
+
]
|
|
38811
|
+
},
|
|
38812
|
+
{
|
|
38813
|
+
kind: "magazine",
|
|
38814
|
+
interfaces: [
|
|
38815
|
+
"mag_curved"
|
|
38816
|
+
],
|
|
38817
|
+
sources: [
|
|
38818
|
+
"gallery"
|
|
38819
|
+
]
|
|
38820
|
+
},
|
|
38821
|
+
{
|
|
38822
|
+
kind: "sling",
|
|
38823
|
+
interfaces: [
|
|
38824
|
+
"swivel"
|
|
38825
|
+
],
|
|
38826
|
+
sources: [
|
|
38827
|
+
"derived"
|
|
38828
|
+
]
|
|
38829
|
+
}
|
|
38830
|
+
],
|
|
38831
|
+
fitted: [
|
|
38832
|
+
{
|
|
38833
|
+
id: "muzzle_brake",
|
|
38834
|
+
mount: "muzzle"
|
|
38835
|
+
}
|
|
38836
|
+
],
|
|
38837
|
+
stock: {
|
|
38838
|
+
kind: "fold",
|
|
38839
|
+
foldAxis: "left"
|
|
38840
|
+
}
|
|
38841
|
+
},
|
|
38842
|
+
{
|
|
38843
|
+
id: "fn_scar_l",
|
|
38844
|
+
name: "FN SCAR-L",
|
|
38845
|
+
maker: "FN",
|
|
38846
|
+
category: "dmr",
|
|
38847
|
+
action: {
|
|
38848
|
+
kind: "firearm",
|
|
38849
|
+
caliber: "5_56x45",
|
|
38850
|
+
rpm: 625,
|
|
38851
|
+
modes: [
|
|
38852
|
+
"auto",
|
|
38853
|
+
"semi"
|
|
38854
|
+
],
|
|
38855
|
+
magazine: 30,
|
|
38856
|
+
reload: 2.6,
|
|
38857
|
+
muzzleSpeed: 870,
|
|
38858
|
+
spreadDeg: 0.25,
|
|
38859
|
+
bloomDeg: 0.3,
|
|
38860
|
+
recoveryDegPerS: 3,
|
|
38861
|
+
recoil: [
|
|
38862
|
+
1.43,
|
|
38863
|
+
0.24
|
|
38864
|
+
],
|
|
38865
|
+
range: 800,
|
|
38866
|
+
family: "magazine",
|
|
38867
|
+
timings: {
|
|
38868
|
+
draw: 0.35,
|
|
38869
|
+
reload: 2.6,
|
|
38870
|
+
clear: 1.2
|
|
38871
|
+
}
|
|
38872
|
+
},
|
|
38873
|
+
mass: 3.29,
|
|
38874
|
+
dimensions: {
|
|
38875
|
+
length: 0.889,
|
|
38876
|
+
barrel: 0.351,
|
|
38877
|
+
emptyMass: 3.29,
|
|
38878
|
+
lengthCollapsed: 0.635
|
|
38879
|
+
},
|
|
38880
|
+
archetype: "rifle_bolt_308",
|
|
38881
|
+
variantOf: "fn_scar_h",
|
|
38882
|
+
sources: [
|
|
38883
|
+
"reference",
|
|
38884
|
+
"derived",
|
|
38885
|
+
"sdk"
|
|
38886
|
+
],
|
|
38887
|
+
mounts: [
|
|
38888
|
+
{
|
|
38889
|
+
kind: "rail_top",
|
|
38890
|
+
interfaces: [
|
|
38891
|
+
"picatinny"
|
|
38892
|
+
],
|
|
38893
|
+
sources: [
|
|
38894
|
+
"gallery"
|
|
38895
|
+
]
|
|
38896
|
+
},
|
|
38897
|
+
{
|
|
38898
|
+
kind: "rail_left",
|
|
38899
|
+
interfaces: [
|
|
38900
|
+
"picatinny"
|
|
38901
|
+
],
|
|
38902
|
+
sources: [
|
|
38903
|
+
"gallery"
|
|
38904
|
+
]
|
|
38905
|
+
},
|
|
38906
|
+
{
|
|
38907
|
+
kind: "rail_right",
|
|
38908
|
+
interfaces: [
|
|
38909
|
+
"picatinny"
|
|
38910
|
+
],
|
|
38911
|
+
sources: [
|
|
38912
|
+
"gallery"
|
|
38913
|
+
]
|
|
38914
|
+
},
|
|
38915
|
+
{
|
|
38916
|
+
kind: "rail_bottom",
|
|
38917
|
+
interfaces: [
|
|
38918
|
+
"picatinny"
|
|
38919
|
+
],
|
|
38920
|
+
sources: [
|
|
38921
|
+
"gallery"
|
|
38922
|
+
]
|
|
38923
|
+
},
|
|
38924
|
+
{
|
|
38925
|
+
kind: "magazine",
|
|
38926
|
+
interfaces: [
|
|
38927
|
+
"mag_box"
|
|
38928
|
+
],
|
|
38929
|
+
sources: [
|
|
38930
|
+
"gallery"
|
|
38931
|
+
]
|
|
38932
|
+
},
|
|
38933
|
+
{
|
|
38934
|
+
kind: "sling",
|
|
38935
|
+
interfaces: [
|
|
38936
|
+
"swivel"
|
|
38937
|
+
],
|
|
38938
|
+
sources: [
|
|
38939
|
+
"derived"
|
|
38940
|
+
]
|
|
38941
|
+
}
|
|
38942
|
+
],
|
|
38943
|
+
fitted: [
|
|
38944
|
+
{
|
|
38945
|
+
id: "acog_4x",
|
|
38946
|
+
mount: "rail_top"
|
|
38947
|
+
}
|
|
38948
|
+
],
|
|
38949
|
+
stock: {
|
|
38950
|
+
kind: "fold",
|
|
38951
|
+
foldAxis: "right"
|
|
38952
|
+
}
|
|
38953
|
+
},
|
|
38954
|
+
{
|
|
38955
|
+
id: "m1_garand",
|
|
38956
|
+
name: "M1 Garand",
|
|
38957
|
+
category: "rifle",
|
|
38958
|
+
action: {
|
|
38959
|
+
kind: "firearm",
|
|
38960
|
+
caliber: "30_06",
|
|
38961
|
+
rpm: 120,
|
|
38962
|
+
modes: [
|
|
38963
|
+
"semi"
|
|
38964
|
+
],
|
|
38965
|
+
magazine: 8,
|
|
38966
|
+
reload: 2.2,
|
|
38967
|
+
muzzleSpeed: 865,
|
|
38968
|
+
spreadDeg: 0.8,
|
|
38969
|
+
bloomDeg: 0.35,
|
|
38970
|
+
recoveryDegPerS: 4.5,
|
|
38971
|
+
recoil: [
|
|
38972
|
+
2.28,
|
|
38973
|
+
1.22
|
|
38974
|
+
],
|
|
38975
|
+
range: 350,
|
|
38976
|
+
family: "top_load_semi",
|
|
38977
|
+
timings: {
|
|
38978
|
+
draw: 0.35,
|
|
38979
|
+
reload: 2.2,
|
|
38980
|
+
clear: 1.2
|
|
38981
|
+
}
|
|
38982
|
+
},
|
|
38983
|
+
mass: 4.31,
|
|
38984
|
+
dimensions: {
|
|
38985
|
+
length: 1.1,
|
|
38986
|
+
barrel: 0.61,
|
|
38987
|
+
emptyMass: 4.31
|
|
38988
|
+
},
|
|
38989
|
+
archetype: "rifle_7_62x39",
|
|
38990
|
+
variantOf: "sks",
|
|
38991
|
+
sources: [
|
|
38992
|
+
"reference",
|
|
38993
|
+
"derived",
|
|
38994
|
+
"sdk"
|
|
38995
|
+
],
|
|
38996
|
+
mounts: [
|
|
38997
|
+
{
|
|
38998
|
+
kind: "sling",
|
|
38999
|
+
interfaces: [
|
|
39000
|
+
"swivel"
|
|
39001
|
+
],
|
|
39002
|
+
sources: [
|
|
39003
|
+
"derived"
|
|
39004
|
+
]
|
|
39005
|
+
}
|
|
39006
|
+
],
|
|
39007
|
+
fitted: []
|
|
39008
|
+
},
|
|
39009
|
+
{
|
|
39010
|
+
id: "accuracy_l96a1",
|
|
39011
|
+
name: "Accuracy L96A1",
|
|
39012
|
+
maker: "Accuracy",
|
|
39013
|
+
category: "sniper",
|
|
39014
|
+
action: {
|
|
39015
|
+
kind: "firearm",
|
|
39016
|
+
caliber: "7_62x51",
|
|
39017
|
+
rpm: 73.17,
|
|
39018
|
+
modes: [
|
|
39019
|
+
"bolt"
|
|
39020
|
+
],
|
|
39021
|
+
magazine: 10,
|
|
39022
|
+
reload: 3.2,
|
|
39023
|
+
muzzleSpeed: 840,
|
|
39024
|
+
spreadDeg: 0.25,
|
|
39025
|
+
bloomDeg: 0.3,
|
|
39026
|
+
recoveryDegPerS: 3,
|
|
39027
|
+
recoil: [
|
|
39028
|
+
1.64,
|
|
39029
|
+
0.27
|
|
39030
|
+
],
|
|
39031
|
+
range: 800,
|
|
39032
|
+
family: "bolt_magazine",
|
|
39033
|
+
timings: {
|
|
39034
|
+
draw: 0.35,
|
|
39035
|
+
reload: 3.2,
|
|
39036
|
+
cycle: 0.82,
|
|
39037
|
+
clear: 1.2
|
|
39038
|
+
}
|
|
39039
|
+
},
|
|
39040
|
+
mass: 6.5,
|
|
39041
|
+
dimensions: {
|
|
39042
|
+
length: 1.18,
|
|
39043
|
+
barrel: 0.66,
|
|
39044
|
+
emptyMass: 6.5
|
|
39045
|
+
},
|
|
39046
|
+
archetype: "rifle_bolt_308",
|
|
39047
|
+
variantOf: "accuracy_awm",
|
|
39048
|
+
sources: [
|
|
39049
|
+
"reference",
|
|
39050
|
+
"derived",
|
|
39051
|
+
"sdk"
|
|
39052
|
+
],
|
|
39053
|
+
mounts: [
|
|
39054
|
+
{
|
|
39055
|
+
kind: "muzzle",
|
|
39056
|
+
interfaces: [
|
|
39057
|
+
"fixed_muzzle_device"
|
|
39058
|
+
],
|
|
39059
|
+
sources: [
|
|
39060
|
+
"derived"
|
|
39061
|
+
]
|
|
39062
|
+
},
|
|
39063
|
+
{
|
|
39064
|
+
kind: "rail_top",
|
|
39065
|
+
interfaces: [
|
|
39066
|
+
"dovetail"
|
|
39067
|
+
],
|
|
39068
|
+
sources: [
|
|
39069
|
+
"derived"
|
|
39070
|
+
]
|
|
39071
|
+
},
|
|
39072
|
+
{
|
|
39073
|
+
kind: "rail_bottom",
|
|
39074
|
+
interfaces: [
|
|
39075
|
+
"bipod_stud"
|
|
39076
|
+
],
|
|
39077
|
+
sources: [
|
|
39078
|
+
"gallery"
|
|
39079
|
+
]
|
|
39080
|
+
},
|
|
39081
|
+
{
|
|
39082
|
+
kind: "magazine",
|
|
39083
|
+
interfaces: [
|
|
39084
|
+
"mag_box"
|
|
39085
|
+
],
|
|
39086
|
+
sources: [
|
|
39087
|
+
"gallery"
|
|
39088
|
+
]
|
|
39089
|
+
},
|
|
39090
|
+
{
|
|
39091
|
+
kind: "sling",
|
|
39092
|
+
interfaces: [
|
|
39093
|
+
"swivel"
|
|
39094
|
+
],
|
|
39095
|
+
sources: [
|
|
39096
|
+
"derived"
|
|
39097
|
+
]
|
|
39098
|
+
}
|
|
39099
|
+
],
|
|
39100
|
+
fitted: [
|
|
39101
|
+
{
|
|
39102
|
+
id: "muzzle_brake",
|
|
39103
|
+
mount: "muzzle"
|
|
39104
|
+
},
|
|
39105
|
+
{
|
|
39106
|
+
id: "scope_3_9x",
|
|
39107
|
+
mount: "rail_top"
|
|
39108
|
+
},
|
|
39109
|
+
{
|
|
39110
|
+
id: "bipod",
|
|
39111
|
+
mount: "rail_bottom"
|
|
39112
|
+
}
|
|
39113
|
+
]
|
|
39114
|
+
},
|
|
39115
|
+
{
|
|
39116
|
+
id: "mossberg_590",
|
|
39117
|
+
name: "Mossberg 590",
|
|
39118
|
+
maker: "Mossberg",
|
|
39119
|
+
category: "shotgun",
|
|
39120
|
+
action: {
|
|
39121
|
+
kind: "firearm",
|
|
39122
|
+
caliber: "12ga_00buck",
|
|
39123
|
+
rpm: 120,
|
|
39124
|
+
modes: [
|
|
39125
|
+
"pump"
|
|
39126
|
+
],
|
|
39127
|
+
magazine: 8,
|
|
39128
|
+
chamber: 1,
|
|
39129
|
+
reload: 0.55,
|
|
39130
|
+
perRound: true,
|
|
39131
|
+
muzzleSpeed: 400,
|
|
39132
|
+
spreadDeg: 1,
|
|
39133
|
+
bloomDeg: 0.5,
|
|
39134
|
+
recoveryDegPerS: 3,
|
|
39135
|
+
pelletSpreadDeg: 3,
|
|
39136
|
+
recoil: [
|
|
39137
|
+
3.62,
|
|
39138
|
+
0.91
|
|
39139
|
+
],
|
|
39140
|
+
range: 40,
|
|
39141
|
+
family: "pump",
|
|
39142
|
+
timings: {
|
|
39143
|
+
draw: 0.35,
|
|
39144
|
+
reload: 0.55,
|
|
39145
|
+
perRound: 0.55,
|
|
39146
|
+
cycle: 0.5,
|
|
39147
|
+
clear: 1.2
|
|
39148
|
+
}
|
|
39149
|
+
},
|
|
39150
|
+
mass: 3.3,
|
|
39151
|
+
dimensions: {
|
|
39152
|
+
length: 1.003,
|
|
39153
|
+
barrel: 0.508,
|
|
39154
|
+
emptyMass: 3.3
|
|
39155
|
+
},
|
|
39156
|
+
archetype: "shotgun_pump_12",
|
|
39157
|
+
variantOf: "mossberg_500",
|
|
39158
|
+
sources: [
|
|
39159
|
+
"reference",
|
|
39160
|
+
"derived",
|
|
39161
|
+
"sdk"
|
|
39162
|
+
],
|
|
39163
|
+
mounts: [
|
|
39164
|
+
{
|
|
39165
|
+
kind: "barrel",
|
|
39166
|
+
interfaces: [
|
|
39167
|
+
"barrel_swap"
|
|
39168
|
+
],
|
|
39169
|
+
sources: [
|
|
39170
|
+
"derived"
|
|
39171
|
+
]
|
|
39172
|
+
},
|
|
39173
|
+
{
|
|
39174
|
+
kind: "sling",
|
|
39175
|
+
interfaces: [
|
|
39176
|
+
"swivel"
|
|
39177
|
+
],
|
|
39178
|
+
sources: [
|
|
39179
|
+
"derived"
|
|
39180
|
+
]
|
|
39181
|
+
}
|
|
39182
|
+
],
|
|
39183
|
+
fitted: []
|
|
39184
|
+
},
|
|
39185
|
+
{
|
|
39186
|
+
id: "fn_m240b",
|
|
39187
|
+
name: "FN M240B",
|
|
39188
|
+
maker: "FN",
|
|
39189
|
+
category: "machine_gun",
|
|
39190
|
+
action: {
|
|
39191
|
+
kind: "firearm",
|
|
39192
|
+
caliber: "7_62x51",
|
|
39193
|
+
rpm: 750,
|
|
39194
|
+
modes: [
|
|
39195
|
+
"auto"
|
|
39196
|
+
],
|
|
39197
|
+
magazine: 100,
|
|
39198
|
+
reload: 8.5,
|
|
39199
|
+
muzzleSpeed: 853,
|
|
39200
|
+
spreadDeg: 0.8,
|
|
39201
|
+
bloomDeg: 0.35,
|
|
39202
|
+
recoveryDegPerS: 4.5,
|
|
39203
|
+
recoil: [
|
|
39204
|
+
0.77,
|
|
39205
|
+
0.41
|
|
39206
|
+
],
|
|
39207
|
+
range: 350,
|
|
39208
|
+
family: "belt",
|
|
39209
|
+
timings: {
|
|
39210
|
+
draw: 0.35,
|
|
39211
|
+
reload: 8.5,
|
|
39212
|
+
clear: 1.2
|
|
39213
|
+
}
|
|
39214
|
+
},
|
|
39215
|
+
mass: 12.5,
|
|
39216
|
+
dimensions: {
|
|
39217
|
+
length: 1.245,
|
|
39218
|
+
barrel: 0.63,
|
|
39219
|
+
emptyMass: 12.5
|
|
39220
|
+
},
|
|
39221
|
+
archetype: "rifle_7_62x39",
|
|
39222
|
+
sources: [
|
|
39223
|
+
"reference",
|
|
39224
|
+
"derived",
|
|
39225
|
+
"sdk"
|
|
39226
|
+
],
|
|
39227
|
+
mounts: [
|
|
39228
|
+
{
|
|
39229
|
+
kind: "rail_top",
|
|
39230
|
+
interfaces: [
|
|
39231
|
+
"picatinny"
|
|
39232
|
+
],
|
|
39233
|
+
sources: [
|
|
39234
|
+
"gallery"
|
|
39235
|
+
]
|
|
39236
|
+
},
|
|
39237
|
+
{
|
|
39238
|
+
kind: "rail_bottom",
|
|
39239
|
+
interfaces: [
|
|
39240
|
+
"bipod_stud"
|
|
39241
|
+
],
|
|
39242
|
+
sources: [
|
|
39243
|
+
"gallery"
|
|
39244
|
+
]
|
|
39245
|
+
},
|
|
39246
|
+
{
|
|
39247
|
+
kind: "sling",
|
|
39248
|
+
interfaces: [
|
|
39249
|
+
"swivel"
|
|
39250
|
+
],
|
|
39251
|
+
sources: [
|
|
39252
|
+
"derived"
|
|
39253
|
+
]
|
|
39254
|
+
},
|
|
39255
|
+
{
|
|
39256
|
+
kind: "feed",
|
|
39257
|
+
interfaces: [
|
|
39258
|
+
"belt"
|
|
39259
|
+
],
|
|
39260
|
+
sources: [
|
|
39261
|
+
"derived"
|
|
39262
|
+
]
|
|
39263
|
+
}
|
|
39264
|
+
],
|
|
39265
|
+
fitted: [
|
|
39266
|
+
{
|
|
39267
|
+
id: "bipod",
|
|
39268
|
+
mount: "rail_bottom"
|
|
39269
|
+
}
|
|
39270
|
+
]
|
|
39271
|
+
},
|
|
39272
|
+
{
|
|
39273
|
+
id: "sig_sauer_p320",
|
|
39274
|
+
name: "SIG Sauer P320",
|
|
39275
|
+
maker: "SIG Sauer",
|
|
39276
|
+
category: "pistol",
|
|
39277
|
+
action: {
|
|
39278
|
+
kind: "firearm",
|
|
39279
|
+
caliber: "9x19",
|
|
39280
|
+
rpm: 300,
|
|
39281
|
+
modes: [
|
|
39282
|
+
"semi"
|
|
39283
|
+
],
|
|
39284
|
+
magazine: 17,
|
|
39285
|
+
reload: 1.9,
|
|
39286
|
+
muzzleSpeed: 350,
|
|
39287
|
+
spreadDeg: 1,
|
|
39288
|
+
bloomDeg: 0.6,
|
|
39289
|
+
recoveryDegPerS: 4,
|
|
39290
|
+
recoil: [
|
|
39291
|
+
1.2,
|
|
39292
|
+
0.4
|
|
39293
|
+
],
|
|
39294
|
+
range: 50,
|
|
39295
|
+
family: "pistol",
|
|
39296
|
+
timings: {
|
|
39297
|
+
draw: 0.35,
|
|
39298
|
+
reload: 1.9,
|
|
39299
|
+
clear: 1.2
|
|
39300
|
+
},
|
|
39301
|
+
faultFactor: 0.8
|
|
39302
|
+
},
|
|
39303
|
+
mass: 0.83,
|
|
39304
|
+
dimensions: {
|
|
39305
|
+
length: 0.203,
|
|
39306
|
+
barrel: 0.12,
|
|
39307
|
+
emptyMass: 0.83
|
|
39308
|
+
},
|
|
39309
|
+
archetype: "pistol_9mm",
|
|
39310
|
+
variantOf: "sig_sauer_p226",
|
|
39311
|
+
sources: [
|
|
39312
|
+
"reference",
|
|
39313
|
+
"derived",
|
|
39314
|
+
"sdk"
|
|
39315
|
+
],
|
|
39316
|
+
mounts: [
|
|
39317
|
+
{
|
|
39318
|
+
kind: "magazine",
|
|
39319
|
+
interfaces: [
|
|
39320
|
+
"mag_pistol"
|
|
39321
|
+
],
|
|
39322
|
+
sources: [
|
|
39323
|
+
"derived"
|
|
39324
|
+
]
|
|
39325
|
+
}
|
|
39326
|
+
],
|
|
39327
|
+
fitted: []
|
|
39328
|
+
},
|
|
39329
|
+
{
|
|
39330
|
+
id: "hk_usp",
|
|
39331
|
+
name: "HK USP",
|
|
39332
|
+
maker: "HK",
|
|
39333
|
+
category: "pistol",
|
|
39334
|
+
action: {
|
|
39335
|
+
kind: "firearm",
|
|
39336
|
+
caliber: "9x19",
|
|
39337
|
+
rpm: 300,
|
|
39338
|
+
modes: [
|
|
39339
|
+
"semi"
|
|
39340
|
+
],
|
|
39341
|
+
magazine: 15,
|
|
39342
|
+
reload: 1.9,
|
|
39343
|
+
muzzleSpeed: 350,
|
|
39344
|
+
spreadDeg: 1,
|
|
39345
|
+
bloomDeg: 0.6,
|
|
39346
|
+
recoveryDegPerS: 4,
|
|
39347
|
+
recoil: [
|
|
39348
|
+
1.29,
|
|
39349
|
+
0.43
|
|
39350
|
+
],
|
|
39351
|
+
range: 50,
|
|
39352
|
+
family: "pistol",
|
|
39353
|
+
timings: {
|
|
39354
|
+
draw: 0.35,
|
|
39355
|
+
reload: 1.9,
|
|
39356
|
+
clear: 1.2
|
|
39357
|
+
},
|
|
39358
|
+
faultFactor: 0.8
|
|
39359
|
+
},
|
|
39360
|
+
mass: 0.77,
|
|
39361
|
+
dimensions: {
|
|
39362
|
+
length: 0.194,
|
|
39363
|
+
barrel: 0.108,
|
|
39364
|
+
emptyMass: 0.77
|
|
39365
|
+
},
|
|
39366
|
+
archetype: "pistol_9mm",
|
|
39367
|
+
variantOf: "sig_sauer_p226",
|
|
39368
|
+
sources: [
|
|
39369
|
+
"reference",
|
|
39370
|
+
"derived",
|
|
39371
|
+
"sdk"
|
|
39372
|
+
],
|
|
39373
|
+
mounts: [
|
|
39374
|
+
{
|
|
39375
|
+
kind: "magazine",
|
|
39376
|
+
interfaces: [
|
|
39377
|
+
"mag_pistol"
|
|
39378
|
+
],
|
|
39379
|
+
sources: [
|
|
39380
|
+
"derived"
|
|
39381
|
+
]
|
|
39382
|
+
}
|
|
39383
|
+
],
|
|
39384
|
+
fitted: []
|
|
39385
|
+
},
|
|
39386
|
+
{
|
|
39387
|
+
id: "walther_p99",
|
|
39388
|
+
name: "Walther P99",
|
|
39389
|
+
maker: "Walther",
|
|
39390
|
+
category: "pistol",
|
|
39391
|
+
action: {
|
|
39392
|
+
kind: "firearm",
|
|
39393
|
+
caliber: "9x19",
|
|
39394
|
+
rpm: 300,
|
|
39395
|
+
modes: [
|
|
39396
|
+
"semi"
|
|
39397
|
+
],
|
|
39398
|
+
magazine: 15,
|
|
39399
|
+
reload: 1.8,
|
|
39400
|
+
muzzleSpeed: 375,
|
|
39401
|
+
spreadDeg: 1,
|
|
39402
|
+
bloomDeg: 0.6,
|
|
39403
|
+
recoveryDegPerS: 4,
|
|
39404
|
+
recoil: [
|
|
39405
|
+
1.81,
|
|
39406
|
+
0.6
|
|
39407
|
+
],
|
|
39408
|
+
range: 50,
|
|
39409
|
+
family: "pistol",
|
|
39410
|
+
timings: {
|
|
39411
|
+
draw: 0.35,
|
|
39412
|
+
reload: 1.8,
|
|
39413
|
+
clear: 1.2
|
|
39414
|
+
},
|
|
39415
|
+
faultFactor: 0.7
|
|
39416
|
+
},
|
|
39417
|
+
mass: 0.63,
|
|
39418
|
+
dimensions: {
|
|
39419
|
+
length: 0.18,
|
|
39420
|
+
barrel: 0.102,
|
|
39421
|
+
emptyMass: 0.63
|
|
39422
|
+
},
|
|
39423
|
+
archetype: "pistol_9mm",
|
|
39424
|
+
variantOf: "glock_17",
|
|
39425
|
+
sources: [
|
|
39426
|
+
"reference",
|
|
39427
|
+
"derived",
|
|
39428
|
+
"sdk"
|
|
39429
|
+
],
|
|
39430
|
+
mounts: [
|
|
39431
|
+
{
|
|
39432
|
+
kind: "magazine",
|
|
39433
|
+
interfaces: [
|
|
39434
|
+
"mag_pistol"
|
|
39435
|
+
],
|
|
39436
|
+
sources: [
|
|
39437
|
+
"derived"
|
|
39438
|
+
]
|
|
39439
|
+
}
|
|
39440
|
+
],
|
|
39441
|
+
fitted: []
|
|
39442
|
+
},
|
|
39443
|
+
{
|
|
39444
|
+
id: "colt_python",
|
|
39445
|
+
name: "Colt Python",
|
|
39446
|
+
maker: "Colt",
|
|
39447
|
+
category: "revolver",
|
|
39448
|
+
action: {
|
|
39449
|
+
kind: "firearm",
|
|
39450
|
+
caliber: "357mag",
|
|
39451
|
+
rpm: 150,
|
|
39452
|
+
modes: [
|
|
39453
|
+
"single"
|
|
39454
|
+
],
|
|
39455
|
+
magazine: 6,
|
|
39456
|
+
reload: 2.4,
|
|
39457
|
+
muzzleSpeed: 430,
|
|
39458
|
+
spreadDeg: 0.8,
|
|
39459
|
+
bloomDeg: 1,
|
|
39460
|
+
recoveryDegPerS: 3,
|
|
39461
|
+
recoil: [
|
|
39462
|
+
3.13,
|
|
39463
|
+
0.94
|
|
39464
|
+
],
|
|
39465
|
+
range: 60,
|
|
39466
|
+
family: "revolver",
|
|
39467
|
+
timings: {
|
|
39468
|
+
draw: 0.35,
|
|
39469
|
+
reload: 2.4,
|
|
39470
|
+
perRound: 1,
|
|
39471
|
+
clear: 1.2
|
|
39472
|
+
}
|
|
39473
|
+
},
|
|
39474
|
+
mass: 1.23,
|
|
39475
|
+
dimensions: {
|
|
39476
|
+
length: 0.292,
|
|
39477
|
+
barrel: 0.152,
|
|
39478
|
+
emptyMass: 1.23
|
|
39479
|
+
},
|
|
39480
|
+
archetype: "revolver_357",
|
|
39481
|
+
variantOf: "sw_686",
|
|
39482
|
+
sources: [
|
|
39483
|
+
"reference",
|
|
39484
|
+
"derived",
|
|
39485
|
+
"sdk"
|
|
39486
|
+
],
|
|
39487
|
+
mounts: [],
|
|
39488
|
+
fitted: []
|
|
39489
|
+
},
|
|
39490
|
+
{
|
|
39491
|
+
id: "fn_ps90",
|
|
39492
|
+
name: "FN PS90",
|
|
39493
|
+
maker: "FN",
|
|
39494
|
+
category: "smg",
|
|
39495
|
+
action: {
|
|
39496
|
+
kind: "firearm",
|
|
39497
|
+
caliber: "5_7x28",
|
|
39498
|
+
rpm: 120,
|
|
39499
|
+
modes: [
|
|
39500
|
+
"semi"
|
|
39501
|
+
],
|
|
39502
|
+
magazine: 50,
|
|
39503
|
+
reload: 3,
|
|
39504
|
+
muzzleSpeed: 715,
|
|
39505
|
+
spreadDeg: 1.5,
|
|
39506
|
+
bloomDeg: 0.35,
|
|
39507
|
+
recoveryDegPerS: 6,
|
|
39508
|
+
recoil: [
|
|
39509
|
+
0.69,
|
|
39510
|
+
0.38
|
|
39511
|
+
],
|
|
39512
|
+
range: 80,
|
|
39513
|
+
family: "top_magazine",
|
|
39514
|
+
timings: {
|
|
39515
|
+
draw: 0.35,
|
|
39516
|
+
reload: 3,
|
|
39517
|
+
clear: 1.2
|
|
39518
|
+
}
|
|
39519
|
+
},
|
|
39520
|
+
mass: 2.86,
|
|
39521
|
+
dimensions: {
|
|
39522
|
+
length: 0.668,
|
|
39523
|
+
barrel: 0.407,
|
|
39524
|
+
emptyMass: 2.86
|
|
39525
|
+
},
|
|
39526
|
+
archetype: "smg_9mm",
|
|
39527
|
+
variantOf: "fn_p90",
|
|
39528
|
+
sources: [
|
|
39529
|
+
"reference",
|
|
39530
|
+
"derived",
|
|
39531
|
+
"sdk"
|
|
39532
|
+
],
|
|
39533
|
+
mounts: [
|
|
39534
|
+
{
|
|
39535
|
+
kind: "sling",
|
|
39536
|
+
interfaces: [
|
|
39537
|
+
"swivel"
|
|
39538
|
+
],
|
|
39539
|
+
sources: [
|
|
39540
|
+
"derived"
|
|
39541
|
+
]
|
|
39542
|
+
}
|
|
39543
|
+
],
|
|
39544
|
+
fitted: []
|
|
39545
|
+
},
|
|
39546
|
+
{
|
|
39547
|
+
id: "bt_mp9",
|
|
39548
|
+
name: "B&T MP9",
|
|
39549
|
+
maker: "B&T",
|
|
39550
|
+
category: "smg",
|
|
39551
|
+
action: {
|
|
39552
|
+
kind: "firearm",
|
|
39553
|
+
caliber: "9x19",
|
|
39554
|
+
rpm: 1100,
|
|
39555
|
+
modes: [
|
|
39556
|
+
"auto",
|
|
39557
|
+
"semi"
|
|
39558
|
+
],
|
|
39559
|
+
magazine: 30,
|
|
39560
|
+
reload: 2.4,
|
|
39561
|
+
muzzleSpeed: 400,
|
|
39562
|
+
spreadDeg: 1.5,
|
|
39563
|
+
bloomDeg: 0.35,
|
|
39564
|
+
recoveryDegPerS: 6,
|
|
39565
|
+
recoil: [
|
|
39566
|
+
1.66,
|
|
39567
|
+
0.92
|
|
39568
|
+
],
|
|
39569
|
+
range: 80,
|
|
39570
|
+
family: "magazine",
|
|
39571
|
+
timings: {
|
|
39572
|
+
draw: 0.35,
|
|
39573
|
+
reload: 2.4,
|
|
39574
|
+
clear: 1.2
|
|
39575
|
+
}
|
|
39576
|
+
},
|
|
39577
|
+
mass: 1.4,
|
|
39578
|
+
dimensions: {
|
|
39579
|
+
length: 0.523,
|
|
39580
|
+
barrel: 0.13,
|
|
39581
|
+
emptyMass: 1.4,
|
|
39582
|
+
lengthCollapsed: 0.303
|
|
39583
|
+
},
|
|
39584
|
+
archetype: "smg_9mm",
|
|
39585
|
+
variantOf: "uzi",
|
|
39586
|
+
sources: [
|
|
39587
|
+
"reference",
|
|
39588
|
+
"derived",
|
|
39589
|
+
"sdk"
|
|
39590
|
+
],
|
|
39591
|
+
mounts: [
|
|
39592
|
+
{
|
|
39593
|
+
kind: "rail_top",
|
|
39594
|
+
interfaces: [
|
|
39595
|
+
"picatinny"
|
|
39596
|
+
],
|
|
39597
|
+
sources: [
|
|
39598
|
+
"gallery"
|
|
39599
|
+
]
|
|
39600
|
+
},
|
|
39601
|
+
{
|
|
39602
|
+
kind: "magazine",
|
|
39603
|
+
interfaces: [
|
|
39604
|
+
"mag_box"
|
|
39605
|
+
],
|
|
39606
|
+
sources: [
|
|
39607
|
+
"gallery"
|
|
39608
|
+
]
|
|
39609
|
+
},
|
|
39610
|
+
{
|
|
39611
|
+
kind: "sling",
|
|
39612
|
+
interfaces: [
|
|
39613
|
+
"swivel"
|
|
39614
|
+
],
|
|
39615
|
+
sources: [
|
|
39616
|
+
"derived"
|
|
39617
|
+
]
|
|
39618
|
+
}
|
|
39619
|
+
],
|
|
39620
|
+
fitted: [],
|
|
39621
|
+
stock: {
|
|
39622
|
+
kind: "fold",
|
|
39623
|
+
foldAxis: "right"
|
|
39624
|
+
}
|
|
39625
|
+
},
|
|
39626
|
+
{
|
|
39627
|
+
id: "mp40",
|
|
39628
|
+
name: "MP40",
|
|
39629
|
+
category: "smg",
|
|
39630
|
+
action: {
|
|
39631
|
+
kind: "firearm",
|
|
39632
|
+
caliber: "9x19",
|
|
39633
|
+
rpm: 550,
|
|
39634
|
+
modes: [
|
|
39635
|
+
"auto",
|
|
39636
|
+
"semi"
|
|
39637
|
+
],
|
|
39638
|
+
magazine: 32,
|
|
39639
|
+
reload: 2.9,
|
|
39640
|
+
spreadDeg: 1.5,
|
|
39641
|
+
bloomDeg: 0.35,
|
|
39642
|
+
recoveryDegPerS: 6,
|
|
39643
|
+
recoil: [
|
|
39644
|
+
0.48,
|
|
39645
|
+
0.27
|
|
39646
|
+
],
|
|
39647
|
+
range: 80,
|
|
39648
|
+
family: "magazine",
|
|
39649
|
+
timings: {
|
|
39650
|
+
draw: 0.35,
|
|
39651
|
+
reload: 2.9,
|
|
39652
|
+
clear: 1.2
|
|
39653
|
+
}
|
|
39654
|
+
},
|
|
39655
|
+
mass: 4,
|
|
39656
|
+
dimensions: {
|
|
39657
|
+
length: 0.833,
|
|
39658
|
+
barrel: 0.251,
|
|
39659
|
+
emptyMass: 4,
|
|
39660
|
+
lengthCollapsed: 0.63
|
|
39661
|
+
},
|
|
39662
|
+
archetype: "smg_9mm",
|
|
39663
|
+
variantOf: "thompson_m1a1",
|
|
39664
|
+
sources: [
|
|
39665
|
+
"reference",
|
|
39666
|
+
"derived",
|
|
39667
|
+
"sdk"
|
|
39668
|
+
],
|
|
39669
|
+
mounts: [
|
|
39670
|
+
{
|
|
39671
|
+
kind: "magazine",
|
|
39672
|
+
interfaces: [
|
|
39673
|
+
"mag_box"
|
|
39674
|
+
],
|
|
39675
|
+
sources: [
|
|
39676
|
+
"gallery"
|
|
39677
|
+
]
|
|
39678
|
+
},
|
|
39679
|
+
{
|
|
39680
|
+
kind: "sling",
|
|
39681
|
+
interfaces: [
|
|
39682
|
+
"swivel"
|
|
39683
|
+
],
|
|
39684
|
+
sources: [
|
|
39685
|
+
"derived"
|
|
39686
|
+
]
|
|
39687
|
+
}
|
|
39688
|
+
],
|
|
39689
|
+
fitted: [],
|
|
39690
|
+
stock: {
|
|
39691
|
+
kind: "fold",
|
|
39692
|
+
foldAxis: "under"
|
|
39693
|
+
}
|
|
39694
|
+
},
|
|
39695
|
+
{
|
|
39696
|
+
id: "famas",
|
|
39697
|
+
name: "FAMAS",
|
|
39698
|
+
category: "rifle",
|
|
39699
|
+
action: {
|
|
39700
|
+
kind: "firearm",
|
|
39701
|
+
caliber: "5_56x45",
|
|
39702
|
+
rpm: 1e3,
|
|
39703
|
+
modes: [
|
|
39704
|
+
"auto",
|
|
39705
|
+
"semi"
|
|
39706
|
+
],
|
|
39707
|
+
magazine: 25,
|
|
39708
|
+
reload: 2.8,
|
|
39709
|
+
muzzleSpeed: 960,
|
|
39710
|
+
spreadDeg: 0.6,
|
|
39711
|
+
bloomDeg: 0.25,
|
|
39712
|
+
recoveryDegPerS: 5,
|
|
39713
|
+
recoil: [
|
|
39714
|
+
0.99,
|
|
39715
|
+
0.6
|
|
39716
|
+
],
|
|
39717
|
+
range: 400,
|
|
39718
|
+
family: "magazine",
|
|
39719
|
+
timings: {
|
|
39720
|
+
draw: 0.35,
|
|
39721
|
+
reload: 2.8,
|
|
39722
|
+
reloadEmpty: 3.4,
|
|
39723
|
+
clear: 1.2
|
|
39724
|
+
}
|
|
39725
|
+
},
|
|
39726
|
+
mass: 3.61,
|
|
39727
|
+
dimensions: {
|
|
39728
|
+
length: 0.757,
|
|
39729
|
+
barrel: 0.488,
|
|
39730
|
+
emptyMass: 3.61
|
|
39731
|
+
},
|
|
39732
|
+
archetype: "rifle_5_56",
|
|
39733
|
+
variantOf: "steyr_aug_a1",
|
|
39734
|
+
sources: [
|
|
39735
|
+
"reference",
|
|
39736
|
+
"derived",
|
|
39737
|
+
"sdk"
|
|
39738
|
+
],
|
|
39739
|
+
mounts: [
|
|
39740
|
+
{
|
|
39741
|
+
kind: "magazine",
|
|
39742
|
+
interfaces: [
|
|
39743
|
+
"mag_box"
|
|
39744
|
+
],
|
|
39745
|
+
sources: [
|
|
39746
|
+
"derived"
|
|
39747
|
+
]
|
|
39748
|
+
},
|
|
39749
|
+
{
|
|
39750
|
+
kind: "sling",
|
|
39751
|
+
interfaces: [
|
|
39752
|
+
"swivel"
|
|
39753
|
+
],
|
|
39754
|
+
sources: [
|
|
39755
|
+
"derived"
|
|
39756
|
+
]
|
|
39757
|
+
}
|
|
39758
|
+
],
|
|
39759
|
+
fitted: []
|
|
39760
|
+
},
|
|
39761
|
+
{
|
|
39762
|
+
id: "iwi_tavor",
|
|
39763
|
+
name: "IWI Tavor",
|
|
39764
|
+
maker: "IWI",
|
|
39765
|
+
category: "rifle",
|
|
39766
|
+
action: {
|
|
39767
|
+
kind: "firearm",
|
|
39768
|
+
caliber: "5_56x45",
|
|
39769
|
+
rpm: 850,
|
|
39770
|
+
modes: [
|
|
39771
|
+
"auto",
|
|
39772
|
+
"semi"
|
|
39773
|
+
],
|
|
39774
|
+
magazine: 30,
|
|
39775
|
+
reload: 2.8,
|
|
39776
|
+
muzzleSpeed: 910,
|
|
39777
|
+
spreadDeg: 0.6,
|
|
39778
|
+
bloomDeg: 0.25,
|
|
39779
|
+
recoveryDegPerS: 5,
|
|
39780
|
+
recoil: [
|
|
39781
|
+
0.98,
|
|
39782
|
+
0.59
|
|
39783
|
+
],
|
|
39784
|
+
range: 400,
|
|
39785
|
+
family: "magazine",
|
|
39786
|
+
timings: {
|
|
39787
|
+
draw: 0.35,
|
|
39788
|
+
reload: 2.8,
|
|
39789
|
+
reloadEmpty: 3.4,
|
|
39790
|
+
clear: 1.2
|
|
39791
|
+
}
|
|
39792
|
+
},
|
|
39793
|
+
mass: 3.27,
|
|
39794
|
+
dimensions: {
|
|
39795
|
+
length: 0.72,
|
|
39796
|
+
barrel: 0.46,
|
|
39797
|
+
emptyMass: 3.27
|
|
39798
|
+
},
|
|
39799
|
+
archetype: "rifle_5_56",
|
|
39800
|
+
variantOf: "steyr_aug_a1",
|
|
39801
|
+
sources: [
|
|
39802
|
+
"reference",
|
|
39803
|
+
"derived",
|
|
39804
|
+
"sdk"
|
|
39805
|
+
],
|
|
39806
|
+
mounts: [
|
|
39807
|
+
{
|
|
39808
|
+
kind: "rail_top",
|
|
39809
|
+
interfaces: [
|
|
39810
|
+
"picatinny"
|
|
39811
|
+
],
|
|
39812
|
+
sources: [
|
|
39813
|
+
"gallery"
|
|
39814
|
+
]
|
|
39815
|
+
},
|
|
39816
|
+
{
|
|
39817
|
+
kind: "magazine",
|
|
39818
|
+
interfaces: [
|
|
39819
|
+
"mag_box"
|
|
39820
|
+
],
|
|
39821
|
+
sources: [
|
|
39822
|
+
"derived"
|
|
39823
|
+
]
|
|
39824
|
+
},
|
|
39825
|
+
{
|
|
39826
|
+
kind: "sling",
|
|
39827
|
+
interfaces: [
|
|
39828
|
+
"swivel"
|
|
39829
|
+
],
|
|
39830
|
+
sources: [
|
|
39831
|
+
"derived"
|
|
39832
|
+
]
|
|
39833
|
+
}
|
|
39834
|
+
],
|
|
39835
|
+
fitted: []
|
|
39836
|
+
},
|
|
39837
|
+
{
|
|
39838
|
+
id: "l85a2",
|
|
39839
|
+
name: "L85A2",
|
|
39840
|
+
category: "rifle",
|
|
39841
|
+
action: {
|
|
39842
|
+
kind: "firearm",
|
|
39843
|
+
caliber: "5_56x45",
|
|
39844
|
+
rpm: 700,
|
|
39845
|
+
modes: [
|
|
39846
|
+
"auto",
|
|
39847
|
+
"semi"
|
|
39848
|
+
],
|
|
39849
|
+
magazine: 30,
|
|
39850
|
+
reload: 2.8,
|
|
39851
|
+
muzzleSpeed: 940,
|
|
39852
|
+
spreadDeg: 0.6,
|
|
39853
|
+
bloomDeg: 0.25,
|
|
39854
|
+
recoveryDegPerS: 5,
|
|
39855
|
+
recoil: [
|
|
39856
|
+
0.9,
|
|
39857
|
+
0.54
|
|
39858
|
+
],
|
|
39859
|
+
range: 400,
|
|
39860
|
+
family: "magazine",
|
|
39861
|
+
timings: {
|
|
39862
|
+
draw: 0.35,
|
|
39863
|
+
reload: 2.8,
|
|
39864
|
+
reloadEmpty: 3.4,
|
|
39865
|
+
clear: 1.2
|
|
39866
|
+
}
|
|
39867
|
+
},
|
|
39868
|
+
mass: 3.82,
|
|
39869
|
+
dimensions: {
|
|
39870
|
+
length: 0.785,
|
|
39871
|
+
barrel: 0.518,
|
|
39872
|
+
emptyMass: 3.82
|
|
39873
|
+
},
|
|
39874
|
+
archetype: "rifle_5_56",
|
|
39875
|
+
variantOf: "steyr_aug_a1",
|
|
39876
|
+
sources: [
|
|
39877
|
+
"reference",
|
|
39878
|
+
"derived",
|
|
39879
|
+
"sdk"
|
|
39880
|
+
],
|
|
39881
|
+
mounts: [
|
|
39882
|
+
{
|
|
39883
|
+
kind: "rail_top",
|
|
39884
|
+
interfaces: [
|
|
39885
|
+
"picatinny"
|
|
39886
|
+
],
|
|
39887
|
+
sources: [
|
|
39888
|
+
"gallery"
|
|
39889
|
+
]
|
|
39890
|
+
},
|
|
39891
|
+
{
|
|
39892
|
+
kind: "magazine",
|
|
39893
|
+
interfaces: [
|
|
39894
|
+
"mag_box"
|
|
39895
|
+
],
|
|
39896
|
+
sources: [
|
|
39897
|
+
"derived"
|
|
39898
|
+
]
|
|
39899
|
+
},
|
|
39900
|
+
{
|
|
39901
|
+
kind: "sling",
|
|
39902
|
+
interfaces: [
|
|
39903
|
+
"swivel"
|
|
39904
|
+
],
|
|
39905
|
+
sources: [
|
|
39906
|
+
"derived"
|
|
39907
|
+
]
|
|
39908
|
+
}
|
|
39909
|
+
],
|
|
39910
|
+
fitted: []
|
|
39911
|
+
},
|
|
39912
|
+
{
|
|
39913
|
+
id: "winchester_1887",
|
|
39914
|
+
name: "Winchester 1887",
|
|
39915
|
+
maker: "Winchester",
|
|
39916
|
+
category: "hunting",
|
|
39917
|
+
action: {
|
|
39918
|
+
kind: "firearm",
|
|
39919
|
+
caliber: "12ga_00buck",
|
|
39920
|
+
rpm: 109.09,
|
|
39921
|
+
modes: [
|
|
39922
|
+
"lever"
|
|
39923
|
+
],
|
|
39924
|
+
magazine: 5,
|
|
39925
|
+
reload: 0.6,
|
|
39926
|
+
perRound: true,
|
|
39927
|
+
spreadDeg: 0.25,
|
|
39928
|
+
bloomDeg: 0.3,
|
|
39929
|
+
recoveryDegPerS: 3,
|
|
39930
|
+
recoil: [
|
|
39931
|
+
2.33,
|
|
39932
|
+
0.39
|
|
39933
|
+
],
|
|
39934
|
+
range: 800,
|
|
39935
|
+
family: "lever",
|
|
39936
|
+
timings: {
|
|
39937
|
+
draw: 0.35,
|
|
39938
|
+
reload: 0.6,
|
|
39939
|
+
perRound: 0.6,
|
|
39940
|
+
cycle: 0.55,
|
|
39941
|
+
clear: 1.2
|
|
39942
|
+
}
|
|
39943
|
+
},
|
|
39944
|
+
mass: 3.6,
|
|
39945
|
+
dimensions: {
|
|
39946
|
+
length: 1.016,
|
|
39947
|
+
barrel: 0.508,
|
|
39948
|
+
emptyMass: 3.6
|
|
39949
|
+
},
|
|
39950
|
+
archetype: "rifle_bolt_308",
|
|
39951
|
+
variantOf: "winchester_1894",
|
|
39952
|
+
sources: [
|
|
39953
|
+
"reference",
|
|
39954
|
+
"derived",
|
|
39955
|
+
"sdk"
|
|
39956
|
+
],
|
|
39957
|
+
mounts: [
|
|
39958
|
+
{
|
|
39959
|
+
kind: "sling",
|
|
39960
|
+
interfaces: [
|
|
39961
|
+
"swivel"
|
|
39962
|
+
],
|
|
39963
|
+
sources: [
|
|
39964
|
+
"derived"
|
|
39965
|
+
]
|
|
39966
|
+
}
|
|
39967
|
+
],
|
|
39968
|
+
fitted: []
|
|
39969
|
+
},
|
|
39970
|
+
{
|
|
39971
|
+
id: "ppsh_41",
|
|
39972
|
+
name: "PPSh-41",
|
|
39973
|
+
category: "smg",
|
|
39974
|
+
action: {
|
|
39975
|
+
kind: "firearm",
|
|
39976
|
+
caliber: "7_62x25",
|
|
39977
|
+
rpm: 1250,
|
|
39978
|
+
modes: [
|
|
39979
|
+
"auto",
|
|
39980
|
+
"semi"
|
|
39981
|
+
],
|
|
39982
|
+
magazine: 71,
|
|
39983
|
+
magazineOptions: [
|
|
39984
|
+
35
|
|
39985
|
+
],
|
|
39986
|
+
reload: 2.9,
|
|
39987
|
+
muzzleSpeed: 488,
|
|
39988
|
+
spreadDeg: 1.5,
|
|
39989
|
+
bloomDeg: 0.35,
|
|
39990
|
+
recoveryDegPerS: 6,
|
|
39991
|
+
recoil: [
|
|
39992
|
+
0.7,
|
|
39993
|
+
0.39
|
|
39994
|
+
],
|
|
39995
|
+
range: 80,
|
|
39996
|
+
family: "magazine",
|
|
39997
|
+
timings: {
|
|
39998
|
+
draw: 0.35,
|
|
39999
|
+
reload: 2.9,
|
|
40000
|
+
clear: 1.2
|
|
40001
|
+
}
|
|
40002
|
+
},
|
|
40003
|
+
mass: 3.63,
|
|
40004
|
+
dimensions: {
|
|
40005
|
+
length: 0.843,
|
|
40006
|
+
barrel: 0.269,
|
|
40007
|
+
emptyMass: 3.63
|
|
40008
|
+
},
|
|
40009
|
+
archetype: "smg_9mm",
|
|
40010
|
+
variantOf: "thompson_m1a1",
|
|
40011
|
+
sources: [
|
|
40012
|
+
"reference",
|
|
40013
|
+
"derived",
|
|
40014
|
+
"sdk"
|
|
40015
|
+
],
|
|
40016
|
+
mounts: [
|
|
40017
|
+
{
|
|
40018
|
+
kind: "magazine",
|
|
40019
|
+
interfaces: [
|
|
40020
|
+
"mag_drum"
|
|
40021
|
+
],
|
|
40022
|
+
sources: [
|
|
40023
|
+
"gallery"
|
|
40024
|
+
]
|
|
40025
|
+
},
|
|
40026
|
+
{
|
|
40027
|
+
kind: "sling",
|
|
40028
|
+
interfaces: [
|
|
40029
|
+
"swivel"
|
|
40030
|
+
],
|
|
40031
|
+
sources: [
|
|
40032
|
+
"derived"
|
|
40033
|
+
]
|
|
40034
|
+
}
|
|
40035
|
+
],
|
|
40036
|
+
fitted: []
|
|
40037
|
+
},
|
|
40038
|
+
{
|
|
40039
|
+
id: "mauser_kar98k",
|
|
40040
|
+
name: "Mauser Kar98k",
|
|
40041
|
+
maker: "Mauser",
|
|
40042
|
+
category: "sniper",
|
|
40043
|
+
action: {
|
|
40044
|
+
kind: "firearm",
|
|
40045
|
+
caliber: "7_92x57",
|
|
40046
|
+
rpm: 73.17,
|
|
40047
|
+
modes: [
|
|
40048
|
+
"bolt"
|
|
40049
|
+
],
|
|
40050
|
+
magazine: 5,
|
|
40051
|
+
reload: 2.4,
|
|
40052
|
+
muzzleSpeed: 760,
|
|
40053
|
+
spreadDeg: 0.25,
|
|
40054
|
+
bloomDeg: 0.3,
|
|
40055
|
+
recoveryDegPerS: 3,
|
|
40056
|
+
recoil: [
|
|
40057
|
+
2.84,
|
|
40058
|
+
0.47
|
|
40059
|
+
],
|
|
40060
|
+
range: 800,
|
|
40061
|
+
family: "bolt",
|
|
40062
|
+
timings: {
|
|
40063
|
+
draw: 0.35,
|
|
40064
|
+
reload: 2.4,
|
|
40065
|
+
cycle: 0.82,
|
|
40066
|
+
clear: 1.2
|
|
40067
|
+
}
|
|
40068
|
+
},
|
|
40069
|
+
mass: 3.7,
|
|
40070
|
+
dimensions: {
|
|
40071
|
+
length: 1.11,
|
|
40072
|
+
barrel: 0.6,
|
|
40073
|
+
emptyMass: 3.7
|
|
40074
|
+
},
|
|
40075
|
+
archetype: "rifle_bolt_308",
|
|
40076
|
+
variantOf: "mosin_nagant_91_30",
|
|
40077
|
+
sources: [
|
|
40078
|
+
"reference",
|
|
40079
|
+
"derived",
|
|
40080
|
+
"sdk"
|
|
40081
|
+
],
|
|
40082
|
+
mounts: [
|
|
40083
|
+
{
|
|
40084
|
+
kind: "sling",
|
|
40085
|
+
interfaces: [
|
|
40086
|
+
"swivel"
|
|
40087
|
+
],
|
|
40088
|
+
sources: [
|
|
40089
|
+
"derived"
|
|
40090
|
+
]
|
|
40091
|
+
}
|
|
40092
|
+
],
|
|
40093
|
+
fitted: []
|
|
40094
|
+
},
|
|
40095
|
+
{
|
|
40096
|
+
id: "mg42",
|
|
40097
|
+
name: "MG42",
|
|
40098
|
+
category: "machine_gun",
|
|
40099
|
+
action: {
|
|
40100
|
+
kind: "firearm",
|
|
40101
|
+
caliber: "7_92x57",
|
|
40102
|
+
rpm: 1200,
|
|
40103
|
+
modes: [
|
|
40104
|
+
"auto"
|
|
40105
|
+
],
|
|
40106
|
+
magazine: 50,
|
|
40107
|
+
reload: 8.5,
|
|
40108
|
+
muzzleSpeed: 740,
|
|
40109
|
+
spreadDeg: 0.8,
|
|
40110
|
+
bloomDeg: 0.35,
|
|
40111
|
+
recoveryDegPerS: 4.5,
|
|
40112
|
+
recoil: [
|
|
40113
|
+
0.75,
|
|
40114
|
+
0.4
|
|
40115
|
+
],
|
|
40116
|
+
range: 350,
|
|
40117
|
+
family: "belt",
|
|
40118
|
+
timings: {
|
|
40119
|
+
draw: 0.35,
|
|
40120
|
+
reload: 8.5,
|
|
40121
|
+
clear: 1.2
|
|
40122
|
+
},
|
|
40123
|
+
faultFactor: 1.3
|
|
40124
|
+
},
|
|
40125
|
+
mass: 11.6,
|
|
40126
|
+
dimensions: {
|
|
40127
|
+
length: 1.22,
|
|
40128
|
+
barrel: 0.53,
|
|
40129
|
+
emptyMass: 11.6
|
|
40130
|
+
},
|
|
40131
|
+
archetype: "rifle_7_62x39",
|
|
40132
|
+
variantOf: "m60",
|
|
40133
|
+
sources: [
|
|
40134
|
+
"reference",
|
|
40135
|
+
"derived",
|
|
40136
|
+
"sdk"
|
|
40137
|
+
],
|
|
40138
|
+
mounts: [
|
|
40139
|
+
{
|
|
40140
|
+
kind: "sling",
|
|
40141
|
+
interfaces: [
|
|
40142
|
+
"swivel"
|
|
40143
|
+
],
|
|
40144
|
+
sources: [
|
|
40145
|
+
"derived"
|
|
40146
|
+
]
|
|
40147
|
+
}
|
|
40148
|
+
],
|
|
40149
|
+
fitted: []
|
|
40150
|
+
},
|
|
40151
|
+
{
|
|
40152
|
+
id: "vss_vintorez",
|
|
40153
|
+
name: "VSS Vintorez",
|
|
40154
|
+
category: "dmr",
|
|
40155
|
+
action: {
|
|
40156
|
+
kind: "firearm",
|
|
40157
|
+
caliber: "9x39",
|
|
40158
|
+
rpm: 850,
|
|
40159
|
+
modes: [
|
|
40160
|
+
"auto",
|
|
40161
|
+
"semi"
|
|
40162
|
+
],
|
|
40163
|
+
magazine: 10,
|
|
40164
|
+
magazineOptions: [
|
|
40165
|
+
20,
|
|
40166
|
+
30
|
|
40167
|
+
],
|
|
40168
|
+
reload: 2.6,
|
|
40169
|
+
muzzleSpeed: 295,
|
|
40170
|
+
noise: 75,
|
|
40171
|
+
spreadDeg: 0.8,
|
|
40172
|
+
bloomDeg: 0.35,
|
|
40173
|
+
recoveryDegPerS: 4.5,
|
|
40174
|
+
recoil: [
|
|
40175
|
+
0.73,
|
|
40176
|
+
0.39
|
|
40177
|
+
],
|
|
40178
|
+
range: 350,
|
|
40179
|
+
family: "magazine",
|
|
40180
|
+
timings: {
|
|
40181
|
+
draw: 0.35,
|
|
40182
|
+
reload: 2.6,
|
|
40183
|
+
reloadEmpty: 3.4,
|
|
40184
|
+
clear: 1.2
|
|
40185
|
+
}
|
|
40186
|
+
},
|
|
40187
|
+
mass: 2.6,
|
|
40188
|
+
dimensions: {
|
|
40189
|
+
length: 0.894,
|
|
40190
|
+
barrel: 0.2,
|
|
40191
|
+
emptyMass: 2.6
|
|
40192
|
+
},
|
|
40193
|
+
archetype: "rifle_7_62x39",
|
|
40194
|
+
variantOf: "svd_dragunov",
|
|
40195
|
+
sources: [
|
|
40196
|
+
"reference",
|
|
40197
|
+
"derived",
|
|
40198
|
+
"sdk"
|
|
40199
|
+
],
|
|
40200
|
+
mounts: [
|
|
40201
|
+
{
|
|
40202
|
+
kind: "muzzle",
|
|
40203
|
+
interfaces: [
|
|
40204
|
+
"integral"
|
|
40205
|
+
],
|
|
40206
|
+
sources: [
|
|
40207
|
+
"reference"
|
|
40208
|
+
]
|
|
40209
|
+
},
|
|
40210
|
+
{
|
|
40211
|
+
kind: "sling",
|
|
40212
|
+
interfaces: [
|
|
40213
|
+
"swivel"
|
|
40214
|
+
],
|
|
40215
|
+
sources: [
|
|
40216
|
+
"derived"
|
|
40217
|
+
]
|
|
40218
|
+
}
|
|
40219
|
+
],
|
|
40220
|
+
fitted: [
|
|
40221
|
+
{
|
|
40222
|
+
id: "integral_suppressor",
|
|
40223
|
+
mount: "muzzle",
|
|
40224
|
+
integral: true
|
|
40225
|
+
}
|
|
40226
|
+
]
|
|
38219
40227
|
}
|
|
38220
40228
|
];
|
|
38221
40229
|
var launchers_default = [
|
|
@@ -38552,6 +40560,46 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
38552
40560
|
"reference",
|
|
38553
40561
|
"derived"
|
|
38554
40562
|
]
|
|
40563
|
+
},
|
|
40564
|
+
{
|
|
40565
|
+
id: "m32_mgl",
|
|
40566
|
+
name: "M32 multiple grenade launcher (Milkor MGL)",
|
|
40567
|
+
category: "launcher",
|
|
40568
|
+
action: {
|
|
40569
|
+
kind: "launcher",
|
|
40570
|
+
projectile: "m79_40mm",
|
|
40571
|
+
magazine: 6,
|
|
40572
|
+
reload: 9.2,
|
|
40573
|
+
timings: {
|
|
40574
|
+
draw: 0.35,
|
|
40575
|
+
reload: 9.2,
|
|
40576
|
+
clear: 1.2
|
|
40577
|
+
},
|
|
40578
|
+
family: "revolver",
|
|
40579
|
+
speed: 75,
|
|
40580
|
+
minArmDistance: 10,
|
|
40581
|
+
backblast: 0,
|
|
40582
|
+
recoil: [
|
|
40583
|
+
2.25,
|
|
40584
|
+
0.6
|
|
40585
|
+
]
|
|
40586
|
+
},
|
|
40587
|
+
mass: 6.68,
|
|
40588
|
+
dimensions: {
|
|
40589
|
+
length: 0.778,
|
|
40590
|
+
lengthCollapsed: 0.565,
|
|
40591
|
+
barrel: 0.3,
|
|
40592
|
+
emptyMass: 5.3
|
|
40593
|
+
},
|
|
40594
|
+
archetype: "shotgun_double_12",
|
|
40595
|
+
stock: {
|
|
40596
|
+
kind: "fold",
|
|
40597
|
+
foldAxis: "over"
|
|
40598
|
+
},
|
|
40599
|
+
sources: [
|
|
40600
|
+
"reference",
|
|
40601
|
+
"derived"
|
|
40602
|
+
]
|
|
38555
40603
|
}
|
|
38556
40604
|
];
|
|
38557
40605
|
var INSTALL_RANK = { field: 0, tool: 1, bench: 2 };
|
|
@@ -38787,6 +40835,69 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
38787
40835
|
tags: [
|
|
38788
40836
|
"silent"
|
|
38789
40837
|
]
|
|
40838
|
+
},
|
|
40839
|
+
{
|
|
40840
|
+
id: "kabar",
|
|
40841
|
+
name: "KA-BAR combat knife",
|
|
40842
|
+
category: "melee_blade",
|
|
40843
|
+
action: {
|
|
40844
|
+
kind: "melee",
|
|
40845
|
+
damage: "slash",
|
|
40846
|
+
energy: 48,
|
|
40847
|
+
radius: 0.03,
|
|
40848
|
+
reach: 0.55,
|
|
40849
|
+
rpm: 100,
|
|
40850
|
+
noise: 1
|
|
40851
|
+
},
|
|
40852
|
+
mass: 0.32,
|
|
40853
|
+
sources: [
|
|
40854
|
+
"reference",
|
|
40855
|
+
"derived"
|
|
40856
|
+
]
|
|
40857
|
+
},
|
|
40858
|
+
{
|
|
40859
|
+
id: "katana",
|
|
40860
|
+
name: "Katana",
|
|
40861
|
+
category: "melee_blade",
|
|
40862
|
+
action: {
|
|
40863
|
+
kind: "melee",
|
|
40864
|
+
damage: "slash",
|
|
40865
|
+
energy: 230,
|
|
40866
|
+
radius: 0.04,
|
|
40867
|
+
reach: 1.1,
|
|
40868
|
+
rpm: 70,
|
|
40869
|
+
noise: 5
|
|
40870
|
+
},
|
|
40871
|
+
mass: 1.1,
|
|
40872
|
+
tags: [
|
|
40873
|
+
"two_handed"
|
|
40874
|
+
],
|
|
40875
|
+
sources: [
|
|
40876
|
+
"reference",
|
|
40877
|
+
"derived"
|
|
40878
|
+
]
|
|
40879
|
+
},
|
|
40880
|
+
{
|
|
40881
|
+
id: "brass_knuckles",
|
|
40882
|
+
name: "Brass knuckles",
|
|
40883
|
+
category: "melee_blunt",
|
|
40884
|
+
action: {
|
|
40885
|
+
kind: "melee",
|
|
40886
|
+
damage: "blunt",
|
|
40887
|
+
energy: 32,
|
|
40888
|
+
radius: 0.04,
|
|
40889
|
+
reach: 0.6,
|
|
40890
|
+
rpm: 120,
|
|
40891
|
+
noise: 5
|
|
40892
|
+
},
|
|
40893
|
+
mass: 0.15,
|
|
40894
|
+
tags: [
|
|
40895
|
+
"fists"
|
|
40896
|
+
],
|
|
40897
|
+
sources: [
|
|
40898
|
+
"reference",
|
|
40899
|
+
"derived"
|
|
40900
|
+
]
|
|
38790
40901
|
}
|
|
38791
40902
|
];
|
|
38792
40903
|
var findDuplicateIds = (kind, entries2) => {
|
|
@@ -38987,7 +41098,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
38987
41098
|
improvised_spear: "Improvised spear",
|
|
38988
41099
|
chainsaw: "Chainsaw",
|
|
38989
41100
|
recurve_bow: "Recurve bow",
|
|
38990
|
-
m134: "M134 Minigun"
|
|
41101
|
+
m134: "M134 Minigun",
|
|
41102
|
+
semtex: "Semtex charge",
|
|
41103
|
+
smoke_grenade: "M18 smoke grenade",
|
|
41104
|
+
kabar: "KA-BAR combat knife",
|
|
41105
|
+
katana: "Katana",
|
|
41106
|
+
brass_knuckles: "Brass knuckles"
|
|
38991
41107
|
},
|
|
38992
41108
|
modes: modesEn,
|
|
38993
41109
|
categories: categoriesEn,
|
|
@@ -39000,6 +41116,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39000
41116
|
c4: "C4 charge",
|
|
39001
41117
|
claymore: "Claymore mine",
|
|
39002
41118
|
flashbang: "Flashbang",
|
|
41119
|
+
semtex: "Semtex charge",
|
|
41120
|
+
smoke_grenade: "M18 colored smoke grenade",
|
|
39003
41121
|
m79_40mm: "40 mm grenade",
|
|
39004
41122
|
m72_66mm: "66 mm HEAT rocket",
|
|
39005
41123
|
at4_84mm: "84 mm HEAT rocket",
|
|
@@ -39081,7 +41199,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39081
41199
|
improvised_spear: "lan\xE7a improvisada",
|
|
39082
41200
|
chainsaw: "motosserra",
|
|
39083
41201
|
recurve_bow: "arco recurvo",
|
|
39084
|
-
m134: "Minigun M134"
|
|
41202
|
+
m134: "Minigun M134",
|
|
41203
|
+
semtex: "Semtex com detonador",
|
|
41204
|
+
smoke_grenade: "granada de fuma\xE7a M18",
|
|
41205
|
+
kabar: "faca KA-BAR",
|
|
41206
|
+
katana: "espada katana",
|
|
41207
|
+
brass_knuckles: "soco ingl\xEAs"
|
|
39085
41208
|
},
|
|
39086
41209
|
modes: { single: "tiro \xFAnico", semi: "semiautom\xE1tico", burst: "rajada", auto: "autom\xE1tico", pump: "repeti\xE7\xE3o", bolt: "ferrolho", lever: "alavanca", break: "basculante", volley: "salva" },
|
|
39087
41210
|
categories: {
|
|
@@ -39117,6 +41240,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39117
41240
|
c4: "C4 com detonador",
|
|
39118
41241
|
claymore: "Mina Claymore",
|
|
39119
41242
|
flashbang: "Granada de luz",
|
|
41243
|
+
semtex: "Semtex com detonador",
|
|
41244
|
+
smoke_grenade: "Granada de fuma\xE7a M18",
|
|
39120
41245
|
m79_40mm: "Granada de 40 mm",
|
|
39121
41246
|
m72_66mm: "Foguete HEAT de 66 mm",
|
|
39122
41247
|
at4_84mm: "Foguete HEAT de 84 mm",
|
|
@@ -39228,6 +41353,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39228
41353
|
// the iron sights close the view in a little: more for a long gun, whose sights sit farther from the eye, than for a handgun
|
|
39229
41354
|
ironSightZoom: { pistol: 1.1, revolver: 1.1, smg: 1.2, carbine: 1.25, shotgun: 1.15, rifle: 1.3, hunting: 1.35, dmr: 1.4, sniper: 1.4, machine_gun: 1.2, launcher: 1.15, bow: 1.15, crossbow: 1.25 }
|
|
39230
41355
|
},
|
|
41356
|
+
// a weapon in each hand: the handguns, the machine pistols short and light enough for one hand, and the one-hand blades; one hand and no sight picture open the cone and lift the muzzle more
|
|
41357
|
+
dualWield: {
|
|
41358
|
+
categories: ["pistol", "revolver"],
|
|
41359
|
+
weapons: ["mini_uzi", "uzi", "mac_10", "vz61", "hk_mp7a1", "knife", "tactical_knife", "machete", "hatchet"],
|
|
41360
|
+
exclude: [],
|
|
41361
|
+
spread: 1.8,
|
|
41362
|
+
recoil: 1.35,
|
|
41363
|
+
// tucking a pistol in the belt and drawing it back is a short motion of one hand
|
|
41364
|
+
stowSeconds: 0.45
|
|
41365
|
+
},
|
|
39231
41366
|
// Arsenal §08/§11: coronha retrátil saca mais rápido, mais recuo; os números são padrões aprovados pelo dono (2026-10-24)
|
|
39232
41367
|
stock: {
|
|
39233
41368
|
seconds: 0.6,
|
|
@@ -39390,7 +41525,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39390
41525
|
destructionLevel: def.destructionLevel,
|
|
39391
41526
|
...def.cone ? { cone: def.cone } : {},
|
|
39392
41527
|
...def.stunSeconds !== void 0 ? { stunSeconds: def.stunSeconds } : {},
|
|
39393
|
-
...def.fire ? { fire: def.fire } : {}
|
|
41528
|
+
...def.fire ? { fire: def.fire } : {},
|
|
41529
|
+
...def.smoke ? { smoke: def.smoke } : {}
|
|
39394
41530
|
};
|
|
39395
41531
|
return { type: "detonate", at: at3, detonation };
|
|
39396
41532
|
}
|
|
@@ -39650,6 +41786,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39650
41786
|
return roll.ammoQuality.faultChance > 0 && roll.random() < roll.ammoQuality.faultChance ? "misfire" : null;
|
|
39651
41787
|
};
|
|
39652
41788
|
var isDudWithoutJam = (action, fault) => fault === "misfire" && action.kind === "firearm" && MISFIRE_ONLY.has(action.family);
|
|
41789
|
+
var ONE_HAND = { spread: 1, recoil: 1 };
|
|
39653
41790
|
var activeParts = (fitted, catalogue) => fitted.flatMap((part) => {
|
|
39654
41791
|
const def = catalogue.get(part.id);
|
|
39655
41792
|
const isOn = def?.kind !== "light" && def?.kind !== "laser" ? true : part.on === true;
|
|
@@ -39716,12 +41853,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
39716
41853
|
const read = (name, base) => stat(name, base, sources, active);
|
|
39717
41854
|
const mass = read("mass", def.mass + parts.reduce((total, part) => total + part.mass, 0) - factory.reduce((total, part) => total + part.mass, 0));
|
|
39718
41855
|
const hipFactor = context.aiming ? 1 : 1 + (context.rules.aim.hipSpread - 1) * read("hipSpreadExcess", 1);
|
|
41856
|
+
const dual = context.dualWield && context.rules.dualWield ? context.rules.dualWield : ONE_HAND;
|
|
39719
41857
|
let action = def.action;
|
|
39720
41858
|
if (firearm) {
|
|
39721
41859
|
const baseNoise = firearm.noise ?? context.caliber?.noise;
|
|
39722
41860
|
const effects = {
|
|
39723
|
-
spread: read("spread", firearm.spreadDeg) * hipFactor,
|
|
39724
|
-
recoil: [read("recoilVertical", firearm.recoil[0]), read("recoilHorizontal", firearm.recoil[1])],
|
|
41861
|
+
spread: read("spread", firearm.spreadDeg) * hipFactor * dual.spread,
|
|
41862
|
+
recoil: [read("recoilVertical", firearm.recoil[0]) * dual.recoil, read("recoilHorizontal", firearm.recoil[1]) * dual.recoil],
|
|
39725
41863
|
range: read("range", firearm.range),
|
|
39726
41864
|
noise: baseNoise === void 0 ? void 0 : read("noise", baseNoise),
|
|
39727
41865
|
speed,
|
|
@@ -40090,6 +42228,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
40090
42228
|
__publicField4(this, "stockState", "extended");
|
|
40091
42229
|
__publicField4(this, "aiming", false);
|
|
40092
42230
|
__publicField4(this, "supportedStance", false);
|
|
42231
|
+
__publicField4(this, "dualStance", false);
|
|
40093
42232
|
__publicField4(this, "reloadCount", 0);
|
|
40094
42233
|
__publicField4(this, "catalogue");
|
|
40095
42234
|
__publicField4(this, "isCompatible");
|
|
@@ -40137,7 +42276,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
40137
42276
|
}
|
|
40138
42277
|
resolve() {
|
|
40139
42278
|
const supported = this.supportedStance;
|
|
40140
|
-
return resolveLoadout(this.def, this.fitted, this.catalogue, { aiming: this.aiming, supported, stock: this.stockState, caliber: this.caliber, rules: this.rules });
|
|
42279
|
+
return resolveLoadout(this.def, this.fitted, this.catalogue, { aiming: this.aiming, supported, stock: this.stockState, caliber: this.caliber, rules: this.rules, dualWield: this.dualStance });
|
|
40141
42280
|
}
|
|
40142
42281
|
/** Takes the numbers of the accessories on the weapon: the action, the capacity (what no longer fits goes back to the reserve) and the draw time. */
|
|
40143
42282
|
applyLoad() {
|
|
@@ -40310,9 +42449,12 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
40310
42449
|
setStance(stance) {
|
|
40311
42450
|
const aiming = stance.aiming ?? this.aiming;
|
|
40312
42451
|
const supported = stance.supported ?? this.supportedStance;
|
|
40313
|
-
|
|
40314
|
-
|
|
42452
|
+
const dual = stance.dualWield ?? this.dualStance;
|
|
42453
|
+
const aimed = dual ? false : aiming;
|
|
42454
|
+
if (aimed === this.aiming && supported === this.supportedStance && dual === this.dualStance) return;
|
|
42455
|
+
this.aiming = aimed;
|
|
40315
42456
|
this.supportedStance = supported;
|
|
42457
|
+
this.dualStance = dual;
|
|
40316
42458
|
this.applyLoad();
|
|
40317
42459
|
}
|
|
40318
42460
|
snapshot() {
|
|
@@ -40840,6 +42982,15 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
|
|
|
40840
42982
|
const from = [use.origin[0], use.origin[1] + wire.height, use.origin[2]];
|
|
40841
42983
|
return { from, to: [from[0] + dx * wire.length, from[1], from[2] + dz * wire.length] };
|
|
40842
42984
|
};
|
|
42985
|
+
function canDualWield(arsenal, weaponId) {
|
|
42986
|
+
const def = arsenal.weapons.get(weaponId);
|
|
42987
|
+
if (!def) return false;
|
|
42988
|
+
return isDualWieldable(arsenal.rules.dualWield, weaponId, def.category);
|
|
42989
|
+
}
|
|
42990
|
+
var isDualWieldable = (rule, weaponId, category) => {
|
|
42991
|
+
if (rule.exclude.includes(weaponId)) return false;
|
|
42992
|
+
return rule.weapons.includes(weaponId) || rule.categories.includes(category);
|
|
42993
|
+
};
|
|
40843
42994
|
var isGenericKind = (kind, category) => kind === "firearm" && category !== "bow" && category !== "crossbow" || kind === "flame";
|
|
40844
42995
|
function genericWeapons(arsenal) {
|
|
40845
42996
|
const archetypes = new Set(ARCHETYPE_IDS);
|
|
@@ -47094,6 +49245,13 @@ void main(){
|
|
|
47094
49245
|
};
|
|
47095
49246
|
|
|
47096
49247
|
// src/explosives.ts
|
|
49248
|
+
var SMOKE_PUFFS_PER_SECOND = 36;
|
|
49249
|
+
var SMOKE_PUFF_LIFE = 3.2;
|
|
49250
|
+
var SMOKE_PUFF_SPEED = 1.7;
|
|
49251
|
+
var SMOKE_PUFF_SIZE = 5;
|
|
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";
|
|
47097
49255
|
var CONE_BLAST_RADIUS = 0.5;
|
|
47098
49256
|
var summarizeBlast = (results) => {
|
|
47099
49257
|
const reports = results.flatMap((r) => r.blast ? [r.blast] : []);
|
|
@@ -47109,13 +49267,35 @@ void main(){
|
|
|
47109
49267
|
function createExplosives(context) {
|
|
47110
49268
|
const { THREE, scene: scene2, camera: camera2, world, renderer, arsenal, translate, format, action } = context;
|
|
47111
49269
|
const projectiles = [];
|
|
49270
|
+
const smokeSources = [];
|
|
49271
|
+
const random = context.random ?? Math.random;
|
|
47112
49272
|
const meshes = [];
|
|
47113
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
|
+
};
|
|
47114
49291
|
const drop = (objects) => {
|
|
47115
49292
|
for (const object of objects) object.removeFromParent();
|
|
47116
49293
|
releaseObject3D(objects, { keep: scene2 });
|
|
47117
49294
|
};
|
|
47118
49295
|
const clear = () => {
|
|
49296
|
+
smokeSources.length = 0;
|
|
49297
|
+
smokeHiss?.stop();
|
|
49298
|
+
smokeHiss = null;
|
|
47119
49299
|
projectiles.length = 0;
|
|
47120
49300
|
drop(meshes);
|
|
47121
49301
|
meshes.length = 0;
|
|
@@ -47169,6 +49349,7 @@ void main(){
|
|
|
47169
49349
|
age: 0,
|
|
47170
49350
|
...use.explosive !== void 0 ? { body: createExplosiveBody(use, arsenal.explosives, arsenal.rules.explosives), aligned: isRocket } : {}
|
|
47171
49351
|
});
|
|
49352
|
+
if (!isRocket && !isMolotov && use.explosive !== void 0) context.audio?.play(GRENADE_PIN_SOUND, { position: use.origin });
|
|
47172
49353
|
const key2 = isRocket ? "act.launch" : isMolotov ? "act.throw.molotov" : "act.throw.grenade";
|
|
47173
49354
|
action(isRocket ? "launch_projectile" : "throw_projectile", translate(key2, { weapon: context.toolName(), fuse: format(use.fuse, 1), arm: format(use.armDistance ?? 0, 0) }), {
|
|
47174
49355
|
data: { weapon: context.toolId(), fuse: use.fuse, explosive: use.explosive }
|
|
@@ -47282,6 +49463,12 @@ void main(){
|
|
|
47282
49463
|
const detonate = (projectile, detonation) => {
|
|
47283
49464
|
const weaponName = context.toolName(projectile.weapon);
|
|
47284
49465
|
const reach = Math.max(detonation.injuryRadius, detonation.lethalRadius);
|
|
49466
|
+
if (detonation.smoke) {
|
|
49467
|
+
const [shortest, longest] = detonation.smoke.seconds;
|
|
49468
|
+
smokeSources.push({ point: detonation.point, left: shortest + (longest - shortest) * random(), owed: 0, color: detonation.smoke.color });
|
|
49469
|
+
action("smoke", translate("act.smoke", { weapon: weaponName, seconds: format(smokeSources[smokeSources.length - 1].left, 0) }), { data: { weapon: projectile.weapon, explosive: detonation.explosive, point: detonation.point } });
|
|
49470
|
+
return;
|
|
49471
|
+
}
|
|
47285
49472
|
const results = detonation.energy <= 0 || detonation.cone ? [] : world.explode(detonation.point, detonation.energy, reach, "player", context.blast());
|
|
47286
49473
|
const reached = detonation.energy <= 0 ? 0 : detonation.cone ? coneBlast(detonation) : results.length;
|
|
47287
49474
|
if (!detonation.cone) told(results);
|
|
@@ -47403,7 +49590,23 @@ void main(){
|
|
|
47403
49590
|
action("tripwire", translate("act.tripwire", { weapon: context.toolName(projectile.weapon), who: crosser }), { data: { weapon: projectile.weapon, body: crosser } });
|
|
47404
49591
|
detonate(projectile, event.detonation);
|
|
47405
49592
|
};
|
|
49593
|
+
const burnSmoke = (dt) => {
|
|
49594
|
+
for (let index = smokeSources.length - 1; index >= 0; index--) {
|
|
49595
|
+
const source = smokeSources[index];
|
|
49596
|
+
source.left -= dt;
|
|
49597
|
+
source.owed += SMOKE_PUFFS_PER_SECOND * dt;
|
|
49598
|
+
for (; source.owed >= 1; source.owed -= 1) {
|
|
49599
|
+
const angle = random() * Math.PI * 2;
|
|
49600
|
+
const speed = SMOKE_PUFF_SPEED * (0.4 + random());
|
|
49601
|
+
const velocity = [Math.cos(angle) * speed, 0.5 + random() * 0.8, Math.sin(angle) * speed];
|
|
49602
|
+
context.effects.spawn("smoke", [source.point[0], source.point[1] + 0.08, source.point[2]], velocity, { color: source.color, size: SMOKE_PUFF_SIZE, life: SMOKE_PUFF_LIFE, alpha: SMOKE_PUFF_ALPHA });
|
|
49603
|
+
}
|
|
49604
|
+
if (source.left <= 0) smokeSources.splice(index, 1);
|
|
49605
|
+
}
|
|
49606
|
+
hissSmoke();
|
|
49607
|
+
};
|
|
47406
49608
|
const update = (dt) => {
|
|
49609
|
+
burnSmoke(dt);
|
|
47407
49610
|
for (let index = projectiles.length - 1; index >= 0; index--) {
|
|
47408
49611
|
const projectile = projectiles[index];
|
|
47409
49612
|
if (!projectile) continue;
|
|
@@ -47424,7 +49627,7 @@ void main(){
|
|
|
47424
49627
|
}
|
|
47425
49628
|
}
|
|
47426
49629
|
};
|
|
47427
|
-
return { throwUse, placeUse, backblastUse, detonateCharges, blastAt, update, clear };
|
|
49630
|
+
return { throwUse, placeUse, backblastUse, detonateCharges, blastAt, update, clear, smokeLeft: () => smokeSources.map((source) => source.left) };
|
|
47428
49631
|
}
|
|
47429
49632
|
|
|
47430
49633
|
// src/test-table.ts
|
|
@@ -47638,6 +49841,65 @@ void main(){
|
|
|
47638
49841
|
}
|
|
47639
49842
|
};
|
|
47640
49843
|
|
|
49844
|
+
// src/dual-reload.ts
|
|
49845
|
+
var otherHand = (hand) => hand === "R" ? "L" : "R";
|
|
49846
|
+
function createDualReload(host, stowSeconds) {
|
|
49847
|
+
let queue = [];
|
|
49848
|
+
let current = null;
|
|
49849
|
+
const next = () => {
|
|
49850
|
+
let hand = queue.shift();
|
|
49851
|
+
while (hand && !host.needs(hand)) hand = queue.shift();
|
|
49852
|
+
if (!hand) {
|
|
49853
|
+
current = null;
|
|
49854
|
+
return;
|
|
49855
|
+
}
|
|
49856
|
+
const seconds = stowSeconds();
|
|
49857
|
+
current = { hand, phase: "stow", left: seconds };
|
|
49858
|
+
host.stow(otherHand(hand), true, seconds);
|
|
49859
|
+
};
|
|
49860
|
+
const draw2 = (hand) => {
|
|
49861
|
+
const seconds = stowSeconds();
|
|
49862
|
+
current = { hand, phase: "draw", left: seconds };
|
|
49863
|
+
host.stow(otherHand(hand), false, seconds);
|
|
49864
|
+
};
|
|
49865
|
+
return {
|
|
49866
|
+
start(order) {
|
|
49867
|
+
if (current) return;
|
|
49868
|
+
queue = [...order];
|
|
49869
|
+
next();
|
|
49870
|
+
},
|
|
49871
|
+
step(dt) {
|
|
49872
|
+
if (!current) return;
|
|
49873
|
+
if (current.phase === "reload") {
|
|
49874
|
+
if (!host.reloading(current.hand)) draw2(current.hand);
|
|
49875
|
+
return;
|
|
49876
|
+
}
|
|
49877
|
+
current.left -= dt;
|
|
49878
|
+
if (current.left > 0) return;
|
|
49879
|
+
if (current.phase === "draw") {
|
|
49880
|
+
next();
|
|
49881
|
+
return;
|
|
49882
|
+
}
|
|
49883
|
+
current.phase = "reload";
|
|
49884
|
+
if (!host.startReload(current.hand)) draw2(current.hand);
|
|
49885
|
+
},
|
|
49886
|
+
get busy() {
|
|
49887
|
+
return current !== null;
|
|
49888
|
+
},
|
|
49889
|
+
get current() {
|
|
49890
|
+
return current ? { hand: current.hand, phase: current.phase } : null;
|
|
49891
|
+
},
|
|
49892
|
+
stowed(hand) {
|
|
49893
|
+
return current !== null && current.phase !== "draw" && otherHand(current.hand) === hand;
|
|
49894
|
+
},
|
|
49895
|
+
cancel() {
|
|
49896
|
+
if (current && current.phase !== "draw") host.stow(otherHand(current.hand), false, 0);
|
|
49897
|
+
current = null;
|
|
49898
|
+
queue = [];
|
|
49899
|
+
}
|
|
49900
|
+
};
|
|
49901
|
+
}
|
|
49902
|
+
|
|
47641
49903
|
// src/loading-view.ts
|
|
47642
49904
|
var LOADING_CLASS = "tr-loading";
|
|
47643
49905
|
var LOADING_CSS = [
|
|
@@ -49644,6 +51906,10 @@ void main(){
|
|
|
49644
51906
|
o.hooks.primaryDown();
|
|
49645
51907
|
};
|
|
49646
51908
|
const onMouseUp = (e) => {
|
|
51909
|
+
if (e.button === 2) {
|
|
51910
|
+
o.hooks.secondaryUp?.();
|
|
51911
|
+
return;
|
|
51912
|
+
}
|
|
49647
51913
|
if (e.button !== 0) return;
|
|
49648
51914
|
if (holdTimer) clearTimeout(holdTimer);
|
|
49649
51915
|
if (dragging) {
|
|
@@ -53517,7 +55783,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
53517
55783
|
}
|
|
53518
55784
|
|
|
53519
55785
|
// src/avatar.ts
|
|
53520
|
-
var DEFAULT_STRIKE_CLIPS = { hand: "punch_right", fists: "punch_right", shove: "shove_two_hands", kick: "front_kick" };
|
|
55786
|
+
var DEFAULT_STRIKE_CLIPS = { hand: "punch_right", fists: "punch_right", brass_knuckles: "punch_right", shove: "shove_two_hands", kick: "front_kick" };
|
|
53521
55787
|
var DEFAULT_HAND_STANCES = { fists: "fists" };
|
|
53522
55788
|
var THIRD_PERSON_DEFAULTS = {
|
|
53523
55789
|
back: 3.8,
|
|
@@ -53690,6 +55956,12 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
53690
55956
|
"act.clear": "Cleared the room",
|
|
53691
55957
|
"act.reset": "Repaired every object",
|
|
53692
55958
|
"act.tool": "Equipped {weapon}",
|
|
55959
|
+
"act.tool_left": "{weapon} in the left hand: left button fires the right one, right button the left one",
|
|
55960
|
+
"act.tool_left_off": "Left hand free: one weapon again",
|
|
55961
|
+
"act.stow": "{weapon} tucked against the body: the {hand} hand is free for the other magazine",
|
|
55962
|
+
"act.unstow": "{weapon} back in the {hand} hand",
|
|
55963
|
+
"hud.no_dual": "{weapon} does not go one in each hand",
|
|
55964
|
+
"hud.left": "L",
|
|
53693
55965
|
"act.shoot.hit": "{weapon}: shot, {energy} J, hit {where} at {dist} m",
|
|
53694
55966
|
"act.shoot.miss": "{weapon}: shot, {energy} J, hit nothing",
|
|
53695
55967
|
"act.shoot.pellets": "{weapon}: shot, {n} projectiles of up to {energy} J, {hits} hit {where} at {dist} m",
|
|
@@ -53719,6 +55991,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
53719
55991
|
"hud.no_charges": "No charge set down waits for the remote detonator",
|
|
53720
55992
|
"blast.zone.lethal": "lethal",
|
|
53721
55993
|
"blast.zone.injury": "injury",
|
|
55994
|
+
"act.smoke": "{weapon}: smoke, burning for {seconds} s",
|
|
53722
55995
|
"act.explode": "{weapon}: exploded, {energy} J within {radius} m, {n} objects in range",
|
|
53723
55996
|
"act.molotov_fire": "{weapon}: fire on the ground, radius {radius} m for {seconds} s",
|
|
53724
55997
|
"act.tool.none": "{weapon}: nothing in reach",
|
|
@@ -53749,6 +56022,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
53749
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)",
|
|
53750
56023
|
"act.player_stunned": "The blast stunned the player for {stun} s, {distance} m from it: dizzy, ears ringing",
|
|
53751
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",
|
|
53752
56026
|
"act.shoved": "{name} stands free: the shots moved it",
|
|
53753
56027
|
"act.impact": "{name} hit {where} at {speed} m/s: {energy} J of blunt damage",
|
|
53754
56028
|
"act.use_item": "{name}: {use}",
|
|
@@ -53857,6 +56131,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
53857
56131
|
"ui.clear_log": "Clear",
|
|
53858
56132
|
"ui.catalog": "Catalogue",
|
|
53859
56133
|
"ui.tool_menu": "Weapons",
|
|
56134
|
+
"ui.tool_menu.dual": "Shift+click: one in each hand",
|
|
53860
56135
|
"ui.tool_menu.search": "Search weapons and tools",
|
|
53861
56136
|
"ui.tool_menu.close": "Close",
|
|
53862
56137
|
"ui.tool_menu.results": "{n} found",
|
|
@@ -54060,6 +56335,12 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54060
56335
|
"act.clear": "Limpou a sala",
|
|
54061
56336
|
"act.reset": "Consertou todos os objetos",
|
|
54062
56337
|
"act.tool": "Equipou {weapon}",
|
|
56338
|
+
"act.tool_left": "{weapon} na m\xE3o esquerda: bot\xE3o esquerdo dispara a da direita, bot\xE3o direito a da esquerda",
|
|
56339
|
+
"act.tool_left_off": "M\xE3o esquerda livre: uma arma de novo",
|
|
56340
|
+
"act.stow": "{weapon} encostada no corpo: a m\xE3o {hand} fica livre para o outro carregador",
|
|
56341
|
+
"act.unstow": "{weapon} de volta na m\xE3o {hand}",
|
|
56342
|
+
"hud.no_dual": "{weapon} n\xE3o vai uma em cada m\xE3o",
|
|
56343
|
+
"hud.left": "E",
|
|
54063
56344
|
"act.shoot.hit": "{weapon}: disparo, {energy} J, acertou {where} a {dist} m",
|
|
54064
56345
|
"act.shoot.miss": "{weapon}: disparo, {energy} J, n\xE3o acertou nada",
|
|
54065
56346
|
"act.shoot.pellets": "{weapon}: disparo, {n} proj\xE9teis de at\xE9 {energy} J, {hits} acertaram {where} a {dist} m",
|
|
@@ -54089,6 +56370,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54089
56370
|
"hud.no_charges": "Nenhuma carga posta espera o detonador",
|
|
54090
56371
|
"blast.zone.lethal": "letal",
|
|
54091
56372
|
"blast.zone.injury": "de ferimento",
|
|
56373
|
+
"act.smoke": "{weapon}: fuma\xE7a, queima por {seconds} s",
|
|
54092
56374
|
"act.explode": "{weapon}: explodiu, {energy} J em {radius} m, {n} objetos ao alcance",
|
|
54093
56375
|
"act.molotov_fire": "{weapon}: fogo no ch\xE3o, raio de {radius} m por {seconds} s",
|
|
54094
56376
|
"act.tool.none": "{weapon}: nada ao alcance",
|
|
@@ -54119,6 +56401,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54119
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)",
|
|
54120
56402
|
"act.player_stunned": "A explos\xE3o atordoou o jogador por {stun} s, a {distance} m dela: tonto, com os ouvidos zumbindo",
|
|
54121
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",
|
|
54122
56405
|
"act.shoved": "{name} estava solto no ch\xE3o: os tiros o moveram",
|
|
54123
56406
|
"act.impact": "{name} bateu em {where} a {speed} m/s: {energy} J de dano de impacto",
|
|
54124
56407
|
"act.use_item": "{name}: {use}",
|
|
@@ -54227,6 +56510,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54227
56510
|
"ui.clear_log": "Limpar",
|
|
54228
56511
|
"ui.catalog": "Cat\xE1logo",
|
|
54229
56512
|
"ui.tool_menu": "Armas",
|
|
56513
|
+
"ui.tool_menu.dual": "Shift+clique: uma em cada m\xE3o",
|
|
54230
56514
|
"ui.tool_menu.search": "Buscar armas e ferramentas",
|
|
54231
56515
|
"ui.tool_menu.close": "Fechar",
|
|
54232
56516
|
"ui.tool_menu.results": "{n} encontradas",
|
|
@@ -54435,6 +56719,27 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54435
56719
|
return wrap;
|
|
54436
56720
|
};
|
|
54437
56721
|
let playhead = null;
|
|
56722
|
+
const sheetImage = (image) => {
|
|
56723
|
+
const figure = mk("figure", "tr-sheet-image");
|
|
56724
|
+
const picture = mk("img");
|
|
56725
|
+
picture.src = image.url;
|
|
56726
|
+
picture.alt = image.alt;
|
|
56727
|
+
picture.loading = "lazy";
|
|
56728
|
+
picture.referrerPolicy = "no-referrer";
|
|
56729
|
+
figure.appendChild(picture);
|
|
56730
|
+
if (image.credit || image.page) {
|
|
56731
|
+
const caption = mk("figcaption", "sub", image.credit ?? "");
|
|
56732
|
+
if (image.page) {
|
|
56733
|
+
const link = mk("a", void 0, image.credit ? " \u2197" : image.page);
|
|
56734
|
+
link.href = image.page;
|
|
56735
|
+
link.target = "_blank";
|
|
56736
|
+
link.rel = "noopener noreferrer";
|
|
56737
|
+
caption.appendChild(link);
|
|
56738
|
+
}
|
|
56739
|
+
figure.appendChild(caption);
|
|
56740
|
+
}
|
|
56741
|
+
return figure;
|
|
56742
|
+
};
|
|
54438
56743
|
const sheetCard = (sheet) => {
|
|
54439
56744
|
const card = mk("div", "tr-sheet");
|
|
54440
56745
|
if (sheet.kicker) card.appendChild(mk("div", "sub", sheet.kicker));
|
|
@@ -54486,6 +56791,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54486
56791
|
playhead = { node: head, metric, end: end3 };
|
|
54487
56792
|
card.appendChild(strip);
|
|
54488
56793
|
}
|
|
56794
|
+
if (sheet.image) card.appendChild(sheetImage(sheet.image));
|
|
54489
56795
|
if (sheet.note) card.appendChild(mk("p", "sub", sheet.note));
|
|
54490
56796
|
return card;
|
|
54491
56797
|
};
|
|
@@ -54719,7 +57025,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54719
57025
|
const close = mk3("button", "tr-btn", "\xD7");
|
|
54720
57026
|
close.type = "button";
|
|
54721
57027
|
close.title = o.t("ui.tool_menu.close");
|
|
54722
|
-
header.append(mk3("h3", void 0, o.t("ui.tool_menu")), search, close);
|
|
57028
|
+
header.append(mk3("h3", void 0, o.t("ui.tool_menu")), mk3("small", "tr-hint", o.t("ui.tool_menu.dual")), search, close);
|
|
54723
57029
|
const tabs = mk3("div", "tr-tabs");
|
|
54724
57030
|
const body = mk3("div", "tr-wmenu-body");
|
|
54725
57031
|
element.append(header, tabs, body);
|
|
@@ -54727,15 +57033,17 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54727
57033
|
let activeGroup = "";
|
|
54728
57034
|
let groups = [];
|
|
54729
57035
|
const toolButton = (tool, group) => {
|
|
54730
|
-
const
|
|
57036
|
+
const left = o.currentLeft?.() === tool.id;
|
|
57037
|
+
const button = mk3("button", tool.id === o.current() || left ? "tr-btn on" : "tr-btn");
|
|
54731
57038
|
button.type = "button";
|
|
54732
57039
|
button.title = tool.name;
|
|
57040
|
+
if (left) button.dataset.left = "";
|
|
54733
57041
|
button.dataset.tool = tool.id;
|
|
54734
57042
|
if (tool.thumbnail) button.appendChild(o.thumb(tool.thumbnail, tool.thumbnailCredit));
|
|
54735
57043
|
button.appendChild(mk3("span", void 0, tool.name));
|
|
54736
57044
|
if (group) button.appendChild(mk3("small", void 0, group));
|
|
54737
|
-
button.onclick = () => {
|
|
54738
|
-
o.onPick(tool.id);
|
|
57045
|
+
button.onclick = (event) => {
|
|
57046
|
+
o.onPick(tool.id, { second: event.shiftKey });
|
|
54739
57047
|
toggle(false);
|
|
54740
57048
|
};
|
|
54741
57049
|
return button;
|
|
@@ -54831,6 +57139,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54831
57139
|
.tr-prog b{display:block;font-weight:500;margin-bottom:3px}
|
|
54832
57140
|
.tr-sheet{margin-top:8px;padding:8px;border:1px solid #2a2e36;border-radius:6px;font-size:12px;line-height:1.4}
|
|
54833
57141
|
.tr-sheet h4{margin:2px 0 4px;font-size:13px}.tr-sheet p{margin:4px 0}
|
|
57142
|
+
/* the picture of a sheet floats over the scene, top centre, next to what it shows: the bench panel is too short to hold it */
|
|
57143
|
+
.tr-sheet-image{position:fixed;top:52px;left:50%;transform:translateX(-50%);width:min(400px,42vw);z-index:5;margin:0;padding:6px;background:rgba(18,20,24,.92);border:1px solid #2a2e36;border-radius:6px;pointer-events:auto}.tr-sheet-image img{display:block;width:100%;max-height:200px;object-fit:contain;background:#f4f4f2;border-radius:4px}.tr-sheet-image figcaption{margin-top:3px;font-size:11px}.tr-sheet-image a{color:#8ab4f8}
|
|
54834
57144
|
.tr-facts{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;margin:4px 0}.tr-facts dt{opacity:.7}.tr-facts dd{margin:0}
|
|
54835
57145
|
.tr-steps{margin:4px 0;padding-left:16px}.tr-swatches{display:flex;flex-wrap:wrap;gap:6px}.tr-swatch i{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:4px}
|
|
54836
57146
|
.tr-timeline{position:relative;height:22px;margin:8px 0;background:#1b1e24;border-radius:4px;overflow:hidden}
|
|
@@ -54958,7 +57268,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
54958
57268
|
bottom.append(held, hotbar);
|
|
54959
57269
|
let currentTool = "";
|
|
54960
57270
|
const menuOptions = o.toolMenu;
|
|
54961
|
-
const toolMenu = menuOptions ? createToolMenu({ t, groups: menuOptions.groups, current: () => currentTool, onPick: menuOptions.onPick, ...menuOptions.onOpen ? { onOpen: menuOptions.onOpen } : {}, thumb }) : null;
|
|
57271
|
+
const toolMenu = menuOptions ? createToolMenu({ t, groups: menuOptions.groups, current: () => currentTool, onPick: menuOptions.onPick, ...menuOptions.onOpen ? { onOpen: menuOptions.onOpen } : {}, ...menuOptions.currentLeft ? { currentLeft: menuOptions.currentLeft } : {}, thumb }) : null;
|
|
54962
57272
|
if (toolMenu && menuOptions) {
|
|
54963
57273
|
const menuButton = mk4("button", "tr-slot tr-menu-btn");
|
|
54964
57274
|
menuButton.type = "button";
|
|
@@ -55606,6 +57916,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
55606
57916
|
|
|
55607
57917
|
// src/room.ts
|
|
55608
57918
|
var FIXED_DT = 1 / 60;
|
|
57919
|
+
var LEFT_HAND_KEY = "#L";
|
|
57920
|
+
var OFF_HAND_SEED = 11;
|
|
55609
57921
|
var TOOL_MENU_KEY_LABELS = { Backquote: "'" };
|
|
55610
57922
|
var EXPLOSIVE_EFFECT_IDS = ["m67", "molotov", "pipe_bomb", "m79_40mm", "m72_66mm", "at4_84mm", "dynamite", "c4", "claymore"];
|
|
55611
57923
|
var DEFAULT_PARTICLE_LIMIT = 900;
|
|
@@ -55820,6 +58132,14 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
55820
58132
|
rendererPieces.set(objectId, piece);
|
|
55821
58133
|
action("loosened", t(cause === "blast" ? "act.loosened" : "act.came_apart", { name: e.name }), { objectId, itemId: e.so.itemId });
|
|
55822
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
|
+
},
|
|
55823
58143
|
onPiece: (piece) => {
|
|
55824
58144
|
rendererPieces.set(piece.objectId, piece);
|
|
55825
58145
|
loose.push(piece);
|
|
@@ -55901,6 +58221,11 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
55901
58221
|
return s;
|
|
55902
58222
|
};
|
|
55903
58223
|
let tool = toolState(toolId);
|
|
58224
|
+
let offToolId = null;
|
|
58225
|
+
let offTool = null;
|
|
58226
|
+
let offHeld = false;
|
|
58227
|
+
let offFirst = false;
|
|
58228
|
+
let firedHand = "R";
|
|
55904
58229
|
const catalogMap = /* @__PURE__ */ new Map();
|
|
55905
58230
|
for (const i of options.catalog) catalogMap.set(i.id, i);
|
|
55906
58231
|
const catalog = [...options.catalog];
|
|
@@ -56110,9 +58435,19 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
56110
58435
|
releaseTrigger();
|
|
56111
58436
|
},
|
|
56112
58437
|
secondary: () => {
|
|
58438
|
+
if (offTool && !heldEntry) {
|
|
58439
|
+
offHeld = true;
|
|
58440
|
+
offFirst = true;
|
|
58441
|
+
return;
|
|
58442
|
+
}
|
|
56113
58443
|
if (heldEntry) throwHeld();
|
|
56114
58444
|
else grab();
|
|
56115
58445
|
},
|
|
58446
|
+
secondaryUp: () => {
|
|
58447
|
+
if (!offHeld) return;
|
|
58448
|
+
offHeld = false;
|
|
58449
|
+
releaseTrigger("L");
|
|
58450
|
+
},
|
|
56116
58451
|
key: (code, down, e) => onKey(code, down, e),
|
|
56117
58452
|
wheel: (dir) => cycleTool(dir)
|
|
56118
58453
|
}
|
|
@@ -56468,9 +58803,14 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
56468
58803
|
bodyBoxes.delete(id);
|
|
56469
58804
|
return null;
|
|
56470
58805
|
}
|
|
56471
|
-
|
|
56472
|
-
if (
|
|
56473
|
-
|
|
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 };
|
|
56474
58814
|
bodyBoxes.set(id, box2);
|
|
56475
58815
|
return box2;
|
|
56476
58816
|
};
|
|
@@ -56487,7 +58827,15 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
56487
58827
|
const reach = c.radius + r;
|
|
56488
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;
|
|
56489
58829
|
}
|
|
56490
|
-
|
|
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;
|
|
56491
58839
|
};
|
|
56492
58840
|
blockers.add((x, z, r) => {
|
|
56493
58841
|
for (const e of entries2.values()) {
|
|
@@ -56651,6 +58999,10 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
56651
58999
|
for (const listener of [...resetListeners]) listener();
|
|
56652
59000
|
tools.clear();
|
|
56653
59001
|
tool = toolState(toolId);
|
|
59002
|
+
dualReload.cancel();
|
|
59003
|
+
offTool = null;
|
|
59004
|
+
offToolId = null;
|
|
59005
|
+
firedHand = "R";
|
|
56654
59006
|
if (pristineWorld) {
|
|
56655
59007
|
for (const id of world.ids()) world.remove(id);
|
|
56656
59008
|
world.load(pristineWorld);
|
|
@@ -56916,12 +59268,13 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
56916
59268
|
const pocketAt = new THREE.Vector3();
|
|
56917
59269
|
const aimTurn = new THREE.Quaternion();
|
|
56918
59270
|
const aimEuler = new THREE.Euler(0, 0, 0, "YXZ");
|
|
56919
|
-
const bodyHoldFrame = () => {
|
|
56920
|
-
const shoulder = avatar?.shoulder?.(
|
|
59271
|
+
const bodyHoldFrame = (hand = "R") => {
|
|
59272
|
+
const shoulder = avatar?.shoulder?.(hand);
|
|
56921
59273
|
if (!shoulder) return null;
|
|
56922
59274
|
shoulder.getWorldPosition(pocketAt);
|
|
56923
|
-
|
|
56924
|
-
pocketAt.
|
|
59275
|
+
const inward = hand === "L" ? -SHOULDER_POCKET_IN : SHOULDER_POCKET_IN;
|
|
59276
|
+
pocketAt.x -= Math.cos(player.yaw) * inward;
|
|
59277
|
+
pocketAt.z += Math.sin(player.yaw) * inward;
|
|
56925
59278
|
aimTurn.setFromEuler(aimEuler.set(player.pitch, player.yaw, 0, "YXZ"));
|
|
56926
59279
|
return { position: [pocketAt.x, pocketAt.y, pocketAt.z], quaternion: [aimTurn.x, aimTurn.y, aimTurn.z, aimTurn.w] };
|
|
56927
59280
|
};
|
|
@@ -57031,17 +59384,18 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57031
59384
|
rng = rng * 1664525 + 1013904223 >>> 0;
|
|
57032
59385
|
return rng / 4294967296;
|
|
57033
59386
|
};
|
|
57034
|
-
const muzzlePose = () => {
|
|
57035
|
-
const lent = muzzleSource?.();
|
|
57036
|
-
return lent ? { position: [...lent.position], drawsFlash: lent.drawsFlash } : { position: muzzle2(), drawsFlash: false };
|
|
59387
|
+
const muzzlePose = (hand = "R") => {
|
|
59388
|
+
const lent = muzzleSource?.(hand);
|
|
59389
|
+
return lent ? { position: [...lent.position], drawsFlash: lent.drawsFlash } : { position: muzzle2(hand), drawsFlash: false };
|
|
57037
59390
|
};
|
|
57038
|
-
const muzzle2 = () => {
|
|
59391
|
+
const muzzle2 = (hand = "R") => {
|
|
57039
59392
|
aim();
|
|
57040
59393
|
const e = camera2.matrixWorld.elements;
|
|
59394
|
+
const side = hand === "L" ? -0.16 : 0.16;
|
|
57041
59395
|
return [
|
|
57042
|
-
aimO[0] + aimD[0] * 0.45 + e[0] *
|
|
57043
|
-
aimO[1] + aimD[1] * 0.45 + e[1] *
|
|
57044
|
-
aimO[2] + aimD[2] * 0.45 + e[2] *
|
|
59396
|
+
aimO[0] + aimD[0] * 0.45 + e[0] * side - e[4] * 0.1,
|
|
59397
|
+
aimO[1] + aimD[1] * 0.45 + e[1] * side - e[5] * 0.1,
|
|
59398
|
+
aimO[2] + aimD[2] * 0.45 + e[2] * side - e[6] * 0.1
|
|
57045
59399
|
];
|
|
57046
59400
|
};
|
|
57047
59401
|
const tracers = [];
|
|
@@ -57221,10 +59575,10 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57221
59575
|
renderer.impulse(objectId, push2.impulse, push2.point);
|
|
57222
59576
|
}
|
|
57223
59577
|
};
|
|
57224
|
-
const fireShots = (shots, recoil) => {
|
|
59578
|
+
const fireShots = (shots, recoil, id = toolId, hand = "R") => {
|
|
57225
59579
|
if (shots.length === 0) return;
|
|
57226
|
-
const w = toolName();
|
|
57227
|
-
const { position: m, drawsFlash } = muzzlePose();
|
|
59580
|
+
const w = toolName(id);
|
|
59581
|
+
const { position: m, drawsFlash } = muzzlePose(hand);
|
|
57228
59582
|
if (!drawsFlash) {
|
|
57229
59583
|
renderer.emit("flash", m, [aimD[0], aimD[1], aimD[2]], 1, 0);
|
|
57230
59584
|
renderer.emit("sparks", m, [aimD[0], aimD[1], aimD[2]], 4, 6);
|
|
@@ -57263,7 +59617,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57263
59617
|
objectId: f?.object,
|
|
57264
59618
|
itemId: f ? entryOf(f.object)?.so.itemId : void 0,
|
|
57265
59619
|
part: f?.part,
|
|
57266
|
-
data: { weapon:
|
|
59620
|
+
data: { weapon: id, shots: shots.length, hits, energyJ: Math.round(energy), caliberMm: shots[0].caliber, distanceM: f ? Math.round(f.distance * 100) / 100 : null, ...hand === "L" ? { hand } : {} }
|
|
57267
59621
|
});
|
|
57268
59622
|
};
|
|
57269
59623
|
const fireRays = (u) => {
|
|
@@ -57367,7 +59721,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57367
59721
|
const kickLeg = options.kickLeg ?? "alternate";
|
|
57368
59722
|
let nextLeg = "right";
|
|
57369
59723
|
const punchHand = options.punchHand ?? "alternate";
|
|
57370
|
-
const punchTools = new Set(options.punchTools ?? ["hand", "fists"]);
|
|
59724
|
+
const punchTools = new Set(options.punchTools ?? ["hand", "fists", "brass_knuckles"]);
|
|
57371
59725
|
let nextHand = "right";
|
|
57372
59726
|
const combo = options.strikeCombo;
|
|
57373
59727
|
let comboOn = !!combo && options.strikeComboOn === true;
|
|
@@ -57381,16 +59735,16 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57381
59735
|
action("combo", t(on ? "act.combo.on" : "act.combo.off", { name }), { data: { combo: combo.id, on } });
|
|
57382
59736
|
say(t(on ? "act.combo.on" : "act.combo.off", { name }));
|
|
57383
59737
|
};
|
|
57384
|
-
const strikeUse = (u, id = toolId) => {
|
|
59738
|
+
const strikeUse = (u, id = toolId, side) => {
|
|
57385
59739
|
const stepIndex = comboOn && combo?.tools.includes(id) && combo.steps.length > 0 ? simTime - comboAt > combo.reset ? 0 : comboNext : null;
|
|
57386
59740
|
const step2 = stepIndex === null ? void 0 : combo.steps[stepIndex];
|
|
57387
59741
|
const clip = step2 ? step2.clip : strikeClips[id];
|
|
57388
59742
|
const leg = id === "kick" ? kickLeg === "alternate" ? nextLeg : kickLeg : void 0;
|
|
57389
|
-
const hand = step2 ? step2.hand : punchTools.has(id) ? punchHand === "alternate" ? nextHand : punchHand : void 0;
|
|
57390
|
-
const
|
|
57391
|
-
const seconds = clip === void 0 ? null : avatar?.strike?.(clip,
|
|
59743
|
+
const hand = side ? side.hand : step2 ? step2.hand : punchTools.has(id) ? punchHand === "alternate" ? nextHand : punchHand : void 0;
|
|
59744
|
+
const limb = leg ? { leg } : hand ? { hand } : void 0;
|
|
59745
|
+
const seconds = clip === void 0 ? null : avatar?.strike?.(clip, limb) ?? null;
|
|
57392
59746
|
if (leg && seconds !== null && kickLeg === "alternate") nextLeg = leg === "right" ? "left" : "right";
|
|
57393
|
-
if (!step2 && hand && seconds !== null && punchHand === "alternate") nextHand = hand === "right" ? "left" : "right";
|
|
59747
|
+
if (!step2 && !side && hand && seconds !== null && punchHand === "alternate") nextHand = hand === "right" ? "left" : "right";
|
|
57394
59748
|
if (step2 && stepIndex !== null) {
|
|
57395
59749
|
comboNext = (stepIndex + 1) % combo.steps.length;
|
|
57396
59750
|
comboAt = simTime;
|
|
@@ -57411,7 +59765,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57411
59765
|
data: { weapon: id, clip, impactIn: seconds, ...leg ? { leg } : {}, ...hand ? { hand } : {}, ...step2 ? { combo: combo.id, step: stepIndex } : {} }
|
|
57412
59766
|
});
|
|
57413
59767
|
strikes.push({
|
|
57414
|
-
key: id,
|
|
59768
|
+
key: side?.key ?? id,
|
|
57415
59769
|
remaining: seconds,
|
|
57416
59770
|
...swing && seconds > lead ? { swing, lead } : {},
|
|
57417
59771
|
land: () => {
|
|
@@ -57539,22 +59893,53 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57539
59893
|
emitHost,
|
|
57540
59894
|
startFire,
|
|
57541
59895
|
say,
|
|
57542
|
-
projectileModel: options.projectileModel
|
|
59896
|
+
projectileModel: options.projectileModel,
|
|
59897
|
+
audio
|
|
57543
59898
|
});
|
|
57544
|
-
const
|
|
59899
|
+
const toolOf = (hand) => hand === "L" ? offTool && offToolId ? { id: offToolId, state: offTool } : null : { id: toolId, state: tool };
|
|
59900
|
+
const dualReload = createDualReload(
|
|
59901
|
+
{
|
|
59902
|
+
needs: (hand) => {
|
|
59903
|
+
const state = toolOf(hand)?.state;
|
|
59904
|
+
return state !== void 0 && (state.def.action.kind === "firearm" || state.def.action.kind === "launcher") && state.ammo < state.capacity && state.reserve > 0;
|
|
59905
|
+
},
|
|
59906
|
+
startReload: (hand) => startReload(hand),
|
|
59907
|
+
reloading: (hand) => toolOf(hand)?.state.reloading ?? false,
|
|
59908
|
+
stow: (hand, on, seconds) => {
|
|
59909
|
+
const slot2 = toolOf(hand);
|
|
59910
|
+
if (!slot2) return;
|
|
59911
|
+
if (on) slot2.state.release();
|
|
59912
|
+
emitHost({ type: "stow", weapon: slot2.id, on, duration: seconds, ...hand === "L" ? { hand } : {} });
|
|
59913
|
+
action("stow", t(on ? "act.stow" : "act.unstow", { weapon: toolName(slot2.id), hand: t(hand === "L" ? "hand.left" : "hand.right") }), { data: { weapon: slot2.id, hand, on, seconds } });
|
|
59914
|
+
}
|
|
59915
|
+
},
|
|
59916
|
+
() => arsenal.rules.dualWield.stowSeconds
|
|
59917
|
+
);
|
|
59918
|
+
const strikeKeyOf = (id, hand) => hand === "L" ? `${id}${LEFT_HAND_KEY}` : id;
|
|
59919
|
+
const useTool = (dt, held, hand = "R") => {
|
|
57545
59920
|
aim();
|
|
57546
|
-
const
|
|
59921
|
+
const slot2 = toolOf(hand);
|
|
59922
|
+
if (!slot2) return "cooldown";
|
|
59923
|
+
const { id, state } = slot2;
|
|
59924
|
+
if (offTool && dualReload.busy) {
|
|
59925
|
+
if (!held) say(t("hud.reloading"));
|
|
59926
|
+
return "reloading";
|
|
59927
|
+
}
|
|
59928
|
+
const cat = state.def.category;
|
|
57547
59929
|
if (cat === "hand") {
|
|
57548
59930
|
if (strikes.some((strike) => strike.key === "hand" || strike.key === "fists")) return "cooldown";
|
|
57549
59931
|
strikeUse({ kind: "melee", damage: "blunt", energy: 25, radius: 0.1, reach: 0.8, origin: [aimO[0], aimO[1], aimO[2]], direction: [aimD[0], aimD[1], aimD[2]] }, "hand");
|
|
57550
59932
|
return "ok";
|
|
57551
59933
|
}
|
|
57552
|
-
if (strikes.some((strike) => strike.key ===
|
|
57553
|
-
const res =
|
|
57554
|
-
flushEvents(
|
|
59934
|
+
if (strikes.some((strike) => strike.key === strikeKeyOf(id, hand))) return "cooldown";
|
|
59935
|
+
const res = state.trigger([aimO[0], aimO[1], aimO[2]], [aimD[0], aimD[1], aimD[2]], dt, held, "player");
|
|
59936
|
+
flushEvents(id, state, res.recoil, hand);
|
|
57555
59937
|
if (res.status === "empty") {
|
|
57556
|
-
if (!
|
|
57557
|
-
|
|
59938
|
+
if (!state.reloading && state.reserve > 0) {
|
|
59939
|
+
if (offTool) dualReload.start([hand, hand === "R" ? "L" : "R"]);
|
|
59940
|
+
else startReload(hand);
|
|
59941
|
+
}
|
|
59942
|
+
say(t(state.reloading ? "hud.reloading" : "hud.empty"));
|
|
57558
59943
|
return res.status;
|
|
57559
59944
|
}
|
|
57560
59945
|
if (res.status !== "ok") {
|
|
@@ -57564,15 +59949,18 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57564
59949
|
else if (res.status === "safe") say(t("hud.safe"));
|
|
57565
59950
|
return res.status;
|
|
57566
59951
|
}
|
|
57567
|
-
|
|
59952
|
+
firedHand = hand;
|
|
59953
|
+
applyUses(res, dt, hand);
|
|
57568
59954
|
return res.status;
|
|
57569
59955
|
};
|
|
57570
|
-
const applyUses = (res, dt) => {
|
|
57571
|
-
|
|
59956
|
+
const applyUses = (res, dt, hand = "R") => {
|
|
59957
|
+
const id = toolOf(hand)?.id ?? toolId;
|
|
59958
|
+
if (res.shots.length) fireShots(res.shots, res.recoil, id, hand);
|
|
57572
59959
|
for (const u of res.uses) {
|
|
57573
59960
|
switch (u.kind) {
|
|
57574
59961
|
case "melee":
|
|
57575
|
-
strikeUse(u);
|
|
59962
|
+
if (hand === "L") strikeUse(u, id, { hand: "left", key: strikeKeyOf(id, hand) });
|
|
59963
|
+
else strikeUse(u);
|
|
57576
59964
|
break;
|
|
57577
59965
|
case "flame":
|
|
57578
59966
|
heatRays(u, "fire");
|
|
@@ -57600,15 +59988,17 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57600
59988
|
}
|
|
57601
59989
|
}
|
|
57602
59990
|
};
|
|
57603
|
-
const releaseTrigger = () => {
|
|
57604
|
-
|
|
57605
|
-
|
|
59991
|
+
const releaseTrigger = (hand = "R") => {
|
|
59992
|
+
const slot2 = toolOf(hand);
|
|
59993
|
+
if (!slot2) return;
|
|
59994
|
+
if (!slot2.state.cooking) {
|
|
59995
|
+
slot2.state.release();
|
|
57606
59996
|
return;
|
|
57607
59997
|
}
|
|
57608
59998
|
aim();
|
|
57609
|
-
const res =
|
|
57610
|
-
flushEvents(
|
|
57611
|
-
if (res) applyUses(res, 0);
|
|
59999
|
+
const res = slot2.state.release([aimO[0], aimO[1], aimO[2]], [aimD[0], aimD[1], aimD[2]]);
|
|
60000
|
+
flushEvents(slot2.id, slot2.state, void 0, hand);
|
|
60001
|
+
if (res) applyUses(res, 0, hand);
|
|
57612
60002
|
};
|
|
57613
60003
|
const kick = () => {
|
|
57614
60004
|
if (!arsenal.weapons.has("kick") || strikes.some((strike) => strike.key === "kick")) return false;
|
|
@@ -57637,8 +60027,43 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
57637
60027
|
}));
|
|
57638
60028
|
};
|
|
57639
60029
|
const toggleToolMenu = (open) => ui?.toolMenu(open) ?? false;
|
|
60030
|
+
const dropOffTool = () => {
|
|
60031
|
+
if (!offTool) return;
|
|
60032
|
+
releaseTrigger("L");
|
|
60033
|
+
offTool = null;
|
|
60034
|
+
offToolId = null;
|
|
60035
|
+
offHeld = false;
|
|
60036
|
+
offFirst = false;
|
|
60037
|
+
dualReload.cancel();
|
|
60038
|
+
firedHand = "R";
|
|
60039
|
+
tool.setStance({ dualWield: false });
|
|
60040
|
+
action("tool_left", t("act.tool_left_off"), { data: { hand: "L" } });
|
|
60041
|
+
emitHost({ type: "tool", weapon: "", hand: "L" });
|
|
60042
|
+
};
|
|
60043
|
+
const setOffTool = (id) => {
|
|
60044
|
+
if (id === null) {
|
|
60045
|
+
dropOffTool();
|
|
60046
|
+
return true;
|
|
60047
|
+
}
|
|
60048
|
+
if (!arsenal.weapons.has(id)) throw new Error(`game-test-room: unknown weapon "${id}"`);
|
|
60049
|
+
if (!canDualWield(arsenal, id) || !canDualWield(arsenal, toolId)) return false;
|
|
60050
|
+
if (offTool) releaseTrigger("L");
|
|
60051
|
+
const state = arsenal.create(id, { seed: OFF_HAND_SEED, reserve: 999, ...toolSettings });
|
|
60052
|
+
state.setFuseLength(fuseLength);
|
|
60053
|
+
state.refill();
|
|
60054
|
+
state.setStance({ dualWield: true });
|
|
60055
|
+
tool.setStance({ dualWield: true });
|
|
60056
|
+
offTool = state;
|
|
60057
|
+
offToolId = id;
|
|
60058
|
+
dualReload.cancel();
|
|
60059
|
+
action("tool_left", t("act.tool_left", { weapon: toolName(id) }), { data: { weapon: id, hand: "L" } });
|
|
60060
|
+
state.draw();
|
|
60061
|
+
flushEvents(id, state, void 0, "L");
|
|
60062
|
+
return true;
|
|
60063
|
+
};
|
|
57640
60064
|
const setTool = (id) => {
|
|
57641
60065
|
if (!arsenal.weapons.has(id)) throw new Error(`game-test-room: unknown weapon "${id}"`);
|
|
60066
|
+
dropOffTool();
|
|
57642
60067
|
releaseTrigger();
|
|
57643
60068
|
toolId = id;
|
|
57644
60069
|
const i = loadout.indexOf(id);
|
|
@@ -58131,15 +60556,13 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58131
60556
|
case "KeyR":
|
|
58132
60557
|
if (heldPiece) heldYaw += Math.PI / 8;
|
|
58133
60558
|
else {
|
|
58134
|
-
|
|
60559
|
+
reloadHands();
|
|
58135
60560
|
say(t("hud.reloading"));
|
|
58136
60561
|
}
|
|
58137
60562
|
break;
|
|
58138
|
-
case "KeyQ":
|
|
58139
|
-
|
|
58140
|
-
flushEvents(toolId, tool);
|
|
60563
|
+
case "KeyQ":
|
|
60564
|
+
cycleToolMode();
|
|
58141
60565
|
break;
|
|
58142
|
-
}
|
|
58143
60566
|
case "KeyX":
|
|
58144
60567
|
clearToolFault();
|
|
58145
60568
|
break;
|
|
@@ -58196,6 +60619,20 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58196
60619
|
if (!loadout.includes(weaponId)) loadout.push(weaponId);
|
|
58197
60620
|
setTool(weaponId);
|
|
58198
60621
|
};
|
|
60622
|
+
const pickTool = (weaponId, pick3 = { second: false }) => {
|
|
60623
|
+
if (!arsenal.weapons.has(weaponId)) return;
|
|
60624
|
+
if (!pick3.second) {
|
|
60625
|
+
equip(weaponId);
|
|
60626
|
+
return;
|
|
60627
|
+
}
|
|
60628
|
+
if (!canDualWield(arsenal, weaponId)) {
|
|
60629
|
+
equip(weaponId);
|
|
60630
|
+
say(t("hud.no_dual", { weapon: toolName(weaponId) }));
|
|
60631
|
+
return;
|
|
60632
|
+
}
|
|
60633
|
+
if (!offTool || !canDualWield(arsenal, toolId)) equip(weaponId);
|
|
60634
|
+
setOffTool(weaponId);
|
|
60635
|
+
};
|
|
58199
60636
|
let hudAimHit;
|
|
58200
60637
|
const inspectTarget = () => {
|
|
58201
60638
|
if (hudAimHit !== void 0) return hudAimHit;
|
|
@@ -58399,6 +60836,11 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58399
60836
|
s.update(dt);
|
|
58400
60837
|
flushEvents(id, s);
|
|
58401
60838
|
}
|
|
60839
|
+
if (offTool && offToolId) {
|
|
60840
|
+
offTool.update(dt);
|
|
60841
|
+
flushEvents(offToolId, offTool, void 0, "L");
|
|
60842
|
+
dualReload.step(dt);
|
|
60843
|
+
}
|
|
58402
60844
|
if (audio) {
|
|
58403
60845
|
const look = camera2.getWorldDirection(listenerDirection);
|
|
58404
60846
|
audio.setListener({ position: [camera2.position.x, camera2.position.y, camera2.position.z], yaw: Math.atan2(-look.x, -look.z) });
|
|
@@ -58444,6 +60886,10 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58444
60886
|
} else if (useHeld) useTool(dt, !useFirst);
|
|
58445
60887
|
useFirst = false;
|
|
58446
60888
|
}
|
|
60889
|
+
if ((offHeld || offFirst) && offTool && !isPaused && !push.active) {
|
|
60890
|
+
useTool(dt, !offFirst, "L");
|
|
60891
|
+
offFirst = false;
|
|
60892
|
+
}
|
|
58447
60893
|
if (!isPaused) followHeld(dt);
|
|
58448
60894
|
};
|
|
58449
60895
|
const updatePresent = (dt) => {
|
|
@@ -58470,12 +60916,18 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58470
60916
|
hudAimHit = void 0;
|
|
58471
60917
|
}
|
|
58472
60918
|
};
|
|
60919
|
+
const ammoText = (state) => {
|
|
60920
|
+
const kind = state.def.action.kind;
|
|
60921
|
+
if (kind === "firearm") return `${state.ammo}/${state.capacity}${state.reserve ? ` \xB7 ${state.reserve}` : ""}`;
|
|
60922
|
+
return kind === "hand" || kind === "melee" || kind === "tool" ? "" : `${num(state.ammo, 0)}`;
|
|
60923
|
+
};
|
|
58473
60924
|
const readHudState = () => {
|
|
58474
60925
|
const page = Math.floor(toolIndex / 10);
|
|
58475
60926
|
const slots = loadout.slice(page * 10, page * 10 + 10).map((id) => arsenal.name(id));
|
|
58476
|
-
const
|
|
60927
|
+
const left = offTool ? ammoText(offTool) : "";
|
|
60928
|
+
const ammo = `${ammoText(tool)}${left ? ` | ${t("hud.left")} ${left}` : ""}`;
|
|
58477
60929
|
return {
|
|
58478
|
-
toolName: toolName(),
|
|
60930
|
+
toolName: offToolId ? `${toolName()} + ${toolName(offToolId)}` : toolName(),
|
|
58479
60931
|
toolId,
|
|
58480
60932
|
ammo,
|
|
58481
60933
|
slots,
|
|
@@ -58665,26 +61117,53 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58665
61117
|
if (event.type === "explosion") knockPlayer(event);
|
|
58666
61118
|
for (const listener of hostListeners) listener(event);
|
|
58667
61119
|
}
|
|
58668
|
-
function flushEvents(id, state, recoil) {
|
|
61120
|
+
function flushEvents(id, state, recoil, hand = "R") {
|
|
58669
61121
|
const phase = state.phase;
|
|
58670
61122
|
for (const event of state.drain()) {
|
|
58671
61123
|
const hostEvent = toHostEvent(id, event, { ...recoil ? { recoil } : {}, phase });
|
|
58672
|
-
if (hostEvent) emitHost(hostEvent);
|
|
61124
|
+
if (hostEvent) emitHost(hand === "L" ? { ...hostEvent, hand } : hostEvent);
|
|
58673
61125
|
}
|
|
58674
61126
|
}
|
|
58675
|
-
function startReload() {
|
|
58676
|
-
|
|
58677
|
-
|
|
61127
|
+
function startReload(hand = "R") {
|
|
61128
|
+
const slot2 = toolOf(hand);
|
|
61129
|
+
if (!slot2) return false;
|
|
61130
|
+
const started = slot2.state.reload();
|
|
61131
|
+
flushEvents(slot2.id, slot2.state, void 0, hand);
|
|
61132
|
+
return started;
|
|
61133
|
+
}
|
|
61134
|
+
function reloadHands() {
|
|
61135
|
+
if (!offTool) {
|
|
61136
|
+
startReload();
|
|
61137
|
+
return;
|
|
61138
|
+
}
|
|
61139
|
+
dualReload.start(["R", "L"]);
|
|
58678
61140
|
}
|
|
61141
|
+
const commandHand = () => offTool ? firedHand : "R";
|
|
58679
61142
|
function clearToolFault() {
|
|
58680
|
-
|
|
58681
|
-
|
|
58682
|
-
|
|
61143
|
+
const order = offTool ? [firedHand, firedHand === "R" ? "L" : "R"] : ["R"];
|
|
61144
|
+
for (const hand of order) {
|
|
61145
|
+
const slot2 = toolOf(hand);
|
|
61146
|
+
if (!slot2?.state.fault || !slot2.state.clearFault()) continue;
|
|
61147
|
+
flushEvents(slot2.id, slot2.state, void 0, hand);
|
|
61148
|
+
say(t("hud.clearing"));
|
|
61149
|
+
return;
|
|
61150
|
+
}
|
|
58683
61151
|
}
|
|
58684
61152
|
function toggleSafety() {
|
|
58685
|
-
|
|
58686
|
-
|
|
58687
|
-
|
|
61153
|
+
const hand = commandHand();
|
|
61154
|
+
const slot2 = toolOf(hand);
|
|
61155
|
+
if (!slot2) return;
|
|
61156
|
+
slot2.state.setSafety(!slot2.state.safety);
|
|
61157
|
+
flushEvents(slot2.id, slot2.state, void 0, hand);
|
|
61158
|
+
say(`${t(slot2.state.safety ? "hud.safety_on" : "hud.safety_off")}${offTool ? ` (${toolName(slot2.id)}, ${t(hand === "L" ? "hand.left" : "hand.right")})` : ""}`);
|
|
61159
|
+
}
|
|
61160
|
+
function cycleToolMode() {
|
|
61161
|
+
const hand = commandHand();
|
|
61162
|
+
const slot2 = toolOf(hand);
|
|
61163
|
+
if (!slot2) return;
|
|
61164
|
+
slot2.state.cycleMode();
|
|
61165
|
+
flushEvents(slot2.id, slot2.state, void 0, hand);
|
|
61166
|
+
if (offTool) say(`${toolName(slot2.id)} (${t(hand === "L" ? "hand.left" : "hand.right")}): ${slot2.state.mode}`);
|
|
58688
61167
|
}
|
|
58689
61168
|
function runToolCommand(request) {
|
|
58690
61169
|
const work = (done) => done.ok ? { ok: true, seconds: done.seconds } : { ok: false, reason: done.reason };
|
|
@@ -58706,6 +61185,24 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58706
61185
|
return { ok: false, reason: "unknown" };
|
|
58707
61186
|
}
|
|
58708
61187
|
}
|
|
61188
|
+
const hostToolOf = (id, state) => ({
|
|
61189
|
+
weapon: id,
|
|
61190
|
+
category: state.def.category,
|
|
61191
|
+
ammo: state.ammo,
|
|
61192
|
+
capacity: state.capacity,
|
|
61193
|
+
reserve: state.reserve,
|
|
61194
|
+
reloading: state.reloading,
|
|
61195
|
+
mode: state.mode,
|
|
61196
|
+
...state.fault ? { fault: state.fault } : {},
|
|
61197
|
+
safety: state.safety,
|
|
61198
|
+
heat: state.heatLevel,
|
|
61199
|
+
attachments: state.attachments.map((part) => ({ id: part.id, mount: part.mount, ...part.integral ? { integral: true } : {}, ...part.on === void 0 ? {} : { on: part.on } })),
|
|
61200
|
+
stock: state.stock,
|
|
61201
|
+
suppressed: state.suppressed,
|
|
61202
|
+
underbarrelActive: state.underbarrelActive,
|
|
61203
|
+
mass: state.mass,
|
|
61204
|
+
aimSeconds: state.aimSeconds
|
|
61205
|
+
});
|
|
58709
61206
|
const host = {
|
|
58710
61207
|
THREE,
|
|
58711
61208
|
scene: scene2,
|
|
@@ -58732,22 +61229,9 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58732
61229
|
...options.blastShove ? { blastShove: options.blastShove } : {},
|
|
58733
61230
|
blastReach: (point, target, ignore) => world.blastReach(point, target, ignore ? { ignore } : {}),
|
|
58734
61231
|
tool: () => ({
|
|
58735
|
-
|
|
58736
|
-
|
|
58737
|
-
|
|
58738
|
-
capacity: tool.capacity,
|
|
58739
|
-
reserve: tool.reserve,
|
|
58740
|
-
reloading: tool.reloading,
|
|
58741
|
-
mode: tool.mode,
|
|
58742
|
-
...tool.fault ? { fault: tool.fault } : {},
|
|
58743
|
-
safety: tool.safety,
|
|
58744
|
-
heat: tool.heatLevel,
|
|
58745
|
-
attachments: tool.attachments.map((part) => ({ id: part.id, mount: part.mount, ...part.integral ? { integral: true } : {}, ...part.on === void 0 ? {} : { on: part.on } })),
|
|
58746
|
-
stock: tool.stock,
|
|
58747
|
-
suppressed: tool.suppressed,
|
|
58748
|
-
underbarrelActive: tool.underbarrelActive,
|
|
58749
|
-
mass: tool.mass,
|
|
58750
|
-
aimSeconds: tool.aimSeconds
|
|
61232
|
+
...hostToolOf(toolId, tool),
|
|
61233
|
+
...dualReload.stowed("R") ? { stowed: true } : {},
|
|
61234
|
+
...offTool && offToolId ? { offHand: { ...hostToolOf(offToolId, offTool), ...dualReload.stowed("L") ? { stowed: true } : {} } } : {}
|
|
58751
61235
|
}),
|
|
58752
61236
|
command(request) {
|
|
58753
61237
|
const result2 = runToolCommand(request);
|
|
@@ -58784,7 +61268,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
58784
61268
|
onFrame(update, frameOptions) {
|
|
58785
61269
|
return scheduler.add((ctx) => update(ctx.dt), { phase: frameOptions?.phase ?? "lateSimulate", order: frameOptions?.order ?? 0, id: `module.frame#${moduleFrameCount++}` });
|
|
58786
61270
|
},
|
|
58787
|
-
holdFrame: () => bodyHoldFrame(),
|
|
61271
|
+
holdFrame: (hand) => bodyHoldFrame(hand),
|
|
58788
61272
|
reachFor(reach) {
|
|
58789
61273
|
if (!avatar) {
|
|
58790
61274
|
reach.onGrip?.();
|
|
@@ -59160,6 +61644,7 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
59160
61644
|
fluids,
|
|
59161
61645
|
film: filmBridge,
|
|
59162
61646
|
vfx: vfx.world,
|
|
61647
|
+
effects: vfx,
|
|
59163
61648
|
fluidRenderer: fluidView,
|
|
59164
61649
|
audio,
|
|
59165
61650
|
environment,
|
|
@@ -59175,6 +61660,29 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
59175
61660
|
clear,
|
|
59176
61661
|
resetAll,
|
|
59177
61662
|
setTool,
|
|
61663
|
+
setOffTool,
|
|
61664
|
+
get offTool() {
|
|
61665
|
+
return offTool;
|
|
61666
|
+
},
|
|
61667
|
+
useOff(hold = 0) {
|
|
61668
|
+
if (!offTool) return "cooldown";
|
|
61669
|
+
const status = useTool(FIXED_DT, false, "L");
|
|
61670
|
+
flush();
|
|
61671
|
+
let t0 = 0;
|
|
61672
|
+
while (t0 < hold) {
|
|
61673
|
+
useTool(FIXED_DT, true, "L");
|
|
61674
|
+
directStep = true;
|
|
61675
|
+
try {
|
|
61676
|
+
runHeadless(() => scheduler.advance(FIXED_DT));
|
|
61677
|
+
} finally {
|
|
61678
|
+
directStep = false;
|
|
61679
|
+
}
|
|
61680
|
+
t0 += FIXED_DT;
|
|
61681
|
+
}
|
|
61682
|
+
releaseTrigger("L");
|
|
61683
|
+
return status;
|
|
61684
|
+
},
|
|
61685
|
+
reload: () => reloadHands(),
|
|
59178
61686
|
useItem,
|
|
59179
61687
|
use(hold = 0) {
|
|
59180
61688
|
if (tool.def.category === "hand") {
|
|
@@ -59472,7 +61980,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
|
|
|
59472
61980
|
...options.toolMenu !== false ? {
|
|
59473
61981
|
toolMenu: {
|
|
59474
61982
|
groups: toolMenuGroups,
|
|
59475
|
-
onPick:
|
|
61983
|
+
onPick: pickTool,
|
|
61984
|
+
currentLeft: () => offToolId,
|
|
59476
61985
|
// the list takes clicks: the mouse leaves the view while it is open
|
|
59477
61986
|
onOpen: (open) => {
|
|
59478
61987
|
if (open && document.pointerLockElement) document.exitPointerLock?.();
|