@adatechnology/game-vehicles-three 0.6.4 → 0.7.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-MRUPKE7J.js → chunk-L63R2JQA.js} +542 -29
- package/dist/chunk-L63R2JQA.js.map +1 -0
- package/dist/index.d.ts +148 -5
- package/dist/index.global.js +1334 -255
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +11 -1
- package/dist/{testing-C-BgV7gj.d.ts → testing-DI0_R8dH.d.ts} +4 -5
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +8 -8
- package/dist/chunk-MRUPKE7J.js.map +0 -1
package/dist/index.global.js
CHANGED
|
@@ -23,6 +23,8 @@ var GameVehiclesThree = (() => {
|
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
BIKE_ENGINE: () => BIKE_ENGINE,
|
|
25
25
|
CAR_ENGINE: () => CAR_ENGINE,
|
|
26
|
+
CAR_RECORDED_RPM: () => CAR_RECORDED_RPM,
|
|
27
|
+
CAR_START_RUN_STOP: () => CAR_START_RUN_STOP,
|
|
26
28
|
CRASH_SPEEDS: () => CRASH_SPEEDS,
|
|
27
29
|
DASHBOARD_LOOK: () => DASHBOARD_LOOK,
|
|
28
30
|
DEFAULT_TEST_VEHICLES: () => DEFAULT_TEST_VEHICLES,
|
|
@@ -41,6 +43,7 @@ var GameVehiclesThree = (() => {
|
|
|
41
43
|
VEHICLE_LOD_DISTANCE: () => VEHICLE_LOD_DISTANCE,
|
|
42
44
|
VEHICLE_LOOPS: () => VEHICLE_LOOPS,
|
|
43
45
|
VEHICLE_LOOP_FALLOFF: () => VEHICLE_LOOP_FALLOFF,
|
|
46
|
+
VEHICLE_SAMPLE_ALIASES: () => VEHICLE_SAMPLE_ALIASES,
|
|
44
47
|
VEHICLE_SCENARIO_IDS: () => VEHICLE_SCENARIO_IDS,
|
|
45
48
|
VEHICLE_SOUNDS: () => VEHICLE_SOUNDS,
|
|
46
49
|
VEHICLE_SOUND_BANK: () => VEHICLE_SOUND_BANK,
|
|
@@ -49,12 +52,14 @@ var GameVehiclesThree = (() => {
|
|
|
49
52
|
createCabinSmoke: () => createCabinSmoke,
|
|
50
53
|
createGearGuess: () => createGearGuess,
|
|
51
54
|
createProvingModule: () => createProvingModule,
|
|
55
|
+
createVehicleBody: () => createVehicleBody,
|
|
52
56
|
createVehicleDashboard: () => createVehicleDashboard,
|
|
53
57
|
createVehicleLamps: () => createVehicleLamps,
|
|
54
58
|
createVehicleLod: () => createVehicleLod,
|
|
55
59
|
createVehicleSound: () => createVehicleSound,
|
|
56
60
|
createVehiclesTestModule: () => createVehiclesTestModule,
|
|
57
61
|
createVehiclesThree: () => createVehiclesThree,
|
|
62
|
+
createWheelRoll: () => createWheelRoll,
|
|
58
63
|
defaultEffectIds: () => defaultEffectIds,
|
|
59
64
|
defaultSoundIds: () => defaultSoundIds,
|
|
60
65
|
engineMix: () => engineMix,
|
|
@@ -70,6 +75,10 @@ var GameVehiclesThree = (() => {
|
|
|
70
75
|
|
|
71
76
|
// ../game-contracts/dist/index.js
|
|
72
77
|
var DEFAULT_BLAST_IMPULSE = { bodyArea: 0.7, lift: 0.15, minSpeed: 0.3, tntPerJoule: 0.24 / 2e5 };
|
|
78
|
+
var LAUNCHED = "launched";
|
|
79
|
+
function launchBody(root, velocity) {
|
|
80
|
+
root.userData[LAUNCHED] = [velocity[0], velocity[1], velocity[2]];
|
|
81
|
+
}
|
|
73
82
|
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
74
83
|
var DEFAULT_IGNITION_RULES = {
|
|
75
84
|
fillCurve: [
|
|
@@ -3861,9 +3870,9 @@ var GameVehiclesThree = (() => {
|
|
|
3861
3870
|
let x = bx + (Hd[0] - bx) * v;
|
|
3862
3871
|
const y = by + (Hd[1] - by) * v;
|
|
3863
3872
|
let z = bz + (Hd[2] - bz) * v;
|
|
3864
|
-
const
|
|
3873
|
+
const chord2 = Math.hypot(C[0] - T[0], C[1] - T[1]) * (1 - v);
|
|
3865
3874
|
x -= roach * u * Math.sin(v * PI) * (u > 0.7 ? 1 : u / 0.7);
|
|
3866
|
-
z += cam *
|
|
3875
|
+
z += cam * chord2 * 4 * u * (1 - u) * (0.6 + 0.4 * (1 - v));
|
|
3867
3876
|
pos.push(x, y, z);
|
|
3868
3877
|
}
|
|
3869
3878
|
}
|
|
@@ -5092,12 +5101,24 @@ var GameVehiclesThree = (() => {
|
|
|
5092
5101
|
}
|
|
5093
5102
|
);
|
|
5094
5103
|
}
|
|
5104
|
+
var WIPER_SWEEP = 1.5;
|
|
5105
|
+
function tagWiper(params) {
|
|
5106
|
+
var _a;
|
|
5107
|
+
const tag = { arm: params.index, sweep: WIPER_SWEEP, spindle: [params.spindle.x, params.spindle.y, params.spindle.z], axis: [params.normal.x, params.normal.y, params.normal.z] };
|
|
5108
|
+
(_a = params.context).wipers ?? (_a.wipers = []);
|
|
5109
|
+
for (const part of params.parts) params.context.wipers.push({ part, tag });
|
|
5110
|
+
}
|
|
5111
|
+
function markSunroof(pane, slide) {
|
|
5112
|
+
pane.name = "sunroof";
|
|
5113
|
+
pane.userData.sunroof = { slide };
|
|
5114
|
+
return pane;
|
|
5115
|
+
}
|
|
5095
5116
|
function installCarsExtra(fx) {
|
|
5096
5117
|
const kit = fx.kit;
|
|
5097
5118
|
const THREE = kit.THREE;
|
|
5098
5119
|
const trl = fx.t;
|
|
5099
5120
|
const lbl = fx.label;
|
|
5100
|
-
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G2 = kit.geo, profX = kit.profX, halfAt = kit.halfAt,
|
|
5121
|
+
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G2 = kit.geo, profX = kit.profX, halfAt = kit.halfAt, clamp5 = kit.clamp, smooth2 = kit.smooth;
|
|
5101
5122
|
const GLOSS = CM("#07080a", { roughness: 0.18, metalness: 0.5 });
|
|
5102
5123
|
const ALLOY = CM("#b9bec2", { metalness: 0.75, roughness: 0.3 });
|
|
5103
5124
|
const ALLOYD = CM("#3a3e42", { metalness: 0.6, roughness: 0.35 });
|
|
@@ -5111,7 +5132,7 @@ var GameVehiclesThree = (() => {
|
|
|
5111
5132
|
const a = (y - o.ys) / (o.belt - o.ys);
|
|
5112
5133
|
return o.kt * a * a;
|
|
5113
5134
|
}
|
|
5114
|
-
const b =
|
|
5135
|
+
const b = clamp5((o.ys - y) / (o.ys - o.yb), 0, 1.25);
|
|
5115
5136
|
return o.kb * b * b;
|
|
5116
5137
|
}
|
|
5117
5138
|
const f = function(x, y, z) {
|
|
@@ -5191,13 +5212,13 @@ var GameVehiclesThree = (() => {
|
|
|
5191
5212
|
});
|
|
5192
5213
|
}
|
|
5193
5214
|
function roofAt(gh, x) {
|
|
5194
|
-
const rR = gh.roofR || gh.roof, t =
|
|
5215
|
+
const rR = gh.roofR || gh.roof, t = clamp5((x - gh.rt) / (gh.ft - gh.rt), 0, 1);
|
|
5195
5216
|
return rR + (gh.roof - rR) * t + (gh.crown || 0) * 4 * t * (1 - t);
|
|
5196
5217
|
}
|
|
5197
5218
|
function windows(gh) {
|
|
5198
5219
|
const inn = 0.07, rise = gh.rise || 0;
|
|
5199
5220
|
function ybAt(x) {
|
|
5200
|
-
return gh.belt + 0.035 + rise *
|
|
5221
|
+
return gh.belt + 0.035 + rise * clamp5((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
|
|
5201
5222
|
}
|
|
5202
5223
|
function lx(x0, x1, y, top) {
|
|
5203
5224
|
return x0 + (x1 - x0) * (y - gh.belt) / (top - gh.belt);
|
|
@@ -5400,7 +5421,7 @@ var GameVehiclesThree = (() => {
|
|
|
5400
5421
|
}
|
|
5401
5422
|
const G22 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: tot * (full ? 2 : 1), E, sd };
|
|
5402
5423
|
G22.at = function(u, y) {
|
|
5403
|
-
const fi =
|
|
5424
|
+
const fi = clamp5(u, 0, 1) * nu, fj = clamp5((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
|
|
5404
5425
|
function L(M) {
|
|
5405
5426
|
return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
|
|
5406
5427
|
}
|
|
@@ -5588,7 +5609,7 @@ var GameVehiclesThree = (() => {
|
|
|
5588
5609
|
off: 3e-3,
|
|
5589
5610
|
nv: 4
|
|
5590
5611
|
});
|
|
5591
|
-
skin(c, Gd, o.refl || REFL, { u0: pu, u1: 1 - pu, yb, yt, off: 6e-3, nv: 4 });
|
|
5612
|
+
const reflector = skin(c, Gd, o.refl || REFL, { u0: pu, u1: 1 - pu, yb, yt, off: 6e-3, nv: 4 });
|
|
5592
5613
|
(o.proj || []).forEach(function(q) {
|
|
5593
5614
|
const r = sh(q[0]), y = (r[0] + r[1]) / 2 + (q[1] || 0), s = Gd.at(q[0], y), rad = q[2] || Math.min(0.034, (r[1] - r[0]) * 0.4);
|
|
5594
5615
|
const cup = onSurf(c.cylinder(rad * 1.35, rad * 1.2, 0.012, LH, 0, 0, 0), s, 8e-3);
|
|
@@ -5619,7 +5640,7 @@ var GameVehiclesThree = (() => {
|
|
|
5619
5640
|
lens.push(ln);
|
|
5620
5641
|
if (!inner.length) inner.push(ln);
|
|
5621
5642
|
const cs = Gd.at(0.5, (rg[0] + rg[1]) / 2).p;
|
|
5622
|
-
regLight(c, "headlight", o.label || (sd > 0 ? trl("labels.right_headlight") : trl("labels.left_headlight")), inner, lens, cs);
|
|
5643
|
+
Object.assign(regLight(c, "headlight", o.label || (sd > 0 ? trl("labels.right_headlight") : trl("labels.left_headlight")), inner, lens, cs), { reflector: [reflector] });
|
|
5623
5644
|
return Gd;
|
|
5624
5645
|
};
|
|
5625
5646
|
K.tail = function(sd, o) {
|
|
@@ -5980,7 +6001,7 @@ var GameVehiclesThree = (() => {
|
|
|
5980
6001
|
c.roundBox(x1 - x0, 0.08, 0.035, 0.015, MT.trim, (x0 + x1) / 2, by + 0.05, sd * (hw - 8e-3));
|
|
5981
6002
|
if (s.clad)
|
|
5982
6003
|
s.arches.forEach(function(a) {
|
|
5983
|
-
const a0 = Math.asin(
|
|
6004
|
+
const a0 = Math.asin(clamp5((by - a[1]) / a[2], -1, 1)), tor = c.add(new THREE.TorusGeometry(a[2] + 0.02, 0.035, 6, 26, PI - 2 * a0), MT.trim, a[0], a[1], sd * (hw - 0.01));
|
|
5984
6005
|
tor.rotation.z = a0;
|
|
5985
6006
|
tor.scale.z = 0.8;
|
|
5986
6007
|
});
|
|
@@ -6005,12 +6026,18 @@ var GameVehiclesThree = (() => {
|
|
|
6005
6026
|
(s.wiper === 1 ? [[0.12, 0.66, -0.5]] : [
|
|
6006
6027
|
[-0.3, 0.42, -0.36],
|
|
6007
6028
|
[0.2, 0.42, -0.36]
|
|
6008
|
-
]).forEach(function(q) {
|
|
6029
|
+
]).forEach(function(q, index) {
|
|
6009
6030
|
const p0 = new V3(gh.fb, gh.belt, q[0]).addScaledVector(wd, 0.07).addScaledVector(wn, 0.035), p1 = p0.clone().addScaledVector(wd, q[1] * 0.42);
|
|
6010
6031
|
p1.z += q[2];
|
|
6011
|
-
c.rod(p0, p1, 9e-3, MT.trim);
|
|
6012
|
-
c.cylinder(0.018, 0.018, 0.03, MT.trim, p0.x, p0.y, p0.z);
|
|
6032
|
+
const blade = c.rod(p0, p1, 9e-3, MT.trim);
|
|
6033
|
+
const cap = c.cylinder(0.018, 0.018, 0.03, MT.trim, p0.x, p0.y, p0.z);
|
|
6034
|
+
tagWiper({ context: c, parts: [blade, cap], spindle: p0, normal: wn, index });
|
|
6013
6035
|
});
|
|
6036
|
+
if (s.sunroof) {
|
|
6037
|
+
const sx = gh.rt + (gh.ft - gh.rt) * 0.62, sl = 0.62, sw = 2 * (halfAt(gh.Wg, T, gh.roof) - 0.24), sy = roofAt(gh, sx), tilt = Math.atan2(roofAt(gh, sx + 0.3) - roofAt(gh, sx - 0.3), 0.6);
|
|
6038
|
+
c.roundBox(sl, 8e-3, sw, 0.01, PLASTIC, sx, sy + 4e-3, 0, { rot: [0, 0, tilt] }).castShadow = false;
|
|
6039
|
+
markSunroof(c.roundBox(sl + 0.03, 0.012, sw + 0.03, 0.01, MT.tint, sx, sy + 0.013, 0, { rot: [0, 0, tilt] }), sl * 0.85);
|
|
6040
|
+
}
|
|
6014
6041
|
if (s.fin !== false)
|
|
6015
6042
|
c.sphere(0.1, s.roofCol ? c.paint(s.roofCol) : GLOSS, gh.rt + 0.2, roofAt(gh, gh.rt + 0.2) + 0.045, 0, 1, 0.45, 0.28, {
|
|
6016
6043
|
tl: PI / 2
|
|
@@ -6111,7 +6138,7 @@ var GameVehiclesThree = (() => {
|
|
|
6111
6138
|
c.cylinder(0.06, 0.06, 0.012, MT.head, x + 0.055, y, z, "x");
|
|
6112
6139
|
}
|
|
6113
6140
|
function sm(t) {
|
|
6114
|
-
t =
|
|
6141
|
+
t = clamp5(t, 0, 1);
|
|
6115
6142
|
return t * t * (3 - 2 * t);
|
|
6116
6143
|
}
|
|
6117
6144
|
function sideGrid(S, sd, o) {
|
|
@@ -6128,7 +6155,7 @@ var GameVehiclesThree = (() => {
|
|
|
6128
6155
|
}
|
|
6129
6156
|
const G22 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: Math.abs(o.x1 - o.x0) };
|
|
6130
6157
|
G22.at = function(u, y) {
|
|
6131
|
-
const fi =
|
|
6158
|
+
const fi = clamp5(u, 0, 1) * nu, fj = clamp5((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
|
|
6132
6159
|
function L(M) {
|
|
6133
6160
|
return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
|
|
6134
6161
|
}
|
|
@@ -6986,6 +7013,7 @@ var GameVehiclesThree = (() => {
|
|
|
6986
7013
|
function(c) {
|
|
6987
7014
|
const hex = "#8c2b2b";
|
|
6988
7015
|
car(c, {
|
|
7016
|
+
sunroof: fx.hasSunroof("turbo_hatch"),
|
|
6989
7017
|
W: 1.751,
|
|
6990
7018
|
col: hex,
|
|
6991
7019
|
shelf: 0.42,
|
|
@@ -7263,6 +7291,7 @@ var GameVehiclesThree = (() => {
|
|
|
7263
7291
|
function(c) {
|
|
7264
7292
|
const hex = "#5a5f66";
|
|
7265
7293
|
car(c, {
|
|
7294
|
+
sunroof: fx.hasSunroof("fastback_sedan"),
|
|
7266
7295
|
W: 1.799,
|
|
7267
7296
|
col: hex,
|
|
7268
7297
|
bottomY: 0.2,
|
|
@@ -7557,6 +7586,8 @@ var GameVehiclesThree = (() => {
|
|
|
7557
7586
|
fx.register(
|
|
7558
7587
|
{
|
|
7559
7588
|
key: "classic_six_sedan",
|
|
7589
|
+
// glass headlight lenses, as on cars of its time: they shatter (a modern car's are polycarbonate)
|
|
7590
|
+
headlightLens: "glass",
|
|
7560
7591
|
group: "cars",
|
|
7561
7592
|
name: trl("vehicles.classic_six_sedan.name"),
|
|
7562
7593
|
inspired: trl("vehicles.classic_six_sedan.inspired"),
|
|
@@ -7858,7 +7889,7 @@ var GameVehiclesThree = (() => {
|
|
|
7858
7889
|
const g = K.grid(1, 0, { z1: 0.66, y0: 0.27, y1: 0.61, du: 0.03, dv: 0.03 }), zt = 0.6, zb = 0.43, yB0 = 0.3, yT = 0.585;
|
|
7859
7890
|
const gyb = function(u) {
|
|
7860
7891
|
const z = Math.abs(g.zu(u));
|
|
7861
|
-
return z <= zb ? yB0 : yB0 + (yT - 0.02 - yB0) *
|
|
7892
|
+
return z <= zb ? yB0 : yB0 + (yT - 0.02 - yB0) * clamp5((z - zb) / (zt - zb), 0, 1);
|
|
7862
7893
|
};
|
|
7863
7894
|
K.skin(g, grilleMat("mesh"), { u0: g.uz(-zt), u1: g.uz(zt), yb: gyb, yt: yT, off: 0.022, tile: 0.05, nv: 6 });
|
|
7864
7895
|
outline(c2, g, { u0: g.uz(-zt), u1: g.uz(zt), yb: gyb, yt: yT }, 0.012, LH, 0.024);
|
|
@@ -8667,7 +8698,7 @@ var GameVehiclesThree = (() => {
|
|
|
8667
8698
|
lightsY: 0.82
|
|
8668
8699
|
});
|
|
8669
8700
|
const tx = -1.25, len2 = 6.2, ty = 2.08, sy = 0.82, sz = 1.16;
|
|
8670
|
-
function
|
|
8701
|
+
function hz2(y) {
|
|
8671
8702
|
const d = (y - ty) / sy;
|
|
8672
8703
|
return sz * Math.sqrt(Math.max(0, 1 - d * d));
|
|
8673
8704
|
}
|
|
@@ -8721,12 +8752,12 @@ var GameVehiclesThree = (() => {
|
|
|
8721
8752
|
[1, -1].forEach(function(s) {
|
|
8722
8753
|
c.cylinder(0.12, 0.12, 2.3, ALU, 0.25, 1.36, s * 1, "x");
|
|
8723
8754
|
c.cylinder(0.1, 0.1, 0.02, MT.dark, -0.91, 1.36, s * 1, "x");
|
|
8724
|
-
c.roundBox(0.42, 0.32, 0.012, 0.01, CM("#d9771c"), tx + 1.6, ty - 0.25, s * (
|
|
8725
|
-
c.text("33", 0.36, 0.13, tx + 1.6, ty - 0.18, s * (
|
|
8726
|
-
c.text("1203", 0.36, 0.13, tx + 1.6, ty - 0.32, s * (
|
|
8727
|
-
const dia = c.roundBox(0.3, 0.3, 0.012, 0.01, CM("#c3271c"), tx + 2.15, ty - 0.25, s * (
|
|
8755
|
+
c.roundBox(0.42, 0.32, 0.012, 0.01, CM("#d9771c"), tx + 1.6, ty - 0.25, s * (hz2(ty - 0.1) + 6e-3));
|
|
8756
|
+
c.text("33", 0.36, 0.13, tx + 1.6, ty - 0.18, s * (hz2(ty - 0.1) + 0.014), s > 0 ? "+z" : "-z", { fg: "#111", size: 0.85 });
|
|
8757
|
+
c.text("1203", 0.36, 0.13, tx + 1.6, ty - 0.32, s * (hz2(ty - 0.1) + 0.014), s > 0 ? "+z" : "-z", { fg: "#111", size: 0.85 });
|
|
8758
|
+
const dia = c.roundBox(0.3, 0.3, 0.012, 0.01, CM("#c3271c"), tx + 2.15, ty - 0.25, s * (hz2(ty - 0.05) + 6e-3));
|
|
8728
8759
|
dia.rotation.z = PI / 4;
|
|
8729
|
-
c.text("3", 0.18, 0.18, tx + 2.15, ty - 0.27, s * (
|
|
8760
|
+
c.text("3", 0.18, 0.18, tx + 2.15, ty - 0.27, s * (hz2(ty - 0.05) + 0.014), s > 0 ? "+z" : "-z", { fg: "#fff", size: 0.9 });
|
|
8730
8761
|
c.text(trl("texts.flammable"), 1.12, 0.3, tx - 0.625, ty + 0.12, s * (sz + 0.012), s > 0 ? "+z" : "-z", { fg: "#b3261e" });
|
|
8731
8762
|
for (let k = 0; k < 11; k++)
|
|
8732
8763
|
c.roundBox(
|
|
@@ -8737,7 +8768,7 @@ var GameVehiclesThree = (() => {
|
|
|
8737
8768
|
k % 2 ? REFL_W : REFL_R,
|
|
8738
8769
|
tx - 2.8 + k * 0.55,
|
|
8739
8770
|
ty - sy * 0.72,
|
|
8740
|
-
s * (
|
|
8771
|
+
s * (hz2(ty - sy * 0.62) + 6e-3)
|
|
8741
8772
|
);
|
|
8742
8773
|
c.roundBox(0.42, 0.42, 0.25, 0.04, CM("#b3261e"), 2.2, 1, s * (W / 2 - 0.15));
|
|
8743
8774
|
});
|
|
@@ -11753,7 +11784,7 @@ var GameVehiclesThree = (() => {
|
|
|
11753
11784
|
m.castShadow = true;
|
|
11754
11785
|
m.receiveShadow = false;
|
|
11755
11786
|
for (let k = 0; k < 14; k++) {
|
|
11756
|
-
const hx = x0 + 0.2 + (x1 - x0 - 0.4) * kit.hash(k, 1, 7),
|
|
11787
|
+
const hx = x0 + 0.2 + (x1 - x0 - 0.4) * kit.hash(k, 1, 7), hz2 = (kit.hash(k, 2, 7) - 0.5) * w * 0.85, n = 3 + Math.floor(kit.hash(k, 4, 7) * 3);
|
|
11757
11788
|
for (let q = 0; q < n; q++) {
|
|
11758
11789
|
const r = 0.07 + kit.hash(k, q, 11) * 0.07, geo = new THREE.IcosahedronGeometry(r, 1), p = geo.attributes.position;
|
|
11759
11790
|
for (let vi = 0; vi < p.count; vi++) {
|
|
@@ -11761,7 +11792,7 @@ var GameVehiclesThree = (() => {
|
|
|
11761
11792
|
p.setXYZ(vi, p.getX(vi) * f, p.getY(vi) * f * 0.8, p.getZ(vi) * f);
|
|
11762
11793
|
}
|
|
11763
11794
|
geo.computeVertexNormals();
|
|
11764
|
-
const ang = q / n * PI * 2 + k, tf = c.add(geo, (k + q) % 2 ? NET : NET2, hx + Math.cos(ang) * 0.1, prof(
|
|
11795
|
+
const ang = q / n * PI * 2 + k, tf = c.add(geo, (k + q) % 2 ? NET : NET2, hx + Math.cos(ang) * 0.1, prof(hz2) + 0.1 + r * 0.5, hz2 + Math.sin(ang) * 0.1);
|
|
11765
11796
|
tf.rotation.set(kit.hash(k, q, 1) * 3, kit.hash(k, q, 2) * 3, 0);
|
|
11766
11797
|
}
|
|
11767
11798
|
}
|
|
@@ -13644,7 +13675,7 @@ var GameVehiclesThree = (() => {
|
|
|
13644
13675
|
}
|
|
13645
13676
|
};
|
|
13646
13677
|
}
|
|
13647
|
-
function militaryRotor(c, x, y, z, n, len,
|
|
13678
|
+
function militaryRotor(c, x, y, z, n, len, chord2, mat, o) {
|
|
13648
13679
|
o = o || {};
|
|
13649
13680
|
const g = new THREE.Group();
|
|
13650
13681
|
g.position.set(x, y, z);
|
|
@@ -13652,7 +13683,7 @@ var GameVehiclesThree = (() => {
|
|
|
13652
13683
|
for (let i = 0; i < n; i++) {
|
|
13653
13684
|
const piv = new THREE.Group();
|
|
13654
13685
|
piv.rotation[o.axis === "z" ? "z" : "y"] = i / n * PI * 2 + (o.phase || 0);
|
|
13655
|
-
const geo = G2.roundedBox(len, o.th || 0.06,
|
|
13686
|
+
const geo = G2.roundedBox(len, o.th || 0.06, chord2, 0.02);
|
|
13656
13687
|
geo.translate(len / 2 + (o.hub || 0.3), 0, 0);
|
|
13657
13688
|
if (o.droop) {
|
|
13658
13689
|
const p = geo.attributes.position;
|
|
@@ -13667,7 +13698,7 @@ var GameVehiclesThree = (() => {
|
|
|
13667
13698
|
if (o.axis === "z") b.rotation.x = PI / 2;
|
|
13668
13699
|
piv.add(b);
|
|
13669
13700
|
if (o.tip) {
|
|
13670
|
-
const tp = new THREE.Mesh(G2.roundedBox(0.25, (o.th || 0.06) + 5e-3,
|
|
13701
|
+
const tp = new THREE.Mesh(G2.roundedBox(0.25, (o.th || 0.06) + 5e-3, chord2 + 5e-3, 0.01), o.tip);
|
|
13671
13702
|
tp.position.set(len + (o.hub || 0.3) - 0.12, -(o.droop || 0), 0);
|
|
13672
13703
|
if (o.axis === "z") tp.rotation.x = PI / 2;
|
|
13673
13704
|
piv.add(tp);
|
|
@@ -14017,7 +14048,7 @@ var GameVehiclesThree = (() => {
|
|
|
14017
14048
|
const THREE = kit.THREE;
|
|
14018
14049
|
const trl = fx.t;
|
|
14019
14050
|
const lbl = fx.label;
|
|
14020
|
-
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G2 = kit.geo, profX = kit.profX, halfAt = kit.halfAt,
|
|
14051
|
+
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, G2 = kit.geo, profX = kit.profX, halfAt = kit.halfAt, clamp5 = kit.clamp, smooth2 = kit.smooth;
|
|
14021
14052
|
const GLOSS = CM("#07080a", { roughness: 0.18, metalness: 0.5 });
|
|
14022
14053
|
const ALLOY = CM("#b9bec2", { metalness: 0.75, roughness: 0.3 });
|
|
14023
14054
|
const ALLOYD = CM("#3a3e42", { metalness: 0.6, roughness: 0.35 });
|
|
@@ -14036,7 +14067,7 @@ var GameVehiclesThree = (() => {
|
|
|
14036
14067
|
const a = (y - o.ys) / (o.belt - o.ys);
|
|
14037
14068
|
return o.kt * a * a;
|
|
14038
14069
|
}
|
|
14039
|
-
const b =
|
|
14070
|
+
const b = clamp5((o.ys - y) / (o.ys - o.yb), 0, 1.25);
|
|
14040
14071
|
return o.kb * b * b;
|
|
14041
14072
|
}
|
|
14042
14073
|
const f = function(x, y, z) {
|
|
@@ -14216,13 +14247,13 @@ var GameVehiclesThree = (() => {
|
|
|
14216
14247
|
return L;
|
|
14217
14248
|
}
|
|
14218
14249
|
function roofAt(gh, x) {
|
|
14219
|
-
const rR = gh.roofR || gh.roof, t =
|
|
14250
|
+
const rR = gh.roofR || gh.roof, t = clamp5((x - gh.rt) / (gh.ft - gh.rt), 0, 1);
|
|
14220
14251
|
return rR + (gh.roof - rR) * t + (gh.crown || 0) * 4 * t * (1 - t);
|
|
14221
14252
|
}
|
|
14222
14253
|
function windows(gh) {
|
|
14223
14254
|
const inn = 0.07, rise = gh.rise || 0;
|
|
14224
14255
|
function ybAt(x) {
|
|
14225
|
-
return gh.belt + 0.035 + rise *
|
|
14256
|
+
return gh.belt + 0.035 + rise * clamp5((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
|
|
14226
14257
|
}
|
|
14227
14258
|
function lx(x0, x1, y, top) {
|
|
14228
14259
|
return x0 + (x1 - x0) * (y - gh.belt) / (top - gh.belt);
|
|
@@ -14433,7 +14464,7 @@ var GameVehiclesThree = (() => {
|
|
|
14433
14464
|
}
|
|
14434
14465
|
const G22 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: tot * (full ? 2 : 1), E, sd };
|
|
14435
14466
|
G22.at = function(u, y) {
|
|
14436
|
-
const fi =
|
|
14467
|
+
const fi = clamp5(u, 0, 1) * nu, fj = clamp5((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
|
|
14437
14468
|
function L(M) {
|
|
14438
14469
|
return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
|
|
14439
14470
|
}
|
|
@@ -14994,7 +15025,7 @@ var GameVehiclesThree = (() => {
|
|
|
14994
15025
|
c.roundBox(x1 - x0, 0.08, 0.035, 0.015, MT.trim, (x0 + x1) / 2, by + 0.05, sd * (hw - 8e-3));
|
|
14995
15026
|
if (s.clad)
|
|
14996
15027
|
s.arches.forEach(function(a) {
|
|
14997
|
-
const a0 = Math.asin(
|
|
15028
|
+
const a0 = Math.asin(clamp5((by - a[1]) / a[2], -1, 1)), tor = c.add(new THREE.TorusGeometry(a[2] + 0.02, 0.035, 6, 26, PI - 2 * a0), MT.trim, a[0], a[1], sd * (hw - 0.01));
|
|
14998
15029
|
tor.rotation.z = a0;
|
|
14999
15030
|
tor.scale.z = 0.8;
|
|
15000
15031
|
});
|
|
@@ -15012,8 +15043,11 @@ var GameVehiclesThree = (() => {
|
|
|
15012
15043
|
);
|
|
15013
15044
|
});
|
|
15014
15045
|
const wy = gh.belt + 0.06, wx = gh.fb - 0.03;
|
|
15015
|
-
[-0.3, 0.2].forEach(function(z) {
|
|
15016
|
-
|
|
15046
|
+
[-0.3, 0.2].forEach(function(z, index) {
|
|
15047
|
+
const spindle = new V3(wx + 0.02, wy, z + 0.02);
|
|
15048
|
+
const blade = c.rod(spindle, new V3(wx - 0.13, wy + 0.13, z - 0.36), 9e-3, MT.trim);
|
|
15049
|
+
const along = new V3(gh.ft - gh.fb, gh.roof - gh.belt, 0).normalize();
|
|
15050
|
+
tagWiper({ context: c, parts: [blade], spindle, normal: new V3(along.y, -along.x, 0), index });
|
|
15017
15051
|
});
|
|
15018
15052
|
if (s.fin !== false)
|
|
15019
15053
|
c.sphere(0.1, s.roofCol ? c.paint(s.roofCol) : GLOSS, gh.rt + 0.2, roofAt(gh, gh.rt + 0.2) + 0.045, 0, 1, 0.45, 0.28, {
|
|
@@ -15115,7 +15149,7 @@ var GameVehiclesThree = (() => {
|
|
|
15115
15149
|
c.cylinder(0.06, 0.06, 0.012, MT.head, x + 0.055, y, z, "x");
|
|
15116
15150
|
}
|
|
15117
15151
|
function sm(t) {
|
|
15118
|
-
t =
|
|
15152
|
+
t = clamp5(t, 0, 1);
|
|
15119
15153
|
return t * t * (3 - 2 * t);
|
|
15120
15154
|
}
|
|
15121
15155
|
function sideGrid(S, sd, o) {
|
|
@@ -15132,7 +15166,7 @@ var GameVehiclesThree = (() => {
|
|
|
15132
15166
|
}
|
|
15133
15167
|
const G22 = { P, N, nu, nv, y0: o.y0, y1: o.y1, len: Math.abs(o.x1 - o.x0) };
|
|
15134
15168
|
G22.at = function(u, y) {
|
|
15135
|
-
const fi =
|
|
15169
|
+
const fi = clamp5(u, 0, 1) * nu, fj = clamp5((y - o.y0) / (o.y1 - o.y0), 0, 1) * nv, i0 = Math.min(nu - 1, Math.floor(fi)), j0 = Math.min(nv - 1, Math.floor(fj)), a = fi - i0, b = fj - j0;
|
|
15136
15170
|
function L(M) {
|
|
15137
15171
|
return M[i0][j0].clone().multiplyScalar((1 - a) * (1 - b)).addScaledVector(M[i0 + 1][j0], a * (1 - b)).addScaledVector(M[i0][j0 + 1], (1 - a) * b).addScaledVector(M[i0 + 1][j0 + 1], a * b);
|
|
15138
15172
|
}
|
|
@@ -15293,7 +15327,7 @@ var GameVehiclesThree = (() => {
|
|
|
15293
15327
|
return function(c, I) {
|
|
15294
15328
|
const gh = I.gh, col = c.meta.color;
|
|
15295
15329
|
function yb(x) {
|
|
15296
|
-
return gh.belt + 0.035 + (gh.rise || 0) *
|
|
15330
|
+
return gh.belt + 0.035 + (gh.rise || 0) * clamp5((gh.fb - x) / (gh.fb - gh.rb), 0, 1);
|
|
15297
15331
|
}
|
|
15298
15332
|
;
|
|
15299
15333
|
[1, -1].forEach(function(sd) {
|
|
@@ -18089,6 +18123,8 @@ var GameVehiclesThree = (() => {
|
|
|
18089
18123
|
fx.register(
|
|
18090
18124
|
{
|
|
18091
18125
|
key: "beetle",
|
|
18126
|
+
// glass headlight lenses, as on cars of its time: they shatter (a modern car's are polycarbonate)
|
|
18127
|
+
headlightLens: "glass",
|
|
18092
18128
|
group: "cars",
|
|
18093
18129
|
name: trl("vehicles.beetle.name"),
|
|
18094
18130
|
inspired: trl("vehicles.beetle.inspired"),
|
|
@@ -18164,7 +18200,7 @@ var GameVehiclesThree = (() => {
|
|
|
18164
18200
|
return n.y > 0.3 && ce.x > 0.9 && ce.y > 0.6;
|
|
18165
18201
|
},
|
|
18166
18202
|
sdf: function(x, y, z) {
|
|
18167
|
-
const t =
|
|
18203
|
+
const t = clamp5((x - 1.17) / 0.5, 0, 1);
|
|
18168
18204
|
return Math.max(0.94 - x, x - 1.69, Math.abs(z) - (0.44 - 0.16 * t * t));
|
|
18169
18205
|
}
|
|
18170
18206
|
});
|
|
@@ -18868,8 +18904,8 @@ var GameVehiclesThree = (() => {
|
|
|
18868
18904
|
];
|
|
18869
18905
|
body(c, RB, col);
|
|
18870
18906
|
[1, -1].forEach(function(s) {
|
|
18871
|
-
const x = -0.3, y = 0.6,
|
|
18872
|
-
c.text(trl("texts.bis_125"), 0.2, 0.05, x, y, s *
|
|
18907
|
+
const x = -0.3, y = 0.6, hz2 = loftHalf(RB, x, y) + 4e-3;
|
|
18908
|
+
c.text(trl("texts.bis_125"), 0.2, 0.05, x, y, s * hz2, s > 0 ? "+z" : "-z", { fg: "#f2f0ea", size: 0.85 });
|
|
18873
18909
|
body(
|
|
18874
18910
|
c,
|
|
18875
18911
|
[
|
|
@@ -22162,7 +22198,7 @@ var GameVehiclesThree = (() => {
|
|
|
22162
22198
|
const THREE = kit.THREE;
|
|
22163
22199
|
const trl = fx.t;
|
|
22164
22200
|
const lbl = fx.label;
|
|
22165
|
-
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial,
|
|
22201
|
+
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, clamp5 = kit.clamp, smooth2 = kit.smooth;
|
|
22166
22202
|
const CLAD = CM("#1a1b1d", { roughness: 0.82 }), LINER = CM("#1c1d1f", { roughness: 0.92 }), RIMDARK = CM("#2a2d30", { metalness: 0.6, roughness: 0.4 });
|
|
22167
22203
|
const RIMSIL = CM("#b9bec2", { metalness: 0.75, roughness: 0.3 }), RIMGUN = CM("#55595d", { metalness: 0.7, roughness: 0.35 });
|
|
22168
22204
|
function axles(WB, oF, L, dF, dR) {
|
|
@@ -22193,16 +22229,16 @@ var GameVehiclesThree = (() => {
|
|
|
22193
22229
|
const m = c.meta, W = m.width, by = o.bottom != null ? o.bottom : m.clearance, top = o.top != null ? o.top : m.belt;
|
|
22194
22230
|
const Wg = m.glasshouse && m.glasshouse.Wg || W, front = m.front, rear = m.rear;
|
|
22195
22231
|
const tuck = o.tuck == null ? 0.045 : o.tuck, peak = o.peak || 0.45;
|
|
22196
|
-
const B = o.sh != null ? o.sh :
|
|
22232
|
+
const B = o.sh != null ? o.sh : clamp5(1 - (Wg + 0.06) / W, 0, 0.06);
|
|
22197
22233
|
const Rf = o.Rf || 0.55, Rr = o.Rr || 0.42, pf = o.pf == null ? 0.08 : o.pf, pr = o.pr == null ? 0.05 : o.pr;
|
|
22198
22234
|
const bowF = o.bowF == null ? 0.07 : o.bowF, bowR = o.bowR == null ? 0.04 : o.bowR, Rb = o.Rb || 0.35, hw = W / 2;
|
|
22199
22235
|
const crown = o.crown == null ? 0.045 : o.crown, xc = o.crownX != null ? o.crownX : m.glasshouse ? m.glasshouse.fb : front - 1, y0 = by + 0.55 * (top - by);
|
|
22200
22236
|
function sc(y) {
|
|
22201
|
-
const t =
|
|
22237
|
+
const t = clamp5((y - by) / (top - by), 0, 1);
|
|
22202
22238
|
return t < peak ? 1 - tuck * Math.pow(1 - t / peak, 2) : 1 - B * Math.pow((t - peak) / (1 - peak), 2);
|
|
22203
22239
|
}
|
|
22204
22240
|
function F(v2) {
|
|
22205
|
-
const a = smooth2((v2.x - (front - Rf)) / Rf), b = smooth2((rear + Rr - v2.x) / Rr), zz =
|
|
22241
|
+
const a = smooth2((v2.x - (front - Rf)) / Rf), b = smooth2((rear + Rr - v2.x) / Rr), zz = clamp5(v2.z / hw, -1, 1) * clamp5(v2.z / hw, -1, 1);
|
|
22206
22242
|
const bf = smooth2((v2.x - (front - Rb)) / Rb), br = smooth2((rear + Rb - v2.x) / Rb);
|
|
22207
22243
|
const cr = crown * (1 - zz) * smooth2((v2.y - y0) / (top - y0)) * smooth2((v2.x - xc) / 0.3);
|
|
22208
22244
|
v2.x += -bowF * zz * bf + bowR * zz * br;
|
|
@@ -22279,7 +22315,7 @@ var GameVehiclesThree = (() => {
|
|
|
22279
22315
|
o = o || {};
|
|
22280
22316
|
const wd = o.w || 0.075, dp = o.d || 0.045, mat = o.mat || CLAD, out = [];
|
|
22281
22317
|
arches.forEach(function(a) {
|
|
22282
|
-
const a0 = Math.asin(
|
|
22318
|
+
const a0 = Math.asin(clamp5((by - a[1]) / a[2], -1, 1)) - 0.02, r0 = a[2] - (o.inner == null ? 0.055 : o.inner), r1 = a[2] + wd;
|
|
22283
22319
|
const sh = new THREE.Shape();
|
|
22284
22320
|
if (o.trap) {
|
|
22285
22321
|
const yb0 = Math.sin(a0) * r1, xb0 = Math.cos(a0) * r1, top = r1 * 0.93, xt = r1 * 0.52;
|
|
@@ -22629,7 +22665,7 @@ var GameVehiclesThree = (() => {
|
|
|
22629
22665
|
function roof(c, drop, x0, x1) {
|
|
22630
22666
|
const m = c.meta, b = m.glasshouse.belt, h = m.top - b;
|
|
22631
22667
|
function dy(x, y) {
|
|
22632
|
-
const t =
|
|
22668
|
+
const t = clamp5((y - b) / h, 0, 1);
|
|
22633
22669
|
return t <= 0 ? 0 : drop * smooth2((x0 - x) / (x0 - x1)) * t;
|
|
22634
22670
|
}
|
|
22635
22671
|
c.root.updateMatrixWorld(true);
|
|
@@ -23116,17 +23152,17 @@ var GameVehiclesThree = (() => {
|
|
|
23116
23152
|
}
|
|
23117
23153
|
beltline(c2, -0.35, 0.035, col);
|
|
23118
23154
|
pillarC(c2, -1.12, [-1.36, 1.25]);
|
|
23119
|
-
const E = glasshouse(c2),
|
|
23155
|
+
const E = glasshouse(c2), hz2 = function(y) {
|
|
23120
23156
|
return kit.halfAt(m.glasshouse.Wg, m.tumble, y) + 0.03;
|
|
23121
23157
|
};
|
|
23122
23158
|
[1, -1].forEach(function(sd) {
|
|
23123
23159
|
c2.tube(
|
|
23124
23160
|
[
|
|
23125
|
-
[m.glasshouse.fb - 0.08, E.yb - 0.03, sd *
|
|
23126
|
-
[-0.35, E.yb - 0.03, sd *
|
|
23127
|
-
[-1.45, E.yb + 0.01, sd *
|
|
23128
|
-
[E.back(E.yb + 0.15) + 0.05, E.yb + 0.15, sd *
|
|
23129
|
-
[E.back(E.top - 0.05) + 0.06, E.top - 0.05, sd *
|
|
23161
|
+
[m.glasshouse.fb - 0.08, E.yb - 0.03, sd * hz2(E.yb)],
|
|
23162
|
+
[-0.35, E.yb - 0.03, sd * hz2(E.yb)],
|
|
23163
|
+
[-1.45, E.yb + 0.01, sd * hz2(E.yb)],
|
|
23164
|
+
[E.back(E.yb + 0.15) + 0.05, E.yb + 0.15, sd * hz2(E.yb + 0.15)],
|
|
23165
|
+
[E.back(E.top - 0.05) + 0.06, E.top - 0.05, sd * hz2(E.top - 0.05)]
|
|
23130
23166
|
],
|
|
23131
23167
|
9e-3,
|
|
23132
23168
|
MT.chrome,
|
|
@@ -23823,8 +23859,8 @@ var GameVehiclesThree = (() => {
|
|
|
23823
23859
|
setOnHit(pt, function(c2, p0) {
|
|
23824
23860
|
const o = p0.obj;
|
|
23825
23861
|
if (o && o.userData.hung && o.parent && o.parent !== c2.vehicle) {
|
|
23826
|
-
o.parent.rotation.x =
|
|
23827
|
-
o.parent.rotation.y =
|
|
23862
|
+
o.parent.rotation.x = clamp5(o.parent.rotation.x, -0.22, 0.22);
|
|
23863
|
+
o.parent.rotation.y = clamp5(o.parent.rotation.y, -0.08, 0.08);
|
|
23828
23864
|
}
|
|
23829
23865
|
});
|
|
23830
23866
|
});
|
|
@@ -24475,7 +24511,7 @@ var GameVehiclesThree = (() => {
|
|
|
24475
24511
|
const shared2 = fx.shared;
|
|
24476
24512
|
const trl = fx.t;
|
|
24477
24513
|
const lbl = fx.label;
|
|
24478
|
-
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, _paintCol = kit.paintColor, G2 = kit.geo, DS = THREE.DoubleSide,
|
|
24514
|
+
const V3 = kit.V3, PI = Math.PI, MT = kit.materials, CM = kit.makeMaterial, _paintCol = kit.paintColor, G2 = kit.geo, DS = THREE.DoubleSide, clamp5 = kit.clamp;
|
|
24479
24515
|
function colorize(geo, fn) {
|
|
24480
24516
|
const p = geo.attributes.position, col = new Float32Array(p.count * 3);
|
|
24481
24517
|
for (let i = 0; i < p.count; i++) {
|
|
@@ -24491,7 +24527,7 @@ var GameVehiclesThree = (() => {
|
|
|
24491
24527
|
return a + (b - a) * t;
|
|
24492
24528
|
}
|
|
24493
24529
|
function sstep(a, b, x) {
|
|
24494
|
-
const t =
|
|
24530
|
+
const t = clamp5((x - a) / (b - a), 0, 1);
|
|
24495
24531
|
return t * t * (3 - 2 * t);
|
|
24496
24532
|
}
|
|
24497
24533
|
function tab(T) {
|
|
@@ -24568,7 +24604,7 @@ var GameVehiclesThree = (() => {
|
|
|
24568
24604
|
o,
|
|
24569
24605
|
sec,
|
|
24570
24606
|
t: function(x) {
|
|
24571
|
-
return
|
|
24607
|
+
return clamp5((x - o.x0) / o.L, 0, 1);
|
|
24572
24608
|
},
|
|
24573
24609
|
/* half beam of the hull at height y (inside, so floors and decks do not leak) */
|
|
24574
24610
|
half: function(x, y) {
|
|
@@ -24742,7 +24778,7 @@ var GameVehiclesThree = (() => {
|
|
|
24742
24778
|
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
24743
24779
|
const a = poly[i], b = poly[j];
|
|
24744
24780
|
if (a[1] > y !== b[1] > y && x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0]) inside = !inside;
|
|
24745
|
-
const vx = b[0] - a[0], vy = b[1] - a[1], wx = x - a[0], wy = y - a[1], t =
|
|
24781
|
+
const vx = b[0] - a[0], vy = b[1] - a[1], wx = x - a[0], wy = y - a[1], t = clamp5((wx * vx + wy * vy) / (vx * vx + vy * vy), 0, 1);
|
|
24746
24782
|
d = Math.min(d, Math.hypot(wx - vx * t, wy - vy * t));
|
|
24747
24783
|
}
|
|
24748
24784
|
return inside ? -d : d;
|
|
@@ -27290,6 +27326,7 @@ var GameVehiclesThree = (() => {
|
|
|
27290
27326
|
shared: shared2,
|
|
27291
27327
|
t,
|
|
27292
27328
|
label: (key) => ({ key, label: t("labels." + key) }),
|
|
27329
|
+
hasSunroof: (key) => !!options.sunroofs?.includes(key),
|
|
27293
27330
|
register(spec, build) {
|
|
27294
27331
|
if (!groups.has(spec.group) || only && !only.has(spec.key) || skip.has(spec.key)) return;
|
|
27295
27332
|
kit.register({ ...spec, seedKey: legacyVehicleSeeds[spec.key] }, build);
|
|
@@ -29267,8 +29304,8 @@ var GameVehiclesThree = (() => {
|
|
|
29267
29304
|
const face = faces[i];
|
|
29268
29305
|
f3(face[2] + offset, face[1] + offset, face[0] + offset);
|
|
29269
29306
|
}
|
|
29270
|
-
const
|
|
29271
|
-
offset = vlen *
|
|
29307
|
+
const layer2 = steps + bevelSegments * 2;
|
|
29308
|
+
offset = vlen * layer2;
|
|
29272
29309
|
for (let i = 0; i < flen; i++) {
|
|
29273
29310
|
const face = faces[i];
|
|
29274
29311
|
f3(face[0] + offset, face[1] + offset, face[2] + offset);
|
|
@@ -30771,7 +30808,7 @@ var GameVehiclesThree = (() => {
|
|
|
30771
30808
|
const nr = g.attributes.normal;
|
|
30772
30809
|
const hx = w / 2 - r;
|
|
30773
30810
|
const hy = h / 2 - r;
|
|
30774
|
-
const
|
|
30811
|
+
const hz2 = d / 2 - r;
|
|
30775
30812
|
function map(v, hi) {
|
|
30776
30813
|
const i = Math.round((v + 0.5) * n);
|
|
30777
30814
|
return i <= nb ? -hi - r * (nb - i) / nb : hi + r * (i - nb - 1) / nb;
|
|
@@ -30780,8 +30817,8 @@ var GameVehiclesThree = (() => {
|
|
|
30780
30817
|
const c = new V3();
|
|
30781
30818
|
const dir = new V3();
|
|
30782
30819
|
for (let i = 0; i < p.count; i++) {
|
|
30783
|
-
q.set(map(p.getX(i), hx), map(p.getY(i), hy), map(p.getZ(i),
|
|
30784
|
-
c.set(clamp(q.x, -hx, hx), clamp(q.y, -hy, hy), clamp(q.z, -
|
|
30820
|
+
q.set(map(p.getX(i), hx), map(p.getY(i), hy), map(p.getZ(i), hz2));
|
|
30821
|
+
c.set(clamp(q.x, -hx, hx), clamp(q.y, -hy, hy), clamp(q.z, -hz2, hz2));
|
|
30785
30822
|
dir.subVectors(q, c);
|
|
30786
30823
|
if (dir.lengthSq() > 1e-12) {
|
|
30787
30824
|
dir.normalize();
|
|
@@ -33504,13 +33541,13 @@ var GameVehiclesThree = (() => {
|
|
|
33504
33541
|
);
|
|
33505
33542
|
const hy = hl.y || s.headY || s.noseY - 0.06;
|
|
33506
33543
|
const hx = I.profX(it, hy, true);
|
|
33507
|
-
const
|
|
33544
|
+
const hz2 = W / 2 - hl.in - hl.w / 2;
|
|
33508
33545
|
[1, -1].forEach(function(sd) {
|
|
33509
33546
|
kit.headlight(c, {
|
|
33510
33547
|
style: hl.style,
|
|
33511
33548
|
x: hx + 0.02 + (hl.dx || 0),
|
|
33512
33549
|
y: hy,
|
|
33513
|
-
z: sd *
|
|
33550
|
+
z: sd * hz2,
|
|
33514
33551
|
w: hl.w,
|
|
33515
33552
|
h: hl.h,
|
|
33516
33553
|
side: sd,
|
|
@@ -33518,7 +33555,7 @@ var GameVehiclesThree = (() => {
|
|
|
33518
33555
|
mat: hl.mat
|
|
33519
33556
|
});
|
|
33520
33557
|
});
|
|
33521
|
-
if (hl.style === "ledbar") kit.lightBar(c, hx + 0.03, hy + hl.h * 0.3, -
|
|
33558
|
+
if (hl.style === "ledbar") kit.lightBar(c, hx + 0.03, hy + hl.h * 0.3, -hz2 + hl.w / 2, hz2 - hl.w / 2, 0.018, 1);
|
|
33522
33559
|
if (s.grille !== false && !s.noGrille) {
|
|
33523
33560
|
const gr = Object.assign({ style: "bars", w: W * 0.44, h: Math.max(0.08, hy - bumperY - 0.12) }, s.grille || {});
|
|
33524
33561
|
const gy = gr.y || (bumperY + hy) / 2 + 0.02;
|
|
@@ -35135,8 +35172,20 @@ var GameVehiclesThree = (() => {
|
|
|
35135
35172
|
if (!L || L.broken) return;
|
|
35136
35173
|
L.broken = true;
|
|
35137
35174
|
L.lens.forEach(function(m) {
|
|
35138
|
-
m.
|
|
35175
|
+
const rim = L.lensMaterial ? glassRim(m.geometry, c.rand) : null;
|
|
35176
|
+
if (!rim) {
|
|
35177
|
+
m.visible = false;
|
|
35178
|
+
return;
|
|
35179
|
+
}
|
|
35180
|
+
m.geometry.dispose();
|
|
35181
|
+
m.geometry = rim;
|
|
35182
|
+
m.material = MATERIALS.glassEdge;
|
|
35183
|
+
m.userData.lensRim = true;
|
|
35139
35184
|
});
|
|
35185
|
+
if (L.lensMaterial)
|
|
35186
|
+
(L.reflector || []).forEach(function(m) {
|
|
35187
|
+
m.material = MATERIALS.broken;
|
|
35188
|
+
});
|
|
35140
35189
|
L.inner.forEach(function(m) {
|
|
35141
35190
|
m.material = MATERIALS.bulbBroken;
|
|
35142
35191
|
});
|
|
@@ -35144,11 +35193,38 @@ var GameVehiclesThree = (() => {
|
|
|
35144
35193
|
h.visible = false;
|
|
35145
35194
|
});
|
|
35146
35195
|
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
35147
|
-
|
|
35148
|
-
I.burst(c, "
|
|
35196
|
+
const glass = L.lensMaterial !== "plastic";
|
|
35197
|
+
I.burst(c, "lens", c.toEffects(p), glass ? 10 : 5, { dir: c.dirToEffects(dir.clone().negate()), speed: glass ? 1.4 : 1.1, variant: L.tint === "red" ? "red" : "clear" });
|
|
35198
|
+
I.burst(c, "plastic", c.toEffects(p), glass ? 2 : 5, { dir: c.dirToEffects(dir.clone().negate()), speed: 1 });
|
|
35149
35199
|
}
|
|
35150
35200
|
kit.breakLight = breakLight;
|
|
35151
|
-
|
|
35201
|
+
const crazedCache = /* @__PURE__ */ new Map();
|
|
35202
|
+
const CRAZED_OPACITY = 0.6;
|
|
35203
|
+
function crazed(material) {
|
|
35204
|
+
const known = crazedCache.get(material);
|
|
35205
|
+
if (known) return known;
|
|
35206
|
+
const m = I.crackLens(material).clone();
|
|
35207
|
+
m.opacity = Math.max(m.opacity ?? 1, CRAZED_OPACITY);
|
|
35208
|
+
m.transparent = true;
|
|
35209
|
+
m.userData.shared = true;
|
|
35210
|
+
crazedCache.set(material, m);
|
|
35211
|
+
return m;
|
|
35212
|
+
}
|
|
35213
|
+
function crackLight(c, L, dir, pierced) {
|
|
35214
|
+
if (!L || L.broken) return;
|
|
35215
|
+
if (!L.cracked) {
|
|
35216
|
+
L.cracked = true;
|
|
35217
|
+
for (const m of L.lens) if (m.material && !Array.isArray(m.material)) m.material = crazed(m.material);
|
|
35218
|
+
}
|
|
35219
|
+
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
35220
|
+
I.burst(c, "lens", c.toEffects(p), 3, { dir: c.dirToEffects(dir.clone().negate()), speed: 0.9, variant: L.tint === "red" ? "red" : "clear" });
|
|
35221
|
+
if (!pierced || L.dark) return;
|
|
35222
|
+
L.dark = true;
|
|
35223
|
+
for (const m of L.inner) m.material = MATERIALS.bulbBroken;
|
|
35224
|
+
for (const h of L.halo ?? []) h.visible = false;
|
|
35225
|
+
}
|
|
35226
|
+
kit.crackLight = crackLight;
|
|
35227
|
+
Object.assign(I, { glassUV, drawWeb, crackGlass, glassRim, shatterGlass, hitGlass, breakLight, crackLight });
|
|
35152
35228
|
}
|
|
35153
35229
|
function installImpact(kit) {
|
|
35154
35230
|
const THREE = kit.THREE;
|
|
@@ -40658,6 +40734,8 @@ var GameVehiclesThree = (() => {
|
|
|
40658
40734
|
heli: ["battery", "alternator", "gearbox"]
|
|
40659
40735
|
};
|
|
40660
40736
|
var HITCH_CHANCE = { car: 30, truck: 60, bus: 0, tractor: 100, quad: 40, bike: 0, tank: 0, boat: 0, plane: 0, heli: 0 };
|
|
40737
|
+
var GLASS_LENS_HP = 320;
|
|
40738
|
+
var PLASTIC_LENS_HP = 900;
|
|
40661
40739
|
var STEEL_KG_M2 = 7;
|
|
40662
40740
|
var ALU_KG_M2 = 3.2;
|
|
40663
40741
|
var kindOf = (c) => {
|
|
@@ -40815,7 +40893,9 @@ var GameVehiclesThree = (() => {
|
|
|
40815
40893
|
continue;
|
|
40816
40894
|
}
|
|
40817
40895
|
if (role === "light") {
|
|
40818
|
-
|
|
40896
|
+
const lens = /^head/.test(id) ? spec.headlightLens ?? "polycarbonate" : "acrylic";
|
|
40897
|
+
const glassLens = lens === "glass";
|
|
40898
|
+
add2({ id, role, name: label, material: glassLens ? "glass" : "plastic", box: box2, mass: 1.2 + 4 * area3, hp: glassLens ? GLASS_LENS_HP : PLASTIC_LENS_HP, zoneId: id, visual: true, tags: ["light", `lens_${lens}`, ...tags] });
|
|
40819
40899
|
continue;
|
|
40820
40900
|
}
|
|
40821
40901
|
if (role === "bumper") {
|
|
@@ -41269,20 +41349,185 @@ var GameVehiclesThree = (() => {
|
|
|
41269
41349
|
mesh2.geometry.dispose();
|
|
41270
41350
|
mesh2.geometry = g;
|
|
41271
41351
|
}
|
|
41352
|
+
var STRIP_SMOOTH_DEG = 180;
|
|
41353
|
+
var SKIN_BAND = 0.05;
|
|
41354
|
+
var BODY_FACING = 0.5;
|
|
41355
|
+
var FIELDS = 4;
|
|
41356
|
+
var SEAM_THIN = 0.03;
|
|
41357
|
+
var SEAM_AT_EDGE = 0.04;
|
|
41358
|
+
var SEAM_TALL = 0.4;
|
|
41359
|
+
var SLIVER_AREA = 1e-10;
|
|
41360
|
+
function cutStripOverDoor(vehicleKit, params) {
|
|
41361
|
+
const kit = vehicleKit;
|
|
41362
|
+
const strip = params.strip;
|
|
41363
|
+
const toVehicle = params.toVehicle;
|
|
41364
|
+
const { x0, x1 } = params;
|
|
41365
|
+
const THREE = kit.THREE;
|
|
41366
|
+
const at = new THREE.Vector3();
|
|
41367
|
+
const along = (x, y, z) => at.set(x, y, z).applyMatrix4(toVehicle).x;
|
|
41368
|
+
const piece = (tests) => {
|
|
41369
|
+
const mesh2 = new THREE.Mesh(strip.geometry.clone(), strip.material);
|
|
41370
|
+
kit.cutMesh(mesh2, tests, STRIP_SMOOTH_DEG, {});
|
|
41371
|
+
return mesh2;
|
|
41372
|
+
};
|
|
41373
|
+
const over = piece([{ sdf: (x, y, z) => along(x, y, z) - x0 }, { sdf: (x, y, z) => x1 - along(x, y, z) }]);
|
|
41374
|
+
const after = piece([{ sdf: (x, y, z) => along(x, y, z) - x1 }]);
|
|
41375
|
+
kit.cutMesh(strip, [{ sdf: (x, y, z) => x0 - along(x, y, z) }], STRIP_SMOOTH_DEG, {});
|
|
41376
|
+
const joined = joinTriangles(THREE, strip.geometry, after.geometry);
|
|
41377
|
+
strip.geometry.dispose();
|
|
41378
|
+
after.geometry.dispose();
|
|
41379
|
+
strip.geometry = joined;
|
|
41380
|
+
return over;
|
|
41381
|
+
}
|
|
41382
|
+
function joinTriangles(THREE, first, second) {
|
|
41383
|
+
const a = first.index ? first.toNonIndexed() : first;
|
|
41384
|
+
const b = second.index ? second.toNonIndexed() : second;
|
|
41385
|
+
const flags = [...solidTriangles(THREE, a), ...solidTriangles(THREE, b)];
|
|
41386
|
+
const both = new THREE.BufferGeometry();
|
|
41387
|
+
for (const name of Object.keys(a.attributes)) {
|
|
41388
|
+
const p = a.getAttribute(name);
|
|
41389
|
+
const q = b.getAttribute(name);
|
|
41390
|
+
if (!q || q.itemSize !== p.itemSize) continue;
|
|
41391
|
+
const array = new Float32Array(p.array.length + q.array.length);
|
|
41392
|
+
array.set(p.array, 0);
|
|
41393
|
+
array.set(q.array, p.array.length);
|
|
41394
|
+
both.setAttribute(name, new THREE.Float32BufferAttribute(array, p.itemSize));
|
|
41395
|
+
}
|
|
41396
|
+
if (a !== first) a.dispose();
|
|
41397
|
+
if (b !== second) b.dispose();
|
|
41398
|
+
const out = pickTriangles(THREE, both, flags, true) ?? new THREE.BufferGeometry();
|
|
41399
|
+
both.dispose();
|
|
41400
|
+
return out;
|
|
41401
|
+
}
|
|
41402
|
+
function solidTriangles(THREE, geometry) {
|
|
41403
|
+
const pos = geometry.getAttribute("position");
|
|
41404
|
+
const triangle = new THREE.Triangle();
|
|
41405
|
+
const flags = [];
|
|
41406
|
+
for (let t = 0; t * 3 + 2 < pos.count; t++) {
|
|
41407
|
+
triangle.a.fromBufferAttribute(pos, t * 3);
|
|
41408
|
+
triangle.b.fromBufferAttribute(pos, t * 3 + 1);
|
|
41409
|
+
triangle.c.fromBufferAttribute(pos, t * 3 + 2);
|
|
41410
|
+
flags.push(triangle.getArea() > SLIVER_AREA);
|
|
41411
|
+
}
|
|
41412
|
+
return flags;
|
|
41413
|
+
}
|
|
41414
|
+
function splitDoorSkin(three, meshes, toVehicleOf, side2, depth) {
|
|
41415
|
+
const THREE = three;
|
|
41416
|
+
const pieces = meshes;
|
|
41417
|
+
const toVehicle = toVehicleOf;
|
|
41418
|
+
const v = new THREE.Vector3();
|
|
41419
|
+
const corners = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()];
|
|
41420
|
+
const triangle = new THREE.Triangle();
|
|
41421
|
+
const centres = pieces.map((mesh2) => {
|
|
41422
|
+
const geometry = mesh2.geometry.index ? mesh2.geometry.toNonIndexed() : mesh2.geometry;
|
|
41423
|
+
const matrix = toVehicle(mesh2);
|
|
41424
|
+
const pos = geometry.getAttribute("position");
|
|
41425
|
+
const out = new Float32Array(pos.count / 3 * FIELDS);
|
|
41426
|
+
for (let t = 0; t * 3 + 2 < pos.count; t++) {
|
|
41427
|
+
let y = 0;
|
|
41428
|
+
let across = 0;
|
|
41429
|
+
for (let k = 0; k < 3; k++) {
|
|
41430
|
+
corners[k].fromBufferAttribute(pos, t * 3 + k).applyMatrix4(matrix);
|
|
41431
|
+
y += corners[k].y / 3;
|
|
41432
|
+
across += side2 * corners[k].z / 3;
|
|
41433
|
+
}
|
|
41434
|
+
out[t * FIELDS] = y;
|
|
41435
|
+
out[t * FIELDS + 1] = across;
|
|
41436
|
+
triangle.set(corners[0], corners[1], corners[2]).getNormal(v);
|
|
41437
|
+
out[t * FIELDS + 2] = v.y;
|
|
41438
|
+
out[t * FIELDS + 3] = v.x;
|
|
41439
|
+
}
|
|
41440
|
+
return { geometry, out };
|
|
41441
|
+
});
|
|
41442
|
+
const outer = /* @__PURE__ */ new Map();
|
|
41443
|
+
for (const { out } of centres)
|
|
41444
|
+
for (let t = 0; t * FIELDS < out.length; t++) {
|
|
41445
|
+
const band = Math.floor(out[t * FIELDS] / SKIN_BAND);
|
|
41446
|
+
outer.set(band, Math.max(outer.get(band) ?? -Infinity, out[t * FIELDS + 1]));
|
|
41447
|
+
}
|
|
41448
|
+
const surfaceAt = (y) => {
|
|
41449
|
+
const band = Math.floor(y / SKIN_BAND);
|
|
41450
|
+
return Math.max(outer.get(band - 1) ?? -Infinity, outer.get(band) ?? -Infinity, outer.get(band + 1) ?? -Infinity);
|
|
41451
|
+
};
|
|
41452
|
+
return centres.map(({ geometry, out }, i) => {
|
|
41453
|
+
const near = [];
|
|
41454
|
+
for (let t = 0; t * FIELDS < out.length; t++) {
|
|
41455
|
+
const f = t * FIELDS;
|
|
41456
|
+
const body = out[f + 1] < surfaceAt(out[f]) - depth && (out[f + 2] < -BODY_FACING || Math.abs(out[f + 3]) > BODY_FACING);
|
|
41457
|
+
near.push(!body);
|
|
41458
|
+
}
|
|
41459
|
+
const result = { skin: pickTriangles(THREE, geometry, near, true), deep: pickTriangles(THREE, geometry, near, false) };
|
|
41460
|
+
if (geometry !== pieces[i].geometry) geometry.dispose();
|
|
41461
|
+
return result;
|
|
41462
|
+
});
|
|
41463
|
+
}
|
|
41464
|
+
function leaveDeepSkinOnBody(three, meshes, params) {
|
|
41465
|
+
const THREE = three;
|
|
41466
|
+
const pieces = meshes;
|
|
41467
|
+
const toVehicle = params.toVehicle;
|
|
41468
|
+
const { side: side2, depth } = params;
|
|
41469
|
+
const split = splitDoorSkin(THREE, pieces, (mesh2) => new THREE.Matrix4().multiplyMatrices(toVehicle, mesh2.matrixWorld), side2, depth);
|
|
41470
|
+
split.forEach(({ skin, deep }, i) => {
|
|
41471
|
+
const piece = pieces[i];
|
|
41472
|
+
if (!deep || !skin || !piece.parent) return;
|
|
41473
|
+
const body = new THREE.Mesh(deep, piece.material);
|
|
41474
|
+
body.position.copy(piece.position);
|
|
41475
|
+
body.quaternion.copy(piece.quaternion);
|
|
41476
|
+
body.scale.copy(piece.scale);
|
|
41477
|
+
body.castShadow = piece.castShadow;
|
|
41478
|
+
body.receiveShadow = piece.receiveShadow;
|
|
41479
|
+
piece.parent.add(body);
|
|
41480
|
+
piece.geometry.dispose();
|
|
41481
|
+
piece.geometry = skin;
|
|
41482
|
+
});
|
|
41483
|
+
}
|
|
41484
|
+
function pickTriangles(THREE, geometry, flags, wanted) {
|
|
41485
|
+
const count2 = flags.filter((flag) => flag === wanted).length;
|
|
41486
|
+
if (!count2) return null;
|
|
41487
|
+
const out = new THREE.BufferGeometry();
|
|
41488
|
+
for (const name of Object.keys(geometry.attributes)) {
|
|
41489
|
+
const attribute = geometry.getAttribute(name);
|
|
41490
|
+
const size = attribute.itemSize;
|
|
41491
|
+
const source = attribute.array;
|
|
41492
|
+
const array = new Float32Array(count2 * 3 * size);
|
|
41493
|
+
let w = 0;
|
|
41494
|
+
flags.forEach((flag, t) => {
|
|
41495
|
+
if (flag !== wanted) return;
|
|
41496
|
+
array.set(source.subarray(t * 3 * size, (t + 1) * 3 * size), w);
|
|
41497
|
+
w += 3 * size;
|
|
41498
|
+
});
|
|
41499
|
+
out.setAttribute(name, new THREE.Float32BufferAttribute(array, size));
|
|
41500
|
+
}
|
|
41501
|
+
out.computeBoundingBox();
|
|
41502
|
+
out.computeBoundingSphere();
|
|
41503
|
+
return out;
|
|
41504
|
+
}
|
|
41505
|
+
function isDoorSeam(three, params) {
|
|
41506
|
+
const THREE = three;
|
|
41507
|
+
const box2 = params.box;
|
|
41508
|
+
const { zone, side: side2, dark } = params;
|
|
41509
|
+
if (!dark || box2.isEmpty()) return false;
|
|
41510
|
+
const size = box2.getSize(new THREE.Vector3());
|
|
41511
|
+
const centre = box2.getCenter(new THREE.Vector3());
|
|
41512
|
+
if (Math.sign(centre.z) !== Math.sign(side2) || size.x > SEAM_THIN || size.z > SEAM_THIN || size.y < (zone.y1 - zone.y0) * SEAM_TALL) return false;
|
|
41513
|
+
if (centre.y < zone.y0 || centre.y > zone.y1) return false;
|
|
41514
|
+
return Math.abs(centre.x - zone.x0) < SEAM_AT_EDGE || Math.abs(centre.x - zone.x1) < SEAM_AT_EDGE;
|
|
41515
|
+
}
|
|
41272
41516
|
var HOLE_DECAL_SCALE = 2.4;
|
|
41273
41517
|
var DOOR_DEPTH = 0.07;
|
|
41518
|
+
var DOOR_SKIN_DEPTH = 0.09;
|
|
41519
|
+
var DOOR_FLOOR_DEPTH = 0.2;
|
|
41274
41520
|
var DOOR_EDGE_PIECES = 8;
|
|
41275
41521
|
var DOOR_EDGE_REACH = 0.08;
|
|
41276
41522
|
var DOOR_CARD_COLOR = "#3b3e42";
|
|
41277
41523
|
var DOOR_LINER_COLOR = "#5a5e64";
|
|
41278
41524
|
var DOOR_LINER_DARK = 0.15;
|
|
41279
|
-
var DOOR_STRIP_THIN = 0.
|
|
41525
|
+
var DOOR_STRIP_THIN = 0.13;
|
|
41280
41526
|
var DOOR_STRIP_NEAR = 0.04;
|
|
41281
41527
|
var DOOR_STRIP_TALL = 0.22;
|
|
41282
|
-
var
|
|
41283
|
-
var DOOR_STRIP_VERTICES = 2e4;
|
|
41528
|
+
var DOOR_STRIP_VERTICES = 6e4;
|
|
41284
41529
|
var DOOR_SILL = 0.25;
|
|
41285
|
-
var DOOR_STRIP_OVER = 0.
|
|
41530
|
+
var DOOR_STRIP_OVER = 0.05;
|
|
41286
41531
|
var DOOR_TOP_OVER_PANE = 0.03;
|
|
41287
41532
|
var HOLE_TEXTURE_SIZE = 64;
|
|
41288
41533
|
var LAMINATED_HP = 1e3;
|
|
@@ -41386,6 +41631,8 @@ var GameVehiclesThree = (() => {
|
|
|
41386
41631
|
__publicField(this, "syncPlates", /* @__PURE__ */ new Map());
|
|
41387
41632
|
__publicField(this, "skins", /* @__PURE__ */ new Map());
|
|
41388
41633
|
__publicField(this, "doorTops", /* @__PURE__ */ new Map());
|
|
41634
|
+
__publicField(this, "seams", /* @__PURE__ */ new Map());
|
|
41635
|
+
__publicField(this, "openNow", /* @__PURE__ */ new Set());
|
|
41389
41636
|
__publicField(this, "lightsOn", true);
|
|
41390
41637
|
__publicField(this, "warming");
|
|
41391
41638
|
__publicField(this, "tornThisFrame", false);
|
|
@@ -41562,6 +41809,11 @@ var GameVehiclesThree = (() => {
|
|
|
41562
41809
|
}
|
|
41563
41810
|
panel.visible = open && !this.skins.get(id);
|
|
41564
41811
|
recess.visible = open && !this.skins.get(id);
|
|
41812
|
+
if (open) this.openNow.add(id);
|
|
41813
|
+
else this.openNow.delete(id);
|
|
41814
|
+
this.seams.forEach((doors, seam) => {
|
|
41815
|
+
if (doors.has(id)) seam.visible = ![...doors].some((door) => this.openNow.has(door));
|
|
41816
|
+
});
|
|
41565
41817
|
};
|
|
41566
41818
|
const euler = piv.rotation;
|
|
41567
41819
|
const quaternion = piv.quaternion;
|
|
@@ -41604,11 +41856,13 @@ var GameVehiclesThree = (() => {
|
|
|
41604
41856
|
const pcs = I.cutPanel(this.c, z, rect, true, dirty, { anyN: true }).map((x) => x.mesh).filter((m) => !!m);
|
|
41605
41857
|
this.mend(dirty);
|
|
41606
41858
|
if (pcs.length) {
|
|
41859
|
+
leaveDeepSkinOnBody(this.THREE, pcs, { toVehicle: inv, side: z.side < 0 ? -1 : 1, depth: DOOR_SKIN_DEPTH });
|
|
41607
41860
|
const panes = I.windowsOver(this.c, z, inv);
|
|
41608
41861
|
const seals = panes.map((g) => I.paneSeals.get(g)).filter((s) => !!s);
|
|
41609
41862
|
const mirrors = this.mirrorsOn(z);
|
|
41610
41863
|
const frames = this.framesAround(panes, inv);
|
|
41611
|
-
|
|
41864
|
+
const followers = I.followersIn(this.c, z, inv).filter((o) => !this.takeSeam(o, id, z, inv) && !this.inFloor(o, piv, z, inv));
|
|
41865
|
+
objs = [...pcs, ...followers, ...panes, ...seals, ...mirrors, ...frames];
|
|
41612
41866
|
}
|
|
41613
41867
|
} else {
|
|
41614
41868
|
const pcs = this.cutZone(z);
|
|
@@ -41636,6 +41890,25 @@ var GameVehiclesThree = (() => {
|
|
|
41636
41890
|
this.c.root.updateMatrixWorld(true);
|
|
41637
41891
|
return objs.length > 0;
|
|
41638
41892
|
}
|
|
41893
|
+
/** A thin part at the foot of the opening, well inside the skin (a step, a sill trim, a rail on the floor), is the floor's: it does not swing with the door. */
|
|
41894
|
+
inFloor(o, piv, z, inv) {
|
|
41895
|
+
const side2 = z.side < 0 ? -1 : 1;
|
|
41896
|
+
const centre = this.I.localBox(this.c, o, inv).getCenter(new this.THREE.Vector3());
|
|
41897
|
+
const skin = side2 * (piv.position.z - side2 * 0.016);
|
|
41898
|
+
return centre.y < z.box.y0 + DOOR_SILL && side2 * centre.z < skin - DOOR_FLOOR_DEPTH;
|
|
41899
|
+
}
|
|
41900
|
+
/** A shut line drawn at the door's edge stays on the body, hidden while a door it marks stands open; answers whether `o` is one. */
|
|
41901
|
+
takeSeam(o, id, z, inv) {
|
|
41902
|
+
const B = z.box;
|
|
41903
|
+
const box2 = this.I.localBox(this.c, o, inv);
|
|
41904
|
+
const color2 = o.material?.color;
|
|
41905
|
+
const dark = !!color2 && color2.getHSL({ h: 0, s: 0, l: 0 }).l < DOOR_LINER_DARK;
|
|
41906
|
+
if (!o.isMesh || !isDoorSeam(this.THREE, { box: box2, zone: { x0: B.x0, x1: B.x1, y0: B.y0, y1: B.y1 }, side: z.side, dark })) return false;
|
|
41907
|
+
const doors = this.seams.get(o) ?? /* @__PURE__ */ new Set();
|
|
41908
|
+
doors.add(id);
|
|
41909
|
+
this.seams.set(o, doors);
|
|
41910
|
+
return true;
|
|
41911
|
+
}
|
|
41639
41912
|
/** A door has edges: a band that follows the skin all round closes it to its card and hides where the cut followed the triangles of the shell; the same band, in the same paint as on a real car, lines the opening. */
|
|
41640
41913
|
doorEdges(piv, z, y1) {
|
|
41641
41914
|
const THREE = this.THREE;
|
|
@@ -41657,13 +41930,15 @@ var GameVehiclesThree = (() => {
|
|
|
41657
41930
|
const at = this.surface("z", side2, B, [[x, y]]);
|
|
41658
41931
|
return at != null && Math.abs(at - flat) < DOOR_EDGE_REACH ? at : flat;
|
|
41659
41932
|
});
|
|
41660
|
-
const band = (lift) => {
|
|
41933
|
+
const band = (lift, path, closed) => {
|
|
41661
41934
|
const position = [];
|
|
41662
41935
|
const index = [];
|
|
41663
|
-
|
|
41664
|
-
|
|
41936
|
+
path.forEach((at, i) => {
|
|
41937
|
+
const [x, y] = loop[at];
|
|
41938
|
+
position.push(x, y, heights[at] + side2 * lift, x, y, heights[at] + side2 * (lift - DOOR_DEPTH));
|
|
41939
|
+
if (!closed && i === path.length - 1) return;
|
|
41665
41940
|
const a = i * 2;
|
|
41666
|
-
const b = (i + 1) %
|
|
41941
|
+
const b = (i + 1) % path.length * 2;
|
|
41667
41942
|
index.push(a, a + 1, b, b, a + 1, b + 1);
|
|
41668
41943
|
});
|
|
41669
41944
|
const g = new THREE.BufferGeometry();
|
|
@@ -41691,8 +41966,11 @@ var GameVehiclesThree = (() => {
|
|
|
41691
41966
|
card.castShadow = false;
|
|
41692
41967
|
this.c.vehicle.add(card);
|
|
41693
41968
|
piv.attach(card);
|
|
41694
|
-
const
|
|
41695
|
-
const
|
|
41969
|
+
const all = loop.map((_, i) => i);
|
|
41970
|
+
const top = 2 * DOOR_EDGE_PIECES;
|
|
41971
|
+
const sides = [...all.slice(3 * DOOR_EDGE_PIECES), ...all.slice(0, top + 1)];
|
|
41972
|
+
const edges = new THREE.Mesh(band(2e-3, all, true), paint);
|
|
41973
|
+
const lining = new THREE.Mesh(band(2e-3, sides, false), paint);
|
|
41696
41974
|
for (const m of [edges, lining]) {
|
|
41697
41975
|
m.userData.noFollow = true;
|
|
41698
41976
|
m.castShadow = false;
|
|
@@ -41749,26 +42027,14 @@ var GameVehiclesThree = (() => {
|
|
|
41749
42027
|
const box2 = this.I.localBox(this.c, o, inv);
|
|
41750
42028
|
const size = box2.getSize(new THREE.Vector3());
|
|
41751
42029
|
const mid = box2.getCenter(new THREE.Vector3());
|
|
41752
|
-
if (Math.sign(mid.z) !== Math.sign(z.side) || size.z > DOOR_STRIP_THIN || size.y > DOOR_STRIP_TALL || Math.min(box2.max.x, x1) - Math.max(box2.min.x, x0) <
|
|
42030
|
+
if (Math.sign(mid.z) !== Math.sign(z.side) || size.z > DOOR_STRIP_THIN || size.y > DOOR_STRIP_TALL || Math.min(box2.max.x, x1) - Math.max(box2.min.x, x0) < DOOR_STRIP_OVER || mid.y < y0 || mid.y > y1) continue;
|
|
41753
42031
|
const toVehicle = new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld);
|
|
41754
|
-
const
|
|
41755
|
-
const sdf = (x, y, zz) => {
|
|
41756
|
-
v.set(x, y, zz).applyMatrix4(toVehicle);
|
|
41757
|
-
return Math.max(x0 - v.x, v.x - x1);
|
|
41758
|
-
};
|
|
41759
|
-
const over = new THREE.Mesh(o.geometry.clone(), o.material);
|
|
42032
|
+
const over = cutStripOverDoor(this.kit, { strip: o, toVehicle, x0, x1 });
|
|
41760
42033
|
over.position.copy(o.position);
|
|
41761
42034
|
over.quaternion.copy(o.quaternion);
|
|
41762
42035
|
over.scale.copy(o.scale);
|
|
41763
42036
|
this.c.vehicle.add(over);
|
|
41764
42037
|
over.updateMatrixWorld(true);
|
|
41765
|
-
const overToVehicle = new THREE.Matrix4().multiplyMatrices(inv, over.matrixWorld);
|
|
41766
|
-
const w = new THREE.Vector3();
|
|
41767
|
-
this.kit.cutMesh(over, [{ sdf: (x, y, zz) => {
|
|
41768
|
-
w.set(x, y, zz).applyMatrix4(overToVehicle);
|
|
41769
|
-
return -Math.max(x0 - w.x, w.x - x1);
|
|
41770
|
-
} }], STRIP_SMOOTH_DEG, {});
|
|
41771
|
-
this.kit.cutMesh(o, [{ sdf }], STRIP_SMOOTH_DEG, {});
|
|
41772
42038
|
over.userData.noFollow = true;
|
|
41773
42039
|
piv.attach(over);
|
|
41774
42040
|
}
|
|
@@ -42349,11 +42615,18 @@ var GameVehiclesThree = (() => {
|
|
|
42349
42615
|
}
|
|
42350
42616
|
}
|
|
42351
42617
|
}
|
|
42618
|
+
/** The lens of a light is holed or cracked and stays; `pierced`: a round went through it to the bulb, and the lamp goes dark. */
|
|
42619
|
+
crackLight(p, pierced) {
|
|
42620
|
+
const z = this.zone(p);
|
|
42621
|
+
if (!z) return;
|
|
42622
|
+
for (const L of z.lights) this.kit.crackLight(this.c, L, new this.THREE.Vector3(p.box.center[0] > 0 ? -1 : 1, 0, 0), pierced);
|
|
42623
|
+
}
|
|
42352
42624
|
/** The lens of a light breaks and its unit stays (damaged) or goes (destroyed). */
|
|
42353
42625
|
breakLight(p, gone) {
|
|
42354
42626
|
const z = this.zone(p);
|
|
42355
42627
|
if (!z) return;
|
|
42356
42628
|
for (const L of z.lights) {
|
|
42629
|
+
L.lensMaterial = p.material;
|
|
42357
42630
|
this.kit.breakLight(this.c, L, new this.THREE.Vector3(p.box.center[0] > 0 ? -1 : 1, 0, 0));
|
|
42358
42631
|
if (gone) {
|
|
42359
42632
|
if (L.group) L.group.visible = false;
|
|
@@ -42995,6 +43268,7 @@ var GameVehiclesThree = (() => {
|
|
|
42995
43268
|
wear(p, amount, out);
|
|
42996
43269
|
syncZone(p);
|
|
42997
43270
|
if (p.hp <= 0) lose(p, "broken", out, cause);
|
|
43271
|
+
else if (p.def.role === "light") vis?.crackLight(p.def, dtype === "ballistic");
|
|
42998
43272
|
if (p.def.role === "tyre" && !p.flat && p.hp < p.def.hp * 0.6 && !p.lost) {
|
|
42999
43273
|
p.flat = true;
|
|
43000
43274
|
vis?.tyreFlat(p.def, 0.24);
|
|
@@ -43807,7 +44081,7 @@ var GameVehiclesThree = (() => {
|
|
|
43807
44081
|
if (!list2.length) return 1;
|
|
43808
44082
|
return Math.min(...list2.map((p) => p.lost || !attached(p.def.id) ? 0 : p.hp / p.def.hp));
|
|
43809
44083
|
};
|
|
43810
|
-
const tyres = [...ps.values()].filter((p) => p.def.role === "tyre").map((p) => ({ x: p.def.box.center[0], z: p.def.box.center[2], health: tyreHealth(p) }));
|
|
44084
|
+
const tyres = [...ps.values()].filter((p) => p.def.role === "tyre").map((p) => ({ x: p.def.box.center[0], z: p.def.box.center[2], health: tyreHealth(p), rim: p.lost }));
|
|
43811
44085
|
const status = computeStatus();
|
|
43812
44086
|
const brakeSystem = [...ps.values()].find((p) => p.def.role === "brakes" && p.def.contains);
|
|
43813
44087
|
const fluidLeft = brakeSystem ? clamp((brakeSystem.liters ?? 0) / brakeSystem.def.contains.liters / BRAKE_FLUID_NEEDED, 0, 1) : 1;
|
|
@@ -44035,7 +44309,7 @@ var GameVehiclesThree = (() => {
|
|
|
44035
44309
|
};
|
|
44036
44310
|
}
|
|
44037
44311
|
|
|
44038
|
-
// ../game-audio/dist/chunk-
|
|
44312
|
+
// ../game-audio/dist/chunk-C2K7XN2K.js
|
|
44039
44313
|
var DISTANCE_CURVES = {
|
|
44040
44314
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
44041
44315
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -44558,6 +44832,7 @@ var GameVehiclesThree = (() => {
|
|
|
44558
44832
|
burst: 20,
|
|
44559
44833
|
burstSpacing: 0.06
|
|
44560
44834
|
};
|
|
44835
|
+
var CATALOG = new Map([...SFX_CATALOG, ...SFX_IMPACTS].map((entry) => [entry.id, entry]));
|
|
44561
44836
|
var PT = {
|
|
44562
44837
|
group: "Som",
|
|
44563
44838
|
name: "Painel de som (arquivos e C1\u2013C10)",
|
|
@@ -44632,9 +44907,9 @@ var GameVehiclesThree = (() => {
|
|
|
44632
44907
|
strengths: { light: "leve", medium: "m\xE9dio", heavy: "forte" },
|
|
44633
44908
|
states: { file: "A", synth: "S", missing: "F" },
|
|
44634
44909
|
criteria: { C1: "C1 Sincronia", C2: "C2 Certo p/ a\xE7\xE3o", C3: "C3 Dist\xE2ncia", C4: "C4 Posi\xE7\xE3o", C5: "C5 Varia\xE7\xE3o", C6: "C6 Volume", C7: "C7 Intensidade", C8: "C8 Cauda", C9: "C9 Rajada", C10: "C10 Escuta" },
|
|
44635
|
-
metrics: { source: "1 \xB7 Fonte do que tocou", chosen: "1 \xB7 Escolhido", matrix: "2 \xB7 Matriz", last: "\xDAltimo tocado", ab: "4 \xB7 A \xD7 B", blind: "4 \xB7 X", verdicts: "4 \xB7 Prefer\xEAncias", state: "5 \xB7 Medi\xE7\xE3o", count: "1 \xB7 Cat\xE1logo" },
|
|
44910
|
+
metrics: { source: "1 \xB7 Fonte do que tocou", chosen: "1 \xB7 Escolhido", matrix: "2 \xB7 Matriz", last: "\xDAltimo tocado", ab: "4 \xB7 A \xD7 B", blind: "4 \xB7 X", verdicts: "4 \xB7 Prefer\xEAncias", state: "5 \xB7 Medi\xE7\xE3o", count: "1 \xB7 Cat\xE1logo", unlicensed: "1 \xB7 Arquivo sem licen\xE7a" },
|
|
44636
44911
|
run: { idle: "parada", measuring: "medindo\u2026", done: "pronta", failed: "falhou", recording: "gravando\u2026", recorded: "grava\xE7\xE3o baixada" },
|
|
44637
|
-
words: { none: "\u2014", hidden: "? (revele)", prefers: "prefere", approved: "aprovado pelo dono" },
|
|
44912
|
+
words: { none: "\u2014", hidden: "? (revele)", prefers: "prefere", approved: "aprovado pelo dono", local_file: "arquivo do pacote local", sdk_file: "grava\xE7\xE3o do SDK", synth: "s\xEDntese do SDK" },
|
|
44638
44913
|
sources: { sdk: "SDK", user: "seu som", synth: "sintetizado" },
|
|
44639
44914
|
probe: "Fonte do som",
|
|
44640
44915
|
wall: "Parede (oclus\xE3o)",
|
|
@@ -44705,9 +44980,9 @@ var GameVehiclesThree = (() => {
|
|
|
44705
44980
|
environments: { indoor: "indoor", outdoor: "outdoor" },
|
|
44706
44981
|
strengths: { light: "light", medium: "medium", heavy: "heavy" },
|
|
44707
44982
|
criteria: { C1: "C1 Sync", C2: "C2 Right sound", C3: "C3 Distance", C4: "C4 Position", C5: "C5 Variation", C6: "C6 Level", C7: "C7 Strength", C8: "C8 Tail", C9: "C9 Burst", C10: "C10 Listening" },
|
|
44708
|
-
metrics: { source: "1 \xB7 Source of what played", chosen: "1 \xB7 Chosen", matrix: "2 \xB7 Matrix", last: "Last played", ab: "4 \xB7 A \xD7 B", blind: "4 \xB7 X", verdicts: "4 \xB7 Preferences", state: "5 \xB7 Measurement", count: "1 \xB7 Catalogue" },
|
|
44983
|
+
metrics: { source: "1 \xB7 Source of what played", chosen: "1 \xB7 Chosen", matrix: "2 \xB7 Matrix", last: "Last played", ab: "4 \xB7 A \xD7 B", blind: "4 \xB7 X", verdicts: "4 \xB7 Preferences", state: "5 \xB7 Measurement", count: "1 \xB7 Catalogue", unlicensed: "1 \xB7 File without licence" },
|
|
44709
44984
|
run: { idle: "idle", measuring: "measuring\u2026", done: "done", failed: "failed", recording: "recording\u2026", recorded: "recording downloaded" },
|
|
44710
|
-
words: { none: "\u2014", hidden: "? (reveal)", prefers: "prefers", approved: "approved by the owner" },
|
|
44985
|
+
words: { none: "\u2014", hidden: "? (reveal)", prefers: "prefers", approved: "approved by the owner", local_file: "local pack file", sdk_file: "SDK recording", synth: "SDK synthesis" },
|
|
44711
44986
|
sources: { sdk: "SDK", user: "your sound", synth: "synthesized" },
|
|
44712
44987
|
probe: "Sound source",
|
|
44713
44988
|
wall: "Wall (occlusion)",
|
|
@@ -44861,9 +45136,77 @@ var GameVehiclesThree = (() => {
|
|
|
44861
45136
|
duration: 0.5,
|
|
44862
45137
|
layers: [{ source: { kind: "osc", wave: "sine", frequency: { set: 880 } }, repeat: { count: 3, spacing: 0.7 }, envelope: [[0, 0.06, "set"], [0.5, 1e-4, "exp", "fixed"]] }],
|
|
44863
45138
|
falloff: { model: "none" }
|
|
45139
|
+
},
|
|
45140
|
+
{
|
|
45141
|
+
// the parking sensor: a short high beep, repeated by the cabin faster as the obstacle comes close
|
|
45142
|
+
id: "parking_beep",
|
|
45143
|
+
bus: "world",
|
|
45144
|
+
duration: 0.09,
|
|
45145
|
+
layers: [{ source: { kind: "osc", wave: "sine", frequency: { set: 2300 } }, envelope: [[0, 0.07, "set"], [0.08, 1e-4, "exp", "fixed"]] }],
|
|
45146
|
+
falloff: { model: "none" }
|
|
44864
45147
|
}
|
|
44865
45148
|
];
|
|
44866
45149
|
var CABIN_BANK = { id: "vehicle_cabin", sounds: CABIN_SOUNDS, loops: [] };
|
|
45150
|
+
var hz = (midi) => 440 * 2 ** ((midi - 69) / 12);
|
|
45151
|
+
var layer = (note, beat, wave, level) => {
|
|
45152
|
+
const [at, midi, beats] = note;
|
|
45153
|
+
const length = beats * beat;
|
|
45154
|
+
return {
|
|
45155
|
+
at: at * beat,
|
|
45156
|
+
duration: length,
|
|
45157
|
+
source: { kind: "osc", wave, frequency: { set: hz(midi) } },
|
|
45158
|
+
envelope: [[0, 1e-4, "set"], [0.01, level, "linear"], [Math.max(0.02, length * 0.9), level * 0.35, "linear"], [length, 1e-4, "exp", "fixed"]]
|
|
45159
|
+
};
|
|
45160
|
+
};
|
|
45161
|
+
function radioTrack(spec) {
|
|
45162
|
+
const beat = 60 / spec.bpm;
|
|
45163
|
+
const recipes = Array.from({ length: spec.bars }, (_, bar) => {
|
|
45164
|
+
const inBar = (notes) => notes.filter((n) => n[0] >= bar * 4 && n[0] < bar * 4 + 4).map((n) => [n[0] - bar * 4, n[1], n[2]]);
|
|
45165
|
+
const layers2 = [
|
|
45166
|
+
...inBar(spec.bass).map((n) => layer(n, beat, "triangle", 0.16)),
|
|
45167
|
+
...inBar(spec.chords).map((n) => layer(n, beat, "sine", 0.05)),
|
|
45168
|
+
...inBar(spec.melody).map((n) => layer(n, beat, spec.wave, 0.07))
|
|
45169
|
+
];
|
|
45170
|
+
return { id: `${spec.id}_${bar}`, bus: "world", duration: 4 * beat, layers: layers2, falloff: { model: "none" } };
|
|
45171
|
+
});
|
|
45172
|
+
return { song: { id: spec.id, barSeconds: 4 * beat, bars: recipes.map((r) => r.id) }, recipes };
|
|
45173
|
+
}
|
|
45174
|
+
var chord = (bar, root, minor = false) => [[bar * 4, root, 4], [bar * 4, root + (minor ? 3 : 4), 4], [bar * 4, root + 7, 4]];
|
|
45175
|
+
var bassLine = (roots, pattern) => roots.flatMap((root, bar) => pattern.map((b) => [bar * 4 + b, root - 12, 0.9]));
|
|
45176
|
+
var TRACKS = [
|
|
45177
|
+
// a bright pop loop: I–V–vi–IV in C
|
|
45178
|
+
radioTrack({
|
|
45179
|
+
id: "radio_track_pop",
|
|
45180
|
+
bpm: 112,
|
|
45181
|
+
bars: 4,
|
|
45182
|
+
wave: "square",
|
|
45183
|
+
bass: bassLine([48, 55, 57, 53], [0, 1.5, 2, 3]),
|
|
45184
|
+
chords: [...chord(0, 60), ...chord(1, 55), ...chord(2, 57, true), ...chord(3, 53)],
|
|
45185
|
+
melody: [[0, 76, 1], [1, 74, 0.5], [1.5, 72, 1.5], [4, 74, 1], [5, 76, 1], [6, 79, 2], [8, 76, 1], [9, 74, 1], [10, 72, 2], [12, 72, 1], [13, 74, 1], [14, 72, 2]]
|
|
45186
|
+
}),
|
|
45187
|
+
// a syncopated loop in A minor, the bass on the off-beats
|
|
45188
|
+
radioTrack({
|
|
45189
|
+
id: "radio_track_groove",
|
|
45190
|
+
bpm: 100,
|
|
45191
|
+
bars: 4,
|
|
45192
|
+
wave: "triangle",
|
|
45193
|
+
bass: bassLine([57, 62, 64, 57], [0, 0.75, 1.5, 2.5, 3.25]),
|
|
45194
|
+
chords: [...chord(0, 57, true), ...chord(1, 62, true), ...chord(2, 64), ...chord(3, 57, true)],
|
|
45195
|
+
melody: [[0.5, 72, 0.5], [1.25, 76, 0.75], [2.5, 74, 1], [4.5, 77, 0.5], [5.25, 74, 0.75], [6.5, 72, 1.5], [8.5, 71, 0.5], [9.25, 74, 0.75], [10.5, 76, 1.5], [12.5, 72, 0.5], [13.25, 69, 2]]
|
|
45196
|
+
}),
|
|
45197
|
+
// a slow blues-like loop in F with a swung melody
|
|
45198
|
+
radioTrack({
|
|
45199
|
+
id: "radio_track_slow",
|
|
45200
|
+
bpm: 76,
|
|
45201
|
+
bars: 4,
|
|
45202
|
+
wave: "sine",
|
|
45203
|
+
bass: bassLine([53, 58, 53, 60], [0, 2]),
|
|
45204
|
+
chords: [...chord(0, 65), ...chord(1, 58), ...chord(2, 65), ...chord(3, 60)],
|
|
45205
|
+
melody: [[0, 72, 1.5], [1.67, 75, 0.33], [2, 77, 2], [4, 74, 1], [5, 72, 1], [6, 70, 2], [8, 72, 1.5], [9.67, 69, 0.33], [10, 65, 2], [12, 67, 1], [13, 70, 1], [14, 72, 2]]
|
|
45206
|
+
})
|
|
45207
|
+
];
|
|
45208
|
+
var RADIO_TRACKS = TRACKS.map((t) => t.song);
|
|
45209
|
+
var RADIO_BANK = { id: "vehicle_radio", sounds: TRACKS.flatMap((t) => t.recipes), loops: [] };
|
|
44867
45210
|
var FOLEY_FALLOFF = {
|
|
44868
45211
|
model: "inverse_square",
|
|
44869
45212
|
k: 0.02,
|
|
@@ -45470,11 +45813,11 @@ var GameVehiclesThree = (() => {
|
|
|
45470
45813
|
...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
|
|
45471
45814
|
];
|
|
45472
45815
|
}
|
|
45473
|
-
var quieter = (level, layers2) => layers2.map((
|
|
45474
|
-
const gain =
|
|
45475
|
-
if (typeof gain === "number") return { ...
|
|
45476
|
-
if (Array.isArray(gain)) return { ...
|
|
45477
|
-
return
|
|
45816
|
+
var quieter = (level, layers2) => layers2.map((layer2) => {
|
|
45817
|
+
const gain = layer2.gain ?? 1;
|
|
45818
|
+
if (typeof gain === "number") return { ...layer2, gain: gain * level };
|
|
45819
|
+
if (Array.isArray(gain)) return { ...layer2, gain: [gain[0] * level, gain[1] * level] };
|
|
45820
|
+
return layer2;
|
|
45478
45821
|
});
|
|
45479
45822
|
var recipe = (id, layers2, falloff, bus = "world") => ({ id, bus, pan: true, layers: layers2, falloff, sources: ["sdk"] });
|
|
45480
45823
|
var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
|
|
@@ -45490,7 +45833,7 @@ var GameVehiclesThree = (() => {
|
|
|
45490
45833
|
(take) => quieter(LANDING_LEVEL, [
|
|
45491
45834
|
// both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
|
|
45492
45835
|
...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
|
|
45493
|
-
...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((
|
|
45836
|
+
...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer2) => ({ ...layer2, at: (typeof layer2.at === "number" ? layer2.at : 0) + 0.018 * take.t })),
|
|
45494
45837
|
sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
|
|
45495
45838
|
sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
|
|
45496
45839
|
]),
|
|
@@ -45536,7 +45879,7 @@ var GameVehiclesThree = (() => {
|
|
|
45536
45879
|
fall_damage: (take) => [
|
|
45537
45880
|
...BODY.body_fall(take),
|
|
45538
45881
|
sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
|
|
45539
|
-
...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((
|
|
45882
|
+
...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer2) => ({ ...layer2, at: 0.08 }))
|
|
45540
45883
|
],
|
|
45541
45884
|
player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
|
|
45542
45885
|
player_breath_run: (take) => [
|
|
@@ -45594,6 +45937,105 @@ var GameVehiclesThree = (() => {
|
|
|
45594
45937
|
};
|
|
45595
45938
|
var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
45596
45939
|
|
|
45940
|
+
// src/audio/engine-mix.ts
|
|
45941
|
+
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
45942
|
+
var firingHz = (profile, rpm) => rpm / 60 * (profile.cylinders / 2);
|
|
45943
|
+
var rpmShare = (profile, rpm) => clamp01((rpm - profile.idleRpm) / (profile.redlineRpm - profile.idleRpm));
|
|
45944
|
+
var layerWeight = (centre, width, n) => {
|
|
45945
|
+
const d = Math.abs(n - centre) / width;
|
|
45946
|
+
return d >= 1 ? 0 : Math.cos(d * Math.PI / 2);
|
|
45947
|
+
};
|
|
45948
|
+
function engineMix(profile, input) {
|
|
45949
|
+
const throttle = clamp01(input.throttle);
|
|
45950
|
+
const on = input.rpm > 0;
|
|
45951
|
+
const n = rpmShare(profile, input.rpm);
|
|
45952
|
+
const level = on ? profile.closedThrottle + (1 - profile.closedThrottle) * throttle : 0;
|
|
45953
|
+
const open = 0.5 * n + 0.5 * throttle;
|
|
45954
|
+
const hz2 = firingHz(profile, input.rpm);
|
|
45955
|
+
const tones = profile.layers.map((layer2) => ({
|
|
45956
|
+
sound: layer2.sound,
|
|
45957
|
+
volume: layer2.gain * level * layerWeight(layer2.centre, layer2.width, n),
|
|
45958
|
+
frequency: hz2 * layer2.harmonic,
|
|
45959
|
+
cutoff: layer2.cutoff[0] + (layer2.cutoff[1] - layer2.cutoff[0]) * open
|
|
45960
|
+
}));
|
|
45961
|
+
const load = profile.load;
|
|
45962
|
+
return [
|
|
45963
|
+
...tones,
|
|
45964
|
+
{ sound: load.sound, volume: on ? load.gain * (0.15 + 0.85 * throttle) * (0.35 + 0.65 * n) : 0, cutoff: load.cutoff[0] + (load.cutoff[1] - load.cutoff[0]) * open }
|
|
45965
|
+
];
|
|
45966
|
+
}
|
|
45967
|
+
function createGearGuess(profile) {
|
|
45968
|
+
let gear = 0;
|
|
45969
|
+
return {
|
|
45970
|
+
rpm(speed, throttle) {
|
|
45971
|
+
const v = Math.abs(speed);
|
|
45972
|
+
const ratios = profile.rpmPerMps;
|
|
45973
|
+
if (v < 0.3) {
|
|
45974
|
+
gear = 0;
|
|
45975
|
+
return profile.idleRpm + (profile.redlineRpm - profile.idleRpm) * 0.12 * clamp01(throttle);
|
|
45976
|
+
}
|
|
45977
|
+
const at = (g) => v * ratios[g];
|
|
45978
|
+
while (gear < ratios.length - 1 && at(gear) > profile.redlineRpm * 0.85) gear++;
|
|
45979
|
+
while (gear > 0 && at(gear) < profile.redlineRpm * 0.4) gear--;
|
|
45980
|
+
return Math.max(profile.idleRpm, Math.min(profile.redlineRpm, at(gear)));
|
|
45981
|
+
},
|
|
45982
|
+
get gear() {
|
|
45983
|
+
return gear;
|
|
45984
|
+
}
|
|
45985
|
+
};
|
|
45986
|
+
}
|
|
45987
|
+
|
|
45988
|
+
// src/audio/profiles.ts
|
|
45989
|
+
var DEFAULT_VEHICLE_SOUND_KEYS = {
|
|
45990
|
+
start: "engine_start",
|
|
45991
|
+
stop: "engine_stop",
|
|
45992
|
+
fail: "engine_fail",
|
|
45993
|
+
crashLight: "crash_light",
|
|
45994
|
+
crashMedium: "crash_medium",
|
|
45995
|
+
crashHeavy: "crash_heavy",
|
|
45996
|
+
skidAsphalt: "tire_skid_asphalt",
|
|
45997
|
+
skidDirt: "tire_skid_dirt",
|
|
45998
|
+
roll: "tire_roll"
|
|
45999
|
+
};
|
|
46000
|
+
var CRASH_SPEEDS = { medium: 6, heavy: 14 };
|
|
46001
|
+
var layers = (id, cutoffs, gains) => [
|
|
46002
|
+
{ sound: `engine_${id}_low`, centre: 0, width: 0.5, harmonic: 1, gain: gains[0], cutoff: cutoffs[0] },
|
|
46003
|
+
{ sound: `engine_${id}_mid`, centre: 0.45, width: 0.5, harmonic: 1, gain: gains[1], cutoff: cutoffs[1] },
|
|
46004
|
+
{ sound: `engine_${id}_high`, centre: 1, width: 0.6, harmonic: 2, gain: gains[2], cutoff: cutoffs[2] }
|
|
46005
|
+
];
|
|
46006
|
+
var CAR_ENGINE = {
|
|
46007
|
+
id: "car",
|
|
46008
|
+
cylinders: 4,
|
|
46009
|
+
idleRpm: 800,
|
|
46010
|
+
redlineRpm: 6500,
|
|
46011
|
+
// a 5-speed hatch: 1st tops at ~13 m/s, 5th at ~50 m/s
|
|
46012
|
+
rpmPerMps: [500, 300, 205, 155, 125],
|
|
46013
|
+
layers: layers("car", [[220, 520], [420, 1300], [900, 2600]], [0.11, 0.075, 0.04]),
|
|
46014
|
+
load: { sound: "engine_car_load", gain: 0.05, cutoff: [350, 1600] },
|
|
46015
|
+
closedThrottle: 0.55
|
|
46016
|
+
};
|
|
46017
|
+
var TRUCK_ENGINE = {
|
|
46018
|
+
id: "truck",
|
|
46019
|
+
cylinders: 6,
|
|
46020
|
+
idleRpm: 650,
|
|
46021
|
+
redlineRpm: 2600,
|
|
46022
|
+
rpmPerMps: [420, 260, 170, 120, 90, 72],
|
|
46023
|
+
layers: layers("truck", [[160, 380], [300, 800], [600, 1500]], [0.13, 0.08, 0.035]),
|
|
46024
|
+
load: { sound: "engine_truck_load", gain: 0.06, cutoff: [250, 900] },
|
|
46025
|
+
closedThrottle: 0.6
|
|
46026
|
+
};
|
|
46027
|
+
var BIKE_ENGINE = {
|
|
46028
|
+
id: "bike",
|
|
46029
|
+
cylinders: 4,
|
|
46030
|
+
idleRpm: 1300,
|
|
46031
|
+
redlineRpm: 12e3,
|
|
46032
|
+
rpmPerMps: [900, 640, 500, 410, 350, 310],
|
|
46033
|
+
layers: layers("bike", [[350, 900], [700, 2200], [1500, 4200]], [0.08, 0.07, 0.05]),
|
|
46034
|
+
load: { sound: "engine_bike_load", gain: 0.045, cutoff: [600, 2600] },
|
|
46035
|
+
closedThrottle: 0.5
|
|
46036
|
+
};
|
|
46037
|
+
var ENGINE_PROFILES = { car: CAR_ENGINE, truck: TRUCK_ENGINE, bike: BIKE_ENGINE };
|
|
46038
|
+
|
|
45597
46039
|
// src/audio/bank.ts
|
|
45598
46040
|
var VEHICLE_LOOP_FALLOFF = { model: "inverse_square", k: 4e-3, cutoffK: 0.01 };
|
|
45599
46041
|
var SDK = "sdk: vehicle sounds (sons/checklist-sons.md 5.6)";
|
|
@@ -45685,106 +46127,33 @@ var GameVehiclesThree = (() => {
|
|
|
45685
46127
|
recipe2("car_trunk_close", [sceneTone({ f: 90, f2: 55, d: 0.15, vol: 0.65 }), sceneNoise({ type: "bandpass", f: 500, q: 1, d: 0.12, vol: 0.5 }), ...latch(0.01, 0.28), sceneNoise({ type: "bandpass", f: 1800, q: 3, d: 0.04, vol: 0.1, delay: 0.06 })], "trunk or hatch shut: a lighter thump, the latch and a small rattle"),
|
|
45686
46128
|
recipe2("car_hood_close", [sceneTone({ f: 70, f2: 40, d: 0.22, vol: 0.8 }), sceneNoise({ type: "bandpass", f: 850, q: 1.4, d: 0.2, vol: 0.6, wet: 0.3 }), sceneTone({ f: 900, f2: 860, d: 0.18, vol: 0.05 }), ...latch(0.015, 0.3)], "hood slammed: a big sheet of metal ringing a little, the latch")
|
|
45687
46129
|
];
|
|
45688
|
-
var
|
|
45689
|
-
|
|
45690
|
-
|
|
45691
|
-
|
|
45692
|
-
|
|
45693
|
-
|
|
45694
|
-
var layerWeight = (centre, width, n) => {
|
|
45695
|
-
const d = Math.abs(n - centre) / width;
|
|
45696
|
-
return d >= 1 ? 0 : Math.cos(d * Math.PI / 2);
|
|
46130
|
+
var CAR_RECORDED_RPM = { idle: 1e3, high: 2520 };
|
|
46131
|
+
var CAR_START_RUN_STOP = {
|
|
46132
|
+
/** The starter and the engine catching (at 0.58 s) and flaring; the loops take over at the crank time of the vehicle. */
|
|
46133
|
+
start: { offset: 0, duration: 1.3 },
|
|
46134
|
+
/** The ignition off: the engine running down to the end of the file. */
|
|
46135
|
+
stop: { offset: 9.96, duration: 0.64 }
|
|
45697
46136
|
};
|
|
45698
|
-
|
|
45699
|
-
|
|
45700
|
-
|
|
45701
|
-
|
|
45702
|
-
|
|
45703
|
-
|
|
45704
|
-
|
|
45705
|
-
|
|
45706
|
-
|
|
45707
|
-
|
|
45708
|
-
|
|
45709
|
-
|
|
45710
|
-
|
|
45711
|
-
|
|
45712
|
-
|
|
45713
|
-
|
|
45714
|
-
|
|
45715
|
-
|
|
45716
|
-
}
|
|
45717
|
-
function createGearGuess(profile) {
|
|
45718
|
-
let gear = 0;
|
|
45719
|
-
return {
|
|
45720
|
-
rpm(speed, throttle) {
|
|
45721
|
-
const v = Math.abs(speed);
|
|
45722
|
-
const ratios = profile.rpmPerMps;
|
|
45723
|
-
if (v < 0.3) {
|
|
45724
|
-
gear = 0;
|
|
45725
|
-
return profile.idleRpm + (profile.redlineRpm - profile.idleRpm) * 0.12 * clamp01(throttle);
|
|
45726
|
-
}
|
|
45727
|
-
const at = (g) => v * ratios[g];
|
|
45728
|
-
while (gear < ratios.length - 1 && at(gear) > profile.redlineRpm * 0.85) gear++;
|
|
45729
|
-
while (gear > 0 && at(gear) < profile.redlineRpm * 0.4) gear--;
|
|
45730
|
-
return Math.max(profile.idleRpm, Math.min(profile.redlineRpm, at(gear)));
|
|
45731
|
-
},
|
|
45732
|
-
get gear() {
|
|
45733
|
-
return gear;
|
|
45734
|
-
}
|
|
45735
|
-
};
|
|
45736
|
-
}
|
|
45737
|
-
|
|
45738
|
-
// src/audio/profiles.ts
|
|
45739
|
-
var DEFAULT_VEHICLE_SOUND_KEYS = {
|
|
45740
|
-
start: "engine_start",
|
|
45741
|
-
stop: "engine_stop",
|
|
45742
|
-
fail: "engine_fail",
|
|
45743
|
-
crashLight: "crash_light",
|
|
45744
|
-
crashMedium: "crash_medium",
|
|
45745
|
-
crashHeavy: "crash_heavy",
|
|
45746
|
-
skidAsphalt: "tire_skid_asphalt",
|
|
45747
|
-
skidDirt: "tire_skid_dirt",
|
|
45748
|
-
roll: "tire_roll"
|
|
45749
|
-
};
|
|
45750
|
-
var CRASH_SPEEDS = { medium: 6, heavy: 14 };
|
|
45751
|
-
var layers = (id, cutoffs, gains) => [
|
|
45752
|
-
{ sound: `engine_${id}_low`, centre: 0, width: 0.5, harmonic: 1, gain: gains[0], cutoff: cutoffs[0] },
|
|
45753
|
-
{ sound: `engine_${id}_mid`, centre: 0.45, width: 0.5, harmonic: 1, gain: gains[1], cutoff: cutoffs[1] },
|
|
45754
|
-
{ sound: `engine_${id}_high`, centre: 1, width: 0.6, harmonic: 2, gain: gains[2], cutoff: cutoffs[2] }
|
|
46137
|
+
var carLayer = (id, file, rpm, harmonic, offset) => ({
|
|
46138
|
+
id,
|
|
46139
|
+
file,
|
|
46140
|
+
loop: true,
|
|
46141
|
+
// the layer's oscillator plays the firing frequency times its harmonic: the file sounds at that frequency for the RPM it was recorded at
|
|
46142
|
+
baseFrequency: firingHz(CAR_ENGINE, rpm) * harmonic,
|
|
46143
|
+
falloff: VEHICLE_LOOP_FALLOFF,
|
|
46144
|
+
...offset === void 0 ? {} : { offset }
|
|
46145
|
+
});
|
|
46146
|
+
var VEHICLE_SAMPLE_ALIASES = [
|
|
46147
|
+
carLayer("engine_car_low", "engine_loop_car_idle", CAR_RECORDED_RPM.idle, 1),
|
|
46148
|
+
carLayer("engine_car_mid", "engine_loop_car_high", CAR_RECORDED_RPM.high, 1),
|
|
46149
|
+
// the same file as the mid layer, read from two seconds in, so the two do not sum in phase where they cross
|
|
46150
|
+
carLayer("engine_car_high", "engine_loop_car_high", CAR_RECORDED_RPM.high, 2, 2),
|
|
46151
|
+
{ id: "engine_start", file: "engine_start_run_stop", ...CAR_START_RUN_STOP.start, falloff: VEHICLE_LOOP_FALLOFF },
|
|
46152
|
+
{ id: "engine_stop", file: "engine_start_run_stop", ...CAR_START_RUN_STOP.stop, falloff: VEHICLE_LOOP_FALLOFF },
|
|
46153
|
+
{ id: "car_door_close", file: "car_door_shut" },
|
|
46154
|
+
{ id: "car_trunk_close", file: "car_trunk_shut" }
|
|
45755
46155
|
];
|
|
45756
|
-
var
|
|
45757
|
-
id: "car",
|
|
45758
|
-
cylinders: 4,
|
|
45759
|
-
idleRpm: 800,
|
|
45760
|
-
redlineRpm: 6500,
|
|
45761
|
-
// a 5-speed hatch: 1st tops at ~13 m/s, 5th at ~50 m/s
|
|
45762
|
-
rpmPerMps: [500, 300, 205, 155, 125],
|
|
45763
|
-
layers: layers("car", [[220, 520], [420, 1300], [900, 2600]], [0.11, 0.075, 0.04]),
|
|
45764
|
-
load: { sound: "engine_car_load", gain: 0.05, cutoff: [350, 1600] },
|
|
45765
|
-
closedThrottle: 0.55
|
|
45766
|
-
};
|
|
45767
|
-
var TRUCK_ENGINE = {
|
|
45768
|
-
id: "truck",
|
|
45769
|
-
cylinders: 6,
|
|
45770
|
-
idleRpm: 650,
|
|
45771
|
-
redlineRpm: 2600,
|
|
45772
|
-
rpmPerMps: [420, 260, 170, 120, 90, 72],
|
|
45773
|
-
layers: layers("truck", [[160, 380], [300, 800], [600, 1500]], [0.13, 0.08, 0.035]),
|
|
45774
|
-
load: { sound: "engine_truck_load", gain: 0.06, cutoff: [250, 900] },
|
|
45775
|
-
closedThrottle: 0.6
|
|
45776
|
-
};
|
|
45777
|
-
var BIKE_ENGINE = {
|
|
45778
|
-
id: "bike",
|
|
45779
|
-
cylinders: 4,
|
|
45780
|
-
idleRpm: 1300,
|
|
45781
|
-
redlineRpm: 12e3,
|
|
45782
|
-
rpmPerMps: [900, 640, 500, 410, 350, 310],
|
|
45783
|
-
layers: layers("bike", [[350, 900], [700, 2200], [1500, 4200]], [0.08, 0.07, 0.05]),
|
|
45784
|
-
load: { sound: "engine_bike_load", gain: 0.045, cutoff: [600, 2600] },
|
|
45785
|
-
closedThrottle: 0.5
|
|
45786
|
-
};
|
|
45787
|
-
var ENGINE_PROFILES = { car: CAR_ENGINE, truck: TRUCK_ENGINE, bike: BIKE_ENGINE };
|
|
46156
|
+
var VEHICLE_SOUND_BANK = { id: "vehicles_three", sounds: VEHICLE_SOUNDS, loops: VEHICLE_LOOPS, sampleAliases: VEHICLE_SAMPLE_ALIASES };
|
|
45788
46157
|
|
|
45789
46158
|
// src/audio/vehicle-sound.ts
|
|
45790
46159
|
var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
@@ -45842,8 +46211,8 @@ var GameVehiclesThree = (() => {
|
|
|
45842
46211
|
const driven = frame.rpm ?? gears.rpm(frame.speed, throttle);
|
|
45843
46212
|
const rpm = state.running ? Math.max(driven, profile.idleRpm * (1 + 1.2 * state.flare * state.flare)) : 0;
|
|
45844
46213
|
state.rpm = rpm;
|
|
45845
|
-
for (const
|
|
45846
|
-
setLoop(
|
|
46214
|
+
for (const layer2 of engineMix(profile, { rpm, throttle: Math.max(throttle, state.flare * 0.6) })) {
|
|
46215
|
+
setLoop(layer2.sound, layer2.volume, frame.position, { ...layer2.frequency === void 0 ? {} : { frequency: layer2.frequency }, cutoff: layer2.cutoff });
|
|
45847
46216
|
}
|
|
45848
46217
|
const speed = Math.abs(frame.speed);
|
|
45849
46218
|
const slide = clamp012(((frame.slip ?? 0) - 0.15) / 0.5) * clamp012(speed / 4);
|
|
@@ -46245,7 +46614,15 @@ var GameVehiclesThree = (() => {
|
|
|
46245
46614
|
var clamp22 = (x, a, b) => Math.max(a, Math.min(b, x));
|
|
46246
46615
|
var MAX_SUBSTEP = 1 / 240;
|
|
46247
46616
|
var FLAT_BELOW = 0.2;
|
|
46617
|
+
var RIM_RADIUS_SHARE = 0.7;
|
|
46618
|
+
var RIM_FRICTION = 0.4;
|
|
46619
|
+
var RIM_ROLLING = 0.25;
|
|
46620
|
+
var FLAT_SAG = 0.05;
|
|
46621
|
+
var FLAT_ROLLING = 6;
|
|
46248
46622
|
var STALL_HEALTH = 0.3;
|
|
46623
|
+
var CLUTCH_BITE = 0.7;
|
|
46624
|
+
var STALL_RPM = 0.5;
|
|
46625
|
+
var STALL_GAS = 0.35;
|
|
46249
46626
|
var defaultSemiSpec = {
|
|
46250
46627
|
mass: 1300,
|
|
46251
46628
|
centreHeight: 0.5,
|
|
@@ -46386,7 +46763,14 @@ var GameVehiclesThree = (() => {
|
|
|
46386
46763
|
const accelInput = reversing ? input.brake : input.throttle;
|
|
46387
46764
|
const brakeInput = clamp22(reversing ? input.throttle : input.brake, 0, 1);
|
|
46388
46765
|
const ratio = ratioOf(state.gear) * spec.finalDrive;
|
|
46389
|
-
state.
|
|
46766
|
+
const engaged = input.clutch == null || state.gear === 0 ? 1 : 1 - clamp22(input.clutch, 0, 1);
|
|
46767
|
+
const coupled = wheelRpm * ratio;
|
|
46768
|
+
const free = spec.engine.idleRpm + clamp22(accelInput, 0, 1) * (spec.engine.redlineRpm * 0.7 - spec.engine.idleRpm);
|
|
46769
|
+
state.rpm = clamp22(coupled * engaged + Math.max(coupled, free) * (1 - engaged), spec.engine.idleRpm, spec.engine.redlineRpm + 200);
|
|
46770
|
+
if (input.clutch != null && state.gear !== 0 && state.engineOn && engaged > CLUTCH_BITE && coupled < spec.engine.idleRpm * STALL_RPM && accelInput < STALL_GAS) {
|
|
46771
|
+
state.engineOn = false;
|
|
46772
|
+
events.push({ type: "stall" });
|
|
46773
|
+
}
|
|
46390
46774
|
if (!state.engineOn || state.fuel <= 0) state.rpm = 0;
|
|
46391
46775
|
let throttle = clamp22(accelInput, 0, 1);
|
|
46392
46776
|
if (engineHealth < STALL_HEALTH && throttle > 0.8) {
|
|
@@ -46397,12 +46781,13 @@ var GameVehiclesThree = (() => {
|
|
|
46397
46781
|
}
|
|
46398
46782
|
throttle *= 0.5;
|
|
46399
46783
|
} else stallWatch.throttleTime = 0;
|
|
46400
|
-
|
|
46784
|
+
const starting = input.start ?? (accelInput > 0 && Math.abs(state.u) < 0.5);
|
|
46785
|
+
if (!state.engineOn && starting && engineHealth >= STALL_HEALTH && state.fuel > 0) state.engineOn = true;
|
|
46401
46786
|
if (state.fuel <= 0 && state.engineOn) {
|
|
46402
46787
|
state.engineOn = false;
|
|
46403
46788
|
events.push({ type: "out_of_fuel" });
|
|
46404
46789
|
}
|
|
46405
|
-
const engineTorque = state.engineOn ? torqueAt(spec, state.rpm) * throttle * (0.55 + 0.45 * engineHealth) : 0;
|
|
46790
|
+
const engineTorque = state.engineOn ? torqueAt(spec, state.rpm) * throttle * (0.55 + 0.45 * engineHealth) * clamp22(input.powerLimit ?? 1, 0, 1) * engaged : 0;
|
|
46406
46791
|
const driveForceTotal = state.gear === 0 ? 0 : (reversing ? -1 : 1) * engineTorque * ratio * spec.efficiency / spec.wheelRadius;
|
|
46407
46792
|
if (state.engineOn) {
|
|
46408
46793
|
const burned = fuelRate(fuelModel, state.rpm / spec.engine.redlineRpm, throttle, cargo / baseMass, engineHealth) * dt;
|
|
@@ -46420,19 +46805,21 @@ var GameVehiclesThree = (() => {
|
|
|
46420
46805
|
const wz = state.z - w.x * sn + w.z * cs;
|
|
46421
46806
|
const ground = world.ground ? world.ground(wx, wz) : {};
|
|
46422
46807
|
const surface = surfaces.get(contamination && contamination.left > 0 && surfaces.get(contamination.id).grip < surfaces.get(ground.surface ?? "dry_asphalt").grip ? contamination.id : ground.surface ?? "dry_asphalt");
|
|
46808
|
+
const tyreHealth = cond.tyres ? clamp22(cond.tyres[i] ?? 1, 0, 1) : 1;
|
|
46809
|
+
const rim = !!cond.rims?.[i];
|
|
46810
|
+
const flat = rim || tyreHealth < FLAT_BELOW;
|
|
46811
|
+
const radius = rim ? spec.rimRadius ?? spec.wheelRadius * RIM_RADIUS_SHARE : flat ? spec.wheelRadius - FLAT_SAG : spec.wheelRadius;
|
|
46423
46812
|
const top = state.height + w.x * state.pitch - w.z * state.roll;
|
|
46424
|
-
const length = top - (ground.height ?? 0) -
|
|
46813
|
+
const length = top - (ground.height ?? 0) - radius;
|
|
46425
46814
|
const compression = ride - length;
|
|
46426
46815
|
const rate = -(state.heightRate + w.x * state.pitchRate - w.z * state.rollRate);
|
|
46427
46816
|
const k = spec.suspension.spring * (0.5 + 0.5 * suspensionHealth);
|
|
46428
46817
|
const c = spec.suspension.damper * (0.5 + 0.5 * suspensionHealth);
|
|
46429
46818
|
const load = Math.max(0, k * compression + c * rate);
|
|
46430
|
-
const tyreHealth = cond.tyres ? clamp22(cond.tyres[i] ?? 1, 0, 1) : 1;
|
|
46431
|
-
const flat = tyreHealth < FLAT_BELOW;
|
|
46432
46819
|
const bog = !!surface.bogs && !spec.fourWheelDrive;
|
|
46433
46820
|
if (bog) bogging = true;
|
|
46434
|
-
const grip = surfaceGrip(surface, state.u) * (flat ? 0.5 : 1);
|
|
46435
|
-
wheelState.push({ n: load, grip, surface: surface.id, bog, flat });
|
|
46821
|
+
const grip = surfaceGrip(surface, state.u) * (rim ? RIM_FRICTION / spec.tyre.friction : flat ? 0.5 : 1);
|
|
46822
|
+
wheelState.push({ n: load, grip, surface: surface.id, bog, flat, rim });
|
|
46436
46823
|
sumN += load;
|
|
46437
46824
|
sumNA += load * w.x;
|
|
46438
46825
|
sumNB += load * w.z;
|
|
@@ -46468,7 +46855,7 @@ var GameVehiclesThree = (() => {
|
|
|
46468
46855
|
if (locked || lockedByBrake) fl += moving ? -Math.sign(vl) * capacity * (lockedByBrake ? SLIDING_GRIP : 1) : 0;
|
|
46469
46856
|
else if (brakeShare > 0 && moving) fl += -Math.sign(vl) * brakeShare;
|
|
46470
46857
|
if (brakeShare > 0 && moving) brakeWork += Math.min(brakeShare, capacity) * Math.abs(vl) * dt;
|
|
46471
|
-
const rolling = ws.bog && accelInput > 0 ? spec.bogResistance : spec.rolling * (ws.flat ?
|
|
46858
|
+
const rolling = ws.bog && accelInput > 0 ? spec.bogResistance : ws.rim ? RIM_ROLLING : spec.rolling * (ws.flat ? FLAT_ROLLING : 1);
|
|
46472
46859
|
const alpha = Math.atan2(vc, Math.abs(vl) + 0.5);
|
|
46473
46860
|
let fc = -Math.tanh(spec.tyre.corneringStiffness * alpha) * capacity;
|
|
46474
46861
|
if (locked) fc *= 0.6;
|
|
@@ -46543,6 +46930,21 @@ var GameVehiclesThree = (() => {
|
|
|
46543
46930
|
const brakeForce = base.brakeForce ?? body.mass * 9.81 * (carRatio + (0.6 - carRatio) * heavyShare);
|
|
46544
46931
|
return { ...spec, mass: body.mass, wheels, fourWheelDrive: driveAll, engine, gears, drag, brakeForce, tyre: { ...spec.tyre, friction: 0.8 + 0.03 * stats2.handling } };
|
|
46545
46932
|
}
|
|
46933
|
+
var DRIVE_PROFILES = {
|
|
46934
|
+
eco: { throttle: 0.7, upshift: 0.55, downshift: 0.25 },
|
|
46935
|
+
normal: { throttle: 1, upshift: 0.85, downshift: 0.35 },
|
|
46936
|
+
sport: { throttle: 1, upshift: 0.97, downshift: 0.55 }
|
|
46937
|
+
};
|
|
46938
|
+
var DEFAULT_TRACTION = { slip: 0.97, floor: 0.35 };
|
|
46939
|
+
function tractionLimit(slips, options = DEFAULT_TRACTION) {
|
|
46940
|
+
const worst = Math.max(0, ...slips);
|
|
46941
|
+
if (worst <= options.slip) return 1;
|
|
46942
|
+
return Math.max(options.floor, 1 - (worst - options.slip) / (1 - options.slip));
|
|
46943
|
+
}
|
|
46944
|
+
var DEFAULT_CRUISE = { gain: 0.35, minSpeed: 8.3 };
|
|
46945
|
+
function cruiseThrottle(target, speed, options = DEFAULT_CRUISE) {
|
|
46946
|
+
return Math.max(0, Math.min(1, (target - speed) * options.gain + 0.12));
|
|
46947
|
+
}
|
|
46546
46948
|
var DEFAULT_CONTROL_KEYS = {
|
|
46547
46949
|
throttle: ["KeyW", "ArrowUp"],
|
|
46548
46950
|
brake: ["KeyS", "ArrowDown"],
|
|
@@ -46550,6 +46952,9 @@ var GameVehiclesThree = (() => {
|
|
|
46550
46952
|
right: ["KeyD", "ArrowRight"],
|
|
46551
46953
|
handbrake: ["Space"],
|
|
46552
46954
|
horn: ["KeyH"],
|
|
46955
|
+
clutch: ["KeyC"],
|
|
46956
|
+
flash: ["KeyX"],
|
|
46957
|
+
washer: ["KeyZ"],
|
|
46553
46958
|
ignition: ["KeyI"],
|
|
46554
46959
|
gearbox: ["KeyM"],
|
|
46555
46960
|
gear_up: ["KeyE"],
|
|
@@ -46563,13 +46968,21 @@ var GameVehiclesThree = (() => {
|
|
|
46563
46968
|
alarm: ["KeyY"],
|
|
46564
46969
|
radio: ["KeyR"],
|
|
46565
46970
|
radio_source: ["KeyT"],
|
|
46971
|
+
radio_track: ["BracketRight"],
|
|
46566
46972
|
exit: ["KeyF"],
|
|
46567
46973
|
indicator_left: ["Comma"],
|
|
46568
46974
|
indicator_right: ["Period"],
|
|
46569
46975
|
hazard: ["KeyB"],
|
|
46570
46976
|
wipers: ["KeyO"],
|
|
46571
|
-
seatbelt: ["KeyJ"]
|
|
46977
|
+
seatbelt: ["KeyJ"],
|
|
46978
|
+
drive_profile: ["Digit7"],
|
|
46979
|
+
traction_control: ["Digit8"],
|
|
46980
|
+
abs: ["Digit9"],
|
|
46981
|
+
cruise: ["Digit0"],
|
|
46982
|
+
climate: ["Minus"],
|
|
46983
|
+
demist: ["Equal"]
|
|
46572
46984
|
};
|
|
46985
|
+
var HELD_KEYS = ["throttle", "brake", "left", "right", "handbrake", "horn", "clutch", "flash", "washer"];
|
|
46573
46986
|
function createVehicleControls(options) {
|
|
46574
46987
|
const keys = { ...DEFAULT_CONTROL_KEYS, ...options.keys };
|
|
46575
46988
|
const auto4 = options.autoFourWheel ?? { slip: 0.18, releaseSeconds: 2 };
|
|
@@ -46577,7 +46990,7 @@ var GameVehiclesThree = (() => {
|
|
|
46577
46990
|
const held = /* @__PURE__ */ new Set();
|
|
46578
46991
|
const any = (codes) => codes.some((c) => held.has(c));
|
|
46579
46992
|
const commandOf = /* @__PURE__ */ new Map();
|
|
46580
|
-
for (const [command2, codes] of Object.entries(keys)) if (!
|
|
46993
|
+
for (const [command2, codes] of Object.entries(keys)) if (!HELD_KEYS.includes(command2)) for (const code of codes) commandOf.set(code, command2);
|
|
46581
46994
|
const driving = new Set(Object.values(keys).flat());
|
|
46582
46995
|
const state = {
|
|
46583
46996
|
ignition: !!options.running,
|
|
@@ -46586,18 +46999,29 @@ var GameVehiclesThree = (() => {
|
|
|
46586
46999
|
parkingBrake: false,
|
|
46587
47000
|
driveMode: options.fourWheelDrive ? "auto" : "4x2",
|
|
46588
47001
|
fourWheelEngaged: false,
|
|
46589
|
-
lamps: { mode: "off", fog: false, interior: false },
|
|
47002
|
+
lamps: { mode: "off", fog: false, interior: false, flash: false },
|
|
46590
47003
|
brakeLights: false,
|
|
46591
47004
|
reverseLights: false,
|
|
46592
47005
|
horn: false,
|
|
46593
47006
|
alarm: "off",
|
|
46594
|
-
radio: { on: false, source: "radio" },
|
|
47007
|
+
radio: { on: false, source: "radio", track: 0 },
|
|
46595
47008
|
indicator: "off",
|
|
46596
47009
|
blinkOn: false,
|
|
46597
47010
|
tick: false,
|
|
46598
47011
|
wipers: "off",
|
|
46599
|
-
|
|
46600
|
-
|
|
47012
|
+
washer: false,
|
|
47013
|
+
seatbelt: false,
|
|
47014
|
+
clutch: 0,
|
|
47015
|
+
profile: "normal",
|
|
47016
|
+
tractionControl: true,
|
|
47017
|
+
abs: true,
|
|
47018
|
+
cruise: { on: false, speed: 0 },
|
|
47019
|
+
climate: { on: false, demist: false },
|
|
47020
|
+
cranking: false
|
|
47021
|
+
};
|
|
47022
|
+
let crankAsked = false;
|
|
47023
|
+
let lastSpeed = 0;
|
|
47024
|
+
let engineRunning = !!options.running;
|
|
46601
47025
|
const blinkHz = options.blinkHz ?? 1.5;
|
|
46602
47026
|
const cancelAt = options.indicatorCancel ?? 0.15;
|
|
46603
47027
|
let blink = 0;
|
|
@@ -46609,15 +47033,19 @@ var GameVehiclesThree = (() => {
|
|
|
46609
47033
|
const emit = (event) => options.onEvent?.(event);
|
|
46610
47034
|
const LAMPS = ["off", "position", "low"];
|
|
46611
47035
|
const MODES = ["4x2", "4x4", "auto"];
|
|
47036
|
+
const WIPERS = ["off", "intermittent", "slow", "fast"];
|
|
47037
|
+
const PROFILES = ["eco", "normal", "sport"];
|
|
46612
47038
|
const command = (c) => {
|
|
46613
47039
|
switch (c) {
|
|
46614
47040
|
case "ignition":
|
|
46615
47041
|
state.ignition = !state.ignition;
|
|
46616
47042
|
if (state.ignition && state.alarm !== "off") state.alarm = "off";
|
|
47043
|
+
if (state.ignition) crankAsked = true;
|
|
47044
|
+
else state.cruise.on = false;
|
|
46617
47045
|
break;
|
|
46618
47046
|
case "gearbox":
|
|
46619
47047
|
state.gearbox = state.gearbox === "automatic" ? "manual" : "automatic";
|
|
46620
|
-
state.gear = Math.max(1, state.gear);
|
|
47048
|
+
state.gear = options.clutchPedal && state.gearbox === "manual" && Math.abs(lastSpeed) < 0.5 ? 0 : Math.max(1, state.gear);
|
|
46621
47049
|
break;
|
|
46622
47050
|
case "gear_up":
|
|
46623
47051
|
case "gear_down":
|
|
@@ -46656,6 +47084,9 @@ var GameVehiclesThree = (() => {
|
|
|
46656
47084
|
case "radio_source":
|
|
46657
47085
|
state.radio.source = state.radio.source === "radio" ? "tape" : "radio";
|
|
46658
47086
|
break;
|
|
47087
|
+
case "radio_track":
|
|
47088
|
+
state.radio.track = (state.radio.track + 1) % Math.max(1, options.radioTracks ?? 3);
|
|
47089
|
+
break;
|
|
46659
47090
|
case "exit":
|
|
46660
47091
|
break;
|
|
46661
47092
|
case "indicator_left":
|
|
@@ -46672,7 +47103,30 @@ var GameVehiclesThree = (() => {
|
|
|
46672
47103
|
blink = 0;
|
|
46673
47104
|
break;
|
|
46674
47105
|
case "wipers":
|
|
46675
|
-
state.wipers = state.wipers
|
|
47106
|
+
state.wipers = WIPERS[(WIPERS.indexOf(state.wipers) + 1) % WIPERS.length];
|
|
47107
|
+
break;
|
|
47108
|
+
case "drive_profile":
|
|
47109
|
+
state.profile = PROFILES[(PROFILES.indexOf(state.profile) + 1) % PROFILES.length];
|
|
47110
|
+
break;
|
|
47111
|
+
case "traction_control":
|
|
47112
|
+
state.tractionControl = !state.tractionControl;
|
|
47113
|
+
break;
|
|
47114
|
+
case "abs":
|
|
47115
|
+
state.abs = !state.abs;
|
|
47116
|
+
break;
|
|
47117
|
+
case "cruise":
|
|
47118
|
+
if (state.cruise.on) state.cruise.on = false;
|
|
47119
|
+
else if (!engineRunning) return emit({ type: "refused", command: c, reason: "ignition_off" });
|
|
47120
|
+
else if (lastSpeed < DEFAULT_CRUISE.minSpeed) return emit({ type: "refused", command: c, reason: "too_slow" });
|
|
47121
|
+
else state.cruise = { on: true, speed: lastSpeed };
|
|
47122
|
+
break;
|
|
47123
|
+
case "climate":
|
|
47124
|
+
state.climate.on = !state.climate.on;
|
|
47125
|
+
if (!state.climate.on) state.climate.demist = false;
|
|
47126
|
+
break;
|
|
47127
|
+
case "demist":
|
|
47128
|
+
state.climate.demist = !state.climate.demist;
|
|
47129
|
+
if (state.climate.demist) state.climate.on = true;
|
|
46676
47130
|
break;
|
|
46677
47131
|
case "seatbelt":
|
|
46678
47132
|
state.seatbelt = !state.seatbelt;
|
|
@@ -46716,16 +47170,48 @@ var GameVehiclesThree = (() => {
|
|
|
46716
47170
|
}
|
|
46717
47171
|
} else state.blinkOn = false;
|
|
46718
47172
|
state.horn = any(keys.horn);
|
|
46719
|
-
|
|
47173
|
+
state.lamps.flash = any(keys.flash);
|
|
47174
|
+
state.washer = any(keys.washer);
|
|
47175
|
+
state.clutch = ease(state.clutch, any(keys.clutch) ? 1 : 0, any(keys.clutch) ? 8 : 2.5, dt);
|
|
47176
|
+
const speed = driver?.state.u ?? 0;
|
|
47177
|
+
lastSpeed = speed;
|
|
47178
|
+
if (state.cruise.on && (brake > 0.05 || state.clutch > 0.5 || handbrake)) {
|
|
47179
|
+
state.cruise.on = false;
|
|
47180
|
+
emit({ type: "cruise_cancelled" });
|
|
47181
|
+
}
|
|
47182
|
+
const tune = DRIVE_PROFILES[state.profile];
|
|
47183
|
+
let gasNow = state.ignition ? throttle * tune.throttle : 0;
|
|
47184
|
+
if (state.cruise.on && state.ignition) gasNow = Math.max(gasNow, cruiseThrottle(state.cruise.speed, speed));
|
|
47185
|
+
const gasStart = state.gearbox === "automatic" && state.ignition && !engineRunning && throttle > 0 && Math.abs(speed) < 0.5;
|
|
47186
|
+
state.cranking = false;
|
|
47187
|
+
if (crankAsked || gasStart) {
|
|
47188
|
+
crankAsked = false;
|
|
47189
|
+
if (options.canCrank && !options.canCrank()) emit({ type: "refused", command: "ignition", reason: "battery_flat" });
|
|
47190
|
+
else {
|
|
47191
|
+
state.cranking = true;
|
|
47192
|
+
options.onCrank?.();
|
|
47193
|
+
}
|
|
47194
|
+
}
|
|
47195
|
+
const driven = (driver?.state.slip ?? []).filter((_, i) => driver?.spec.wheels?.[i]?.drive ?? true);
|
|
47196
|
+
const powerLimit = (state.tractionControl ? tractionLimit(driven) : 1) * (options.powerLimit?.() ?? 1);
|
|
46720
47197
|
const reversing = state.gearbox === "manual" ? state.gear < 0 : (driver?.state.gear ?? 1) < 0;
|
|
46721
47198
|
state.reverseLights = reversing;
|
|
46722
|
-
state.brakeLights = handbrake || (reversing ? gasNow > 0.05 &&
|
|
46723
|
-
|
|
46724
|
-
|
|
47199
|
+
state.brakeLights = handbrake || (reversing ? gasNow > 0.05 && speed < -0.3 : brake > 0.05);
|
|
47200
|
+
const common = { steer, handbrake, horn: state.horn, start: state.cranking, powerLimit };
|
|
47201
|
+
if (state.gearbox === "automatic") return { ...common, throttle: gasNow, brake };
|
|
47202
|
+
const pedals = state.gear < 0 ? { throttle: brake, brake: gasNow } : { throttle: gasNow, brake };
|
|
47203
|
+
return { ...common, ...pedals, gear: state.gear, ...options.clutchPedal ? { clutch: state.clutch } : {} };
|
|
46725
47204
|
},
|
|
46726
47205
|
after(driver, dt) {
|
|
46727
|
-
if (driver.state.engineOn && !state.ignition && driver.state.u !== 0) state.ignition = true;
|
|
46728
47206
|
if (!state.ignition && driver.state.engineOn) driver.state.engineOn = false;
|
|
47207
|
+
engineRunning = driver.state.engineOn;
|
|
47208
|
+
if (!engineRunning) state.cruise.on = false;
|
|
47209
|
+
const spec = driver.spec;
|
|
47210
|
+
spec.abs = state.abs;
|
|
47211
|
+
if (spec.upshiftFraction != null) {
|
|
47212
|
+
spec.upshiftFraction = DRIVE_PROFILES[state.profile].upshift;
|
|
47213
|
+
spec.downshiftFraction = DRIVE_PROFILES[state.profile].downshift;
|
|
47214
|
+
}
|
|
46729
47215
|
if (state.driveMode === "auto" && options.fourWheelDrive) {
|
|
46730
47216
|
const slip = Math.max(0, ...driver.state.slip);
|
|
46731
47217
|
if (slip > auto4.slip) {
|
|
@@ -46739,6 +47225,10 @@ var GameVehiclesThree = (() => {
|
|
|
46739
47225
|
held.clear();
|
|
46740
47226
|
throttle = brake = steer = 0;
|
|
46741
47227
|
state.horn = false;
|
|
47228
|
+
state.lamps.flash = false;
|
|
47229
|
+
state.washer = false;
|
|
47230
|
+
state.clutch = 0;
|
|
47231
|
+
state.cruise.on = false;
|
|
46742
47232
|
}
|
|
46743
47233
|
};
|
|
46744
47234
|
}
|
|
@@ -46753,15 +47243,126 @@ var GameVehiclesThree = (() => {
|
|
|
46753
47243
|
best = tyre;
|
|
46754
47244
|
}
|
|
46755
47245
|
}
|
|
46756
|
-
return best
|
|
47246
|
+
return best;
|
|
46757
47247
|
});
|
|
46758
47248
|
return {
|
|
46759
|
-
tyres,
|
|
47249
|
+
tyres: tyres.map((tyre) => tyre?.health ?? 1),
|
|
47250
|
+
rims: tyres.map((tyre) => !!tyre?.rim),
|
|
46760
47251
|
...report.brakes != null ? { brakes: report.brakes } : {},
|
|
46761
47252
|
...report.engine != null ? { engine: report.engine } : {},
|
|
46762
47253
|
...report.suspension != null ? { suspension: report.suspension } : {}
|
|
46763
47254
|
};
|
|
46764
47255
|
}
|
|
47256
|
+
var DEFAULT_LOADS = { position: 20, low: 110, high: 130, fog: 110, interior: 10, radio: 25, wipers: 60, indicators: 42, horn: 60, alarm: 5, brakeLights: 42, climate: 180 };
|
|
47257
|
+
var clamp3 = (x, a, b) => Math.max(a, Math.min(b, x));
|
|
47258
|
+
function createVehicleSystems(options = {}) {
|
|
47259
|
+
const loads = { ...DEFAULT_LOADS, ...options.loads };
|
|
47260
|
+
const capacity = options.batteryWh ?? 720;
|
|
47261
|
+
const alternator = options.alternatorW ?? { idle: 350, full: 1100 };
|
|
47262
|
+
const crankCharge = options.crankCharge ?? 0.25;
|
|
47263
|
+
const startWh = options.startWh ?? 0.6;
|
|
47264
|
+
const ambient = options.ambientC ?? 25;
|
|
47265
|
+
const thermostat = options.thermostatC ?? 90;
|
|
47266
|
+
const limp = options.limpC ?? 115;
|
|
47267
|
+
const seize = options.seizeC ?? 130;
|
|
47268
|
+
const heat = options.heatPerSecond ?? 0.5;
|
|
47269
|
+
const cooling = options.cooling ?? { stopped: 4e-3, fast: 0.03 };
|
|
47270
|
+
const state = { charge: clamp3(options.charge ?? 1, 0, 1), coolantC: ambient, temperature: 0, overheating: false, seized: false, spares: options.spares ?? 1, netW: 0 };
|
|
47271
|
+
const draw = (c) => {
|
|
47272
|
+
const lamps = c.lamps;
|
|
47273
|
+
let w = lamps.mode === "off" ? 0 : loads.position;
|
|
47274
|
+
if (lamps.mode === "low") w += loads.low;
|
|
47275
|
+
if (lamps.mode === "high") w += loads.low + loads.high;
|
|
47276
|
+
if (lamps.flash) w += loads.high;
|
|
47277
|
+
if (lamps.fog) w += loads.fog;
|
|
47278
|
+
if (lamps.interior) w += loads.interior;
|
|
47279
|
+
if (c.radio.on) w += loads.radio;
|
|
47280
|
+
if (c.wipers !== "off" || c.washer) w += loads.wipers;
|
|
47281
|
+
if (c.indicator !== "off") w += loads.indicators * (c.indicator === "hazard" ? 2 : 1) * 0.5;
|
|
47282
|
+
if (c.horn) w += loads.horn;
|
|
47283
|
+
if (c.alarm !== "off") w += loads.alarm;
|
|
47284
|
+
if (c.brakeLights) w += loads.brakeLights;
|
|
47285
|
+
if (c.climate.on) w += loads.climate;
|
|
47286
|
+
return w;
|
|
47287
|
+
};
|
|
47288
|
+
return {
|
|
47289
|
+
state,
|
|
47290
|
+
canCrank: () => state.charge >= crankCharge && !state.seized,
|
|
47291
|
+
crank() {
|
|
47292
|
+
state.charge = clamp3(state.charge - startWh / capacity, 0, 1);
|
|
47293
|
+
},
|
|
47294
|
+
powerLimit: () => state.coolantC <= limp ? 1 : clamp3(1 - (state.coolantC - limp) / (seize - limp), 0.25, 1),
|
|
47295
|
+
step({ driver, controls, throttle, dt, radiator = 1, coolantC }) {
|
|
47296
|
+
const events = [];
|
|
47297
|
+
const running = driver.state.engineOn;
|
|
47298
|
+
const charging = running ? alternator.idle + (alternator.full - alternator.idle) * clamp3((driver.state.rpm - 900) / 1100, 0, 1) : 0;
|
|
47299
|
+
const net = charging - draw(controls);
|
|
47300
|
+
state.netW = net;
|
|
47301
|
+
const before = state.charge;
|
|
47302
|
+
state.charge = clamp3(state.charge + net * dt / 3600 / capacity, 0, 1);
|
|
47303
|
+
if (before >= crankCharge && state.charge < crankCharge && !running) events.push({ type: "battery_flat" });
|
|
47304
|
+
const work = running ? clamp3(0.15 + 0.85 * throttle * (driver.state.rpm / 6500), 0, 1) : 0;
|
|
47305
|
+
const air = cooling.stopped + (cooling.fast - cooling.stopped) * clamp3(Math.abs(driver.state.u) / 27.8, 0, 1);
|
|
47306
|
+
const cool = air * clamp3(radiator, 0.05, 1) * (running ? 1 + (controls.climate.on ? 0.15 : 0) : 1);
|
|
47307
|
+
const opened = state.coolantC >= thermostat ? 1 : 0.15;
|
|
47308
|
+
state.coolantC += (heat * work - cool * opened * (state.coolantC - ambient)) * dt;
|
|
47309
|
+
state.coolantC = coolantC ?? Math.max(ambient, state.coolantC);
|
|
47310
|
+
state.temperature = clamp3((state.coolantC - ambient) / (seize - ambient), 0, 1);
|
|
47311
|
+
const hot = state.coolantC > limp;
|
|
47312
|
+
if (hot && !state.overheating) events.push({ type: "overheating" });
|
|
47313
|
+
state.overheating = hot;
|
|
47314
|
+
if (state.coolantC >= seize && !state.seized) {
|
|
47315
|
+
state.seized = true;
|
|
47316
|
+
events.push({ type: "seized" });
|
|
47317
|
+
}
|
|
47318
|
+
if (state.seized) driver.state.engineOn = false;
|
|
47319
|
+
return events;
|
|
47320
|
+
},
|
|
47321
|
+
fitSpare(driver, wheel) {
|
|
47322
|
+
if (state.spares <= 0) return { type: "no_spare" };
|
|
47323
|
+
state.spares--;
|
|
47324
|
+
const tyres = [...driver.condition.tyres ?? []];
|
|
47325
|
+
tyres[wheel] = 1;
|
|
47326
|
+
driver.condition = { ...driver.condition, tyres };
|
|
47327
|
+
return { type: "spare_fitted", wheel };
|
|
47328
|
+
},
|
|
47329
|
+
charge(share) {
|
|
47330
|
+
state.charge = clamp3(state.charge + share, 0, 1);
|
|
47331
|
+
}
|
|
47332
|
+
};
|
|
47333
|
+
}
|
|
47334
|
+
var DEFAULT_EJECTION = { frontal: 8.3, side: 6, keep: 0.8, lift: 2.5 };
|
|
47335
|
+
function ejectionOf(pulse, belted, options = DEFAULT_EJECTION) {
|
|
47336
|
+
if (belted) return null;
|
|
47337
|
+
const du = pulse.after.u - pulse.before.u;
|
|
47338
|
+
const dw = pulse.after.w - pulse.before.w;
|
|
47339
|
+
const deltaV = Math.hypot(du, dw);
|
|
47340
|
+
const frontal = Math.abs(du) >= Math.abs(dw);
|
|
47341
|
+
if (frontal && Math.abs(du) < options.frontal && !pulse.rolled) return null;
|
|
47342
|
+
if (!frontal && Math.abs(dw) < options.side && !pulse.rolled) return null;
|
|
47343
|
+
const exit = frontal ? du < 0 ? "windshield" : "rear_window" : dw < 0 ? "right_window" : "left_window";
|
|
47344
|
+
return { exit, velocity: [pulse.before.u * options.keep, pulse.before.w * options.keep, options.lift], deltaV };
|
|
47345
|
+
}
|
|
47346
|
+
var DEFAULT_PARKING_SENSOR = { range: 1.5, solid: 0.3, slowest: 0.8, fastest: 0.12 };
|
|
47347
|
+
function parkingBeepInterval(distance, options = DEFAULT_PARKING_SENSOR) {
|
|
47348
|
+
if (distance > options.range) return null;
|
|
47349
|
+
if (distance <= options.solid) return 0;
|
|
47350
|
+
const t = (distance - options.solid) / (options.range - options.solid);
|
|
47351
|
+
return options.fastest + (options.slowest - options.fastest) * t;
|
|
47352
|
+
}
|
|
47353
|
+
function bumperDistance(params) {
|
|
47354
|
+
const [hx, hz2] = params.heading;
|
|
47355
|
+
const sign2 = params.forward ? 1 : -1;
|
|
47356
|
+
let best = Infinity;
|
|
47357
|
+
for (const [ox, oz] of params.obstacles) {
|
|
47358
|
+
const dx = ox - params.x;
|
|
47359
|
+
const dz = oz - params.z;
|
|
47360
|
+
const along = (dx * hx + dz * hz2) * sign2 - params.length / 2;
|
|
47361
|
+
const side2 = Math.abs(-dx * hz2 + dz * hx);
|
|
47362
|
+
if (along >= 0 && side2 <= params.width / 2 + 0.2) best = Math.min(best, along);
|
|
47363
|
+
}
|
|
47364
|
+
return best;
|
|
47365
|
+
}
|
|
46765
47366
|
|
|
46766
47367
|
// src/vehicle-lod.ts
|
|
46767
47368
|
var VEHICLE_LOD_DISTANCE = 14;
|
|
@@ -47402,6 +48003,7 @@ var GameVehiclesThree = (() => {
|
|
|
47402
48003
|
var itemId = (key) => ITEM_OF[key] ?? key;
|
|
47403
48004
|
var LANE = { z: 11, from: -12, wall: 13 };
|
|
47404
48005
|
var SPARK_EVERY_S = 0.08;
|
|
48006
|
+
var TYRE_DAMAGE = /* @__PURE__ */ new Set(["flat_tyre", "torn_tyre", "run_flat"]);
|
|
47405
48007
|
var LEAF_REST_RADIANS = 0.02;
|
|
47406
48008
|
var MATERIAL_COLOURS = { metal: 13421772, glass: 4500223, rubber: 16750848, fuel: 16724736 };
|
|
47407
48009
|
var LANE_START_S = 1.2;
|
|
@@ -47697,7 +48299,8 @@ var GameVehiclesThree = (() => {
|
|
|
47697
48299
|
state.time += dt;
|
|
47698
48300
|
const braking = settings.damage !== "into_wall" && gap() < brakeAt() && state.time > 0.5;
|
|
47699
48301
|
const moving = state.time > LANE_START_S;
|
|
47700
|
-
const
|
|
48302
|
+
const straight = settings.damage === "into_wall" || TYRE_DAMAGE.has(settings.damage) || !moving;
|
|
48303
|
+
const input = { ...idle2, throttle: braking || !moving ? 0 : 1, brake: braking ? 1 : 0, steer: straight ? 0 : Math.sin(state.time * 1.4) * 0.15 };
|
|
47701
48304
|
if (mode === "arcade") {
|
|
47702
48305
|
const { events } = arcade.step(input, dt, { blocked: (x, z, yaw) => vehicleCorners(arcade.spec, x, z, yaw).some(([px]) => px >= LANE.wall) });
|
|
47703
48306
|
for (const e of events) {
|
|
@@ -48348,6 +48951,127 @@ var GameVehiclesThree = (() => {
|
|
|
48348
48951
|
return { ...certificate, results, status: statusOf(results) };
|
|
48349
48952
|
}
|
|
48350
48953
|
|
|
48954
|
+
// src/body.ts
|
|
48955
|
+
var OPEN_SOUND = "car_door_open";
|
|
48956
|
+
var SHUT_SOUND = { door: "car_door_close", hood: "car_hood_close", lid: "car_trunk_close" };
|
|
48957
|
+
var clamp4 = (x, a, b) => Math.max(a, Math.min(b, x));
|
|
48958
|
+
function createVehicleBody(built2, options) {
|
|
48959
|
+
const swing = options.swingSeconds ?? 1.2;
|
|
48960
|
+
const windowSeconds = options.windowSeconds ?? 3.5;
|
|
48961
|
+
const travel = options.windowTravel ?? 0.92;
|
|
48962
|
+
const root = built2.context?.vehicle ?? built2.group;
|
|
48963
|
+
const poses = /* @__PURE__ */ new Map();
|
|
48964
|
+
for (const clip of options.clips) {
|
|
48965
|
+
if (!clip.id.startsWith("open_")) continue;
|
|
48966
|
+
const last = clip.keys[clip.keys.length - 1];
|
|
48967
|
+
for (const [id, pose] of Object.entries(last?.parts ?? {})) if (pose.rotation) poses.set(id, pose.rotation);
|
|
48968
|
+
}
|
|
48969
|
+
const hinges = /* @__PURE__ */ new Map();
|
|
48970
|
+
for (const [id, open] of poses) {
|
|
48971
|
+
const pivot = root.getObjectByName(id);
|
|
48972
|
+
if (pivot) hinges.set(id, { pivot, open, now: 0, target: 0 });
|
|
48973
|
+
}
|
|
48974
|
+
const kind = (part) => part.startsWith("door") ? "door" : part === "hood" ? "hood" : "lid";
|
|
48975
|
+
const panes = /* @__PURE__ */ new Map();
|
|
48976
|
+
for (const glass of built2.context?.glass ?? []) {
|
|
48977
|
+
const g = glass.userData.glass;
|
|
48978
|
+
if (!g || g.key !== "window_front" && g.key !== "window_rear") continue;
|
|
48979
|
+
const id = `${g.key === "window_front" ? "front" : "rear"}_${(g.side ?? 1) < 0 ? "left" : "right"}`;
|
|
48980
|
+
if (panes.has(id) || !glass.geometry) continue;
|
|
48981
|
+
glass.geometry.computeBoundingBox();
|
|
48982
|
+
const box2 = glass.geometry.boundingBox;
|
|
48983
|
+
if (box2) panes.set(id, { pane: glass, drop: (box2.max.y - box2.min.y) * travel, now: 0, target: 0, applied: 0 });
|
|
48984
|
+
}
|
|
48985
|
+
const sunroofs = [];
|
|
48986
|
+
const wipers = [];
|
|
48987
|
+
const T = options.THREE;
|
|
48988
|
+
const turn = new T.Quaternion();
|
|
48989
|
+
root.traverse((o) => {
|
|
48990
|
+
const sun = o.userData.sunroof;
|
|
48991
|
+
if (sun) sunroofs.push({ obj: o, slide: sun.slide, base: o.position.x });
|
|
48992
|
+
});
|
|
48993
|
+
for (const { part, tag } of built2.context?.wipers ?? [])
|
|
48994
|
+
wipers.push({ obj: part, arm: tag.arm, sweep: tag.sweep, spindle: new T.Vector3(...tag.spindle), axis: new T.Vector3(...tag.axis), rest: part.position.clone(), restQ: part.quaternion.clone() });
|
|
48995
|
+
let sunNow = 0;
|
|
48996
|
+
let sunTarget = 0;
|
|
48997
|
+
let wipePhase = 0;
|
|
48998
|
+
let locked = false;
|
|
48999
|
+
const placeWiper = (w, angle) => {
|
|
49000
|
+
if (angle === 0) {
|
|
49001
|
+
w.obj.position.copy(w.rest);
|
|
49002
|
+
w.obj.quaternion.copy(w.restQ);
|
|
49003
|
+
return;
|
|
49004
|
+
}
|
|
49005
|
+
turn.setFromAxisAngle(w.axis, angle);
|
|
49006
|
+
w.obj.position.copy(w.rest).sub(w.spindle).applyQuaternion(turn).add(w.spindle);
|
|
49007
|
+
w.obj.quaternion.copy(turn).multiply(w.restQ);
|
|
49008
|
+
};
|
|
49009
|
+
const ease = (now, target, seconds, dt) => now + clamp4(target - now, -dt / seconds, dt / seconds);
|
|
49010
|
+
return {
|
|
49011
|
+
parts: [...hinges.keys()],
|
|
49012
|
+
windows: [...panes.keys()],
|
|
49013
|
+
hasSunroof: sunroofs.length > 0,
|
|
49014
|
+
wipers: new Set(wipers.map((w) => w.arm)).size,
|
|
49015
|
+
get locked() {
|
|
49016
|
+
return locked;
|
|
49017
|
+
},
|
|
49018
|
+
openness: (part) => hinges.get(part)?.now ?? 0,
|
|
49019
|
+
toggle(part) {
|
|
49020
|
+
const h = hinges.get(part);
|
|
49021
|
+
if (!h) return [{ type: "refused", part, reason: "missing" }];
|
|
49022
|
+
if (locked && kind(part) !== "hood" && h.target === 0) return [{ type: "refused", part, reason: "locked" }];
|
|
49023
|
+
h.target = h.target > 0 ? 0 : 1;
|
|
49024
|
+
return h.target > 0 ? [{ type: "sound", key: OPEN_SOUND, part }] : [];
|
|
49025
|
+
},
|
|
49026
|
+
window: (id) => panes.get(id)?.now ?? 0,
|
|
49027
|
+
toggleWindow(id) {
|
|
49028
|
+
const w = panes.get(id);
|
|
49029
|
+
if (w) w.target = w.target > 0.5 ? 0 : 1;
|
|
49030
|
+
},
|
|
49031
|
+
setWindow(id, open) {
|
|
49032
|
+
const w = panes.get(id);
|
|
49033
|
+
if (w) w.target = clamp4(open, 0, 1);
|
|
49034
|
+
},
|
|
49035
|
+
toggleSunroof() {
|
|
49036
|
+
sunTarget = sunTarget > 0.5 ? 0 : 1;
|
|
49037
|
+
},
|
|
49038
|
+
lock(on) {
|
|
49039
|
+
locked = on;
|
|
49040
|
+
return [{ type: "sound", key: "blinker_tick", part: "doors" }];
|
|
49041
|
+
},
|
|
49042
|
+
update(dt, wipe = 0) {
|
|
49043
|
+
const events = [];
|
|
49044
|
+
for (const [id, h] of hinges) {
|
|
49045
|
+
if (h.now === h.target) continue;
|
|
49046
|
+
h.now = ease(h.now, h.target, swing, dt);
|
|
49047
|
+
h.pivot.rotation.set(h.open[0] * h.now, h.open[1] * h.now, h.open[2] * h.now);
|
|
49048
|
+
if (h.now === 0) events.push({ type: "sound", key: SHUT_SOUND[kind(id)] ?? "car_door_close", part: id });
|
|
49049
|
+
}
|
|
49050
|
+
for (const w of panes.values()) {
|
|
49051
|
+
if (w.now === w.target) continue;
|
|
49052
|
+
w.now = ease(w.now, w.target, windowSeconds, dt);
|
|
49053
|
+
const offset = -w.drop * w.now;
|
|
49054
|
+
w.pane.position.y += offset - w.applied;
|
|
49055
|
+
w.applied = offset;
|
|
49056
|
+
}
|
|
49057
|
+
if (sunNow !== sunTarget) {
|
|
49058
|
+
sunNow = ease(sunNow, sunTarget, 2.5, dt);
|
|
49059
|
+
for (const s of sunroofs) s.obj.position.x = s.base - s.slide * sunNow;
|
|
49060
|
+
}
|
|
49061
|
+
wipePhase = wipe > 0 ? (wipePhase + dt * wipe) % 1 : wipePhase > 0 ? Math.min(1, wipePhase + dt * 1.5) % 1 : 0;
|
|
49062
|
+
const sweep = Math.sin(Math.PI * wipePhase);
|
|
49063
|
+
for (const w of wipers) placeWiper(w, w.sweep * sweep);
|
|
49064
|
+
return events;
|
|
49065
|
+
},
|
|
49066
|
+
dispose() {
|
|
49067
|
+
for (const h of hinges.values()) h.pivot.rotation.set(0, 0, 0);
|
|
49068
|
+
for (const w of panes.values()) w.pane.position.y -= w.applied;
|
|
49069
|
+
for (const s of sunroofs) s.obj.position.x = s.base;
|
|
49070
|
+
for (const w of wipers) placeWiper(w, 0);
|
|
49071
|
+
}
|
|
49072
|
+
};
|
|
49073
|
+
}
|
|
49074
|
+
|
|
48351
49075
|
// src/dashboard.ts
|
|
48352
49076
|
var DASHBOARD_LOOK = {
|
|
48353
49077
|
face: "#0b1110",
|
|
@@ -48522,6 +49246,8 @@ var GameVehiclesThree = (() => {
|
|
|
48522
49246
|
lamp(g, 104, 214, r.highBeam, DASHBOARD_LOOK.blue, beam);
|
|
48523
49247
|
lamp(g, 152, 214, r.lowBeam || r.fog, r.fog ? DASHBOARD_LOOK.amber : DASHBOARD_LOOK.green, beam);
|
|
48524
49248
|
bar(g, 40, r.fuel, false, "F");
|
|
49249
|
+
lamp(g, 104, 244, !!r.cruise, DASHBOARD_LOOK.green, letter("CRZ"));
|
|
49250
|
+
if (r.profile && r.profile !== "normal") lamp(g, 152, 244, true, r.profile === "eco" ? DASHBOARD_LOOK.green : DASHBOARD_LOOK.amber, letter(r.profile === "eco" ? "E" : "S"));
|
|
48525
49251
|
} else {
|
|
48526
49252
|
dial(g, r.rpm / 1e3, Math.ceil(redline / 1e3) + 1, 1, redline / 1e3, (v) => String(Math.round(v)));
|
|
48527
49253
|
g.fillStyle = DASHBOARD_LOOK.marks;
|
|
@@ -48530,7 +49256,9 @@ var GameVehiclesThree = (() => {
|
|
|
48530
49256
|
g.fillText(r.gear < 0 ? "R" : r.gear === 0 ? "N" : String(r.gear), SIZE / 2, SIZE / 2 + 54);
|
|
48531
49257
|
lamp(g, 86, 178, r.parkingBrake, DASHBOARD_LOOK.red, ring2("P"));
|
|
48532
49258
|
lamp(g, 128, 214, r.ignition && !r.seatbelt, DASHBOARD_LOOK.red, belt);
|
|
48533
|
-
lamp(g, 170, 178, warn, DASHBOARD_LOOK.red, letter("+\u2212"));
|
|
49259
|
+
lamp(g, 170, 178, warn || r.engineOn && r.charging === false, DASHBOARD_LOOK.red, letter("+\u2212"));
|
|
49260
|
+
lamp(g, 104, 244, r.ignition && !!r.tractionOff, DASHBOARD_LOOK.amber, letter("TC"));
|
|
49261
|
+
lamp(g, 152, 244, r.ignition && !!r.absOff, DASHBOARD_LOOK.amber, letter("ABS"));
|
|
48534
49262
|
lamp(g, 86, 214, warn, DASHBOARD_LOOK.red, letter("OIL"));
|
|
48535
49263
|
lamp(g, 170, 214, r.fourWheel, DASHBOARD_LOOK.green, letter("4x4"));
|
|
48536
49264
|
bar(g, SIZE - 40, r.temperature, true, "T");
|
|
@@ -48718,7 +49446,18 @@ var GameVehiclesThree = (() => {
|
|
|
48718
49446
|
}
|
|
48719
49447
|
|
|
48720
49448
|
// src/test-area/proving/cockpit.ts
|
|
48721
|
-
var DEFAULT_COCKPIT_SOUNDS = { horn: "horn", blinker: "blinker_tick", starter: "starter_crank", alarm: "car_alarm", chirp: "alarm_chirp", wiper: "wiper_swish", chime: "seatbelt_chime", radio: "radio_static" };
|
|
49449
|
+
var DEFAULT_COCKPIT_SOUNDS = { horn: "horn", blinker: "blinker_tick", starter: "starter_crank", alarm: "car_alarm", chirp: "alarm_chirp", wiper: "wiper_swish", chime: "seatbelt_chime", radio: "radio_static", beep: "parking_beep" };
|
|
49450
|
+
var BODY_KEYS = {
|
|
49451
|
+
Digit1: "door_front_left",
|
|
49452
|
+
Digit2: "door_front_right",
|
|
49453
|
+
Digit3: "door_rear_left",
|
|
49454
|
+
Digit4: "door_rear_right",
|
|
49455
|
+
Digit5: "hood",
|
|
49456
|
+
Digit6: "lid"
|
|
49457
|
+
};
|
|
49458
|
+
var WINDOW_KEYS = { Digit1: "front_left", Digit2: "front_right", Digit3: "rear_left", Digit4: "rear_right" };
|
|
49459
|
+
var LOCK_KEY = "KeyV";
|
|
49460
|
+
var WIPE_RATE = { off: 0, intermittent: 0.25, slow: 0.7, fast: 1.4 };
|
|
48722
49461
|
function createCockpit(o) {
|
|
48723
49462
|
const sounds = { ...DEFAULT_COCKPIT_SOUNDS, ...o.sounds };
|
|
48724
49463
|
const spec = subjectSpec(o.subject);
|
|
@@ -48735,19 +49474,38 @@ var GameVehiclesThree = (() => {
|
|
|
48735
49474
|
let wipeAgain = 0;
|
|
48736
49475
|
let chimeAgain = 0;
|
|
48737
49476
|
let radioAgain = 0;
|
|
49477
|
+
let beepAgain = 0;
|
|
49478
|
+
let bar = 0;
|
|
48738
49479
|
let exitAsked = false;
|
|
48739
49480
|
let gas = 0;
|
|
49481
|
+
const systems = createVehicleSystems();
|
|
49482
|
+
const context = o.built.context;
|
|
49483
|
+
const destructible = o.kit && context ? createVehicleDestructible(o.kit, context, { id: "driven", frame: o.built.group }) : null;
|
|
49484
|
+
const body = destructible ? createVehicleBody(o.built, { THREE: o.THREE, clips: destructible.animations() }) : null;
|
|
49485
|
+
const bodyEvents = (events) => {
|
|
49486
|
+
for (const event of events) {
|
|
49487
|
+
if (event.type === "sound") play(event.key, 0.9);
|
|
49488
|
+
o.onEvent?.(event);
|
|
49489
|
+
}
|
|
49490
|
+
};
|
|
48740
49491
|
const controls = createVehicleControls({
|
|
49492
|
+
canCrank: systems.canCrank,
|
|
49493
|
+
onCrank: systems.crank,
|
|
49494
|
+
powerLimit: systems.powerLimit,
|
|
48741
49495
|
gears: spec.gears?.length ?? 5,
|
|
48742
49496
|
fourWheelDrive: !!o.subject.body.fourWheelDrive,
|
|
48743
49497
|
fogLamps: lamps.has.fog,
|
|
49498
|
+
clutchPedal: true,
|
|
49499
|
+
radioTracks: RADIO_TRACKS.length,
|
|
48744
49500
|
onEvent: (event) => {
|
|
48745
49501
|
if (event.type === "command") {
|
|
48746
|
-
if (event.command === "ignition" && event.state.ignition && sounds.starter) play(sounds.starter);
|
|
48747
49502
|
if (event.command === "ignition" && !event.state.ignition) driver.state.engineOn = false;
|
|
48748
49503
|
if (event.command === "alarm" && event.state.alarm === "armed") play(sounds.chirp);
|
|
48749
49504
|
if (event.command === "alarm") alarmAgain = 0;
|
|
48750
|
-
if (event.command === "radio" || event.command === "radio_source"
|
|
49505
|
+
if (event.command === "radio" || event.command === "radio_source" || event.command === "radio_track") {
|
|
49506
|
+
radioAgain = 0;
|
|
49507
|
+
bar = 0;
|
|
49508
|
+
}
|
|
48751
49509
|
if (event.command === "exit") exitAsked = true;
|
|
48752
49510
|
}
|
|
48753
49511
|
o.onEvent?.(event);
|
|
@@ -48755,7 +49513,20 @@ var GameVehiclesThree = (() => {
|
|
|
48755
49513
|
});
|
|
48756
49514
|
driver.state.engineOn = false;
|
|
48757
49515
|
const target = typeof window !== "undefined" ? window : null;
|
|
49516
|
+
const bodyKey = (e) => {
|
|
49517
|
+
if (!body) return false;
|
|
49518
|
+
if (e.code === LOCK_KEY) bodyEvents(body.lock(!body.locked));
|
|
49519
|
+
else if (e.shiftKey && e.code === "Digit5") body.toggleSunroof();
|
|
49520
|
+
else if (e.shiftKey && WINDOW_KEYS[e.code]) body.toggleWindow(WINDOW_KEYS[e.code]);
|
|
49521
|
+
else if (!e.shiftKey && BODY_KEYS[e.code]) bodyEvents(body.toggle(BODY_KEYS[e.code]));
|
|
49522
|
+
else return false;
|
|
49523
|
+
return true;
|
|
49524
|
+
};
|
|
48758
49525
|
const down = (e) => {
|
|
49526
|
+
if (!e.repeat && bodyKey(e)) {
|
|
49527
|
+
e.stopImmediatePropagation();
|
|
49528
|
+
return;
|
|
49529
|
+
}
|
|
48759
49530
|
if (!controls.key(e.code, true)) return;
|
|
48760
49531
|
e.stopImmediatePropagation();
|
|
48761
49532
|
if (e.code.startsWith("Arrow") || e.code === "Space") e.preventDefault();
|
|
@@ -48767,9 +49538,13 @@ var GameVehiclesThree = (() => {
|
|
|
48767
49538
|
target?.addEventListener("keydown", down, true);
|
|
48768
49539
|
target?.addEventListener("keyup", up, true);
|
|
48769
49540
|
target?.addEventListener("blur", blur);
|
|
48770
|
-
|
|
49541
|
+
const cockpit = {
|
|
49542
|
+
radiator: 1,
|
|
48771
49543
|
driver,
|
|
48772
49544
|
controls,
|
|
49545
|
+
systems,
|
|
49546
|
+
body,
|
|
49547
|
+
destructible,
|
|
48773
49548
|
lamps,
|
|
48774
49549
|
dashboard,
|
|
48775
49550
|
get throttle() {
|
|
@@ -48778,11 +49553,16 @@ var GameVehiclesThree = (() => {
|
|
|
48778
49553
|
step(dt) {
|
|
48779
49554
|
const input = controls.input(dt, driver);
|
|
48780
49555
|
gas = input.throttle;
|
|
48781
|
-
|
|
49556
|
+
if (input.start && sounds.starter) play(sounds.starter);
|
|
49557
|
+
const engine = driver.step(input, dt).events;
|
|
48782
49558
|
controls.after(driver, dt);
|
|
48783
49559
|
const s = controls.state;
|
|
49560
|
+
const report = [...engine.filter((e) => e.type !== "shift"), ...systems.step({ driver, controls: s, throttle: input.throttle, dt, radiator: cockpit.radiator })];
|
|
49561
|
+
for (const event of report) o.onEvent?.(event);
|
|
49562
|
+
const powered = driver.state.engineOn || systems.state.charge > 0;
|
|
49563
|
+
const high = s.lamps.mode === "high" || s.lamps.flash;
|
|
48784
49564
|
lamps.set({
|
|
48785
|
-
mode: s.lamps.mode,
|
|
49565
|
+
mode: !powered ? "off" : s.lamps.flash ? "high" : s.lamps.mode,
|
|
48786
49566
|
fog: s.lamps.fog,
|
|
48787
49567
|
interior: s.lamps.interior,
|
|
48788
49568
|
brake: s.brakeLights,
|
|
@@ -48799,7 +49579,14 @@ var GameVehiclesThree = (() => {
|
|
|
48799
49579
|
engineOn: driver.state.engineOn,
|
|
48800
49580
|
left: s.blinkOn && (s.indicator === "left" || s.indicator === "hazard"),
|
|
48801
49581
|
right: s.blinkOn && (s.indicator === "right" || s.indicator === "hazard"),
|
|
48802
|
-
highBeam:
|
|
49582
|
+
highBeam: high,
|
|
49583
|
+
fuel: driver.state.fuel / spec.tank.capacity,
|
|
49584
|
+
temperature: systems.state.temperature,
|
|
49585
|
+
cruise: s.cruise.on,
|
|
49586
|
+
profile: s.profile,
|
|
49587
|
+
tractionOff: !s.tractionControl,
|
|
49588
|
+
absOff: !s.abs,
|
|
49589
|
+
charging: systems.state.netW >= 0,
|
|
48803
49590
|
lowBeam: s.lamps.mode === "low" || s.lamps.mode === "position",
|
|
48804
49591
|
fog: s.lamps.fog,
|
|
48805
49592
|
parkingBrake: s.parkingBrake,
|
|
@@ -48808,7 +49595,19 @@ var GameVehiclesThree = (() => {
|
|
|
48808
49595
|
},
|
|
48809
49596
|
dt
|
|
48810
49597
|
);
|
|
49598
|
+
if (body) bodyEvents(body.update(dt, s.washer ? 1.2 : WIPE_RATE[s.wipers] ?? 0));
|
|
48811
49599
|
if (s.tick) play(sounds.blinker, 0.8);
|
|
49600
|
+
const near = o.obstacles?.() ?? [];
|
|
49601
|
+
if (s.ignition && near.length && Math.abs(driver.state.u) < 3) {
|
|
49602
|
+
const g = o.built.group;
|
|
49603
|
+
const reversing = driver.state.gear < 0 || driver.state.u < -0.05;
|
|
49604
|
+
const distance = bumperDistance({ x: g.position.x, z: g.position.z, heading: [Math.sin(g.rotation.y), Math.cos(g.rotation.y)], length: o.built.length, width: o.built.width, forward: !reversing, obstacles: near });
|
|
49605
|
+
const every = parkingBeepInterval(distance);
|
|
49606
|
+
if (every != null && (beepAgain -= dt) <= 0) {
|
|
49607
|
+
play(sounds.beep, 0.7);
|
|
49608
|
+
beepAgain = every === 0 ? 0.09 : every;
|
|
49609
|
+
}
|
|
49610
|
+
}
|
|
48812
49611
|
if (s.horn && (hornAgain -= dt) <= 0) {
|
|
48813
49612
|
play(sounds.horn);
|
|
48814
49613
|
hornAgain = 0.5;
|
|
@@ -48817,9 +49616,9 @@ var GameVehiclesThree = (() => {
|
|
|
48817
49616
|
play(sounds.alarm);
|
|
48818
49617
|
alarmAgain = 2.9;
|
|
48819
49618
|
}
|
|
48820
|
-
if (s.wipers !== "off" && (wipeAgain -= dt) <= 0) {
|
|
49619
|
+
if ((s.wipers !== "off" || s.washer) && (wipeAgain -= dt) <= 0) {
|
|
48821
49620
|
play(sounds.wiper, 0.7);
|
|
48822
|
-
wipeAgain = s.wipers === "fast" ? 0.6 : 1.4;
|
|
49621
|
+
wipeAgain = s.washer || s.wipers === "fast" ? 0.6 : s.wipers === "slow" ? 1.4 : 4;
|
|
48823
49622
|
}
|
|
48824
49623
|
const kmh2 = Math.abs(driver.state.u) * 3.6;
|
|
48825
49624
|
if (!s.seatbelt && s.ignition && kmh2 > 10 && (chimeAgain -= dt) <= 0) {
|
|
@@ -48827,8 +49626,13 @@ var GameVehiclesThree = (() => {
|
|
|
48827
49626
|
chimeAgain = 6;
|
|
48828
49627
|
}
|
|
48829
49628
|
if (s.radio.on && (radioAgain -= dt) <= 0) {
|
|
48830
|
-
|
|
48831
|
-
|
|
49629
|
+
const song = RADIO_TRACKS[s.radio.track % RADIO_TRACKS.length];
|
|
49630
|
+
if (song) {
|
|
49631
|
+
play(song.bars[bar % song.bars.length], s.radio.source === "tape" ? 0.8 : 0.7);
|
|
49632
|
+
bar++;
|
|
49633
|
+
}
|
|
49634
|
+
if (s.radio.source === "radio") play(sounds.radio, 0.12);
|
|
49635
|
+
radioAgain = song?.barSeconds ?? 2;
|
|
48832
49636
|
}
|
|
48833
49637
|
if (exitAsked) {
|
|
48834
49638
|
exitAsked = false;
|
|
@@ -48842,10 +49646,149 @@ var GameVehiclesThree = (() => {
|
|
|
48842
49646
|
controls.release();
|
|
48843
49647
|
lamps.dispose();
|
|
48844
49648
|
dashboard?.dispose();
|
|
49649
|
+
body?.dispose();
|
|
49650
|
+
destructible?.dispose?.();
|
|
49651
|
+
}
|
|
49652
|
+
};
|
|
49653
|
+
return cockpit;
|
|
49654
|
+
}
|
|
49655
|
+
|
|
49656
|
+
// src/wheel-roll.ts
|
|
49657
|
+
var DEFAULT_RADIUS = 0.32;
|
|
49658
|
+
function createWheelRoll(THREE, built2) {
|
|
49659
|
+
const wheels = (built2.context?.wheels ?? []).map((wheel) => ({
|
|
49660
|
+
wheel,
|
|
49661
|
+
rest: wheel.quaternion.clone(),
|
|
49662
|
+
radius: Number(wheel.userData.r) || DEFAULT_RADIUS,
|
|
49663
|
+
// in the kit's frame the front is +x: the wheels ahead of the middle steer
|
|
49664
|
+
front: wheel.position.x > 0,
|
|
49665
|
+
angle: 0
|
|
49666
|
+
}));
|
|
49667
|
+
const axle = new THREE.Vector3(0, 0, 1);
|
|
49668
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
49669
|
+
const spin = new THREE.Quaternion();
|
|
49670
|
+
const steering = new THREE.Quaternion();
|
|
49671
|
+
return {
|
|
49672
|
+
count: wheels.length,
|
|
49673
|
+
roll(distance, steer) {
|
|
49674
|
+
for (const w of wheels) {
|
|
49675
|
+
w.angle = (w.angle - distance / w.radius) % (Math.PI * 2);
|
|
49676
|
+
spin.setFromAxisAngle(axle, w.angle);
|
|
49677
|
+
steering.setFromAxisAngle(up, w.front ? steer : 0);
|
|
49678
|
+
w.wheel.quaternion.copy(w.rest).premultiply(spin).premultiply(steering);
|
|
49679
|
+
}
|
|
49680
|
+
},
|
|
49681
|
+
dispose() {
|
|
49682
|
+
for (const w of wheels) w.wheel.quaternion.copy(w.rest);
|
|
48845
49683
|
}
|
|
48846
49684
|
};
|
|
48847
49685
|
}
|
|
48848
49686
|
|
|
49687
|
+
// src/test-area/proving/crash.ts
|
|
49688
|
+
var GLASS_BLOW = 6e3;
|
|
49689
|
+
function createProvingCrash(host, occupant) {
|
|
49690
|
+
const thrown = [];
|
|
49691
|
+
const breakGlass = (destructible, exit, forward) => {
|
|
49692
|
+
if (!destructible) return;
|
|
49693
|
+
const want = exit === "windshield" ? "windshield" : exit === "rear_window" ? "rear" : exit.startsWith("left") ? "left" : "right";
|
|
49694
|
+
const pane = destructible.describe().parts.find((p) => p.id.includes(exit === "windshield" || exit === "rear_window" ? want : "window") && (exit === "windshield" || exit === "rear_window" || p.id.includes(want)));
|
|
49695
|
+
if (!pane) return;
|
|
49696
|
+
destructible.hit({ type: "blunt", partId: pane.id, point: destructible.partWorldCenter(pane.id), direction: forward, energy: GLASS_BLOW, radius: 0.35, source: "occupant" });
|
|
49697
|
+
};
|
|
49698
|
+
return {
|
|
49699
|
+
crash({ car, u, w, belted, destructible }) {
|
|
49700
|
+
const ejection = ejectionOf({ before: { u, w }, after: { u: 0, w: 0 } }, belted);
|
|
49701
|
+
if (!ejection) return null;
|
|
49702
|
+
const s = Math.sin(car.rotationY);
|
|
49703
|
+
const c = Math.cos(car.rotationY);
|
|
49704
|
+
const forward = [s, 0, c];
|
|
49705
|
+
const right = [-c, 0, s];
|
|
49706
|
+
breakGlass(destructible, ejection.exit, forward);
|
|
49707
|
+
const [vf, vr, vu] = ejection.velocity;
|
|
49708
|
+
const start2 = [car.position.x + forward[0] * 1.2, 1.1, car.position.z + forward[2] * 1.2];
|
|
49709
|
+
const placed = occupant ? host.addObject?.(occupant, { position: start2, rotationY: car.rotationY }) : null;
|
|
49710
|
+
if (placed) {
|
|
49711
|
+
launchBody(placed.root, [forward[0] * vf + right[0] * vr, vu, forward[2] * vf + right[2] * vr]);
|
|
49712
|
+
thrown.push(placed.remove);
|
|
49713
|
+
}
|
|
49714
|
+
return ejection;
|
|
49715
|
+
},
|
|
49716
|
+
dispose() {
|
|
49717
|
+
for (const remove of thrown.splice(0)) remove();
|
|
49718
|
+
}
|
|
49719
|
+
};
|
|
49720
|
+
}
|
|
49721
|
+
|
|
49722
|
+
// src/test-area/proving/systems-text.ts
|
|
49723
|
+
var SYSTEMS_TEXT = {
|
|
49724
|
+
"pt-BR": {
|
|
49725
|
+
keys: "C embreagem (c\xE2mbio manual) \xB7 X lampejo do farol alto \xB7 Z esguicho do limpador \xB7 7 modo econ\xF4mico/normal/esporte \xB7 8 controle de tra\xE7\xE3o \xB7 9 ABS \xB7 0 piloto autom\xE1tico \xB7 - ar-condicionado \xB7 = desemba\xE7ador \xB7 ] troca a faixa do r\xE1dio ou da fita",
|
|
49726
|
+
profile: "Modo de condu\xE7\xE3o",
|
|
49727
|
+
profiles: { eco: "econ\xF4mico", normal: "normal", sport: "esporte" },
|
|
49728
|
+
traction: "Controle de tra\xE7\xE3o",
|
|
49729
|
+
abs: "ABS",
|
|
49730
|
+
cruise: "Piloto autom\xE1tico",
|
|
49731
|
+
cruiseSet: "Piloto autom\xE1tico: segurando {0} km/h",
|
|
49732
|
+
climate: "Ar-condicionado",
|
|
49733
|
+
demist: "Desemba\xE7ador",
|
|
49734
|
+
wiperModes: { off: "desligado", intermittent: "intermitente", slow: "lento", fast: "r\xE1pido" },
|
|
49735
|
+
refused: { battery_flat: "Bateria descarregada: o motor de partida n\xE3o gira", too_slow: "Piloto autom\xE1tico s\xF3 acima de 30 km/h", locked: "Porta travada", missing: "Este ve\xEDculo n\xE3o tem essa pe\xE7a" },
|
|
49736
|
+
events: {
|
|
49737
|
+
stall: "O motor morreu",
|
|
49738
|
+
out_of_fuel: "Acabou o combust\xEDvel",
|
|
49739
|
+
bogged: "Atolou",
|
|
49740
|
+
aquaplaning: "Aquaplanagem",
|
|
49741
|
+
battery_flat: "A bateria descarregou",
|
|
49742
|
+
overheating: "Motor superaquecendo: perdendo pot\xEAncia",
|
|
49743
|
+
seized: "O motor fundiu de tanto esquentar",
|
|
49744
|
+
spare_fitted: "Estepe colocado",
|
|
49745
|
+
no_spare: "N\xE3o h\xE1 estepe",
|
|
49746
|
+
cruise_cancelled: "Piloto autom\xE1tico desligado"
|
|
49747
|
+
},
|
|
49748
|
+
actions: { puncture: "Furar pneu (dianteiro esquerdo)", fit_spare: "Trocar pelo estepe", refuel: "Abastecer", drain_battery: "Descarregar bateria", jump_start: "Chupeta (carregar bateria)", break_radiator: "Furar o radiador", empty_tank: "Deixar o tanque na reserva", remote_lock: "Controle: travar (arma o alarme)", remote_unlock: "Controle: destravar", force_door: "For\xE7ar a porta do motorista", crash_wall: "Bater no muro (parada seca)", park_behind: "Estacionar um carro atr\xE1s (sensor de r\xE9)" },
|
|
49749
|
+
parking: { placed: "Carro parado 3 m atr\xE1s: d\xEA r\xE9 e escute o sensor", none: "Sem cat\xE1logo de ve\xEDculos nesta sala: o sensor n\xE3o tem o que ver" },
|
|
49750
|
+
track: "faixa {0}",
|
|
49751
|
+
adjust: { seat_forward: "Banco: frente/tr\xE1s (m)", seat_height: "Banco: altura (m)", mirror_left: "Retrovisor esquerdo: \xE2ngulo", mirror_right: "Retrovisor direito: \xE2ngulo" },
|
|
49752
|
+
crash: { mild: "Batida a {0} km/h: fraca demais para lan\xE7ar quem est\xE1 sem cinto", stayed: "Batida a {0} km/h: o cinto segurou o motorista", ejected: "Batida a {0} km/h sem cinto: o motorista saiu pelo {1}", exits: { windshield: "para-brisa", left_window: "vidro esquerdo", right_window: "vidro direito", rear_window: "vidro traseiro" } },
|
|
49753
|
+
bodyKeys: "1 a 4 portas \xB7 5 cap\xF4 \xB7 6 porta-malas \xB7 Shift+1 a 4 vidros \xB7 Shift+5 teto solar \xB7 V trava as portas",
|
|
49754
|
+
remote: { locked: "Travado pelo controle: alarme armado", unlocked: "Destravado pelo controle", forced: "Porta for\xE7ada" },
|
|
49755
|
+
done: { puncture: "Pneu dianteiro esquerdo furado", refuel: "Tanque cheio", drain_battery: "Bateria quase sem carga", jump_start: "Bateria carregada", break_radiator: "Radiador furado: o motor vai esquentar", empty_tank: "Tanque na reserva" }
|
|
49756
|
+
},
|
|
49757
|
+
en: {
|
|
49758
|
+
keys: "C clutch (manual gearbox) \xB7 X flash the high beam \xB7 Z washer \xB7 7 eco/normal/sport \xB7 8 traction control \xB7 9 ABS \xB7 0 cruise control \xB7 - air conditioning \xB7 = demister \xB7 ] next radio or tape track",
|
|
49759
|
+
profile: "Drive profile",
|
|
49760
|
+
profiles: { eco: "eco", normal: "normal", sport: "sport" },
|
|
49761
|
+
traction: "Traction control",
|
|
49762
|
+
abs: "ABS",
|
|
49763
|
+
cruise: "Cruise control",
|
|
49764
|
+
cruiseSet: "Cruise control: holding {0} km/h",
|
|
49765
|
+
climate: "Air conditioning",
|
|
49766
|
+
demist: "Demister",
|
|
49767
|
+
wiperModes: { off: "off", intermittent: "intermittent", slow: "slow", fast: "fast" },
|
|
49768
|
+
refused: { battery_flat: "Flat battery: the starter does not turn", too_slow: "Cruise control only above 30 km/h", locked: "The door is locked", missing: "This vehicle has no such part" },
|
|
49769
|
+
events: {
|
|
49770
|
+
stall: "The engine stalled",
|
|
49771
|
+
out_of_fuel: "Out of fuel",
|
|
49772
|
+
bogged: "Bogged down",
|
|
49773
|
+
aquaplaning: "Aquaplaning",
|
|
49774
|
+
battery_flat: "The battery went flat",
|
|
49775
|
+
overheating: "Engine overheating: losing power",
|
|
49776
|
+
seized: "The engine seized from the heat",
|
|
49777
|
+
spare_fitted: "Spare tyre fitted",
|
|
49778
|
+
no_spare: "No spare tyre left",
|
|
49779
|
+
cruise_cancelled: "Cruise control off"
|
|
49780
|
+
},
|
|
49781
|
+
actions: { puncture: "Puncture a tyre (front left)", fit_spare: "Fit the spare", refuel: "Refuel", drain_battery: "Drain the battery", jump_start: "Jump start (charge the battery)", break_radiator: "Puncture the radiator", empty_tank: "Leave the tank on reserve", remote_lock: "Remote: lock (arms the alarm)", remote_unlock: "Remote: unlock", force_door: "Force the driver's door", crash_wall: "Hit a wall (dead stop)", park_behind: "Park a car behind (rear sensor)" },
|
|
49782
|
+
parking: { placed: "A car parked 3 m behind: reverse and listen to the sensor", none: "No vehicle catalogue in this room: the sensor has nothing to see" },
|
|
49783
|
+
track: "track {0}",
|
|
49784
|
+
adjust: { seat_forward: "Seat: forward/back (m)", seat_height: "Seat: height (m)", mirror_left: "Left mirror: angle", mirror_right: "Right mirror: angle" },
|
|
49785
|
+
crash: { mild: "Crash at {0} km/h: too mild to throw an unbelted occupant out", stayed: "Crash at {0} km/h: the belt held the driver", ejected: "Crash at {0} km/h without a belt: the driver went out through the {1}", exits: { windshield: "windshield", left_window: "left window", right_window: "right window", rear_window: "rear window" } },
|
|
49786
|
+
bodyKeys: "1 to 4 doors \xB7 5 hood \xB7 6 trunk \xB7 Shift+1 to 4 windows \xB7 Shift+5 sunroof \xB7 V locks the doors",
|
|
49787
|
+
remote: { locked: "Locked by the remote: alarm armed", unlocked: "Unlocked by the remote", forced: "Door forced" },
|
|
49788
|
+
done: { puncture: "Front left tyre punctured", refuel: "Tank full", drain_battery: "Battery almost flat", jump_start: "Battery charged", break_radiator: "Radiator punctured: the engine will heat up", empty_tank: "Tank on reserve" }
|
|
49789
|
+
}
|
|
49790
|
+
};
|
|
49791
|
+
|
|
48849
49792
|
// src/test-area/proving/text.ts
|
|
48850
49793
|
var PROVING_TEXT = {
|
|
48851
49794
|
"pt-BR": {
|
|
@@ -48990,9 +49933,11 @@ var GameVehiclesThree = (() => {
|
|
|
48990
49933
|
right: { x: 0.79, y: 0.72, width: 0.19, height: 0.13, rounded: 0.3 },
|
|
48991
49934
|
reverse: { x: 0.36, y: 0.13, width: 0.28, height: 0.18, rounded: 0.06 }
|
|
48992
49935
|
};
|
|
49936
|
+
var ADJUST_KEYS = { seat_forward: "seatForward", seat_height: "seatHeight", mirror_left: "mirrorLeft", mirror_right: "mirrorRight" };
|
|
48993
49937
|
function createProvingModule(options) {
|
|
48994
49938
|
const locale = options.locale ?? "pt-BR";
|
|
48995
49939
|
const text = PROVING_TEXT[locale] ?? PROVING_TEXT["pt-BR"];
|
|
49940
|
+
const sysText = SYSTEMS_TEXT[locale] ?? SYSTEMS_TEXT["pt-BR"];
|
|
48996
49941
|
const tests = options.tests ?? createProvingTests();
|
|
48997
49942
|
const vehicles = options.vehicles;
|
|
48998
49943
|
const sounds = { ...defaultSoundIds, ...options.sounds };
|
|
@@ -49009,6 +49954,15 @@ var GameVehiclesThree = (() => {
|
|
|
49009
49954
|
let run = null;
|
|
49010
49955
|
let parking = null;
|
|
49011
49956
|
let driving = null;
|
|
49957
|
+
let crashes = null;
|
|
49958
|
+
let wheels = null;
|
|
49959
|
+
let lastAt = null;
|
|
49960
|
+
let parked = null;
|
|
49961
|
+
const adjust = { seatForward: 0, seatHeight: 0, mirrorLeft: 0, mirrorRight: 0 };
|
|
49962
|
+
const aimed = (pose2) => {
|
|
49963
|
+
const turn = pose2.id === "left" ? adjust.mirrorLeft : pose2.id === "right" ? -adjust.mirrorRight : 0;
|
|
49964
|
+
return turn ? { ...pose2, look: [pose2.look[0] + turn, pose2.look[1], pose2.look[2]] } : pose2;
|
|
49965
|
+
};
|
|
49012
49966
|
let gearbox = "automatic";
|
|
49013
49967
|
let lastGear = null;
|
|
49014
49968
|
let driveClock = 0;
|
|
@@ -49170,6 +50124,8 @@ var GameVehiclesThree = (() => {
|
|
|
49170
50124
|
};
|
|
49171
50125
|
const removeCar = () => {
|
|
49172
50126
|
if (!car) return;
|
|
50127
|
+
wheels = null;
|
|
50128
|
+
lastAt = null;
|
|
49173
50129
|
discardObject3D(car.built.group);
|
|
49174
50130
|
car = null;
|
|
49175
50131
|
};
|
|
@@ -49183,6 +50139,7 @@ var GameVehiclesThree = (() => {
|
|
|
49183
50139
|
built2.group.add(guides);
|
|
49184
50140
|
host.scene.add(built2.group);
|
|
49185
50141
|
car = { built: built2, guides, subject };
|
|
50142
|
+
wheels = createWheelRoll(three, built2);
|
|
49186
50143
|
pose({ x: 0, z: TRACK.start, yaw: 0, pitch: 0, roll: 0, height: 0 }, 0);
|
|
49187
50144
|
rig.vehicle?.reset();
|
|
49188
50145
|
drawBoard();
|
|
@@ -49233,11 +50190,18 @@ var GameVehiclesThree = (() => {
|
|
|
49233
50190
|
car.built.group.position.set(TRACK.x0 + wrapped, 0, TRACK.z + s.z);
|
|
49234
50191
|
car.built.group.rotation.set(-s.pitch, s.yaw + Math.PI / 2, s.roll, "YXZ");
|
|
49235
50192
|
bendGuides(steer);
|
|
50193
|
+
if (lastAt) {
|
|
50194
|
+
const along = (s.x - lastAt.x) * Math.cos(s.yaw) - (s.z - lastAt.z) * Math.sin(s.yaw);
|
|
50195
|
+
if (Math.abs(along) < 5) wheels?.roll(along, steer);
|
|
50196
|
+
}
|
|
50197
|
+
lastAt = { x: s.x, z: s.z };
|
|
49236
50198
|
};
|
|
49237
50199
|
const frameOf = (speed) => {
|
|
49238
50200
|
if (!car) return null;
|
|
49239
50201
|
const g = car.built.group;
|
|
49240
|
-
|
|
50202
|
+
const size = { length: car.built.length, width: car.built.width, height: car.built.height };
|
|
50203
|
+
const [ex, ey, ez] = car.built.driverEye ?? defaultEye(size);
|
|
50204
|
+
return { x: g.position.x, y: g.position.y, z: g.position.z, yaw: g.rotation.y, pitch: -g.rotation.x, roll: g.rotation.z, speed, ...size, eye: [ex, ey + adjust.seatHeight, ez + adjust.seatForward] };
|
|
49241
50205
|
};
|
|
49242
50206
|
const applyCamera = () => {
|
|
49243
50207
|
if (!host?.setViewCamera) return;
|
|
@@ -49326,19 +50290,26 @@ var GameVehiclesThree = (() => {
|
|
|
49326
50290
|
built: car.built,
|
|
49327
50291
|
subject: car.subject,
|
|
49328
50292
|
place: [0, 0, 0],
|
|
50293
|
+
kit: kitFor(three),
|
|
50294
|
+
obstacles: () => parked ? [[parked.root.position.x, parked.root.position.z]] : [],
|
|
49329
50295
|
gauge: kitFor(three).materials?.gauge,
|
|
49330
50296
|
needle: kitFor(three).materials?.amber,
|
|
49331
50297
|
// the engine's own sound turns the key (starter, catch, idle by RPM)
|
|
49332
50298
|
sounds: { starter: "" },
|
|
49333
50299
|
onExit: () => stopDriving(),
|
|
49334
50300
|
onEvent: (event) => {
|
|
49335
|
-
if (event.type === "refused") return host?.log(text.cabin.refused[event.reason] ?? event.reason);
|
|
50301
|
+
if (event.type === "refused") return host?.log(text.cabin.refused[event.reason] ?? sysText.refused[event.reason] ?? event.reason);
|
|
50302
|
+
if (event.type === "sound") return;
|
|
50303
|
+
if (event.type !== "command") {
|
|
50304
|
+
if (event.type === "out_of_fuel" || event.type === "stall" || event.type === "seized") engine?.stop([car?.built.group.position.x ?? 0, 0.5, car?.built.group.position.z ?? 0]);
|
|
50305
|
+
return host?.log(sysText.events[event.type] ?? event.type, { vehicle: settings.vehicle, ...event }, "proving_system");
|
|
50306
|
+
}
|
|
49336
50307
|
const st = event.state;
|
|
49337
50308
|
const line = cabinLine(event.command, st);
|
|
49338
50309
|
if (line) host?.log(line);
|
|
49339
50310
|
if (event.command === "ignition" && car) {
|
|
49340
50311
|
const at = [car.built.group.position.x, 0.5, car.built.group.position.z];
|
|
49341
|
-
if (st.ignition) engine?.start(at);
|
|
50312
|
+
if (st.ignition && driving?.systems.canCrank() !== false) engine?.start(at);
|
|
49342
50313
|
else engine?.stop(at);
|
|
49343
50314
|
}
|
|
49344
50315
|
if (event.command === "gearbox") {
|
|
@@ -49357,7 +50328,7 @@ var GameVehiclesThree = (() => {
|
|
|
49357
50328
|
rig.vehicle?.reset();
|
|
49358
50329
|
stopEngine();
|
|
49359
50330
|
engine = createVehicleSound(host.audio, { sounds });
|
|
49360
|
-
host.bench?.sheet?.({ kicker: text.name, title: fillText(text.log.drive, name(settings.vehicle)), body: text.cabin.keys });
|
|
50331
|
+
host.bench?.sheet?.({ kicker: text.name, title: fillText(text.log.drive, name(settings.vehicle)), body: `${text.cabin.keys} \xB7 ${sysText.keys} \xB7 ${sysText.bodyKeys}` });
|
|
49361
50332
|
host.log(fillText(text.log.drive, name(settings.vehicle)));
|
|
49362
50333
|
};
|
|
49363
50334
|
const onOff = (on) => on ? text.cabin.on : text.cabin.off;
|
|
@@ -49386,19 +50357,109 @@ var GameVehiclesThree = (() => {
|
|
|
49386
50357
|
return `${c.alarm}: ${c.alarmStates[st.alarm]}`;
|
|
49387
50358
|
case "radio":
|
|
49388
50359
|
case "radio_source":
|
|
49389
|
-
|
|
50360
|
+
case "radio_track":
|
|
50361
|
+
return `${c.radio}: ${st.radio.on ? `${c.sources[st.radio.source]} \xB7 ${fillText(sysText.track, st.radio.track + 1)}` : c.off}`;
|
|
49390
50362
|
case "indicator_left":
|
|
49391
50363
|
case "indicator_right":
|
|
49392
50364
|
case "hazard":
|
|
49393
50365
|
return `${c.indicator}: ${c.indicators[st.indicator]}`;
|
|
49394
50366
|
case "wipers":
|
|
49395
|
-
return `${c.wipers}: ${
|
|
50367
|
+
return `${c.wipers}: ${sysText.wiperModes[st.wipers]}`;
|
|
50368
|
+
case "drive_profile":
|
|
50369
|
+
return `${sysText.profile}: ${sysText.profiles[st.profile]}`;
|
|
50370
|
+
case "traction_control":
|
|
50371
|
+
return `${sysText.traction}: ${onOff(st.tractionControl)}`;
|
|
50372
|
+
case "abs":
|
|
50373
|
+
return `${sysText.abs}: ${onOff(st.abs)}`;
|
|
50374
|
+
case "cruise":
|
|
50375
|
+
return st.cruise.on ? fillText(sysText.cruiseSet, Math.round(st.cruise.speed * 3.6)) : `${sysText.cruise}: ${c.off}`;
|
|
50376
|
+
case "climate":
|
|
50377
|
+
return `${sysText.climate}: ${onOff(st.climate.on)}`;
|
|
50378
|
+
case "demist":
|
|
50379
|
+
return `${sysText.demist}: ${onOff(st.climate.demist)}`;
|
|
49396
50380
|
case "seatbelt":
|
|
49397
50381
|
return `${c.seatbelt}: ${onOff(st.seatbelt)}`;
|
|
49398
50382
|
default:
|
|
49399
50383
|
return null;
|
|
49400
50384
|
}
|
|
49401
50385
|
};
|
|
50386
|
+
const systemsAction = (id) => {
|
|
50387
|
+
if (!driving) startDriving();
|
|
50388
|
+
const cockpit = driving;
|
|
50389
|
+
if (!cockpit) return;
|
|
50390
|
+
const d = cockpit.driver;
|
|
50391
|
+
const done = (key) => host?.log(sysText.done[key], { vehicle: settings.vehicle, action: key }, "proving_system");
|
|
50392
|
+
switch (id) {
|
|
50393
|
+
case "puncture": {
|
|
50394
|
+
const tyres = d.spec.wheels.map((_, i) => d.condition.tyres?.[i] ?? 1);
|
|
50395
|
+
tyres[0] = 0;
|
|
50396
|
+
d.condition = { ...d.condition, tyres };
|
|
50397
|
+
return done("puncture");
|
|
50398
|
+
}
|
|
50399
|
+
case "fit_spare": {
|
|
50400
|
+
const flat = d.spec.wheels.findIndex((_, i) => (d.condition.tyres?.[i] ?? 1) < 0.2);
|
|
50401
|
+
const event = cockpit.systems.fitSpare(d, Math.max(0, flat));
|
|
50402
|
+
host?.log(sysText.events[event.type], { vehicle: settings.vehicle, ...event }, "proving_system");
|
|
50403
|
+
return;
|
|
50404
|
+
}
|
|
50405
|
+
case "refuel":
|
|
50406
|
+
d.refuel(d.spec.tank.capacity);
|
|
50407
|
+
return done("refuel");
|
|
50408
|
+
case "empty_tank":
|
|
50409
|
+
d.state.fuel = Math.min(d.state.fuel, d.spec.tank.capacity * 0.04);
|
|
50410
|
+
return done("empty_tank");
|
|
50411
|
+
case "drain_battery":
|
|
50412
|
+
cockpit.systems.charge(-(cockpit.systems.state.charge - 0.15));
|
|
50413
|
+
return done("drain_battery");
|
|
50414
|
+
case "jump_start":
|
|
50415
|
+
cockpit.systems.charge(1);
|
|
50416
|
+
return done("jump_start");
|
|
50417
|
+
case "break_radiator":
|
|
50418
|
+
cockpit.radiator = 0.05;
|
|
50419
|
+
return done("break_radiator");
|
|
50420
|
+
case "remote_lock":
|
|
50421
|
+
case "remote_unlock": {
|
|
50422
|
+
const lock = id === "remote_lock";
|
|
50423
|
+
cockpit.body?.lock(lock);
|
|
50424
|
+
cockpit.controls.state.alarm = lock ? "armed" : "off";
|
|
50425
|
+
host?.audio?.play?.({ sound: "alarm_chirp", position: [car?.built.group.position.x ?? 0, 1, car?.built.group.position.z ?? 0] });
|
|
50426
|
+
host?.log(sysText.remote[lock ? "locked" : "unlocked"], { vehicle: settings.vehicle, locked: lock }, "proving_system");
|
|
50427
|
+
return;
|
|
50428
|
+
}
|
|
50429
|
+
case "crash_wall": {
|
|
50430
|
+
const g = car?.built.group;
|
|
50431
|
+
if (!g || !host) return;
|
|
50432
|
+
crashes ?? (crashes = createProvingCrash(host, options.occupant));
|
|
50433
|
+
const speed = d.state.u;
|
|
50434
|
+
const ejection = crashes.crash({ car: { position: g.position, rotationY: g.rotation.y }, u: d.state.u, w: d.state.w, belted: cockpit.controls.state.seatbelt, destructible: cockpit.destructible });
|
|
50435
|
+
d.state.u = d.state.w = d.state.yawRate = 0;
|
|
50436
|
+
const kmh2 = Math.round(Math.abs(speed) * 3.6);
|
|
50437
|
+
const belted = cockpit.controls.state.seatbelt;
|
|
50438
|
+
host.log(ejection ? fillText(sysText.crash.ejected, kmh2, sysText.crash.exits[ejection.exit]) : fillText(belted ? sysText.crash.stayed : sysText.crash.mild, kmh2), { vehicle: settings.vehicle, kmh: kmh2, belted: cockpit.controls.state.seatbelt, ejection }, "proving_crash");
|
|
50439
|
+
return;
|
|
50440
|
+
}
|
|
50441
|
+
case "park_behind": {
|
|
50442
|
+
const g = car?.built.group;
|
|
50443
|
+
if (!g || !host) return;
|
|
50444
|
+
parked?.remove();
|
|
50445
|
+
const back = (car?.built.length ?? 4.5) / 2 + 3 + 2.3;
|
|
50446
|
+
const at = [g.position.x - Math.sin(g.rotation.y) * back, 0, g.position.z - Math.cos(g.rotation.y) * back];
|
|
50447
|
+
const placed = host.addObject?.(options.parkedCar ?? "sedan", { position: at, rotationY: g.rotation.y });
|
|
50448
|
+
parked = placed ? { root: placed.root, remove: placed.remove } : null;
|
|
50449
|
+
host.log(placed ? sysText.parking.placed : sysText.parking.none, { vehicle: settings.vehicle }, "proving_system");
|
|
50450
|
+
return;
|
|
50451
|
+
}
|
|
50452
|
+
case "force_door": {
|
|
50453
|
+
const body = cockpit.body;
|
|
50454
|
+
if (!body) return;
|
|
50455
|
+
if (body.locked) body.lock(false);
|
|
50456
|
+
body.toggle("door_front_left");
|
|
50457
|
+
if (cockpit.controls.state.alarm === "armed") cockpit.controls.state.alarm = "ringing";
|
|
50458
|
+
host?.log(sysText.remote.forced, { vehicle: settings.vehicle, alarm: cockpit.controls.state.alarm }, "proving_system");
|
|
50459
|
+
return;
|
|
50460
|
+
}
|
|
50461
|
+
}
|
|
50462
|
+
};
|
|
49402
50463
|
const gearName = (gear) => gear < 0 ? "R" : gear === 0 ? "N" : String(gear);
|
|
49403
50464
|
const onChange = (change) => {
|
|
49404
50465
|
if (change.kind === "layer") {
|
|
@@ -49407,6 +50468,7 @@ var GameVehiclesThree = (() => {
|
|
|
49407
50468
|
}
|
|
49408
50469
|
if (change.kind === "action") {
|
|
49409
50470
|
if (change.id === "drive") return driving ? stopDriving() : startDriving();
|
|
50471
|
+
if (change.id in sysText.actions) return systemsAction(change.id);
|
|
49410
50472
|
if (change.id === "run_test" || change.id === "run_all" || change.id === "reverse_park") stopDriving();
|
|
49411
50473
|
if (change.id === "run_test") {
|
|
49412
50474
|
queue = [];
|
|
@@ -49453,6 +50515,12 @@ var GameVehiclesThree = (() => {
|
|
|
49453
50515
|
settings.camera = String(change.value);
|
|
49454
50516
|
applyCamera();
|
|
49455
50517
|
break;
|
|
50518
|
+
case "seat_forward":
|
|
50519
|
+
case "seat_height":
|
|
50520
|
+
case "mirror_left":
|
|
50521
|
+
case "mirror_right":
|
|
50522
|
+
adjust[ADJUST_KEYS[change.id]] = Number(change.value);
|
|
50523
|
+
break;
|
|
49456
50524
|
case "time_scale":
|
|
49457
50525
|
settings.timeScale = Number(change.value);
|
|
49458
50526
|
break;
|
|
@@ -49502,7 +50570,7 @@ var GameVehiclesThree = (() => {
|
|
|
49502
50570
|
if (car) engine?.update({ dt, position: [car.built.group.position.x, 0.5, car.built.group.position.z], speed, throttle: run ? 1 : driving?.throttle ?? 0, rpm: driving ? driving.driver.state.rpm : void 0 });
|
|
49503
50571
|
const f = frameOf(speed);
|
|
49504
50572
|
if (f && rig.vehicle && settings.camera !== "room") rig.vehicle.update(rig.camera, f, dt);
|
|
49505
|
-
if (f && rig.vehicle) for (const m of rig.mirrors) rig.vehicle.placeMirror(m.camera, m.pose, f);
|
|
50573
|
+
if (f && rig.vehicle) for (const m of rig.mirrors) rig.vehicle.placeMirror(m.camera, aimed(m.pose), f);
|
|
49506
50574
|
void steer;
|
|
49507
50575
|
const key = `${host.bench?.layer("mirrors")}|${host.bench?.layer("reverse_camera")}`;
|
|
49508
50576
|
if (key !== mirrorKey) {
|
|
@@ -49521,6 +50589,10 @@ var GameVehiclesThree = (() => {
|
|
|
49521
50589
|
const speedNow = () => Math.abs(run?.driver.state.u ?? driving?.driver.state.u ?? parking?.driver.state.u ?? 0) * 3.6;
|
|
49522
50590
|
const counts = (verdict) => certificateOf(settings.vehicle)?.results.filter((r) => r.verdict === verdict).length ?? 0;
|
|
49523
50591
|
const teardown = () => {
|
|
50592
|
+
crashes?.dispose();
|
|
50593
|
+
crashes = null;
|
|
50594
|
+
parked?.remove();
|
|
50595
|
+
parked = null;
|
|
49524
50596
|
stopDriving();
|
|
49525
50597
|
stopEngine();
|
|
49526
50598
|
for (const m of rig.mirrors) m.remove();
|
|
@@ -49551,9 +50623,14 @@ var GameVehiclesThree = (() => {
|
|
|
49551
50623
|
{ kind: "choice", id: "vehicle", name: text.controls.vehicle, hint: text.hints.vehicle, options: vehicles.map((id) => ({ id, name: name(id) })), default: settings.vehicle },
|
|
49552
50624
|
{ kind: "choice", id: "test", name: text.controls.test, hint: text.hints.test, options: measured.map((id) => ({ id, name: text.tests[id] ?? id })), default: settings.test },
|
|
49553
50625
|
{ kind: "choice", id: "camera", name: text.controls.camera, hint: text.hints.camera, options: ["room", "chase", "cockpit", "hood"].map((id) => ({ id, name: text.cameras[id] })), default: settings.camera },
|
|
50626
|
+
{ kind: "number", id: "seat_forward", name: sysText.adjust.seat_forward, min: -0.2, max: 0.2, step: 0.02, default: 0 },
|
|
50627
|
+
{ kind: "number", id: "seat_height", name: sysText.adjust.seat_height, min: -0.1, max: 0.1, step: 0.01, default: 0 },
|
|
50628
|
+
{ kind: "number", id: "mirror_left", name: sysText.adjust.mirror_left, min: -0.3, max: 0.3, step: 0.02, default: 0 },
|
|
50629
|
+
{ kind: "number", id: "mirror_right", name: sysText.adjust.mirror_right, min: -0.3, max: 0.3, step: 0.02, default: 0 },
|
|
49554
50630
|
{ kind: "number", id: "time_scale", name: text.controls.time_scale, hint: text.hints.time_scale, min: 1, max: 20, step: 1, default: settings.timeScale },
|
|
49555
50631
|
{ kind: "choice", id: "visual_check", name: text.controls.visual_check, hint: text.hints.visual_check, options: VISUAL_CHECKS.map((id) => ({ id, name: text.tests[id] ?? id })), default: settings.visual },
|
|
49556
50632
|
{ kind: "action", id: "drive", name: text.actions.drive },
|
|
50633
|
+
...Object.keys(sysText.actions).map((id) => ({ kind: "action", id, name: sysText.actions[id] })),
|
|
49557
50634
|
{ kind: "choice", id: "gearbox", name: text.controls.gearbox, hint: text.hints.gearbox, options: ["automatic", "manual"].map((id) => ({ id, name: text.gearbox[id] })), default: gearbox },
|
|
49558
50635
|
{ kind: "action", id: "run_test", name: text.actions.run_test },
|
|
49559
50636
|
{ kind: "action", id: "run_all", name: text.actions.run_all },
|
|
@@ -49587,6 +50664,8 @@ var GameVehiclesThree = (() => {
|
|
|
49587
50664
|
rig.vehicle = createVehicleCamera(three);
|
|
49588
50665
|
lent.audio?.register?.(LIBRARY_BANK);
|
|
49589
50666
|
lent.audio?.register?.(CABIN_BANK);
|
|
50667
|
+
lent.audio?.register?.(VEHICLE_SOUND_BANK);
|
|
50668
|
+
lent.audio?.register?.(RADIO_BANK);
|
|
49590
50669
|
const stopChange = lent.bench?.onChange(onChange);
|
|
49591
50670
|
const stopFrame = lent.onFrame(frame);
|
|
49592
50671
|
const stopClear = lent.onClear?.(() => {
|