@adatechnology/game-vehicles 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -445,10 +445,11 @@ function merge(base, extra) {
445
445
  return out;
446
446
  }
447
447
  function assignDeep(target, source) {
448
+ const t = target;
448
449
  Object.keys(source || {}).forEach(function(k) {
449
450
  const v = source[k];
450
- if (v && typeof v === "object" && !Array.isArray(v)) target[k] = assignDeep(target[k] && typeof target[k] === "object" ? target[k] : {}, v);
451
- else if (v !== void 0) target[k] = v;
451
+ if (v && typeof v === "object" && !Array.isArray(v)) t[k] = assignDeep(t[k] && typeof t[k] === "object" ? t[k] : {}, v);
452
+ else if (v !== void 0) t[k] = v;
452
453
  });
453
454
  return target;
454
455
  }
@@ -831,7 +832,9 @@ function installMaterials(kit) {
831
832
  return t;
832
833
  }
833
834
  kit.textTexture = textTexture;
834
- Object.assign(I, { RUST, cache: cache2, studioEnvironment, makeMaterial, makeFlatMaterial, DOUBLE_SIDE, toColor, MATERIALS, paintColor, texCache, textTexture });
835
+ const shared = { RUST, cache: cache2, studioEnvironment, makeMaterial, makeFlatMaterial, DOUBLE_SIDE, toColor, MATERIALS, paintColor, texCache, textTexture };
836
+ Object.assign(I, shared);
837
+ return shared;
835
838
  }
836
839
 
837
840
  // src/tri-index.ts
@@ -986,7 +989,8 @@ function scratchOf(THREE) {
986
989
  }
987
990
  return s;
988
991
  }
