@adatechnology/game-weapons-three 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GBO6L7CQ.js → chunk-BDYIF7JD.js} +263 -68
- package/dist/chunk-BDYIF7JD.js.map +1 -0
- package/dist/index.d.ts +45 -3
- package/dist/index.global.js +1033 -595
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/{testing-Cf1M1sWN.d.ts → testing-BKDGK6cq.d.ts} +32 -5
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +8 -8
- package/dist/chunk-GBO6L7CQ.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/test-area/area.ts
|
|
2
2
|
import { markKitOrigin as markKitOrigin6 } from "@adatechnology/game-contracts";
|
|
3
|
-
import { createArsenal, genericWeapons } from "@adatechnology/game-weapons";
|
|
3
|
+
import { createArsenal, genericWeapons, roundsToWarningOf } from "@adatechnology/game-weapons";
|
|
4
4
|
import { furnitureSpec } from "@adatechnology/game-floorplan-three";
|
|
5
5
|
import { isLightBudget, isRenderPools } from "@adatechnology/game-render-three";
|
|
6
6
|
|
|
@@ -183,6 +183,9 @@ function createAccessoryBench(options) {
|
|
|
183
183
|
signature = "";
|
|
184
184
|
});
|
|
185
185
|
return {
|
|
186
|
+
get aimProgress() {
|
|
187
|
+
return aimProgress;
|
|
188
|
+
},
|
|
186
189
|
handleChange(change) {
|
|
187
190
|
if (change.kind === "action") return act(change.id);
|
|
188
191
|
if (change.kind === "control" && change.id === SUPPORTED_CONTROL_ID && typeof change.value === "boolean") {
|
|
@@ -1413,6 +1416,10 @@ function computeCasingSound(input) {
|
|
|
1413
1416
|
const surfaceName = surface === "hard" ? "concreto" : surface === "soft" ? "terra" : surface === "wood" ? "madeira" : "metal";
|
|
1414
1417
|
return { surface, kind, sample: `casing_drop_${surfaceName}_${kind}`, voices };
|
|
1415
1418
|
}
|
|
1419
|
+
function magazineDropId(material) {
|
|
1420
|
+
const surface = casingSurfaceOf(material);
|
|
1421
|
+
return `mag_drop_${surface === "hard" ? "concreto" : surface === "soft" ? "terra" : surface === "wood" ? "madeira" : "metal"}`;
|
|
1422
|
+
}
|
|
1416
1423
|
|
|
1417
1424
|
// src/audio/flyby.ts
|
|
1418
1425
|
function computeFlyby(input) {
|
|
@@ -2217,7 +2224,7 @@ var CUES_IN_SAMPLE = { mag_release: "mag_out", mag_insert: "mag_seat" };
|
|
|
2217
2224
|
var isGunshot = (id) => GUNSHOT_SAMPLE_IDS.includes(id);
|
|
2218
2225
|
function weaponSampleDefaults(sound3, overrides = {}) {
|
|
2219
2226
|
if (sound3.category !== "armas") return void 0;
|
|
2220
|
-
const base = sound3.id.startsWith("suppressed_") ? SUPPRESSED_SAMPLE : isGunshot(sound3.id) ? GUNSHOT_SAMPLE : sound3.id.startsWith("casing_drop_") ? CASING_SAMPLE : RELOAD_SAMPLE;
|
|
2227
|
+
const base = sound3.id.startsWith("suppressed_") ? SUPPRESSED_SAMPLE : isGunshot(sound3.id) ? GUNSHOT_SAMPLE : sound3.id.startsWith("casing_drop_") || sound3.id.startsWith("mag_drop_") ? CASING_SAMPLE : RELOAD_SAMPLE;
|
|
2221
2228
|
return { ...base, ...overrides[sound3.id] };
|
|
2222
2229
|
}
|
|
2223
2230
|
function weaponSampleBank(options) {
|
|
@@ -2268,6 +2275,8 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2268
2275
|
void loadSamples();
|
|
2269
2276
|
return false;
|
|
2270
2277
|
};
|
|
2278
|
+
const fittedReady = (id) => sampleIds.has(id) && engine.registry.sound(id) === void 0 && engine.registry.set(id) !== void 0;
|
|
2279
|
+
const hostReady = (id) => sampleReady(id) || engine.registry.sound(id) === void 0 && engine.registry.set(id) !== void 0;
|
|
2271
2280
|
const burst = (config) => {
|
|
2272
2281
|
const id = config.filter === "highpass" ? BURST_IDS.highpass : BURST_IDS.lowpass;
|
|
2273
2282
|
const params = { frequency: config.frequency, q: config.q ?? 0.8, gain: Math.max(config.gain, 2e-4), seconds: config.seconds };
|
|
@@ -2322,6 +2331,11 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2322
2331
|
options.onCasing?.(sound3, "sample");
|
|
2323
2332
|
return;
|
|
2324
2333
|
}
|
|
2334
|
+
if (recorded && fittedReady(recorded)) {
|
|
2335
|
+
if ((input.bounce ?? 0) === 0) channel.play(recorded, { volume: Math.min(1, Math.max(0.2, (input.speed ?? 1.5) / 2.5)) });
|
|
2336
|
+
options.onCasing?.(sound3, "fitted");
|
|
2337
|
+
return;
|
|
2338
|
+
}
|
|
2325
2339
|
for (const voice of sound3.voices) playCasingVoice(voice);
|
|
2326
2340
|
options.onCasing?.(sound3, "synth");
|
|
2327
2341
|
};
|
|
@@ -2346,7 +2360,9 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2346
2360
|
const pitch = 1 + (channel.random() * 2 - 1) * pitchSpread;
|
|
2347
2361
|
const shot = computeGunshot({ ...base, take, pitch }, options.gunshot);
|
|
2348
2362
|
const recorded = shot.samples.close;
|
|
2349
|
-
|
|
2363
|
+
const sample = shot.audible && sampleReady(recorded);
|
|
2364
|
+
const fitted = shot.audible && !sample && fittedReady(recorded);
|
|
2365
|
+
if (sample || fitted) {
|
|
2350
2366
|
const occlusion = Math.min(1, Math.max(0, extras.occlusion ?? 0));
|
|
2351
2367
|
const handle = channel.play(recorded, {
|
|
2352
2368
|
volume: (extras.volume ?? 1) * (1 - 0.5 * occlusion),
|
|
@@ -2359,7 +2375,7 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2359
2375
|
for (const voice of shot.voices) if (voice.group === "tail") playGunshotVoice(voice, extras);
|
|
2360
2376
|
}
|
|
2361
2377
|
const layers = handle?.kind === "sample" ? handle.plan.layers.map((layer) => ({ role: layer.role, take: layer.take, rate: layer.rate })) : [];
|
|
2362
|
-
options.onGunshot?.(shot, { ...base, take, pitch }, { kind: "sample", id: recorded, layers });
|
|
2378
|
+
options.onGunshot?.(shot, { ...base, take, pitch }, fitted ? { kind: "fitted", id: recorded } : { kind: "sample", id: recorded, layers });
|
|
2363
2379
|
return;
|
|
2364
2380
|
}
|
|
2365
2381
|
for (const voice of shot.voices) playGunshotVoice(voice, extras);
|
|
@@ -2380,10 +2396,11 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2380
2396
|
mechanical,
|
|
2381
2397
|
cue(id, profile, lead = 0) {
|
|
2382
2398
|
const carriedBy = CUES_IN_SAMPLE[id];
|
|
2383
|
-
if (carriedBy && sampleReady(carriedBy)) return "carried";
|
|
2384
|
-
|
|
2399
|
+
if (carriedBy && (sampleReady(carriedBy) || fittedReady(carriedBy))) return "carried";
|
|
2400
|
+
const recorded = sampleReady(id);
|
|
2401
|
+
if (recorded || fittedReady(id)) {
|
|
2385
2402
|
channel.play(id, { intensity: WEIGHT_INTENSITY[(profile ?? DEFAULT_SOUND_PROFILE).weight] });
|
|
2386
|
-
return "sample";
|
|
2403
|
+
return recorded ? "sample" : "fitted";
|
|
2387
2404
|
}
|
|
2388
2405
|
for (const entry of reloadCueSteps(id, profile, cueTable)) step(lead > 0 ? { ...entry, at: entry.at + lead } : entry);
|
|
2389
2406
|
return "synth";
|
|
@@ -2410,7 +2427,12 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2410
2427
|
casing,
|
|
2411
2428
|
flyby(input) {
|
|
2412
2429
|
const sound3 = computeFlyby({ ...input, pitch: input.pitch ?? 1 + (channel.random() * 2 - 1) * 0.05 });
|
|
2413
|
-
|
|
2430
|
+
if (!sound3) return;
|
|
2431
|
+
if (hostReady(sound3.sample)) {
|
|
2432
|
+
channel.play(sound3.sample, { volume: Math.min(1, 1 / (1 + input.miss / 1.5)), ...input.position ? { position: [input.position[0], input.position[1], input.position[2]] } : {} });
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
for (const voice of sound3.voices) playCasingVoice(voice, input.position);
|
|
2414
2436
|
},
|
|
2415
2437
|
clink() {
|
|
2416
2438
|
if (options.shotModel !== "game") {
|
|
@@ -2421,8 +2443,13 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2421
2443
|
const at = 0.25 + channel.random() * 0.2;
|
|
2422
2444
|
sceneTone({ layer: "scene_tone", at, wave: "sine", f, f2: 3800, d: 0.06, vol: 0.06 });
|
|
2423
2445
|
},
|
|
2424
|
-
thud() {
|
|
2425
|
-
|
|
2446
|
+
thud(input = {}) {
|
|
2447
|
+
const id = magazineDropId(input.surface);
|
|
2448
|
+
if (hostReady(id)) {
|
|
2449
|
+
if ((input.bounce ?? 0) === 0) channel.play(id, { volume: Math.min(1, Math.max(0.2, (input.speed ?? 1.5) / 2.5)) });
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
burst({ seconds: 0.09, gain: 0.35 * (input.speed === void 0 ? 1 : Math.min(1, Math.max(0.3, input.speed / 2.5))) * ((input.bounce ?? 0) === 0 ? 1 : 0.5), filter: "lowpass", frequency: 380 });
|
|
2426
2453
|
},
|
|
2427
2454
|
setVolume(next) {
|
|
2428
2455
|
const level = Math.max(0, Math.min(1, next));
|
|
@@ -2436,6 +2463,7 @@ function createWeaponAudio(volume = 0.6, options = {}) {
|
|
|
2436
2463
|
}
|
|
2437
2464
|
|
|
2438
2465
|
// src/box-fx.ts
|
|
2466
|
+
import { FIRST_PERSON_LAYER } from "@adatechnology/game-contracts";
|
|
2439
2467
|
import { createRng, muzzleEnergy as muzzleEnergy3 } from "@adatechnology/game-weapons";
|
|
2440
2468
|
|
|
2441
2469
|
// src/fx/layers.ts
|
|
@@ -2497,6 +2525,8 @@ var BODY_HOLD = {
|
|
|
2497
2525
|
shoulderGripTo: 0.34,
|
|
2498
2526
|
/** The farthest apart two hands hold a long gun (a support hand past it slides back along the gun, toward the grip). */
|
|
2499
2527
|
supportSpan: 0.5,
|
|
2528
|
+
/** A gun carried at the hip (`carry: 'hip'`): its rear grip low at the right hip, a little ahead of it. */
|
|
2529
|
+
hipGrip: [0.25, -0.5, -0.24],
|
|
2500
2530
|
/** How much of the aim's pitch a tool held low in one hand follows. */
|
|
2501
2531
|
toolPitch: 0.5
|
|
2502
2532
|
};
|
|
@@ -2508,6 +2538,7 @@ var placeOnBody = (placement) => {
|
|
|
2508
2538
|
holder.scale.setScalar(1);
|
|
2509
2539
|
hand.position.set(...frame.position);
|
|
2510
2540
|
hand.quaternion.set(...frame.quaternion);
|
|
2541
|
+
if (placement.hip) return placeAtHip(placement);
|
|
2511
2542
|
const shouldered = !placement.handgun && placement.left !== null && placement.lengthMetres >= BODY_HOLD.shoulderedFrom;
|
|
2512
2543
|
if (shouldered) {
|
|
2513
2544
|
holder.position.set(0, BODY_HOLD.boreAbove, placement.rear);
|
|
@@ -2531,6 +2562,21 @@ var placeOnBody = (placement) => {
|
|
|
2531
2562
|
hand.position.add(want.sub(now));
|
|
2532
2563
|
hand.updateMatrixWorld(true);
|
|
2533
2564
|
};
|
|
2565
|
+
var placeAtHip = (placement) => {
|
|
2566
|
+
const { THREE, hand, holder } = placement;
|
|
2567
|
+
scratch.a ?? (scratch.a = new THREE.Vector3());
|
|
2568
|
+
scratch.b ?? (scratch.b = new THREE.Vector3());
|
|
2569
|
+
scratch.euler ?? (scratch.euler = new THREE.Euler());
|
|
2570
|
+
const turn2 = scratch.euler.setFromQuaternion(hand.quaternion, "YXZ");
|
|
2571
|
+
hand.quaternion.setFromEuler(turn2.set(turn2.x * BODY_HOLD.toolPitch, turn2.y, 0, "YXZ"));
|
|
2572
|
+
holder.position.set(0, 0, 0);
|
|
2573
|
+
hand.updateMatrixWorld(true);
|
|
2574
|
+
if (!placement.right) return;
|
|
2575
|
+
const want = scratch.a.set(...BODY_HOLD.hipGrip).applyQuaternion(hand.quaternion).add(hand.position);
|
|
2576
|
+
const now = placement.right.part.localToWorld(scratch.b.copy(placement.right.offset));
|
|
2577
|
+
hand.position.add(want.sub(now));
|
|
2578
|
+
hand.updateMatrixWorld(true);
|
|
2579
|
+
};
|
|
2534
2580
|
var limitSupportSpan = (targets) => {
|
|
2535
2581
|
const right = targets.R?.position;
|
|
2536
2582
|
const left = targets.L?.position;
|
|
@@ -2554,6 +2600,57 @@ var createEyeFrame = (THREE, camera) => {
|
|
|
2554
2600
|
};
|
|
2555
2601
|
};
|
|
2556
2602
|
|
|
2603
|
+
// src/fx/view-hold.ts
|
|
2604
|
+
var VIEW_HOLD = {
|
|
2605
|
+
long: { grip: [0.2, -0.28, -0.38], fov: 84, sights: { distance: 0.34, fromRear: 0.3 } },
|
|
2606
|
+
handgun: { grip: [0.13, -0.15, -0.33], fov: 76, sights: { distance: 0.42, fromRear: 0.02 } },
|
|
2607
|
+
launcher: { grip: [0.2, -0.14, -0.3], fov: 84, sights: { distance: 0.18, fromRear: 0.25 } },
|
|
2608
|
+
tool: { grip: [0.21, -0.31, -0.36], fov: 84, sights: { distance: 0.3, fromRear: 0.1 }, tilt: 0.3 },
|
|
2609
|
+
// the gun at the hip, its rear grip low and to the right, the barrels ahead of the body; it has no sights to bring to the eye
|
|
2610
|
+
hip: { grip: [0.44, -0.32, -0.42], fov: 84, sights: { distance: 0.6, fromRear: 0.1 } }
|
|
2611
|
+
};
|
|
2612
|
+
var NO_GRIP_REAR = [0.2, -0.24, -0.12];
|
|
2613
|
+
var SIGHT_UNDER_EYE = 0.02;
|
|
2614
|
+
var scratch2 = { grip: null, hip: null, aimed: null, tilt: null };
|
|
2615
|
+
var ease = (k) => k * k * (3 - 2 * k);
|
|
2616
|
+
function placeInView(placement) {
|
|
2617
|
+
const { THREE, hand, holder, camera, right, box: box2, kind } = placement;
|
|
2618
|
+
const hold = VIEW_HOLD[kind];
|
|
2619
|
+
holder.scale.setScalar(1);
|
|
2620
|
+
hand.position.copy(camera.position);
|
|
2621
|
+
hand.quaternion.copy(camera.quaternion);
|
|
2622
|
+
if (hold.tilt) {
|
|
2623
|
+
scratch2.tilt ?? (scratch2.tilt = new THREE.Quaternion());
|
|
2624
|
+
hand.quaternion.multiply(scratch2.tilt.setFromAxisAngle(new THREE.Vector3(1, 0, 0), hold.tilt));
|
|
2625
|
+
}
|
|
2626
|
+
hand.scale.setScalar(1);
|
|
2627
|
+
holder.position.set(0, 0, 0);
|
|
2628
|
+
hand.updateMatrixWorld(true);
|
|
2629
|
+
scratch2.hip ?? (scratch2.hip = new THREE.Vector3());
|
|
2630
|
+
const hip = scratch2.hip;
|
|
2631
|
+
if (right) {
|
|
2632
|
+
scratch2.grip ?? (scratch2.grip = new THREE.Vector3());
|
|
2633
|
+
const grip = hand.worldToLocal(right.part.localToWorld(scratch2.grip.copy(right.offset)));
|
|
2634
|
+
hip.set(hold.grip[0] - grip.x, hold.grip[1] - grip.y, hold.grip[2] - grip.z);
|
|
2635
|
+
} else hip.set(NO_GRIP_REAR[0], NO_GRIP_REAR[1], NO_GRIP_REAR[2] + box2.min.x);
|
|
2636
|
+
const k = ease(Math.max(0, Math.min(1, placement.aim)));
|
|
2637
|
+
if (k > 0) {
|
|
2638
|
+
const along = box2.min.x + hold.sights.fromRear;
|
|
2639
|
+
scratch2.aimed ?? (scratch2.aimed = new THREE.Vector3());
|
|
2640
|
+
hip.lerp(scratch2.aimed.set(0, -SIGHT_UNDER_EYE - box2.max.y, along - hold.sights.distance), k);
|
|
2641
|
+
}
|
|
2642
|
+
holder.position.copy(hip).add(placement.offset);
|
|
2643
|
+
hand.updateMatrixWorld(true);
|
|
2644
|
+
}
|
|
2645
|
+
function toWorldLens(camera, point, worldFov, lensFov) {
|
|
2646
|
+
const k = Math.tan(worldFov * Math.PI / 360) / Math.tan(lensFov * Math.PI / 360);
|
|
2647
|
+
camera.updateMatrixWorld();
|
|
2648
|
+
camera.worldToLocal(point);
|
|
2649
|
+
point.x *= k;
|
|
2650
|
+
point.y *= k;
|
|
2651
|
+
return camera.localToWorld(point);
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2557
2654
|
// src/fx/hand-hold.ts
|
|
2558
2655
|
var HAND_HOLD = {
|
|
2559
2656
|
/** Half the width of a pistol grip, the palm sits this far (plus the palm) off its middle. */
|
|
@@ -2632,6 +2729,7 @@ function holdOfModel(THREE, model, holderScale) {
|
|
|
2632
2729
|
const toRoot = (x, y, z) => V(THREE, inner.position.x + x * inner.scale.x, y, z);
|
|
2633
2730
|
const handgun = parts.family === "pistol" || parts.family === "revolver";
|
|
2634
2731
|
root.updateWorldMatrix(true, true);
|
|
2732
|
+
if (parts.holdRight || parts.holdLeft) return namedHold(THREE, model, holderScale);
|
|
2635
2733
|
let gripAt;
|
|
2636
2734
|
let up;
|
|
2637
2735
|
if (parts.grip) {
|
|
@@ -2678,6 +2776,18 @@ function holdOfModel(THREE, model, holderScale) {
|
|
|
2678
2776
|
gripLeft: HAND_HOLD.forend
|
|
2679
2777
|
};
|
|
2680
2778
|
}
|
|
2779
|
+
function namedHold(THREE, model, holderScale) {
|
|
2780
|
+
const { parts } = model;
|
|
2781
|
+
const on = (mark, fingers) => mark ? { part: mark, offset: V(THREE, 0, HAND_HOLD.palm, 0), quaternion: handFrame(THREE, fingers, V(THREE, 0, 1, 0)) } : null;
|
|
2782
|
+
return {
|
|
2783
|
+
right: on(parts.holdRight, V(THREE, 1, 0, 0)),
|
|
2784
|
+
left: on(parts.holdLeft, V(THREE, 0, 0, 1)),
|
|
2785
|
+
leftMagazine: null,
|
|
2786
|
+
scale: holderScale,
|
|
2787
|
+
grip: HAND_HOLD.closed,
|
|
2788
|
+
gripLeft: HAND_HOLD.closed
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2681
2791
|
function holdOfProp(THREE, group, category, scale) {
|
|
2682
2792
|
const handle = group.getObjectByName("handle") ?? group.getObjectByName("body") ?? group;
|
|
2683
2793
|
const head = group.getObjectByName("head");
|
|
@@ -3186,6 +3296,15 @@ function createBoxFx(options) {
|
|
|
3186
3296
|
const q2 = new THREE.Quaternion();
|
|
3187
3297
|
const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
|
|
3188
3298
|
const frontOf = new THREE.Vector3();
|
|
3299
|
+
const restBox = new THREE.Box3();
|
|
3300
|
+
const noMotion = new THREE.Vector3();
|
|
3301
|
+
let inLens = false;
|
|
3302
|
+
const setLens = (on) => {
|
|
3303
|
+
if (on !== inLens) options.viewmodelFov?.(on ? VIEW_HOLD.tool.fov : null);
|
|
3304
|
+
inLens = on;
|
|
3305
|
+
const layer = on ? FIRST_PERSON_LAYER : 0;
|
|
3306
|
+
hand.traverse((node) => node.layers.set(layer));
|
|
3307
|
+
};
|
|
3189
3308
|
const equip = (weapon) => {
|
|
3190
3309
|
if (model) hand.remove(model);
|
|
3191
3310
|
model = null;
|
|
@@ -3193,6 +3312,8 @@ function createBoxFx(options) {
|
|
|
3193
3312
|
def = arsenal.weapons.get(weapon) ?? null;
|
|
3194
3313
|
if (!def || BODY_ACTIONS.has(def.id)) return;
|
|
3195
3314
|
model = buildWeaponModel(THREE, def, options.items);
|
|
3315
|
+
restBox.setFromObject(model);
|
|
3316
|
+
inLens = false;
|
|
3196
3317
|
const length = new THREE.Box3().setFromObject(model).getSize(v).x;
|
|
3197
3318
|
rear = new THREE.Box3().setFromObject(model).min.x;
|
|
3198
3319
|
lengthMetres = length;
|
|
@@ -3293,6 +3414,7 @@ function createBoxFx(options) {
|
|
|
3293
3414
|
return {
|
|
3294
3415
|
unequip() {
|
|
3295
3416
|
if (model) hand.remove(model);
|
|
3417
|
+
if (inLens) setLens(false);
|
|
3296
3418
|
model = null;
|
|
3297
3419
|
inHand = null;
|
|
3298
3420
|
hold = null;
|
|
@@ -3301,6 +3423,7 @@ function createBoxFx(options) {
|
|
|
3301
3423
|
if (!model || !def || !SPOUTS.has(def.action.kind)) return null;
|
|
3302
3424
|
hand.updateMatrixWorld(true);
|
|
3303
3425
|
const at = model.localToWorld(v.copy(frontOf));
|
|
3426
|
+
if (inLens) toWorldLens(camera, at, camera.fov ?? 72, VIEW_HOLD.tool.fov);
|
|
3304
3427
|
const along = v2.set(1, 0, 0).applyQuaternion(model.getWorldQuaternion(q2)).normalize();
|
|
3305
3428
|
return { position: [at.x, at.y, at.z], direction: [along.x, along.y, along.z], drawsFlash: false };
|
|
3306
3429
|
},
|
|
@@ -3323,7 +3446,13 @@ function createBoxFx(options) {
|
|
|
3323
3446
|
update(dt) {
|
|
3324
3447
|
kick *= Math.exp(-dt * 14);
|
|
3325
3448
|
dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8);
|
|
3326
|
-
if (inHand && model) {
|
|
3449
|
+
if (inHand && model && options.firstPerson?.()) {
|
|
3450
|
+
setLens(true);
|
|
3451
|
+
placeInView({ THREE, hand, holder: model, camera, right: hold?.right ?? null, box: restBox, kind: "tool", aim: 0, offset: noMotion });
|
|
3452
|
+
hand.position.add(v.set(0, -0.12 * dip, 0.05 * kick).applyQuaternion(camera.quaternion));
|
|
3453
|
+
hand.updateMatrixWorld(true);
|
|
3454
|
+
} else if (inHand && model) {
|
|
3455
|
+
if (inLens) setLens(false);
|
|
3327
3456
|
const handgun = def?.category === "pistol" || def?.category === "revolver";
|
|
3328
3457
|
placeOnBody({ THREE, hand, holder: model, frame: options.bodyHold?.() ?? eyeFrame(), rear, lengthMetres, handgun, right: hold?.right ?? null, left: hold?.left ?? null });
|
|
3329
3458
|
hand.position.add(v.set(0, -0.12 * dip, 0.05 * kick).applyQuaternion(camera.quaternion));
|
|
@@ -5868,6 +5997,9 @@ function buildHousing(kit, parts, group) {
|
|
|
5868
5997
|
cylinderZ(group, 0.016, 0.07, M.poly, 0.06, -0.01, side * 0.2, 14);
|
|
5869
5998
|
}
|
|
5870
5999
|
parts.trigger = box2(group, 0.012, 0.02, 0.016, M.dark, 0.1, 0.085, 0.14, 0.3);
|
|
6000
|
+
parts.holdRight = kit.mark(group, 0.06, 6e-3, 0.2);
|
|
6001
|
+
parts.holdLeft = kit.mark(group, 0.22, 0.164, 0);
|
|
6002
|
+
parts.carry = "hip";
|
|
5871
6003
|
parts.safety = box2(group, 0.014, 6e-3, 0.01, M.dark, 0.2, 0.073, -0.076, 0.4);
|
|
5872
6004
|
}
|
|
5873
6005
|
function buildFeed(kit, parts, group) {
|
|
@@ -6295,7 +6427,7 @@ function buildWeaponModelById(THREE, id) {
|
|
|
6295
6427
|
}
|
|
6296
6428
|
|
|
6297
6429
|
// src/fx/viewmodel.ts
|
|
6298
|
-
import { createToolClipPlayer, toolAnimationStep, warnOnce as warnOnce2 } from "@adatechnology/game-contracts";
|
|
6430
|
+
import { createToolClipPlayer, FIRST_PERSON_LAYER as FIRST_PERSON_LAYER2, toolAnimationStep, warnOnce as warnOnce2 } from "@adatechnology/game-contracts";
|
|
6299
6431
|
import { discardObject3D as discardObject3D3 } from "@adatechnology/game-shapes-three";
|
|
6300
6432
|
import { createRng as createRng2, muzzleEnergy as muzzleEnergy4 } from "@adatechnology/game-weapons";
|
|
6301
6433
|
|
|
@@ -6305,7 +6437,7 @@ var RELOAD_SAMPLE_HITS2 = reload_sample_hits_default.hits;
|
|
|
6305
6437
|
var ROOT = "root";
|
|
6306
6438
|
var AXIS_INDEX = { x: 0, y: 1, z: 2 };
|
|
6307
6439
|
var EASE_STEPS = 10;
|
|
6308
|
-
var
|
|
6440
|
+
var ease2 = (progress) => progress < 0.5 ? 2 * progress * progress : 1 - (-2 * progress + 2) ** 2 / 2;
|
|
6309
6441
|
var move = (part, axis, start, duration, from, to) => ({ part, channel: "position", axis, start, duration, from, to });
|
|
6310
6442
|
var turn = (part, axis, start, duration, from, to) => ({ part, channel: "rotation", axis, start, duration, from, to });
|
|
6311
6443
|
var cue = (t, type, data) => ({ t, type, ...data ? { data } : {} });
|
|
@@ -6319,7 +6451,7 @@ var valueAt = (tweens, t, includeInstants) => {
|
|
|
6319
6451
|
for (const tween of tweens) {
|
|
6320
6452
|
if (tween.start > t || tween.duration === 0 && (tween.start === t ? !includeInstants : false)) continue;
|
|
6321
6453
|
if (tween.duration === 0 || t >= tween.start + tween.duration) value = tween.to;
|
|
6322
|
-
else value = tween.from + (tween.to - tween.from) *
|
|
6454
|
+
else value = tween.from + (tween.to - tween.from) * ease2((t - tween.start) / tween.duration);
|
|
6323
6455
|
}
|
|
6324
6456
|
return value;
|
|
6325
6457
|
};
|
|
@@ -7401,38 +7533,6 @@ function createEffects(THREE, sink, random) {
|
|
|
7401
7533
|
};
|
|
7402
7534
|
}
|
|
7403
7535
|
|
|
7404
|
-
// src/fx/view-hold.ts
|
|
7405
|
-
var VIEW_HOLD = {
|
|
7406
|
-
/** The right hand's grip, from the eye. */
|
|
7407
|
-
grip: [0.2, -0.2, -0.3],
|
|
7408
|
-
/** A handgun's grip: higher and nearer the middle, the arms out in front. */
|
|
7409
|
-
handgunGrip: [0.13, -0.15, -0.33],
|
|
7410
|
-
/** A rocket launcher's grip: its tube rides on the shoulder, so the hands are higher. */
|
|
7411
|
-
launcherGrip: [0.2, -0.14, -0.3],
|
|
7412
|
-
/** A gun with no grip anchor: the rear of its bore, from the eye. */
|
|
7413
|
-
rear: [0.2, -0.24, -0.12]
|
|
7414
|
-
};
|
|
7415
|
-
var scratch2 = { grip: null };
|
|
7416
|
-
function placeInView(placement) {
|
|
7417
|
-
const { THREE, hand, holder, camera, right } = placement;
|
|
7418
|
-
holder.scale.setScalar(1);
|
|
7419
|
-
hand.position.copy(camera.position);
|
|
7420
|
-
hand.quaternion.copy(camera.quaternion);
|
|
7421
|
-
hand.scale.setScalar(1);
|
|
7422
|
-
holder.position.set(0, 0, 0);
|
|
7423
|
-
hand.updateMatrixWorld(true);
|
|
7424
|
-
if (!right) {
|
|
7425
|
-
holder.position.set(VIEW_HOLD.rear[0], VIEW_HOLD.rear[1], VIEW_HOLD.rear[2] + placement.rear);
|
|
7426
|
-
hand.updateMatrixWorld(true);
|
|
7427
|
-
return;
|
|
7428
|
-
}
|
|
7429
|
-
scratch2.grip ?? (scratch2.grip = new THREE.Vector3());
|
|
7430
|
-
const grip = hand.worldToLocal(right.part.localToWorld(scratch2.grip.copy(right.offset)));
|
|
7431
|
-
const [x, y, z] = placement.kind === "handgun" ? VIEW_HOLD.handgunGrip : placement.kind === "launcher" ? VIEW_HOLD.launcherGrip : VIEW_HOLD.grip;
|
|
7432
|
-
holder.position.set(x - grip.x, y - grip.y, z - grip.z);
|
|
7433
|
-
hand.updateMatrixWorld(true);
|
|
7434
|
-
}
|
|
7435
|
-
|
|
7436
7536
|
// src/fx/heavy.ts
|
|
7437
7537
|
import { LIGHT_PRIORITY as LIGHT_PRIORITY2, geometryKey, materialKey } from "@adatechnology/game-render-three";
|
|
7438
7538
|
import { discardObject3D } from "@adatechnology/game-shapes-three";
|
|
@@ -7821,11 +7921,12 @@ function createClipEventHandler(world, session) {
|
|
|
7821
7921
|
switch (event.type) {
|
|
7822
7922
|
case "muzzle_flash":
|
|
7823
7923
|
if (data.at === "ugl") {
|
|
7824
|
-
if (parts.uglMuzzle && world.layers.muzzleFlash) effects.muzzleFlash(positionOf(parts.uglMuzzle), root.getWorldQuaternion(new THREE.Quaternion()), UGL_FLASH);
|
|
7924
|
+
if (parts.uglMuzzle && world.layers.muzzleFlash) world.afterPose(() => parts.uglMuzzle && effects.muzzleFlash(positionOf(parts.uglMuzzle), root.getWorldQuaternion(new THREE.Quaternion()), UGL_FLASH));
|
|
7825
7925
|
} else if (parts.muzzle && world.layers.muzzleFlash && (session.def.action.kind === "firearm" || session.def.action.kind === "launcher")) {
|
|
7826
7926
|
const profile = muzzleSmokeOf({ caliber: session.caliber, weapon: session.def, suppressed: session.suppressed });
|
|
7827
7927
|
const size = session.def.action.kind === "launcher" ? 2.2 : FLASH_BY_CASE[session.caseType];
|
|
7828
|
-
|
|
7928
|
+
const muzzle = parts.muzzle;
|
|
7929
|
+
world.afterPose(() => effects.muzzleFlash(positionOf(muzzle), root.getWorldQuaternion(new THREE.Quaternion()), size, profile));
|
|
7829
7930
|
}
|
|
7830
7931
|
break;
|
|
7831
7932
|
case "eject_casing":
|
|
@@ -8366,7 +8467,9 @@ function workWeights(works, running) {
|
|
|
8366
8467
|
|
|
8367
8468
|
// src/fx/viewmodel.ts
|
|
8368
8469
|
var PI14 = Math.PI;
|
|
8369
|
-
var HEAT_DISPLAY_MAX_FALLBACK =
|
|
8470
|
+
var HEAT_DISPLAY_MAX_FALLBACK = 750;
|
|
8471
|
+
var SMOKE_WISPS_PER_SECOND = 10;
|
|
8472
|
+
var SMOKE_THICKNESS_GAIN = 1.7;
|
|
8370
8473
|
var WALL_PULL = { margin: 0.03, max: 1.2, rate: 14, probe: 3 };
|
|
8371
8474
|
var HAND_IN_VIEW = { floor: -0.88, max: 0.3, rate: 10 };
|
|
8372
8475
|
var LAUNCH_VOLUME = 0.6;
|
|
@@ -8385,6 +8488,7 @@ var indexCurl = (turn2, sinceShot) => {
|
|
|
8385
8488
|
const held = sinceShot < TRIGGER_FINGER.burstSeconds ? TRIGGER_FINGER.pulled - TRIGGER_FINGER.tremor * (0.5 + 0.5 * Math.sin(sinceShot * 220)) : 0;
|
|
8386
8489
|
return Math.max(ready + (TRIGGER_FINGER.pulled - ready) * pull, held);
|
|
8387
8490
|
};
|
|
8491
|
+
var VIEW_MOTION = { sway: 6e-3, swayMax: 0.02, swayRate: 9, bobX: 6e-3, bobY: 8e-3, run: 1.6, fullSpeed: 4, frequency: 3.2 };
|
|
8388
8492
|
function createViewmodel(options) {
|
|
8389
8493
|
const { THREE, scene, camera, arsenal, groundAt, audio } = options;
|
|
8390
8494
|
const kit = modelKit(THREE);
|
|
@@ -8392,16 +8496,33 @@ function createViewmodel(options) {
|
|
|
8392
8496
|
const layers = options.layers ?? createLayers();
|
|
8393
8497
|
const distance = () => options.soundDistance?.() ?? 0;
|
|
8394
8498
|
const particles = createParticles(THREE, scene, groundAt, options.particleLimit);
|
|
8499
|
+
const spawnParticle = particles.spawn.bind(particles);
|
|
8500
|
+
particles.spawn = (object, velocity, particleOptions) => {
|
|
8501
|
+
if (lens) toWorldLens(camera, object.position, worldFovOf(), lens.fov);
|
|
8502
|
+
spawnParticle(object, velocity, particleOptions);
|
|
8503
|
+
};
|
|
8395
8504
|
particles.onBounce = (kind, hit) => {
|
|
8396
|
-
if (kind
|
|
8397
|
-
if (kind !== "casing" || !audio) return;
|
|
8398
|
-
const caseType = hit?.object.userData["caseType"];
|
|
8505
|
+
if (!audio || kind !== "casing" && kind !== "magazine") return;
|
|
8399
8506
|
const surface = hit ? options.surfaceAt?.(hit.object.position.x, hit.object.position.z) : void 0;
|
|
8507
|
+
if (kind === "magazine") {
|
|
8508
|
+
audio.thud({ ...surface ? { surface } : {}, ...hit ? { speed: hit.speed, bounce: hit.bounce } : {} });
|
|
8509
|
+
return;
|
|
8510
|
+
}
|
|
8511
|
+
const caseType = hit?.object.userData["caseType"];
|
|
8400
8512
|
audio.casing({ ...caseType ? { caseType } : {}, ...surface ? { surface } : {}, ...hit ? { speed: hit.speed, bounce: hit.bounce } : {} });
|
|
8401
8513
|
};
|
|
8402
8514
|
if (!options.effects) warnOnce2("game-weapons-three.own-effects-world", "game-weapons-three made an effects world of its own because the host lent none (options.effects); pass the host effects so the effects share one budget");
|
|
8403
8515
|
const ownVfx = options.effects ? void 0 : createVfxRenderer(THREE, { scene, camera, vfx: { profile: "medium" }, ...options.lights ? { lights: options.lights } : {} });
|
|
8404
|
-
const
|
|
8516
|
+
const worldSink = options.effects ?? ownVfx;
|
|
8517
|
+
const lensSink = worldSink ? {
|
|
8518
|
+
play: (request) => {
|
|
8519
|
+
if (!lens || !request.point) return worldSink.play(request);
|
|
8520
|
+
const at = toWorldLens(camera, lensPoint.set(...request.point), worldFovOf(), lens.fov);
|
|
8521
|
+
return worldSink.play({ ...request, point: [at.x, at.y, at.z] });
|
|
8522
|
+
},
|
|
8523
|
+
stop: (handle) => worldSink.stop(handle)
|
|
8524
|
+
} : void 0;
|
|
8525
|
+
const effects = createEffects(THREE, lensSink, random);
|
|
8405
8526
|
const hand = new THREE.Group();
|
|
8406
8527
|
hand.name = VIEWMODEL_NODE_NAME;
|
|
8407
8528
|
scene.add(hand);
|
|
@@ -8427,6 +8548,7 @@ function createViewmodel(options) {
|
|
|
8427
8548
|
const rootQuaternion = new THREE.Quaternion();
|
|
8428
8549
|
const muzzleAt = new THREE.Vector3();
|
|
8429
8550
|
const muzzleAlong = new THREE.Vector3();
|
|
8551
|
+
const posed = [];
|
|
8430
8552
|
const world = {
|
|
8431
8553
|
kit,
|
|
8432
8554
|
scene,
|
|
@@ -8440,13 +8562,17 @@ function createViewmodel(options) {
|
|
|
8440
8562
|
const played = audio?.cue(cue2, profile, lead);
|
|
8441
8563
|
options.onSound?.({ weapon, cue: cue2, profile, ...played ? { played } : {} });
|
|
8442
8564
|
},
|
|
8443
|
-
|
|
8565
|
+
afterPose: (emit) => void posed.push(emit),
|
|
8566
|
+
schedule: (delay, seconds, step, done) => void tasks.push({ delay, seconds, elapsed: 0, step: (progress) => step(ease2(progress)), done })
|
|
8444
8567
|
};
|
|
8445
8568
|
const heatMax = arsenal.rules.heat.displayMax || HEAT_DISPLAY_MAX_FALLBACK;
|
|
8446
8569
|
const visual = arsenal.rules.heat.visual;
|
|
8447
8570
|
const hasSuppressor = (parts) => parts.some((part) => arsenal.attachments.get(part.id)?.kind === "suppressor");
|
|
8448
8571
|
const release = () => {
|
|
8449
8572
|
if (!session) return;
|
|
8573
|
+
if (lens) options.viewmodelFov?.(null);
|
|
8574
|
+
lens = null;
|
|
8575
|
+
lensLayer = -1;
|
|
8450
8576
|
session.attachments?.dispose();
|
|
8451
8577
|
discardObject3D3(session.model.root);
|
|
8452
8578
|
player = null;
|
|
@@ -8486,6 +8612,9 @@ function createViewmodel(options) {
|
|
|
8486
8612
|
const modelBox = new THREE.Box3().setFromObject(model.root);
|
|
8487
8613
|
const length = modelBox.getSize(new THREE.Vector3()).x;
|
|
8488
8614
|
modelRear = modelBox.min.x;
|
|
8615
|
+
restBox.copy(modelBox);
|
|
8616
|
+
lens = null;
|
|
8617
|
+
lensLayer = -1;
|
|
8489
8618
|
holder.add(model.root);
|
|
8490
8619
|
const caliber = caliberOf(arsenal, def);
|
|
8491
8620
|
const dims = caliber?.cartridge;
|
|
@@ -8548,6 +8677,42 @@ function createViewmodel(options) {
|
|
|
8548
8677
|
let reach = 0;
|
|
8549
8678
|
let pull = 0;
|
|
8550
8679
|
let lift = 0;
|
|
8680
|
+
const restBox = new THREE.Box3();
|
|
8681
|
+
let lens = null;
|
|
8682
|
+
let lensLayer = -1;
|
|
8683
|
+
const lensPoint = new THREE.Vector3();
|
|
8684
|
+
const worldFovOf = () => camera.fov ?? 72;
|
|
8685
|
+
const setLens = (kind) => {
|
|
8686
|
+
const fov = kind ? VIEW_HOLD[kind].fov : null;
|
|
8687
|
+
if ((lens?.fov ?? null) !== fov) options.viewmodelFov?.(fov);
|
|
8688
|
+
lens = kind && fov !== null ? { kind, fov } : null;
|
|
8689
|
+
const layer = lens ? FIRST_PERSON_LAYER2 : 0;
|
|
8690
|
+
if (layer === lensLayer && !lens) return;
|
|
8691
|
+
lensLayer = layer;
|
|
8692
|
+
hand.traverse((node) => node.layers.set(layer));
|
|
8693
|
+
};
|
|
8694
|
+
const motion = { phase: 0, swayX: 0, swayY: 0, lastYaw: Number.NaN, lastPitch: Number.NaN };
|
|
8695
|
+
const motionEuler = new THREE.Euler(0, 0, 0, "YXZ");
|
|
8696
|
+
const motionOffset = new THREE.Vector3();
|
|
8697
|
+
const viewMotion = (dt, aim) => {
|
|
8698
|
+
const moving = options.motion?.() ?? { speed: 0, running: false };
|
|
8699
|
+
const calm = 1 - 0.8 * Math.max(0, Math.min(1, aim));
|
|
8700
|
+
motionEuler.setFromQuaternion(camera.quaternion, "YXZ");
|
|
8701
|
+
const turnYaw = Number.isNaN(motion.lastYaw) || dt <= 0 ? 0 : Math.atan2(Math.sin(motionEuler.y - motion.lastYaw), Math.cos(motionEuler.y - motion.lastYaw)) / dt;
|
|
8702
|
+
const turnPitch = Number.isNaN(motion.lastPitch) || dt <= 0 ? 0 : (motionEuler.x - motion.lastPitch) / dt;
|
|
8703
|
+
motion.lastYaw = motionEuler.y;
|
|
8704
|
+
motion.lastPitch = motionEuler.x;
|
|
8705
|
+
const follow = Math.min(1, dt * VIEW_MOTION.swayRate);
|
|
8706
|
+
motion.swayX += (Math.max(-VIEW_MOTION.swayMax, Math.min(VIEW_MOTION.swayMax, turnYaw * VIEW_MOTION.sway)) - motion.swayX) * follow;
|
|
8707
|
+
motion.swayY += (Math.max(-VIEW_MOTION.swayMax, Math.min(VIEW_MOTION.swayMax, -turnPitch * VIEW_MOTION.sway)) - motion.swayY) * follow;
|
|
8708
|
+
const stride = Math.min(1, moving.speed / VIEW_MOTION.fullSpeed) * (moving.running ? VIEW_MOTION.run : 1);
|
|
8709
|
+
motion.phase += dt * moving.speed * VIEW_MOTION.frequency;
|
|
8710
|
+
return motionOffset.set(
|
|
8711
|
+
(motion.swayX + Math.sin(motion.phase) * VIEW_MOTION.bobX * stride) * calm,
|
|
8712
|
+
(motion.swayY - Math.abs(Math.cos(motion.phase)) * VIEW_MOTION.bobY * stride) * calm,
|
|
8713
|
+
0
|
|
8714
|
+
);
|
|
8715
|
+
};
|
|
8551
8716
|
const ahead = new THREE.Vector3();
|
|
8552
8717
|
const up = new THREE.Vector3();
|
|
8553
8718
|
const seen = new THREE.Vector3();
|
|
@@ -8580,13 +8745,17 @@ function createViewmodel(options) {
|
|
|
8580
8745
|
const family = session.model.parts.family;
|
|
8581
8746
|
session.holderScale = 1;
|
|
8582
8747
|
if (options.firstPerson?.()) {
|
|
8583
|
-
|
|
8748
|
+
const kind = session.model.parts.carry === "hip" ? "hip" : family === "pistol" || family === "revolver" ? "handgun" : family === "rocket" ? "launcher" : "long";
|
|
8749
|
+
const aim = kind === "hip" ? 0 : options.aim?.() ?? 0;
|
|
8750
|
+
placeInView({ THREE, hand, holder, camera, right: hold?.right ?? null, box: restBox, kind, aim, offset: viewMotion(dt, aim) });
|
|
8751
|
+
setLens(kind);
|
|
8584
8752
|
lift = 0;
|
|
8585
|
-
|
|
8753
|
+
pull = 0;
|
|
8586
8754
|
return;
|
|
8587
8755
|
}
|
|
8756
|
+
setLens(null);
|
|
8588
8757
|
hand.scale.set(1, 1, 1);
|
|
8589
|
-
placeOnBody({ THREE, hand, holder, frame: options.bodyHold?.() ?? eyeFrame(), rear: modelRear, lengthMetres: session.lengthMetres, handgun: family === "pistol" || family === "revolver", right: hold?.right ?? null, left: hold?.left ?? null });
|
|
8758
|
+
placeOnBody({ THREE, hand, holder, frame: options.bodyHold?.() ?? eyeFrame(), rear: modelRear, lengthMetres: session.lengthMetres, handgun: family === "pistol" || family === "revolver", hip: session.model.parts.carry === "hip", right: hold?.right ?? null, left: hold?.left ?? null });
|
|
8590
8759
|
hand.updateMatrixWorld(true);
|
|
8591
8760
|
lift += (liftToKeepInView() - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
|
|
8592
8761
|
if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera.quaternion), lift);
|
|
@@ -8662,6 +8831,10 @@ function createViewmodel(options) {
|
|
|
8662
8831
|
if (session) {
|
|
8663
8832
|
place(dt);
|
|
8664
8833
|
player?.update(dt);
|
|
8834
|
+
if (posed.length) {
|
|
8835
|
+
hand.updateMatrixWorld(true);
|
|
8836
|
+
for (const emit of posed.splice(0)) emit();
|
|
8837
|
+
}
|
|
8665
8838
|
const wantsMagazine = reloading || options.tool?.().reloading === true;
|
|
8666
8839
|
magazineHand += ((wantsMagazine ? 1 : 0) - magazineHand) * Math.min(1, dt * HAND_HOLD.magRate);
|
|
8667
8840
|
const tool = options.tool?.();
|
|
@@ -8677,10 +8850,11 @@ function createViewmodel(options) {
|
|
|
8677
8850
|
}
|
|
8678
8851
|
secondsSinceShot += dt;
|
|
8679
8852
|
if (tool && tool.heat !== void 0 && tool.heat * heatMax > visual.smokeFrom && secondsSinceShot < visual.smokeSeconds && session.model.parts.muzzle) {
|
|
8680
|
-
|
|
8853
|
+
const smokeLevel = Math.min(1, (tool.heat * heatMax - visual.smokeFrom) / visual.smokeSpan);
|
|
8854
|
+
smokeDebt += dt * smokeLevel * SMOKE_WISPS_PER_SECOND;
|
|
8681
8855
|
while (smokeDebt > 1) {
|
|
8682
8856
|
smokeDebt -= 1;
|
|
8683
|
-
const k = 0.5 +
|
|
8857
|
+
const k = 0.5 + smokeLevel * SMOKE_THICKNESS_GAIN;
|
|
8684
8858
|
effects.smoke(worldPositionOf(session, session.model.parts.muzzle, new THREE.Vector3()), new THREE.Vector3((random() - 0.5) * 0.015, 0.07 + random() * 0.08, (random() - 0.5) * 0.015), 4e-3 * k, 1.2 + random() * 0.6, Math.min(0.18, 0.06 + k * 0.04));
|
|
8685
8859
|
}
|
|
8686
8860
|
}
|
|
@@ -8707,6 +8881,7 @@ function createViewmodel(options) {
|
|
|
8707
8881
|
if (!session || !mark) return null;
|
|
8708
8882
|
hand.updateMatrixWorld(true);
|
|
8709
8883
|
const at = worldPositionOf(session, mark, muzzleAt);
|
|
8884
|
+
if (lens) toWorldLens(camera, at, worldFovOf(), lens.fov);
|
|
8710
8885
|
const along = muzzleAlong.set(1, 0, 0).applyQuaternion(session.model.root.getWorldQuaternion(rootQuaternion)).normalize();
|
|
8711
8886
|
const kind = session.def.action.kind;
|
|
8712
8887
|
return { position: [at.x, at.y, at.z], direction: [along.x, along.y, along.z], drawsFlash: kind === "firearm" || kind === "launcher" };
|
|
@@ -8716,7 +8891,8 @@ function createViewmodel(options) {
|
|
|
8716
8891
|
hand.updateMatrixWorld(true);
|
|
8717
8892
|
const targets = targetsOf(THREE, hold, session.model.root.getWorldQuaternion(rootQuaternion), magazineHand, hold.works ? workWeights(hold.works, player?.running ?? []) : void 0);
|
|
8718
8893
|
const trigger = session.model.parts.trigger;
|
|
8719
|
-
if (
|
|
8894
|
+
if (session.model.parts.carry === "hip") targets.fingers = { index: 1 };
|
|
8895
|
+
else if (trigger) targets.fingers = { index: indexCurl(trigger.rotation.z - triggerRest, clock - lastShot) };
|
|
8720
8896
|
return limitSupportSpan(targets);
|
|
8721
8897
|
},
|
|
8722
8898
|
get inHand() {
|
|
@@ -8766,6 +8942,9 @@ function createWeaponFx(options) {
|
|
|
8766
8942
|
...options.contact ? { contact: options.contact } : {},
|
|
8767
8943
|
...options.bodyHold ? { bodyHold: options.bodyHold } : {},
|
|
8768
8944
|
...options.firstPerson ? { firstPerson: options.firstPerson } : {},
|
|
8945
|
+
...options.viewmodelFov ? { viewmodelFov: options.viewmodelFov } : {},
|
|
8946
|
+
...options.aim ? { aim: options.aim } : {},
|
|
8947
|
+
...options.motion ? { motion: options.motion } : {},
|
|
8769
8948
|
...options.effects ? { effects: options.effects } : {},
|
|
8770
8949
|
...options.flyRocket === void 0 ? {} : { flyRocket: options.flyRocket },
|
|
8771
8950
|
...options.particleLimit ? { particleLimit: options.particleLimit } : {},
|
|
@@ -9858,6 +10037,7 @@ var TEXT = {
|
|
|
9858
10037
|
reloadClip: "Reload of {weapon}: {kind}, clip {clip} ({seconds} s)",
|
|
9859
10038
|
soundCue: "Sound: {cue} ({weapon})",
|
|
9860
10039
|
cueRecorded: " \xB7 recorded",
|
|
10040
|
+
cueFitted: " \xB7 fitted copy of the recording",
|
|
9861
10041
|
cueCarried: " \xB7 inside the next recording",
|
|
9862
10042
|
gunshotSound: "Shot sound: {profile}, take {take}/4, pitch {pitch}, {space}, {distance} m{far}",
|
|
9863
10043
|
gunshotRecorded: " \xB7 recorded take {takes}",
|
|
@@ -9882,7 +10062,7 @@ var TEXT = {
|
|
|
9882
10062
|
fuseLength: "Dynamite fuse length",
|
|
9883
10063
|
fuseLengthHint: "Metres of safety fuse (131 s per metre): a long one is set down lit, for a delayed trap",
|
|
9884
10064
|
layers: { muzzle_flash: "Muzzle flash", casings: "Ejected casings", magazines: "Dropped magazines" },
|
|
9885
|
-
metrics: { casings_live: "Casings in the scene", magazines_live: "Magazines in the scene", heat: "Barrel heat" }
|
|
10065
|
+
metrics: { casings_live: "Casings in the scene", magazines_live: "Magazines in the scene", heat: "Barrel temperature above ambient", rounds_to_warning: "Rounds to the heat warning (cyclic rate, from cold)" }
|
|
9886
10066
|
},
|
|
9887
10067
|
"pt-BR": {
|
|
9888
10068
|
group: "Armas",
|
|
@@ -9893,6 +10073,7 @@ var TEXT = {
|
|
|
9893
10073
|
reloadClip: "Recarga de {weapon}: {kind}, clipe {clip} ({seconds} s)",
|
|
9894
10074
|
soundCue: "Som: {cue} ({weapon})",
|
|
9895
10075
|
cueRecorded: " \xB7 grava\xE7\xE3o",
|
|
10076
|
+
cueFitted: " \xB7 c\xF3pia medida da grava\xE7\xE3o",
|
|
9896
10077
|
cueCarried: " \xB7 dentro da grava\xE7\xE3o seguinte",
|
|
9897
10078
|
gunshotSound: "Som do tiro: {profile}, tomada {take}/4, tom {pitch}, {space}, {distance} m{far}",
|
|
9898
10079
|
gunshotRecorded: " \xB7 grava\xE7\xE3o, tomada {takes}",
|
|
@@ -9917,7 +10098,7 @@ var TEXT = {
|
|
|
9917
10098
|
fuseLength: "Pavio da dinamite",
|
|
9918
10099
|
fuseLengthHint: "Metros de pavio de seguran\xE7a (131 s por metro): um longo \xE9 posto no ch\xE3o aceso, para armadilha com atraso",
|
|
9919
10100
|
layers: { muzzle_flash: "Clar\xE3o do cano", casings: "C\xE1psulas ejetadas", magazines: "Pentes que caem" },
|
|
9920
|
-
metrics: { casings_live: "C\xE1psulas na cena", magazines_live: "Pentes na cena", heat: "
|
|
10101
|
+
metrics: { casings_live: "C\xE1psulas na cena", magazines_live: "Pentes na cena", heat: "Temperatura do cano acima do ambiente", rounds_to_warning: "Tiros at\xE9 o aviso de calor (cad\xEAncia, a frio)" }
|
|
9921
10102
|
}
|
|
9922
10103
|
};
|
|
9923
10104
|
var textOf = (locale) => TEXT[locale] ?? TEXT.en;
|
|
@@ -10041,7 +10222,12 @@ function createWeaponsTestModule(options = {}) {
|
|
|
10041
10222
|
let host = null;
|
|
10042
10223
|
let soundDistance = 0;
|
|
10043
10224
|
let soundIndoors = false;
|
|
10044
|
-
const
|
|
10225
|
+
const barrelCelsius = () => (host?.tool().heat ?? 0) * arsenal.rules.heat.displayMax;
|
|
10226
|
+
const roundsToWarning = () => {
|
|
10227
|
+
const weaponId = host?.tool().weapon;
|
|
10228
|
+
const rounds = weaponId ? roundsToWarningOf(arsenal, weaponId) : Infinity;
|
|
10229
|
+
return Number.isFinite(rounds) ? rounds : "-";
|
|
10230
|
+
};
|
|
10045
10231
|
return {
|
|
10046
10232
|
id: "weapons_area",
|
|
10047
10233
|
name: text.name,
|
|
@@ -10094,7 +10280,8 @@ function createWeaponsTestModule(options = {}) {
|
|
|
10094
10280
|
metrics: () => [
|
|
10095
10281
|
{ id: "casings_live", name: text.metrics.casings_live, read: () => fx?.casings ?? 0 },
|
|
10096
10282
|
{ id: "magazines_live", name: text.metrics.magazines_live, read: () => fx?.magazines ?? 0 },
|
|
10097
|
-
{ id: "heat", name: text.metrics.heat, unit: "
|
|
10283
|
+
{ id: "heat", name: text.metrics.heat, unit: "\xB0C", max: arsenal.rules.heat.displayMax, digits: 0, read: barrelCelsius },
|
|
10284
|
+
{ id: "rounds_to_warning", name: text.metrics.rounds_to_warning, read: roundsToWarning },
|
|
10098
10285
|
...accessoryMetrics(accessoryText, () => host?.tool() ?? EMPTY_TOOL)
|
|
10099
10286
|
],
|
|
10100
10287
|
attach(lent) {
|
|
@@ -10107,20 +10294,20 @@ function createWeaponsTestModule(options = {}) {
|
|
|
10107
10294
|
engine: lentEngine,
|
|
10108
10295
|
...options.samples ? { samples: options.samples } : {},
|
|
10109
10296
|
onGunshot: (shot, input, played) => {
|
|
10110
|
-
const recorded = played.kind === "sample" && played.layers.length > 0 ? fill(text.gunshotRecorded, { takes: played.layers.map((layer) => `${layer.role} ${layer.take + 1}`).join(", ") }) : "";
|
|
10297
|
+
const recorded = played.kind === "sample" && played.layers.length > 0 ? fill(text.gunshotRecorded, { takes: played.layers.map((layer) => `${layer.role} ${layer.take + 1}`).join(", ") }) : played.kind === "fitted" ? text.cueFitted : "";
|
|
10111
10298
|
lent.log(
|
|
10112
10299
|
fill(text.gunshotSound, { profile: shot.profile, take: String(shot.take + 1), pitch: `${shot.pitch >= 1 ? "+" : ""}${((shot.pitch - 1) * 100).toFixed(1)} %`, space: text.spaces[input.space ?? "outdoor"], distance: String(Math.round(input.distance ?? 0)), far: shot.closeMix < 1 ? ` (${Math.round((1 - shot.closeMix) * 100)} % far)` : "" }) + recorded,
|
|
10113
10300
|
{ profile: shot.profile, take: shot.take, pitch: shot.pitch, space: input.space ?? "outdoor", distance: input.distance ?? 0, closeMix: shot.closeMix, delay: shot.delay, samples: shot.samples, played },
|
|
10114
10301
|
"sound"
|
|
10115
10302
|
);
|
|
10116
10303
|
},
|
|
10117
|
-
onCasing: (sound3, played) => lent.log(fill(text.casingSound, { case: sound3.kind, surface: text.surfaces[sound3.surface] }) + (played === "sample" ? text.cueRecorded : ""), { case: sound3.kind, surface: sound3.surface, sample: sound3.sample, played }, "sound")
|
|
10304
|
+
onCasing: (sound3, played) => lent.log(fill(text.casingSound, { case: sound3.kind, surface: text.surfaces[sound3.surface] }) + (played === "sample" ? text.cueRecorded : played === "fitted" ? text.cueFitted : ""), { case: sound3.kind, surface: sound3.surface, sample: sound3.sample, played }, "sound")
|
|
10118
10305
|
});
|
|
10119
10306
|
}
|
|
10120
10307
|
let simulatedMs = 0;
|
|
10121
10308
|
const offClock = lent.onFrame((dt) => simulatedMs += dt * 1e3);
|
|
10122
10309
|
const created = createWeaponFx({
|
|
10123
|
-
onSound: ({ weapon, cue: cue2, profile, played }) => lent.log(fill(text.soundCue, { weapon: arsenal.name(weapon), cue: text.soundCues[cue2] ?? cue2 }) + (played === "sample" ? text.cueRecorded : played === "carried" ? text.cueCarried : ""), { weapon, cue: cue2, weight: profile.weight, material: profile.material, ...played ? { played } : {} }, "sound"),
|
|
10310
|
+
onSound: ({ weapon, cue: cue2, profile, played }) => lent.log(fill(text.soundCue, { weapon: arsenal.name(weapon), cue: text.soundCues[cue2] ?? cue2 }) + (played === "sample" ? text.cueRecorded : played === "fitted" ? text.cueFitted : played === "carried" ? text.cueCarried : ""), { weapon, cue: cue2, weight: profile.weight, material: profile.material, ...played ? { played } : {} }, "sound"),
|
|
10124
10311
|
THREE: lent.THREE,
|
|
10125
10312
|
scene: lent.scene,
|
|
10126
10313
|
camera: lent.camera,
|
|
@@ -10137,6 +10324,10 @@ function createWeaponsTestModule(options = {}) {
|
|
|
10137
10324
|
contact: createLaserRaycast(lent.THREE, lent.scene, lent.camera, { now: () => simulatedMs }),
|
|
10138
10325
|
...lent.holdFrame ? { bodyHold: () => lent.holdFrame?.() ?? null } : {},
|
|
10139
10326
|
firstPerson: () => lent.player().cameraMode === "first",
|
|
10327
|
+
// the lens of the gun in first person, how far it is brought to the eye (the right button) and how the body moves (the bob and the sway follow it)
|
|
10328
|
+
...lent.setViewmodelFov ? { viewmodelFov: (degrees) => lent.setViewmodelFov?.(degrees) } : {},
|
|
10329
|
+
aim: () => accessories.aimProgress,
|
|
10330
|
+
motion: () => ({ speed: lent.player().speed, running: lent.player().running }),
|
|
10140
10331
|
...lent.effects ? { effects: lent.effects } : {},
|
|
10141
10332
|
...lent.particleLimit ? { particleLimit: lent.particleLimit } : {},
|
|
10142
10333
|
...isLightBudget(lent.lights) ? { lights: lent.lights } : {},
|
|
@@ -10170,6 +10361,7 @@ function createWeaponsTestModule(options = {}) {
|
|
|
10170
10361
|
const offEvents = lent.on((event) => {
|
|
10171
10362
|
worldEffects.handle(event, lent.effects);
|
|
10172
10363
|
if (event.type === "explosion") {
|
|
10364
|
+
if (lentEngine) return;
|
|
10173
10365
|
const away = Math.hypot(event.point[0] - camera.position.x, event.point[1] - camera.position.y, event.point[2] - camera.position.z);
|
|
10174
10366
|
ownAudio()?.play("explosion", Math.min(1, EXPLOSION_VOLUME_BASE + event.radius * EXPLOSION_VOLUME_PER_METRE), away);
|
|
10175
10367
|
return;
|
|
@@ -11238,6 +11430,8 @@ export {
|
|
|
11238
11430
|
weaponSampleDefaults,
|
|
11239
11431
|
weaponSampleBank,
|
|
11240
11432
|
createWeaponAudio,
|
|
11433
|
+
VIEW_HOLD,
|
|
11434
|
+
toWorldLens,
|
|
11241
11435
|
BODY_ACTIONS,
|
|
11242
11436
|
weaponProp,
|
|
11243
11437
|
buildProp,
|
|
@@ -11272,6 +11466,7 @@ export {
|
|
|
11272
11466
|
buildCartridge,
|
|
11273
11467
|
buildCasing,
|
|
11274
11468
|
buildRound,
|
|
11469
|
+
VIEW_MOTION,
|
|
11275
11470
|
createWeaponFx,
|
|
11276
11471
|
buildModeledProp,
|
|
11277
11472
|
buildTripodMountedProp,
|
|
@@ -11299,4 +11494,4 @@ export {
|
|
|
11299
11494
|
DEFAULT_EXPLOSIVES_SCENE,
|
|
11300
11495
|
createExplosivesTestModule
|
|
11301
11496
|
};
|
|
11302
|
-
//# sourceMappingURL=chunk-
|
|
11497
|
+
//# sourceMappingURL=chunk-BDYIF7JD.js.map
|