989
- function indexable(THREE, mesh) {
992
+ function indexable(THREE, object) {
993
+ const mesh = object;
990
994
  const geo = mesh.geometry;
991
995
  if (mesh.raycast !== THREE.Mesh.prototype.raycast) return false;
992
996
  if (!mesh.isMesh || mesh.isSkinnedMesh || mesh.isInstancedMesh || mesh.isBatchedMesh || !geo || !geo.isBufferGeometry) return false;
@@ -1585,21 +1589,23 @@ function installGeometry(kit) {
1585
1589
  let wb = 0;
1586
1590
  segs.forEach(function(seg) {
1587
1591
  if (typeof seg[0] === "number") {
1592
+ const run = seg;
1588
1593
  for (let a = 0; a < names.length; a++) {
1589
1594
  const size = sizes[a];
1590
1595
  if (!idx) {
1591
- dst[a].set(arrs[a].subarray(seg[0] * 3 * size, seg[1] * 3 * size), w[a]);
1592
- w[a] += (seg[1] - seg[0]) * 3 * size;
1596
+ dst[a].set(arrs[a].subarray(run[0] * 3 * size, run[1] * 3 * size), w[a]);
1597
+ w[a] += (run[1] - run[0]) * 3 * size;
1593
1598
  continue;
1594
1599
  }
1595
- for (let f = seg[0] * 3; f < seg[1] * 3; f++) for (let q = 0; q < size; q++) dst[a][w[a]++] = arrs[a][idx[f] * size + q];
1600
+ for (let f = run[0] * 3; f < run[1] * 3; f++) for (let q = 0; q < size; q++) dst[a][w[a]++] = arrs[a][idx[f] * size + q];
1596
1601
  }
1597
1602
  if (boxesOut2 && ti) {
1598
- boxesOut2.set(ti.boxes.subarray(seg[0] * 6, seg[1] * 6), wb);
1599
- wb += (seg[1] - seg[0]) * 6;
1603
+ boxesOut2.set(ti.boxes.subarray(run[0] * 6, run[1] * 6), wb);
1604
+ wb += (run[1] - run[0]) * 6;
1600
1605
  }
1601
1606
  return;
1602
1607
  }
1608
+ ;
1603
1609
  seg.forEach(function(t) {
1604
1610
  if (boxesOut2) {
1605
1611
  for (let k = 0; k < 3; k++) {
@@ -1793,7 +1799,9 @@ function installGeometry(kit) {
1793
1799
  }
1794
1800
  };
1795
1801
  };
1796
- Object.assign(I, { roundedBoxGeometry, taper, subdivide, crease, creaseTask, refreshBounds, pathShape, tumble, loftGeometry, hullGeometry, cutGeometry, mergeGeometries, uvProject });
1802
+ const shared = { roundedBoxGeometry, taper, subdivide, crease, creaseTask, refreshBounds, pathShape, tumble, loftGeometry, hullGeometry, cutGeometry, mergeGeometries, uvProject };
1803
+ Object.assign(I, shared);
1804
+ return shared;
1797
1805
  }
1798
1806
 
1799
1807
  // src/context.ts
@@ -2048,6 +2056,7 @@ function installContext(kit) {
2048
2056
  }
2049
2057
  kit.BuildContext = BuildContext;
2050
2058
  I.BuildContext = BuildContext;
2059
+ return { resolveLabel, BuildContext };
2051
2060
  }
2052
2061
 
2053
2062
  // src/warp.ts
@@ -2243,7 +2252,9 @@ function installWarp(kit) {
2243
2252
  kit.applyWarp = applyWarp;
2244
2253
  kit.geo.resample = resample;
2245
2254
  kit.geo.smoothLoft = smoothLoft;
2246
- Object.assign(I, { makeWarp, applyWarp, resample, smoothLoft });
2255
+ const shared = { makeWarp, applyWarp, resample, smoothLoft };
2256
+ Object.assign(I, shared);
2257
+ return shared;
2247
2258
  }
2248
2259
 
2249
2260
  // src/body.ts
@@ -2309,23 +2320,24 @@ function installBody(kit) {
2309
2320
  o.arches.forEach(function(a) {
2310
2321
  const a0 = Math.asin(clamp((o.bottomY - a[1]) / a[2], -1, 1));
2311
2322
  const rr = a[2] - 0.01;
2323
+ const archInner = o.archInner;
2312
2324
  if (o.archInner == null) {
2313
2325
  const wl = c.add(new THREE.CylinderGeometry(rr, rr, o.W - 0.12, 20, 1, true, a0 + PI / 2, PI - 2 * a0), MATERIALS.dark, a[0], a[1], 0);
2314
2326
  wl.rotation.x = PI / 2;
2315
2327
  wl.castShadow = false;
2316
2328
  } else
2317
2329
  [1, -1].forEach(function(s) {
2318
- const len = o.W / 2 - 0.06 - o.archInner;
2330
+ const len = o.W / 2 - 0.06 - archInner;
2319
2331
  const w2 = c.add(
2320
2332
  new THREE.CylinderGeometry(rr, rr, len, 20, 1, true, a0 + PI / 2, PI - 2 * a0),
2321
2333
  MATERIALS.dark,
2322
2334
  a[0],
2323
2335
  a[1],
2324
- s * (o.archInner + len / 2)
2336
+ s * (archInner + len / 2)
2325
2337
  );
2326
2338
  w2.rotation.x = PI / 2;
2327
2339
  w2.castShadow = false;
2328
- const cap = c.add(new THREE.CircleGeometry(rr, 20, a0, PI - 2 * a0), MATERIALS.dark, a[0], a[1], s * o.archInner);
2340
+ const cap = c.add(new THREE.CircleGeometry(rr, 20, a0, PI - 2 * a0), MATERIALS.dark, a[0], a[1], s * archInner);
2329
2341
  cap.castShadow = false;
2330
2342
  });
2331
2343
  });
@@ -2402,8 +2414,12 @@ function installBody(kit) {
2402
2414
  if (!q.isMesh || !q.visible || q.userData.glass || q.userData.decal || q.isSprite) return;
2403
2415
  if (o.all || dset.has(q) || c.paints && c.paints.has(q.material)) cands.push(q);
2404
2416
  });
2405
- const d = (dir.isVector3 ? dir.clone() : new V3(dir[0], dir[1], dir[2])).normalize();
2406
- const p0 = org.isVector3 ? org.clone() : new V3(org[0], org[1], org[2]);
2417
+ const dv = dir;
2418
+ const da = dir;
2419
+ const d = (dv.isVector3 ? dv.clone() : new V3(da[0], da[1], da[2])).normalize();
2420
+ const ov = org;
2421
+ const oa = org;
2422
+ const p0 = ov.isVector3 ? ov.clone() : new V3(oa[0], oa[1], oa[2]);
2407
2423
  _sr.set(p0.clone().applyMatrix4(Mw), d.clone().transformDirection(Mw));
2408
2424
  _sr.near = 0;
2409
2425
  _sr.far = o.max || 20;
@@ -2420,7 +2436,9 @@ function installBody(kit) {
2420
2436
  const h = kit.surfaceAt(c, new V3(x0, y, z || 0), new V3(-sg, 0, 0));
2421
2437
  return h ? h.p.x : items ? profX(items, y, front !== false) : null;
2422
2438
  };
2423
- Object.assign(I, { halfAt, shell, autoWindows, profX, _sr });
2439
+ const shared = { halfAt, shell, autoWindows, profX, _sr };
2440
+ Object.assign(I, shared);
2441
+ return shared;
2424
2442
  }
2425
2443
 
2426
2444
  // src/glass.ts
@@ -2429,7 +2447,8 @@ function installGlass(kit) {
2429
2447
  const V3 = THREE.Vector3;
2430
2448
  const I = kit._;
2431
2449
  const { MATERIALS, crease, cutGeometry, pathShape, subdivide, tumble, uvProject } = I;
2432
- I.paneSeals = /* @__PURE__ */ new WeakMap();
2450
+ const paneSeals = /* @__PURE__ */ new WeakMap();
2451
+ I.paneSeals = paneSeals;
2433
2452
  kit.addGlass = function(c, geo, label, type, o) {
2434
2453
  o = o || {};
2435
2454
  if (!geo.attributes.uv || o.reuv) o.aspect = uvProject(geo, o.uvAxes);
@@ -2526,7 +2545,7 @@ function installGlass(kit) {
2526
2545
  };
2527
2546
  kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
2528
2547
  o = o || {};
2529
- bev = bev == null ? 0.05 : bev;
2548
+ const bv = bev == null ? 0.05 : bev;
2530
2549
  const dx = pt[0] - pb[0];
2531
2550
  const dy = pt[1] - pb[1];
2532
2551
  const L = Math.hypot(dx, dy);
@@ -2535,13 +2554,13 @@ function installGlass(kit) {
2535
2554
  const nx = which === "front" ? uy : -uy;
2536
2555
  const ny = which === "front" ? -ux : ux;
2537
2556
  const ins = o.inset || 0.09;
2538
- const off = bev + 6e-3;
2557
+ const off = bv + 6e-3;
2539
2558
  const sd = o.side || 0.1;
2540
2559
  const b = [pb[0] + ux * ins + nx * off, pb[1] + uy * ins + ny * off];
2541
2560
  const t = [pt[0] - ux * ins * 1.3 + nx * off, pt[1] - uy * ins * 1.3 + ny * off];
2542
2561
  const hb = I.halfAt(Wg, T, b[1]) - sd;
2543
2562
  const ht = I.halfAt(Wg, T, t[1]) - sd;
2544
- return { pb, pt, L, u: [ux, uy], n: [nx, ny], ins, b, t, hb, ht, which, bev, Wg, T };
2563
+ return { pb, pt, L, u: [ux, uy], n: [nx, ny], ins, b, t, hb, ht, which, bev: bv, Wg, T };
2545
2564
  };
2546
2565
  kit.glassQuad = function(c, q, label, o) {
2547
2566
  o = o || {};
@@ -2595,7 +2614,9 @@ function installGlass(kit) {
2595
2614
  c.quads.push(q);
2596
2615
  return m;
2597
2616
  };
2598
- Object.assign(I, {});
2617
+ const shared = { paneSeals };
2618
+ Object.assign(I, shared);
2619
+ return shared;
2599
2620
  }
2600
2621
 
2601
2622
  // src/lights.ts
@@ -2932,7 +2953,9 @@ function installLights(kit) {
2932
2953
  if (o.emblem !== false) kit.emblem(c, 0.05, st === "closed" ? h * 0.12 : 0, 0, Math.min(0.05, h * 0.3), g);
2933
2954
  return g;
2934
2955
  };
2935
- Object.assign(I, { lampGroup, gm, wedgeY, haloTex, glowTexture, haloMats, halo, lampKit });
2956
+ const shared = { lampGroup, gm, wedgeY, haloTex, glowTexture, haloMats, halo, lampKit };
2957
+ Object.assign(I, shared);
2958
+ return shared;
2936
2959
  }
2937
2960
 
2938
2961
  // src/interior.ts
@@ -3184,7 +3207,9 @@ function installInterior(kit) {
3184
3207
  c.roundBox(o.x0 - o.x1 + 1, 0.03, zi * 2, 0.01, MATERIALS.carpet, (o.x0 + o.x1) / 2, o.floor, 0);
3185
3208
  return n;
3186
3209
  };
3187
- Object.assign(I, { consoleBox });
3210
+ const shared = { consoleBox };
3211
+ Object.assign(I, shared);
3212
+ return shared;
3188
3213
  }
3189
3214
 
3190
3215
  // src/hood.ts
@@ -3579,7 +3604,9 @@ function installHood(kit) {
3579
3604
  };
3580
3605
  return out;
3581
3606
  };
3582
- Object.assign(I, {});
3607
+ const shared = {};
3608
+ Object.assign(I, shared);
3609
+ return shared;
3583
3610
  }
3584
3611
 
3585
3612
  // src/car.ts
@@ -3937,7 +3964,9 @@ function installCar(kit) {
3937
3964
  });
3938
3965
  return out;
3939
3966
  };
3940
- Object.assign(I, {});
3967
+ const shared = {};
3968
+ Object.assign(I, shared);
3969
+ return shared;
3941
3970
  }
3942
3971
 
3943
3972
  // src/trucks.ts
@@ -4082,7 +4111,9 @@ function installTrucks(kit) {
4082
4111
  m.rear = rear;
4083
4112
  m.length = L;
4084
4113
  };
4085
- Object.assign(I, {});
4114
+ const shared = {};
4115
+ Object.assign(I, shared);
4116
+ return shared;
4086
4117
  }
4087
4118
 
4088
4119
  // src/wheels.ts
@@ -4221,7 +4252,9 @@ function installWheels(kit) {
4221
4252
  c.part("track", kit.t("parts.track", { s: z > 0 ? 1 : -1 }), tm, new V3((x0 + x1) / 2, 0.3, z), 1.4);
4222
4253
  return { mesh: tm, wheels: gs, sprocket: spr, idler: idl };
4223
4254
  };
4224
- Object.assign(I, {});
4255
+ const shared = {};
4256
+ Object.assign(I, shared);
4257
+ return shared;
4225
4258
  }
4226
4259
 
4227
4260
  // src/particles.ts
@@ -4653,7 +4686,7 @@ function installParticles(kit) {
4653
4686
  }
4654
4687
  });
4655
4688
  }
4656
- Object.assign(I, {
4689
+ const shared = {
4657
4690
  PARTICLE_LIMIT,
4658
4691
  PARTICLE_GEOMETRIES,
4659
4692
  PARTICLE_MATERIALS,
@@ -4673,7 +4706,9 @@ function installParticles(kit) {
4673
4706
  burst,
4674
4707
  exhaustLook,
4675
4708
  tickEmit
4676
- });
4709
+ };
4710
+ Object.assign(I, shared);
4711
+ return shared;
4677
4712
  }
4678
4713
 
4679
4714
  // src/fluids.ts
@@ -5147,7 +5182,9 @@ function installFluids(kit) {
5147
5182
  }
5148
5183
  return n;
5149
5184
  };
5150
- Object.assign(I, { _fr, buildDrip, DOWNHILL, PN, puddleGeometry, rimGeometry, puddle, shapePuddle, updateFluids });
5185
+ const shared = { _fr, buildDrip, DOWNHILL, PN, puddleGeometry, rimGeometry, puddle, shapePuddle, updateFluids };
5186
+ Object.assign(I, shared);
5187
+ return shared;
5151
5188
  }
5152
5189
 
5153
5190
  // src/glass-damage.ts
@@ -5471,8 +5508,20 @@ function installGlassDamage(kit) {
5471
5508
  if (!L || L.broken) return;
5472
5509
  L.broken = true;
5473
5510
  L.lens.forEach(function(m) {
5474
- m.visible = false;
5511
+ const rim = L.lensMaterial ? glassRim(m.geometry, c.rand) : null;
5512
+ if (!rim) {
5513
+ m.visible = false;
5514
+ return;
5515
+ }
5516
+ m.geometry.dispose();
5517
+ m.geometry = rim;
5518
+ m.material = MATERIALS.glassEdge;
5519
+ m.userData.lensRim = true;
5475
5520
  });
5521
+ if (L.lensMaterial)
5522
+ (L.reflector || []).forEach(function(m) {
5523
+ m.material = MATERIALS.broken;
5524
+ });
5476
5525
  L.inner.forEach(function(m) {
5477
5526
  m.material = MATERIALS.bulbBroken;
5478
5527
  });
@@ -5480,11 +5529,40 @@ function installGlassDamage(kit) {
5480
5529
  h.visible = false;
5481
5530
  });
5482
5531
  const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
5483
- I.burst(c, "lens", c.toEffects(p), 10, { dir: c.dirToEffects(dir.clone().negate()), speed: 1.4, variant: L.tint === "red" ? "red" : "clear" });
5484
- I.burst(c, "plastic", c.toEffects(p), 2, { dir: c.dirToEffects(dir.clone().negate()), speed: 1 });
5532
+ const glass = L.lensMaterial !== "plastic";
5533
+ 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" });
5534
+ I.burst(c, "plastic", c.toEffects(p), glass ? 2 : 5, { dir: c.dirToEffects(dir.clone().negate()), speed: 1 });
5485
5535
  }
5486
5536
  kit.breakLight = breakLight;
5487
- Object.assign(I, { glassUV, drawWeb, crackGlass, glassRim, shatterGlass, hitGlass, breakLight });
5537
+ const crazedCache = /* @__PURE__ */ new Map();
5538
+ const CRAZED_OPACITY = 0.6;
5539
+ function crazed(material) {
5540
+ const known = crazedCache.get(material);
5541
+ if (known) return known;
5542
+ const m = I.crackLens(material).clone();
5543
+ m.opacity = Math.max(m.opacity ?? 1, CRAZED_OPACITY);
5544
+ m.transparent = true;
5545
+ m.userData.shared = true;
5546
+ crazedCache.set(material, m);
5547
+ return m;
5548
+ }
5549
+ function crackLight(c, L, dir, pierced) {
5550
+ if (!L || L.broken) return;
5551
+ if (!L.cracked) {
5552
+ L.cracked = true;
5553
+ for (const m of L.lens) if (m.material && !Array.isArray(m.material)) m.material = crazed(m.material);
5554
+ }
5555
+ const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
5556
+ I.burst(c, "lens", c.toEffects(p), 3, { dir: c.dirToEffects(dir.clone().negate()), speed: 0.9, variant: L.tint === "red" ? "red" : "clear" });
5557
+ if (!pierced || L.dark) return;
5558
+ L.dark = true;
5559
+ for (const m of L.inner) m.material = MATERIALS.bulbBroken;
5560
+ for (const h of L.halo ?? []) h.visible = false;
5561
+ }
5562
+ kit.crackLight = crackLight;
5563
+ const shared = { glassUV, drawWeb, crackGlass, glassRim, shatterGlass, hitGlass, breakLight, crackLight };
5564
+ Object.assign(I, shared);
5565
+ return shared;
5488
5566
  }
5489
5567
 
5490
5568
  // src/impact.ts
@@ -5958,7 +6036,7 @@ function installImpact(kit) {
5958
6036
  return hit;
5959
6037
  }
5960
6038
  kit.zoneImpact = zoneImpact;
5961
- Object.assign(I, {
6039
+ const shared = {
5962
6040
  dentField,
5963
6041
  hangBumper,
5964
6042
  tankFuel,
@@ -5974,7 +6052,9 @@ function installImpact(kit) {
5974
6052
  _pr,
5975
6053
  probe,
5976
6054
  zoneImpact
5977
- });
6055
+ };
6056
+ Object.assign(I, shared);
6057
+ return shared;
5978
6058
  }
5979
6059
 
5980
6060
  // src/explosion.ts
@@ -6359,7 +6439,9 @@ function installExplosion(kit) {
6359
6439
  }
6360
6440
  }
6361
6441
  }
6362
- Object.assign(I, { BURNT, BURNT_RUB, boomMat, enginePos, fling, burnAll, bigBoomFx, detonate, tickBoom, tickFlying });
6442
+ const shared = { BURNT, BURNT_RUB, boomMat, enginePos, fling, burnAll, bigBoomFx, detonate, tickBoom, tickFlying };
6443
+ Object.assign(I, shared);
6444
+ return shared;
6363
6445
  }
6364
6446
 
6365
6447
  // src/rollover.ts
@@ -6561,7 +6643,8 @@ function installRollover(kit) {
6561
6643
  c.parts.forEach(function(p) {
6562
6644
  if (p.kind !== "rotor" && p.kind !== "tail_rotor" || !p.obj) return;
6563
6645
  const main = p.kind === "rotor";
6564
- p.obj.traverse(function(o) {
6646
+ p.obj.traverse(function(node) {
6647
+ const o = node;
6565
6648
  if (!o.isMesh || !o.visible) return;
6566
6649
  const bb = new THREE.Box3().setFromObject(o).applyMatrix4(inv);
6567
6650
  if (!main && bb.min.y >= -0.01) return;
@@ -6633,7 +6716,9 @@ function installRollover(kit) {
6633
6716
  if (p >= 1) finishRoll(c);
6634
6717
  }
6635
6718
  kit.rollContact = rollContact;
6636
- Object.assign(I, { rollMeshes, convexHull, rollContact, exactMinY, finishRoll, tickRoll });
6719
+ const shared = { rollMeshes, convexHull, rollContact, exactMinY, finishRoll, tickRoll };
6720
+ Object.assign(I, shared);
6721
+ return shared;
6637
6722
  }
6638
6723
 
6639
6724
  // src/tick.ts
@@ -6671,7 +6756,9 @@ function installTick(kit) {
6671
6756
  c.hood.target = open ? c.hood.open : 0;
6672
6757
  return true;
6673
6758
  };
6674
- Object.assign(I, {});
6759
+ const shared = {};
6760
+ Object.assign(I, shared);
6761
+ return shared;
6675
6762
  }
6676
6763
 
6677
6764
  // src/armor.ts
@@ -6923,7 +7010,9 @@ function installArmor(kit) {
6923
7010
  }
6924
7011
  }
6925
7012
  kit.applyArmor = applyArmor;
6926
- Object.assign(I, { STEEL_MATERIAL, PLATE_MATERIALS, jerryCan, applyArmor });
7013
+ const shared = { STEEL_MATERIAL, PLATE_MATERIALS, jerryCan, applyArmor };
7014
+ Object.assign(I, shared);
7015
+ return shared;
6927
7016
  }
6928
7017
 
6929
7018
  // src/zone-core.ts
@@ -6942,8 +7031,8 @@ function installZoneCore(kit) {
6942
7031
  return { id: b.id, name: kit.t("bands." + b.id), min: b.min, health: b.health, color: b.color };
6943
7032
  });
6944
7033
  function bandOf(h) {
6945
- h = Math.round(clamp(h == null ? 100 : h, 0, 100));
6946
- for (let i = 0; i < BANDS.length; i++) if (h >= BANDS[i].min) return i;
7034
+ const v = Math.round(clamp(h == null ? 100 : h, 0, 100));
7035
+ for (let i = 0; i < BANDS.length; i++) if (v >= BANDS[i].min) return i;
6947
7036
  return 4;
6948
7037
  }
6949
7038
  kit.bands = BANDS;
@@ -6969,8 +7058,12 @@ function installZoneCore(kit) {
6969
7058
  z0: Math.min(b[4], b[5]),
6970
7059
  z1: Math.max(b[4], b[5])
6971
7060
  };
6972
- if (b.isBox3) return { x0: b.min.x, x1: b.max.x, y0: b.min.y, y1: b.max.y, z0: b.min.z, z1: b.max.z };
6973
- return { x0: b.x0, x1: b.x1, y0: b.y0, y1: b.y1, z0: b.z0, z1: b.z1 };
7061
+ if (b.isBox3) {
7062
+ const box = b;
7063
+ return { x0: box.min.x, x1: box.max.x, y0: box.min.y, y1: box.max.y, z0: box.min.z, z1: box.max.z };
7064
+ }
7065
+ const r = b;
7066
+ return { x0: r.x0, x1: r.x1, y0: r.y0, y1: r.y1, z0: r.z0, z1: r.z1 };
6974
7067
  }
6975
7068
  kit.normalizeBox = normalizeBox;
6976
7069
  function boxLo(B, a) {
@@ -7007,7 +7100,8 @@ function installZoneCore(kit) {
7007
7100
  const bb = new THREE.Box3();
7008
7101
  const tmp = new THREE.Box3();
7009
7102
  const M = new THREE.Matrix4();
7010
- o.traverse(function(x) {
7103
+ o.traverse(function(node) {
7104
+ const x = node;
7011
7105
  if (!x.isMesh || !x.geometry || !x.geometry.attributes.position || x.userData.decal) return;
7012
7106
  if (!x.geometry.boundingBox) x.geometry.computeBoundingBox();
7013
7107
  tmp.copy(x.geometry.boundingBox).applyMatrix4(M.multiplyMatrices(inv, x.matrixWorld));
@@ -7073,7 +7167,7 @@ function installZoneCore(kit) {
7073
7167
  for (let q = o; q && q !== c.vehicle; q = q.parent) if (q.visible === false) return false;
7074
7168
  return !!o;
7075
7169
  }
7076
- Object.assign(I, {
7170
+ const shared = {
7077
7171
  BANDS,
7078
7172
  bandOf,
7079
7173
  ZONE_WEIGHTS,
@@ -7096,7 +7190,9 @@ function installZoneCore(kit) {
7096
7190
  lightBoxOf,
7097
7191
  hideObj,
7098
7192
  shown
7099
- });
7193
+ };
7194
+ Object.assign(I, shared);
7195
+ return shared;
7100
7196
  }
7101
7197
 
7102
7198
  // src/zone-decals.ts
@@ -7381,7 +7477,10 @@ function installZoneDecals(kit) {
7381
7477
  narrowTo(rc, q);
7382
7478
  }
7383
7479
  const hits = rc.intersectObjects(cands, false);
7384
- for (let i = 0; i < hits.length; i++) if (hits[i].object.proxyOf) hits[i].object = hits[i].object.proxyOf;
7480
+ for (let i = 0; i < hits.length; i++) {
7481
+ const proxyOf = hits[i].object.proxyOf;
7482
+ if (proxyOf) hits[i].object = proxyOf;
7483
+ }
7385
7484
  return hits;
7386
7485
  }
7387
7486
  function zoneCands(c, z, inv, glassToo) {
@@ -7582,7 +7681,7 @@ function installZoneDecals(kit) {
7582
7681
  for (i = 0; i < (spec.tear || 0); i++) if (s = nx()) tears.push(tearDecal(c, s.p, s.n, 0.07 + R() * 0.06, R, s.obj));
7583
7682
  return S;
7584
7683
  }
7585
- Object.assign(I, {
7684
+ const shared = {
7586
7685
  dmat,
7587
7686
  DAMAGE_MATERIALS,
7588
7687
  _zq,
@@ -7603,7 +7702,9 @@ function installZoneDecals(kit) {
7603
7702
  ribbonGeo,
7604
7703
  scratchDecal,
7605
7704
  zoneDecals
7606
- });
7705
+ };
7706
+ Object.assign(I, shared);
7707
+ return shared;
7607
7708
  }
7608
7709
 
7609
7710
  // src/zone-dent.ts
@@ -7961,7 +8062,9 @@ function installZoneDent(kit) {
7961
8062
  });
7962
8063
  return piv;
7963
8064
  }
7964
- Object.assign(I, { sm, subdivIn, dentTargets, zoneDent, cutPanel, pivotOf });
8065
+ const shared = { sm, subdivIn, dentTargets, zoneDent, cutPanel, pivotOf };
8066
+ Object.assign(I, shared);
8067
+ return shared;
7965
8068
  }
7966
8069
 
7967
8070
  // src/damage-panels.ts
@@ -8314,7 +8417,9 @@ function installDamagePanels(kit) {
8314
8417
  const sgn = new V3().crossVectors(axisV, tangent).dot(outv) > 0 ? 1 : -1;
8315
8418
  piv.quaternion.setFromAxisAngle(axisV, sgn * (0.6 + R() * 0.3));
8316
8419
  }
8317
- Object.assign(I, { panelBand, roofOff, tailOff, followersIn, windowsOver, doorGap, mirrorHang, doorHang, lidHang, flapHang });
8420
+ const shared = { panelBand, roofOff, tailOff, followersIn, windowsOver, doorGap, mirrorHang, doorHang, lidHang, flapHang };
8421
+ Object.assign(I, shared);
8422
+ return shared;
8318
8423
  }
8319
8424
 
8320
8425
  // src/damage-parts.ts
@@ -8430,7 +8535,8 @@ function installDamageParts(kit) {
8430
8535
  }
8431
8536
  function tireMeshes(w) {
8432
8537
  const out = [];
8433
- w.traverse(function(o) {
8538
+ w.traverse(function(node) {
8539
+ const o = node;
8434
8540
  if (o.isMesh && o.material === MATERIALS.rubber) out.push(o);
8435
8541
  });
8436
8542
  return out;
@@ -8489,7 +8595,8 @@ function installDamageParts(kit) {
8489
8595
  c.zoneDrops.push({ x: w.position.x, z: w.position.z, d: r * 0.24 });
8490
8596
  } else if (b === 4) {
8491
8597
  let rr = 0;
8492
- w.traverse(function(o) {
8598
+ w.traverse(function(node) {
8599
+ const o = node;
8493
8600
  if (!o.isMesh || o.material === MATERIALS.rubber) return;
8494
8601
  if (!o.geometry.boundingSphere) o.geometry.computeBoundingSphere();
8495
8602
  const cc = o.geometry.boundingSphere.center.clone().applyMatrix4(o.matrix);
@@ -8697,7 +8804,8 @@ function installDamageParts(kit) {
8697
8804
  const xf = H.x0 + (H.x1 - H.x0) * (0.5 + R() * 0.1);
8698
8805
  const k = 0.55 + R() * 0.2;
8699
8806
  const v = new V3();
8700
- H.piv.children.forEach(function(msh) {
8807
+ H.piv.children.forEach(function(node) {
8808
+ const msh = node;
8701
8809
  if (!msh.isMesh) return;
8702
8810
  msh.updateMatrix();
8703
8811
  const M = msh.matrix;
@@ -8890,7 +8998,7 @@ function installDamageParts(kit) {
8890
8998
  });
8891
8999
  void K;
8892
9000
  }
8893
- Object.assign(I, {
9001
+ const shared = {
8894
9002
  glassPt,
8895
9003
  glassIn,
8896
9004
  glassBand,
@@ -8908,7 +9016,9 @@ function installDamageParts(kit) {
8908
9016
  bumperBand,
8909
9017
  partBand,
8910
9018
  bendAft
8911
- });
9019
+ };
9020
+ Object.assign(I, shared);
9021
+ return shared;
8912
9022
  }
8913
9023
 
8914
9024
  // src/zone-derive.ts
@@ -8931,7 +9041,8 @@ function installZoneDerive(kit) {
8931
9041
  }
8932
9042
  function resolveObjs(c, list, z) {
8933
9043
  const out = [];
8934
- (list || []).forEach(function(o) {
9044
+ (list || []).forEach(function(item) {
9045
+ const o = item;
8935
9046
  if (!o) return;
8936
9047
  if (typeof o === "string") {
8937
9048
  c.parts.forEach(function(p) {
@@ -9180,7 +9291,9 @@ function installZoneDerive(kit) {
9180
9291
  return null;
9181
9292
  }
9182
9293
  kit.zoneOwner = ownerOf;
9183
- Object.assign(I, { parseDir, resolveObjs, deriveZones, carMap, zonesOf, ownerOf });
9294
+ const shared = { parseDir, resolveObjs, deriveZones, carMap, zonesOf, ownerOf };
9295
+ Object.assign(I, shared);
9296
+ return shared;
9184
9297
  }
9185
9298
 
9186
9299
  // src/zone-auto.ts
@@ -9975,7 +10088,9 @@ function installZoneAuto(kit) {
9975
10088
  if (!wheelsDone) wheelZones(false);
9976
10089
  return Z;
9977
10090
  }
9978
- Object.assign(I, { autoZones });
10091
+ const shared = { autoZones };
10092
+ Object.assign(I, shared);
10093
+ return shared;
9979
10094
  }
9980
10095
 
9981
10096
  // src/zone-apply.ts
@@ -10186,7 +10301,9 @@ function installZoneApply(kit) {
10186
10301
  c.rand = R0;
10187
10302
  return c.zoneState;
10188
10303
  };
10189
- Object.assign(I, { fadePaint, abandonFx, burntFx, settleDrops });
10304
+ const shared = { fadePaint, abandonFx, burntFx, settleDrops };
10305
+ Object.assign(I, shared);
10306
+ return shared;
10190
10307
  }
10191
10308
 
10192
10309
  // src/scenarios.ts
@@ -10343,7 +10460,9 @@ function installScenarios(kit) {
10343
10460
  }
10344
10461
  return { p, dir };
10345
10462
  };
10346
- Object.assign(I, { BODY_TYPES });
10463
+ const shared = { BODY_TYPES };
10464
+ Object.assign(I, shared);
10465
+ return shared;
10347
10466
  }
10348
10467
 
10349
10468
  // src/ui.ts
@@ -10500,7 +10619,8 @@ function installUi(kit) {
10500
10619
  }
10501
10620
  g.addEventListener("click", pick);
10502
10621
  g.addEventListener("keydown", function(e) {
10503
- if (e.key === "Enter" || e.key === " ") {
10622
+ const key = e.key;
10623
+ if (key === "Enter" || key === " ") {
10504
10624
  e.preventDefault();
10505
10625
  pick();
10506
10626
  }
@@ -10513,7 +10633,7 @@ function installUi(kit) {
10513
10633
  return kit.bands.map(function(B, i) {
10514
10634
  const b = el("button", null, B.name);
10515
10635
  b.type = "button";
10516
- b.dataset.b = i;
10636
+ b.dataset.b = String(i);
10517
10637
  b.style.setProperty("--band", B.color);
10518
10638
  b.addEventListener("click", function() {
10519
10639
  if (onPick) onPick(B.health, i);
@@ -10741,6 +10861,7 @@ function createVehicleKit(options) {
10741
10861
  hash,
10742
10862
  clamp,
10743
10863
  smooth,
10864
+ // filled by the modules below, each with the helpers it shares
10744
10865
  _: {}
10745
10866
  };
10746
10867
  kit.fuels = {};
@@ -10818,8 +10939,8 @@ function createVehicleKit(options) {
10818
10939
  fn.apply(kit, args);
10819
10940
  });
10820
10941
  };
10821
- kit.configure = function(o) {
10822
- o = o || {};
10942
+ kit.configure = function(cfg) {
10943
+ const o = cfg || {};
10823
10944
  if (o.bands)
10824
10945
  o.bands.forEach(function(b, i) {
10825
10946
  if (kit.bands[i] && b) Object.assign(kit.bands[i], b);
@@ -10855,7 +10976,8 @@ function createVehicleKit(options) {
10855
10976
  }
10856
10977
  ;
10857
10978
  ["impactStain", "defaultSplat", "defaultGroup", "damagedScenario"].forEach(function(k) {
10858
- if (o[k] !== void 0) kit.config[k] = o[k];
10979
+ const v = o[k];
10980
+ if (v !== void 0) kit.config[k] = v;
10859
10981
  });
10860
10982
  return kit;
10861
10983
  };
@@ -11037,6 +11159,8 @@ var MECHANICS = {
11037
11159
  heli: ["battery", "alternator", "gearbox"]
11038
11160
  };
11039
11161
  var HITCH_CHANCE = { car: 30, truck: 60, bus: 0, tractor: 100, quad: 40, bike: 0, tank: 0, boat: 0, plane: 0, heli: 0 };
11162
+ var GLASS_LENS_HP = 320;
11163
+ var PLASTIC_LENS_HP = 900;
11040
11164
  var STEEL_KG_M2 = 7;
11041
11165
  var ALU_KG_M2 = 3.2;
11042
11166
  var kindOf = (c) => {
@@ -11194,7 +11318,9 @@ function buildVehicleModel(kit, c, options = {}) {
11194
11318
  continue;
11195
11319
  }
11196
11320
  if (role === "light") {
11197
- add({ id, role, name: label, material: "glass", box, mass: 1.2 + 4 * area, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
11321
+ const lens = /^head/.test(id) ? spec.headlightLens ?? "polycarbonate" : "acrylic";
11322
+ const glassLens = lens === "glass";
11323
+ add({ id, role, name: label, material: glassLens ? "glass" : "plastic", box, mass: 1.2 + 4 * area, hp: glassLens ? GLASS_LENS_HP : PLASTIC_LENS_HP, zoneId: id, visual: true, tags: ["light", `lens_${lens}`, ...tags] });
11198
11324
  continue;
11199
11325
  }
11200
11326
  if (role === "bumper") {
@@ -11651,21 +11777,188 @@ function subdivideNear(THREE, params) {
11651
11777
  mesh.geometry = g;
11652
11778
  }
11653
11779
 
11780
+ // src/destructible/door-open.ts
11781
+ var STRIP_SMOOTH_DEG = 180;
11782
+ var SKIN_BAND = 0.05;
11783
+ var BODY_FACING = 0.5;
11784
+ var FIELDS = 4;
11785
+ var SEAM_THIN = 0.03;
11786
+ var SEAM_AT_EDGE = 0.04;
11787
+ var SEAM_TALL = 0.4;
11788
+ var SLIVER_AREA = 1e-10;
11789
+ function cutStripOverDoor(vehicleKit, params) {
11790
+ const kit = vehicleKit;
11791
+ const strip = params.strip;
11792
+ const toVehicle = params.toVehicle;
11793
+ const { x0, x1 } = params;
11794
+ const THREE = kit.THREE;
11795
+ const at = new THREE.Vector3();
11796
+ const along = (x, y, z) => at.set(x, y, z).applyMatrix4(toVehicle).x;
11797
+ const piece = (tests) => {
11798
+ const mesh = new THREE.Mesh(strip.geometry.clone(), strip.material);
11799
+ kit.cutMesh(mesh, tests, STRIP_SMOOTH_DEG, {});
11800
+ return mesh;
11801
+ };
11802
+ const over = piece([{ sdf: (x, y, z) => along(x, y, z) - x0 }, { sdf: (x, y, z) => x1 - along(x, y, z) }]);
11803
+ const after = piece([{ sdf: (x, y, z) => along(x, y, z) - x1 }]);
11804
+ kit.cutMesh(strip, [{ sdf: (x, y, z) => x0 - along(x, y, z) }], STRIP_SMOOTH_DEG, {});
11805
+ const joined = joinTriangles(THREE, strip.geometry, after.geometry);
11806
+ strip.geometry.dispose();
11807
+ after.geometry.dispose();
11808
+ strip.geometry = joined;
11809
+ return over;
11810
+ }
11811
+ function joinTriangles(THREE, first, second) {
11812
+ const a = first.index ? first.toNonIndexed() : first;
11813
+ const b = second.index ? second.toNonIndexed() : second;
11814
+ const flags = [...solidTriangles(THREE, a), ...solidTriangles(THREE, b)];
11815
+ const both = new THREE.BufferGeometry();
11816
+ for (const name of Object.keys(a.attributes)) {
11817
+ const p = a.getAttribute(name);
11818
+ const q = b.getAttribute(name);
11819
+ if (!q || q.itemSize !== p.itemSize) continue;
11820
+ const array = new Float32Array(p.array.length + q.array.length);
11821
+ array.set(p.array, 0);
11822
+ array.set(q.array, p.array.length);
11823
+ both.setAttribute(name, new THREE.Float32BufferAttribute(array, p.itemSize));
11824
+ }
11825
+ if (a !== first) a.dispose();
11826
+ if (b !== second) b.dispose();
11827
+ const out = pickTriangles(THREE, both, flags, true) ?? new THREE.BufferGeometry();
11828
+ both.dispose();
11829
+ return out;
11830
+ }
11831
+ function solidTriangles(THREE, geometry) {
11832
+ const pos = geometry.getAttribute("position");
11833
+ const triangle = new THREE.Triangle();
11834
+ const flags = [];
11835
+ for (let t = 0; t * 3 + 2 < pos.count; t++) {
11836
+ triangle.a.fromBufferAttribute(pos, t * 3);
11837
+ triangle.b.fromBufferAttribute(pos, t * 3 + 1);
11838
+ triangle.c.fromBufferAttribute(pos, t * 3 + 2);
11839
+ flags.push(triangle.getArea() > SLIVER_AREA);
11840
+ }
11841
+ return flags;
11842
+ }
11843
+ function splitDoorSkin(three, meshes, toVehicleOf, side2, depth) {
11844
+ const THREE = three;
11845
+ const pieces = meshes;
11846
+ const toVehicle = toVehicleOf;
11847
+ const v = new THREE.Vector3();
11848
+ const corners = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()];
11849
+ const triangle = new THREE.Triangle();
11850
+ const centres = pieces.map((mesh) => {
11851
+ const geometry = mesh.geometry.index ? mesh.geometry.toNonIndexed() : mesh.geometry;
11852
+ const matrix = toVehicle(mesh);
11853
+ const pos = geometry.getAttribute("position");
11854
+ const out = new Float32Array(pos.count / 3 * FIELDS);
11855
+ for (let t = 0; t * 3 + 2 < pos.count; t++) {
11856
+ let y = 0;
11857
+ let across = 0;
11858
+ for (let k = 0; k < 3; k++) {
11859
+ corners[k].fromBufferAttribute(pos, t * 3 + k).applyMatrix4(matrix);
11860
+ y += corners[k].y / 3;
11861
+ across += side2 * corners[k].z / 3;
11862
+ }
11863
+ out[t * FIELDS] = y;
11864
+ out[t * FIELDS + 1] = across;
11865
+ triangle.set(corners[0], corners[1], corners[2]).getNormal(v);
11866
+ out[t * FIELDS + 2] = v.y;
11867
+ out[t * FIELDS + 3] = v.x;
11868
+ }
11869
+ return { geometry, out };
11870
+ });
11871
+ const outer = /* @__PURE__ */ new Map();
11872
+ for (const { out } of centres)
11873
+ for (let t = 0; t * FIELDS < out.length; t++) {
11874
+ const band = Math.floor(out[t * FIELDS] / SKIN_BAND);
11875
+ outer.set(band, Math.max(outer.get(band) ?? -Infinity, out[t * FIELDS + 1]));
11876
+ }
11877
+ const surfaceAt = (y) => {
11878
+ const band = Math.floor(y / SKIN_BAND);
11879
+ return Math.max(outer.get(band - 1) ?? -Infinity, outer.get(band) ?? -Infinity, outer.get(band + 1) ?? -Infinity);
11880
+ };
11881
+ return centres.map(({ geometry, out }, i) => {
11882
+ const near = [];
11883
+ for (let t = 0; t * FIELDS < out.length; t++) {
11884
+ const f = t * FIELDS;
11885
+ const body = out[f + 1] < surfaceAt(out[f]) - depth && (out[f + 2] < -BODY_FACING || Math.abs(out[f + 3]) > BODY_FACING);
11886
+ near.push(!body);
11887
+ }
11888
+ const result = { skin: pickTriangles(THREE, geometry, near, true), deep: pickTriangles(THREE, geometry, near, false) };
11889
+ if (geometry !== pieces[i].geometry) geometry.dispose();
11890
+ return result;
11891
+ });
11892
+ }
11893
+ function leaveDeepSkinOnBody(three, meshes, params) {
11894
+ const THREE = three;
11895
+ const pieces = meshes;
11896
+ const toVehicle = params.toVehicle;
11897
+ const { side: side2, depth } = params;
11898
+ const split = splitDoorSkin(THREE, pieces, (mesh) => new THREE.Matrix4().multiplyMatrices(toVehicle, mesh.matrixWorld), side2, depth);
11899
+ split.forEach(({ skin, deep }, i) => {
11900
+ const piece = pieces[i];
11901
+ if (!deep || !skin || !piece.parent) return;
11902
+ const body = new THREE.Mesh(deep, piece.material);
11903
+ body.position.copy(piece.position);
11904
+ body.quaternion.copy(piece.quaternion);
11905
+ body.scale.copy(piece.scale);
11906
+ body.castShadow = piece.castShadow;
11907
+ body.receiveShadow = piece.receiveShadow;
11908
+ piece.parent.add(body);
11909
+ piece.geometry.dispose();
11910
+ piece.geometry = skin;
11911
+ });
11912
+ }
11913
+ function pickTriangles(THREE, geometry, flags, wanted) {
11914
+ const count = flags.filter((flag) => flag === wanted).length;
11915
+ if (!count) return null;
11916
+ const out = new THREE.BufferGeometry();
11917
+ for (const name of Object.keys(geometry.attributes)) {
11918
+ const attribute = geometry.getAttribute(name);
11919
+ const size = attribute.itemSize;
11920
+ const source = attribute.array;
11921
+ const array = new Float32Array(count * 3 * size);
11922
+ let w = 0;
11923
+ flags.forEach((flag, t) => {
11924
+ if (flag !== wanted) return;
11925
+ array.set(source.subarray(t * 3 * size, (t + 1) * 3 * size), w);
11926
+ w += 3 * size;
11927
+ });
11928
+ out.setAttribute(name, new THREE.Float32BufferAttribute(array, size));
11929
+ }
11930
+ out.computeBoundingBox();
11931
+ out.computeBoundingSphere();
11932
+ return out;
11933
+ }
11934
+ function isDoorSeam(three, params) {
11935
+ const THREE = three;
11936
+ const box = params.box;
11937
+ const { zone, side: side2, dark } = params;
11938
+ if (!dark || box.isEmpty()) return false;
11939
+ const size = box.getSize(new THREE.Vector3());
11940
+ const centre = box.getCenter(new THREE.Vector3());
11941
+ 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;
11942
+ if (centre.y < zone.y0 || centre.y > zone.y1) return false;
11943
+ return Math.abs(centre.x - zone.x0) < SEAM_AT_EDGE || Math.abs(centre.x - zone.x1) < SEAM_AT_EDGE;
11944
+ }
11945
+
11654
11946
  // src/destructible/visual.ts
11655
11947
  var HOLE_DECAL_SCALE = 2.4;
11656
11948
  var DOOR_DEPTH = 0.07;
11949
+ var DOOR_SKIN_DEPTH = 0.09;
11950
+ var DOOR_FLOOR_DEPTH = 0.2;
11657
11951
  var DOOR_EDGE_PIECES = 8;
11658
11952
  var DOOR_EDGE_REACH = 0.08;
11659
11953
  var DOOR_CARD_COLOR = "#3b3e42";
11660
11954
  var DOOR_LINER_COLOR = "#5a5e64";
11661
11955
  var DOOR_LINER_DARK = 0.15;
11662
- var DOOR_STRIP_THIN = 0.08;
11956
+ var DOOR_STRIP_THIN = 0.13;
11663
11957
  var DOOR_STRIP_NEAR = 0.04;
11664
11958
  var DOOR_STRIP_TALL = 0.22;
11665
- var STRIP_SMOOTH_DEG = 180;
11666
- var DOOR_STRIP_VERTICES = 2e4;
11959
+ var DOOR_STRIP_VERTICES = 6e4;
11667
11960
  var DOOR_SILL = 0.25;
11668
- var DOOR_STRIP_OVER = 0.3;
11961
+ var DOOR_STRIP_OVER = 0.05;
11669
11962
  var DOOR_TOP_OVER_PANE = 0.03;
11670
11963
  var HOLE_TEXTURE_SIZE = 64;
11671
11964
  var LAMINATED_HP = 1e3;
@@ -11774,6 +12067,10 @@ var VehicleVisuals = class {
11774
12067
  __publicField(this, "skins", /* @__PURE__ */ new Map());
11775
12068
  /** How high each door's cut reaches. */
11776
12069
  __publicField(this, "doorTops", /* @__PURE__ */ new Map());
12070
+ /** The shut lines drawn on the body at a door's edges, by line: the doors whose edge it marks. Shown only while all of them are closed. */
12071
+ __publicField(this, "seams", /* @__PURE__ */ new Map());
12072
+ /** Doors and lids standing open now. */
12073
+ __publicField(this, "openNow", /* @__PURE__ */ new Set());
11777
12074
  __publicField(this, "lightsOn", true);
11778
12075
  /** Heavy body meshes whose triangle index is still to build (null once done). */
11779
12076
  __publicField(this, "warming");
@@ -11955,6 +12252,11 @@ var VehicleVisuals = class {
11955
12252
  }
11956
12253
  panel.visible = open && !this.skins.get(id);
11957
12254
  recess.visible = open && !this.skins.get(id);
12255
+ if (open) this.openNow.add(id);
12256
+ else this.openNow.delete(id);
12257
+ this.seams.forEach((doors, seam) => {
12258
+ if (doors.has(id)) seam.visible = ![...doors].some((door) => this.openNow.has(door));
12259
+ });
11958
12260
  };
11959
12261
  const euler = piv.rotation;
11960
12262
  const quaternion = piv.quaternion;
@@ -11997,11 +12299,13 @@ var VehicleVisuals = class {
11997
12299
  const pcs = I.cutPanel(this.c, z, rect, true, dirty, { anyN: true }).map((x) => x.mesh).filter((m) => !!m);
11998
12300
  this.mend(dirty);
11999
12301
  if (pcs.length) {
12302
+ leaveDeepSkinOnBody(this.THREE, pcs, { toVehicle: inv, side: z.side < 0 ? -1 : 1, depth: DOOR_SKIN_DEPTH });
12000
12303
  const panes = I.windowsOver(this.c, z, inv);
12001
12304
  const seals = panes.map((g) => I.paneSeals.get(g)).filter((s) => !!s);
12002
12305
  const mirrors = this.mirrorsOn(z);
12003
12306
  const frames = this.framesAround(panes, inv);
12004
- objs = [...pcs, ...I.followersIn(this.c, z, inv), ...panes, ...seals, ...mirrors, ...frames];
12307
+ const followers = I.followersIn(this.c, z, inv).filter((o) => !this.takeSeam(o, id, z, inv) && !this.inFloor(o, piv, z, inv));
12308
+ objs = [...pcs, ...followers, ...panes, ...seals, ...mirrors, ...frames];
12005
12309
  }
12006
12310
  } else {
12007
12311
  const pcs = this.cutZone(z);
@@ -12029,6 +12333,25 @@ var VehicleVisuals = class {
12029
12333
  this.c.root.updateMatrixWorld(true);
12030
12334
  return objs.length > 0;
12031
12335
  }
12336
+ /** 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. */
12337
+ inFloor(o, piv, z, inv) {
12338
+ const side2 = z.side < 0 ? -1 : 1;
12339
+ const centre = this.I.localBox(this.c, o, inv).getCenter(new this.THREE.Vector3());
12340
+ const skin = side2 * (piv.position.z - side2 * 0.016);
12341
+ return centre.y < z.box.y0 + DOOR_SILL && side2 * centre.z < skin - DOOR_FLOOR_DEPTH;
12342
+ }
12343
+ /** 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. */
12344
+ takeSeam(o, id, z, inv) {
12345
+ const B = z.box;
12346
+ const box = this.I.localBox(this.c, o, inv);
12347
+ const color = o.material?.color;
12348
+ const dark = !!color && color.getHSL({ h: 0, s: 0, l: 0 }).l < DOOR_LINER_DARK;
12349
+ if (!o.isMesh || !isDoorSeam(this.THREE, { box, zone: { x0: B.x0, x1: B.x1, y0: B.y0, y1: B.y1 }, side: z.side, dark })) return false;
12350
+ const doors = this.seams.get(o) ?? /* @__PURE__ */ new Set();
12351
+ doors.add(id);
12352
+ this.seams.set(o, doors);
12353
+ return true;
12354
+ }
12032
12355
  /** 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. */
12033
12356
  doorEdges(piv, z, y1) {
12034
12357
  const THREE = this.THREE;
@@ -12050,13 +12373,15 @@ var VehicleVisuals = class {
12050
12373
  const at = this.surface("z", side2, B, [[x, y]]);
12051
12374
  return at != null && Math.abs(at - flat) < DOOR_EDGE_REACH ? at : flat;
12052
12375
  });
12053
- const band = (lift) => {
12376
+ const band = (lift, path, closed) => {
12054
12377
  const position = [];
12055
12378
  const index = [];
12056
- loop.forEach(([x, y], i) => {
12057
- position.push(x, y, heights[i] + side2 * lift, x, y, heights[i] + side2 * (lift - DOOR_DEPTH));
12379
+ path.forEach((at, i) => {
12380
+ const [x, y] = loop[at];
12381
+ position.push(x, y, heights[at] + side2 * lift, x, y, heights[at] + side2 * (lift - DOOR_DEPTH));
12382
+ if (!closed && i === path.length - 1) return;
12058
12383
  const a = i * 2;
12059
- const b = (i + 1) % loop.length * 2;
12384
+ const b = (i + 1) % path.length * 2;
12060
12385
  index.push(a, a + 1, b, b, a + 1, b + 1);
12061
12386
  });
12062
12387
  const g = new THREE.BufferGeometry();
@@ -12084,8 +12409,11 @@ var VehicleVisuals = class {
12084
12409
  card.castShadow = false;
12085
12410
  this.c.vehicle.add(card);
12086
12411
  piv.attach(card);
12087
- const edges = new THREE.Mesh(band(2e-3), paint);
12088
- const lining = new THREE.Mesh(band(2e-3), paint);
12412
+ const all = loop.map((_, i) => i);
12413
+ const top = 2 * DOOR_EDGE_PIECES;
12414
+ const sides = [...all.slice(3 * DOOR_EDGE_PIECES), ...all.slice(0, top + 1)];
12415
+ const edges = new THREE.Mesh(band(2e-3, all, true), paint);
12416
+ const lining = new THREE.Mesh(band(2e-3, sides, false), paint);
12089
12417
  for (const m of [edges, lining]) {
12090
12418
  m.userData.noFollow = true;
12091
12419
  m.castShadow = false;
@@ -12142,26 +12470,14 @@ var VehicleVisuals = class {
12142
12470
  const box = this.I.localBox(this.c, o, inv);
12143
12471
  const size = box.getSize(new THREE.Vector3());
12144
12472
  const mid = box.getCenter(new THREE.Vector3());
12145
- if (Math.sign(mid.z) !== Math.sign(z.side) || size.z > DOOR_STRIP_THIN || size.y > DOOR_STRIP_TALL || Math.min(box.max.x, x1) - Math.max(box.min.x, x0) < (x1 - x0) * DOOR_STRIP_OVER || mid.y < y0 || mid.y > y1) continue;
12473
+ if (Math.sign(mid.z) !== Math.sign(z.side) || size.z > DOOR_STRIP_THIN || size.y > DOOR_STRIP_TALL || Math.min(box.max.x, x1) - Math.max(box.min.x, x0) < DOOR_STRIP_OVER || mid.y < y0 || mid.y > y1) continue;
12146
12474
  const toVehicle = new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld);
12147
- const v = new THREE.Vector3();
12148
- const sdf = (x, y, zz) => {
12149
- v.set(x, y, zz).applyMatrix4(toVehicle);
12150
- return Math.max(x0 - v.x, v.x - x1);
12151
- };
12152
- const over = new THREE.Mesh(o.geometry.clone(), o.material);
12475
+ const over = cutStripOverDoor(this.kit, { strip: o, toVehicle, x0, x1 });
12153
12476
  over.position.copy(o.position);
12154
12477
  over.quaternion.copy(o.quaternion);
12155
12478
  over.scale.copy(o.scale);
12156
12479
  this.c.vehicle.add(over);
12157
12480
  over.updateMatrixWorld(true);
12158
- const overToVehicle = new THREE.Matrix4().multiplyMatrices(inv, over.matrixWorld);
12159
- const w = new THREE.Vector3();
12160
- this.kit.cutMesh(over, [{ sdf: (x, y, zz) => {
12161
- w.set(x, y, zz).applyMatrix4(overToVehicle);
12162
- return -Math.max(x0 - w.x, w.x - x1);
12163
- } }], STRIP_SMOOTH_DEG, {});
12164
- this.kit.cutMesh(o, [{ sdf }], STRIP_SMOOTH_DEG, {});
12165
12481
  over.userData.noFollow = true;
12166
12482
  piv.attach(over);
12167
12483
  }
@@ -12742,11 +13058,18 @@ var VehicleVisuals = class {
12742
13058
  }
12743
13059
  }
12744
13060
  }
13061
+ /** 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. */
13062
+ crackLight(p, pierced) {
13063
+ const z = this.zone(p);
13064
+ if (!z) return;
13065
+ 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);
13066
+ }
12745
13067
  /** The lens of a light breaks and its unit stays (damaged) or goes (destroyed). */
12746
13068
  breakLight(p, gone) {
12747
13069
  const z = this.zone(p);
12748
13070
  if (!z) return;
12749
13071
  for (const L of z.lights) {
13072
+ L.lensMaterial = p.material;
12750
13073
  this.kit.breakLight(this.c, L, new this.THREE.Vector3(p.box.center[0] > 0 ? -1 : 1, 0, 0));
12751
13074
  if (gone) {
12752
13075
  if (L.group) L.group.visible = false;
@@ -13392,6 +13715,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
13392
13715
  wear(p, amount, out);
13393
13716
  syncZone(p);
13394
13717
  if (p.hp <= 0) lose(p, "broken", out, cause);
13718
+ else if (p.def.role === "light") vis?.crackLight(p.def, dtype === "ballistic");
13395
13719
  if (p.def.role === "tyre" && !p.flat && p.hp < p.def.hp * 0.6 && !p.lost) {
13396
13720
  p.flat = true;
13397
13721
  vis?.tyreFlat(p.def, 0.24);
@@ -14204,7 +14528,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
14204
14528
  if (!list.length) return 1;
14205
14529
  return Math.min(...list.map((p) => p.lost || !attached(p.def.id) ? 0 : p.hp / p.def.hp));
14206
14530
  };
14207
- 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) }));
14531
+ 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 }));
14208
14532
  const status = computeStatus();
14209
14533
  const brakeSystem = [...ps.values()].find((p) => p.def.role === "brakes" && p.def.contains);
14210
14534
  const fluidLeft = brakeSystem ? clamp((brakeSystem.liters ?? 0) / brakeSystem.def.contains.liters / BRAKE_FLUID_NEEDED, 0, 1) : 1;