@adatechnology/game-vehicles 0.6.2 → 0.6.3
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.d.ts +6 -1
- package/dist/index.global.js +272 -31
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +272 -31
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -5471,8 +5471,20 @@ function installGlassDamage(kit) {
|
|
|
5471
5471
|
if (!L || L.broken) return;
|
|
5472
5472
|
L.broken = true;
|
|
5473
5473
|
L.lens.forEach(function(m) {
|
|
5474
|
-
m.
|
|
5474
|
+
const rim = L.lensMaterial ? glassRim(m.geometry, c.rand) : null;
|
|
5475
|
+
if (!rim) {
|
|
5476
|
+
m.visible = false;
|
|
5477
|
+
return;
|
|
5478
|
+
}
|
|
5479
|
+
m.geometry.dispose();
|
|
5480
|
+
m.geometry = rim;
|
|
5481
|
+
m.material = MATERIALS.glassEdge;
|
|
5482
|
+
m.userData.lensRim = true;
|
|
5475
5483
|
});
|
|
5484
|
+
if (L.lensMaterial)
|
|
5485
|
+
(L.reflector || []).forEach(function(m) {
|
|
5486
|
+
m.material = MATERIALS.broken;
|
|
5487
|
+
});
|
|
5476
5488
|
L.inner.forEach(function(m) {
|
|
5477
5489
|
m.material = MATERIALS.bulbBroken;
|
|
5478
5490
|
});
|
|
@@ -5480,11 +5492,38 @@ function installGlassDamage(kit) {
|
|
|
5480
5492
|
h.visible = false;
|
|
5481
5493
|
});
|
|
5482
5494
|
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
5483
|
-
|
|
5484
|
-
I.burst(c, "
|
|
5495
|
+
const glass = L.lensMaterial !== "plastic";
|
|
5496
|
+
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" });
|
|
5497
|
+
I.burst(c, "plastic", c.toEffects(p), glass ? 2 : 5, { dir: c.dirToEffects(dir.clone().negate()), speed: 1 });
|
|
5485
5498
|
}
|
|
5486
5499
|
kit.breakLight = breakLight;
|
|
5487
|
-
|
|
5500
|
+
const crazedCache = /* @__PURE__ */ new Map();
|
|
5501
|
+
const CRAZED_OPACITY = 0.6;
|
|
5502
|
+
function crazed(material) {
|
|
5503
|
+
const known = crazedCache.get(material);
|
|
5504
|
+
if (known) return known;
|
|
5505
|
+
const m = I.crackLens(material).clone();
|
|
5506
|
+
m.opacity = Math.max(m.opacity ?? 1, CRAZED_OPACITY);
|
|
5507
|
+
m.transparent = true;
|
|
5508
|
+
m.userData.shared = true;
|
|
5509
|
+
crazedCache.set(material, m);
|
|
5510
|
+
return m;
|
|
5511
|
+
}
|
|
5512
|
+
function crackLight(c, L, dir, pierced) {
|
|
5513
|
+
if (!L || L.broken) return;
|
|
5514
|
+
if (!L.cracked) {
|
|
5515
|
+
L.cracked = true;
|
|
5516
|
+
for (const m of L.lens) if (m.material && !Array.isArray(m.material)) m.material = crazed(m.material);
|
|
5517
|
+
}
|
|
5518
|
+
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
5519
|
+
I.burst(c, "lens", c.toEffects(p), 3, { dir: c.dirToEffects(dir.clone().negate()), speed: 0.9, variant: L.tint === "red" ? "red" : "clear" });
|
|
5520
|
+
if (!pierced || L.dark) return;
|
|
5521
|
+
L.dark = true;
|
|
5522
|
+
for (const m of L.inner) m.material = MATERIALS.bulbBroken;
|
|
5523
|
+
for (const h of L.halo ?? []) h.visible = false;
|
|
5524
|
+
}
|
|
5525
|
+
kit.crackLight = crackLight;
|
|
5526
|
+
Object.assign(I, { glassUV, drawWeb, crackGlass, glassRim, shatterGlass, hitGlass, breakLight, crackLight });
|
|
5488
5527
|
}
|
|
5489
5528
|
|
|
5490
5529
|
// src/impact.ts
|
|
@@ -11037,6 +11076,8 @@ var MECHANICS = {
|
|
|
11037
11076
|
heli: ["battery", "alternator", "gearbox"]
|
|
11038
11077
|
};
|
|
11039
11078
|
var HITCH_CHANCE = { car: 30, truck: 60, bus: 0, tractor: 100, quad: 40, bike: 0, tank: 0, boat: 0, plane: 0, heli: 0 };
|
|
11079
|
+
var GLASS_LENS_HP = 320;
|
|
11080
|
+
var PLASTIC_LENS_HP = 900;
|
|
11040
11081
|
var STEEL_KG_M2 = 7;
|
|
11041
11082
|
var ALU_KG_M2 = 3.2;
|
|
11042
11083
|
var kindOf = (c) => {
|
|
@@ -11194,7 +11235,9 @@ function buildVehicleModel(kit, c, options = {}) {
|
|
|
11194
11235
|
continue;
|
|
11195
11236
|
}
|
|
11196
11237
|
if (role === "light") {
|
|
11197
|
-
|
|
11238
|
+
const lens = /^head/.test(id) ? spec.headlightLens ?? "polycarbonate" : "acrylic";
|
|
11239
|
+
const glassLens = lens === "glass";
|
|
11240
|
+
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
11241
|
continue;
|
|
11199
11242
|
}
|
|
11200
11243
|
if (role === "bumper") {
|
|
@@ -11651,21 +11694,188 @@ function subdivideNear(THREE, params) {
|
|
|
11651
11694
|
mesh.geometry = g;
|
|
11652
11695
|
}
|
|
11653
11696
|
|
|
11697
|
+
// src/destructible/door-open.ts
|
|
11698
|
+
var STRIP_SMOOTH_DEG = 180;
|
|
11699
|
+
var SKIN_BAND = 0.05;
|
|
11700
|
+
var BODY_FACING = 0.5;
|
|
11701
|
+
var FIELDS = 4;
|
|
11702
|
+
var SEAM_THIN = 0.03;
|
|
11703
|
+
var SEAM_AT_EDGE = 0.04;
|
|
11704
|
+
var SEAM_TALL = 0.4;
|
|
11705
|
+
var SLIVER_AREA = 1e-10;
|
|
11706
|
+
function cutStripOverDoor(vehicleKit, params) {
|
|
11707
|
+
const kit = vehicleKit;
|
|
11708
|
+
const strip = params.strip;
|
|
11709
|
+
const toVehicle = params.toVehicle;
|
|
11710
|
+
const { x0, x1 } = params;
|
|
11711
|
+
const THREE = kit.THREE;
|
|
11712
|
+
const at = new THREE.Vector3();
|
|
11713
|
+
const along = (x, y, z) => at.set(x, y, z).applyMatrix4(toVehicle).x;
|
|
11714
|
+
const piece = (tests) => {
|
|
11715
|
+
const mesh = new THREE.Mesh(strip.geometry.clone(), strip.material);
|
|
11716
|
+
kit.cutMesh(mesh, tests, STRIP_SMOOTH_DEG, {});
|
|
11717
|
+
return mesh;
|
|
11718
|
+
};
|
|
11719
|
+
const over = piece([{ sdf: (x, y, z) => along(x, y, z) - x0 }, { sdf: (x, y, z) => x1 - along(x, y, z) }]);
|
|
11720
|
+
const after = piece([{ sdf: (x, y, z) => along(x, y, z) - x1 }]);
|
|
11721
|
+
kit.cutMesh(strip, [{ sdf: (x, y, z) => x0 - along(x, y, z) }], STRIP_SMOOTH_DEG, {});
|
|
11722
|
+
const joined = joinTriangles(THREE, strip.geometry, after.geometry);
|
|
11723
|
+
strip.geometry.dispose();
|
|
11724
|
+
after.geometry.dispose();
|
|
11725
|
+
strip.geometry = joined;
|
|
11726
|
+
return over;
|
|
11727
|
+
}
|
|
11728
|
+
function joinTriangles(THREE, first, second) {
|
|
11729
|
+
const a = first.index ? first.toNonIndexed() : first;
|
|
11730
|
+
const b = second.index ? second.toNonIndexed() : second;
|
|
11731
|
+
const flags = [...solidTriangles(THREE, a), ...solidTriangles(THREE, b)];
|
|
11732
|
+
const both = new THREE.BufferGeometry();
|
|
11733
|
+
for (const name of Object.keys(a.attributes)) {
|
|
11734
|
+
const p = a.getAttribute(name);
|
|
11735
|
+
const q = b.getAttribute(name);
|
|
11736
|
+
if (!q || q.itemSize !== p.itemSize) continue;
|
|
11737
|
+
const array = new Float32Array(p.array.length + q.array.length);
|
|
11738
|
+
array.set(p.array, 0);
|
|
11739
|
+
array.set(q.array, p.array.length);
|
|
11740
|
+
both.setAttribute(name, new THREE.Float32BufferAttribute(array, p.itemSize));
|
|
11741
|
+
}
|
|
11742
|
+
if (a !== first) a.dispose();
|
|
11743
|
+
if (b !== second) b.dispose();
|
|
11744
|
+
const out = pickTriangles(THREE, both, flags, true) ?? new THREE.BufferGeometry();
|
|
11745
|
+
both.dispose();
|
|
11746
|
+
return out;
|
|
11747
|
+
}
|
|
11748
|
+
function solidTriangles(THREE, geometry) {
|
|
11749
|
+
const pos = geometry.getAttribute("position");
|
|
11750
|
+
const triangle = new THREE.Triangle();
|
|
11751
|
+
const flags = [];
|
|
11752
|
+
for (let t = 0; t * 3 + 2 < pos.count; t++) {
|
|
11753
|
+
triangle.a.fromBufferAttribute(pos, t * 3);
|
|
11754
|
+
triangle.b.fromBufferAttribute(pos, t * 3 + 1);
|
|
11755
|
+
triangle.c.fromBufferAttribute(pos, t * 3 + 2);
|
|
11756
|
+
flags.push(triangle.getArea() > SLIVER_AREA);
|
|
11757
|
+
}
|
|
11758
|
+
return flags;
|
|
11759
|
+
}
|
|
11760
|
+
function splitDoorSkin(three, meshes, toVehicleOf, side2, depth) {
|
|
11761
|
+
const THREE = three;
|
|
11762
|
+
const pieces = meshes;
|
|
11763
|
+
const toVehicle = toVehicleOf;
|
|
11764
|
+
const v = new THREE.Vector3();
|
|
11765
|
+
const corners = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()];
|
|
11766
|
+
const triangle = new THREE.Triangle();
|
|
11767
|
+
const centres = pieces.map((mesh) => {
|
|
11768
|
+
const geometry = mesh.geometry.index ? mesh.geometry.toNonIndexed() : mesh.geometry;
|
|
11769
|
+
const matrix = toVehicle(mesh);
|
|
11770
|
+
const pos = geometry.getAttribute("position");
|
|
11771
|
+
const out = new Float32Array(pos.count / 3 * FIELDS);
|
|
11772
|
+
for (let t = 0; t * 3 + 2 < pos.count; t++) {
|
|
11773
|
+
let y = 0;
|
|
11774
|
+
let across = 0;
|
|
11775
|
+
for (let k = 0; k < 3; k++) {
|
|
11776
|
+
corners[k].fromBufferAttribute(pos, t * 3 + k).applyMatrix4(matrix);
|
|
11777
|
+
y += corners[k].y / 3;
|
|
11778
|
+
across += side2 * corners[k].z / 3;
|
|
11779
|
+
}
|
|
11780
|
+
out[t * FIELDS] = y;
|
|
11781
|
+
out[t * FIELDS + 1] = across;
|
|
11782
|
+
triangle.set(corners[0], corners[1], corners[2]).getNormal(v);
|
|
11783
|
+
out[t * FIELDS + 2] = v.y;
|
|
11784
|
+
out[t * FIELDS + 3] = v.x;
|
|
11785
|
+
}
|
|
11786
|
+
return { geometry, out };
|
|
11787
|
+
});
|
|
11788
|
+
const outer = /* @__PURE__ */ new Map();
|
|
11789
|
+
for (const { out } of centres)
|
|
11790
|
+
for (let t = 0; t * FIELDS < out.length; t++) {
|
|
11791
|
+
const band = Math.floor(out[t * FIELDS] / SKIN_BAND);
|
|
11792
|
+
outer.set(band, Math.max(outer.get(band) ?? -Infinity, out[t * FIELDS + 1]));
|
|
11793
|
+
}
|
|
11794
|
+
const surfaceAt = (y) => {
|
|
11795
|
+
const band = Math.floor(y / SKIN_BAND);
|
|
11796
|
+
return Math.max(outer.get(band - 1) ?? -Infinity, outer.get(band) ?? -Infinity, outer.get(band + 1) ?? -Infinity);
|
|
11797
|
+
};
|
|
11798
|
+
return centres.map(({ geometry, out }, i) => {
|
|
11799
|
+
const near = [];
|
|
11800
|
+
for (let t = 0; t * FIELDS < out.length; t++) {
|
|
11801
|
+
const f = t * FIELDS;
|
|
11802
|
+
const body = out[f + 1] < surfaceAt(out[f]) - depth && (out[f + 2] < -BODY_FACING || Math.abs(out[f + 3]) > BODY_FACING);
|
|
11803
|
+
near.push(!body);
|
|
11804
|
+
}
|
|
11805
|
+
const result = { skin: pickTriangles(THREE, geometry, near, true), deep: pickTriangles(THREE, geometry, near, false) };
|
|
11806
|
+
if (geometry !== pieces[i].geometry) geometry.dispose();
|
|
11807
|
+
return result;
|
|
11808
|
+
});
|
|
11809
|
+
}
|
|
11810
|
+
function leaveDeepSkinOnBody(three, meshes, params) {
|
|
11811
|
+
const THREE = three;
|
|
11812
|
+
const pieces = meshes;
|
|
11813
|
+
const toVehicle = params.toVehicle;
|
|
11814
|
+
const { side: side2, depth } = params;
|
|
11815
|
+
const split = splitDoorSkin(THREE, pieces, (mesh) => new THREE.Matrix4().multiplyMatrices(toVehicle, mesh.matrixWorld), side2, depth);
|
|
11816
|
+
split.forEach(({ skin, deep }, i) => {
|
|
11817
|
+
const piece = pieces[i];
|
|
11818
|
+
if (!deep || !skin || !piece.parent) return;
|
|
11819
|
+
const body = new THREE.Mesh(deep, piece.material);
|
|
11820
|
+
body.position.copy(piece.position);
|
|
11821
|
+
body.quaternion.copy(piece.quaternion);
|
|
11822
|
+
body.scale.copy(piece.scale);
|
|
11823
|
+
body.castShadow = piece.castShadow;
|
|
11824
|
+
body.receiveShadow = piece.receiveShadow;
|
|
11825
|
+
piece.parent.add(body);
|
|
11826
|
+
piece.geometry.dispose();
|
|
11827
|
+
piece.geometry = skin;
|
|
11828
|
+
});
|
|
11829
|
+
}
|
|
11830
|
+
function pickTriangles(THREE, geometry, flags, wanted) {
|
|
11831
|
+
const count = flags.filter((flag) => flag === wanted).length;
|
|
11832
|
+
if (!count) return null;
|
|
11833
|
+
const out = new THREE.BufferGeometry();
|
|
11834
|
+
for (const name of Object.keys(geometry.attributes)) {
|
|
11835
|
+
const attribute = geometry.getAttribute(name);
|
|
11836
|
+
const size = attribute.itemSize;
|
|
11837
|
+
const source = attribute.array;
|
|
11838
|
+
const array = new Float32Array(count * 3 * size);
|
|
11839
|
+
let w = 0;
|
|
11840
|
+
flags.forEach((flag, t) => {
|
|
11841
|
+
if (flag !== wanted) return;
|
|
11842
|
+
array.set(source.subarray(t * 3 * size, (t + 1) * 3 * size), w);
|
|
11843
|
+
w += 3 * size;
|
|
11844
|
+
});
|
|
11845
|
+
out.setAttribute(name, new THREE.Float32BufferAttribute(array, size));
|
|
11846
|
+
}
|
|
11847
|
+
out.computeBoundingBox();
|
|
11848
|
+
out.computeBoundingSphere();
|
|
11849
|
+
return out;
|
|
11850
|
+
}
|
|
11851
|
+
function isDoorSeam(three, params) {
|
|
11852
|
+
const THREE = three;
|
|
11853
|
+
const box = params.box;
|
|
11854
|
+
const { zone, side: side2, dark } = params;
|
|
11855
|
+
if (!dark || box.isEmpty()) return false;
|
|
11856
|
+
const size = box.getSize(new THREE.Vector3());
|
|
11857
|
+
const centre = box.getCenter(new THREE.Vector3());
|
|
11858
|
+
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;
|
|
11859
|
+
if (centre.y < zone.y0 || centre.y > zone.y1) return false;
|
|
11860
|
+
return Math.abs(centre.x - zone.x0) < SEAM_AT_EDGE || Math.abs(centre.x - zone.x1) < SEAM_AT_EDGE;
|
|
11861
|
+
}
|
|
11862
|
+
|
|
11654
11863
|
// src/destructible/visual.ts
|
|
11655
11864
|
var HOLE_DECAL_SCALE = 2.4;
|
|
11656
11865
|
var DOOR_DEPTH = 0.07;
|
|
11866
|
+
var DOOR_SKIN_DEPTH = 0.09;
|
|
11867
|
+
var DOOR_FLOOR_DEPTH = 0.2;
|
|
11657
11868
|
var DOOR_EDGE_PIECES = 8;
|
|
11658
11869
|
var DOOR_EDGE_REACH = 0.08;
|
|
11659
11870
|
var DOOR_CARD_COLOR = "#3b3e42";
|
|
11660
11871
|
var DOOR_LINER_COLOR = "#5a5e64";
|
|
11661
11872
|
var DOOR_LINER_DARK = 0.15;
|
|
11662
|
-
var DOOR_STRIP_THIN = 0.
|
|
11873
|
+
var DOOR_STRIP_THIN = 0.13;
|
|
11663
11874
|
var DOOR_STRIP_NEAR = 0.04;
|
|
11664
11875
|
var DOOR_STRIP_TALL = 0.22;
|
|
11665
|
-
var
|
|
11666
|
-
var DOOR_STRIP_VERTICES = 2e4;
|
|
11876
|
+
var DOOR_STRIP_VERTICES = 6e4;
|
|
11667
11877
|
var DOOR_SILL = 0.25;
|
|
11668
|
-
var DOOR_STRIP_OVER = 0.
|
|
11878
|
+
var DOOR_STRIP_OVER = 0.05;
|
|
11669
11879
|
var DOOR_TOP_OVER_PANE = 0.03;
|
|
11670
11880
|
var HOLE_TEXTURE_SIZE = 64;
|
|
11671
11881
|
var LAMINATED_HP = 1e3;
|
|
@@ -11774,6 +11984,10 @@ var VehicleVisuals = class {
|
|
|
11774
11984
|
__publicField(this, "skins", /* @__PURE__ */ new Map());
|
|
11775
11985
|
/** How high each door's cut reaches. */
|
|
11776
11986
|
__publicField(this, "doorTops", /* @__PURE__ */ new Map());
|
|
11987
|
+
/** 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. */
|
|
11988
|
+
__publicField(this, "seams", /* @__PURE__ */ new Map());
|
|
11989
|
+
/** Doors and lids standing open now. */
|
|
11990
|
+
__publicField(this, "openNow", /* @__PURE__ */ new Set());
|
|
11777
11991
|
__publicField(this, "lightsOn", true);
|
|
11778
11992
|
/** Heavy body meshes whose triangle index is still to build (null once done). */
|
|
11779
11993
|
__publicField(this, "warming");
|
|
@@ -11955,6 +12169,11 @@ var VehicleVisuals = class {
|
|
|
11955
12169
|
}
|
|
11956
12170
|
panel.visible = open && !this.skins.get(id);
|
|
11957
12171
|
recess.visible = open && !this.skins.get(id);
|
|
12172
|
+
if (open) this.openNow.add(id);
|
|
12173
|
+
else this.openNow.delete(id);
|
|
12174
|
+
this.seams.forEach((doors, seam) => {
|
|
12175
|
+
if (doors.has(id)) seam.visible = ![...doors].some((door) => this.openNow.has(door));
|
|
12176
|
+
});
|
|
11958
12177
|
};
|
|
11959
12178
|
const euler = piv.rotation;
|
|
11960
12179
|
const quaternion = piv.quaternion;
|
|
@@ -11997,11 +12216,13 @@ var VehicleVisuals = class {
|
|
|
11997
12216
|
const pcs = I.cutPanel(this.c, z, rect, true, dirty, { anyN: true }).map((x) => x.mesh).filter((m) => !!m);
|
|
11998
12217
|
this.mend(dirty);
|
|
11999
12218
|
if (pcs.length) {
|
|
12219
|
+
leaveDeepSkinOnBody(this.THREE, pcs, { toVehicle: inv, side: z.side < 0 ? -1 : 1, depth: DOOR_SKIN_DEPTH });
|
|
12000
12220
|
const panes = I.windowsOver(this.c, z, inv);
|
|
12001
12221
|
const seals = panes.map((g) => I.paneSeals.get(g)).filter((s) => !!s);
|
|
12002
12222
|
const mirrors = this.mirrorsOn(z);
|
|
12003
12223
|
const frames = this.framesAround(panes, inv);
|
|
12004
|
-
|
|
12224
|
+
const followers = I.followersIn(this.c, z, inv).filter((o) => !this.takeSeam(o, id, z, inv) && !this.inFloor(o, piv, z, inv));
|
|
12225
|
+
objs = [...pcs, ...followers, ...panes, ...seals, ...mirrors, ...frames];
|
|
12005
12226
|
}
|
|
12006
12227
|
} else {
|
|
12007
12228
|
const pcs = this.cutZone(z);
|
|
@@ -12029,6 +12250,25 @@ var VehicleVisuals = class {
|
|
|
12029
12250
|
this.c.root.updateMatrixWorld(true);
|
|
12030
12251
|
return objs.length > 0;
|
|
12031
12252
|
}
|
|
12253
|
+
/** 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. */
|
|
12254
|
+
inFloor(o, piv, z, inv) {
|
|
12255
|
+
const side2 = z.side < 0 ? -1 : 1;
|
|
12256
|
+
const centre = this.I.localBox(this.c, o, inv).getCenter(new this.THREE.Vector3());
|
|
12257
|
+
const skin = side2 * (piv.position.z - side2 * 0.016);
|
|
12258
|
+
return centre.y < z.box.y0 + DOOR_SILL && side2 * centre.z < skin - DOOR_FLOOR_DEPTH;
|
|
12259
|
+
}
|
|
12260
|
+
/** 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. */
|
|
12261
|
+
takeSeam(o, id, z, inv) {
|
|
12262
|
+
const B = z.box;
|
|
12263
|
+
const box = this.I.localBox(this.c, o, inv);
|
|
12264
|
+
const color = o.material?.color;
|
|
12265
|
+
const dark = !!color && color.getHSL({ h: 0, s: 0, l: 0 }).l < DOOR_LINER_DARK;
|
|
12266
|
+
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;
|
|
12267
|
+
const doors = this.seams.get(o) ?? /* @__PURE__ */ new Set();
|
|
12268
|
+
doors.add(id);
|
|
12269
|
+
this.seams.set(o, doors);
|
|
12270
|
+
return true;
|
|
12271
|
+
}
|
|
12032
12272
|
/** 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
12273
|
doorEdges(piv, z, y1) {
|
|
12034
12274
|
const THREE = this.THREE;
|
|
@@ -12050,13 +12290,15 @@ var VehicleVisuals = class {
|
|
|
12050
12290
|
const at = this.surface("z", side2, B, [[x, y]]);
|
|
12051
12291
|
return at != null && Math.abs(at - flat) < DOOR_EDGE_REACH ? at : flat;
|
|
12052
12292
|
});
|
|
12053
|
-
const band = (lift) => {
|
|
12293
|
+
const band = (lift, path, closed) => {
|
|
12054
12294
|
const position = [];
|
|
12055
12295
|
const index = [];
|
|
12056
|
-
|
|
12057
|
-
|
|
12296
|
+
path.forEach((at, i) => {
|
|
12297
|
+
const [x, y] = loop[at];
|
|
12298
|
+
position.push(x, y, heights[at] + side2 * lift, x, y, heights[at] + side2 * (lift - DOOR_DEPTH));
|
|
12299
|
+
if (!closed && i === path.length - 1) return;
|
|
12058
12300
|
const a = i * 2;
|
|
12059
|
-
const b = (i + 1) %
|
|
12301
|
+
const b = (i + 1) % path.length * 2;
|
|
12060
12302
|
index.push(a, a + 1, b, b, a + 1, b + 1);
|
|
12061
12303
|
});
|
|
12062
12304
|
const g = new THREE.BufferGeometry();
|
|
@@ -12084,8 +12326,11 @@ var VehicleVisuals = class {
|
|
|
12084
12326
|
card.castShadow = false;
|
|
12085
12327
|
this.c.vehicle.add(card);
|
|
12086
12328
|
piv.attach(card);
|
|
12087
|
-
const
|
|
12088
|
-
const
|
|
12329
|
+
const all = loop.map((_, i) => i);
|
|
12330
|
+
const top = 2 * DOOR_EDGE_PIECES;
|
|
12331
|
+
const sides = [...all.slice(3 * DOOR_EDGE_PIECES), ...all.slice(0, top + 1)];
|
|
12332
|
+
const edges = new THREE.Mesh(band(2e-3, all, true), paint);
|
|
12333
|
+
const lining = new THREE.Mesh(band(2e-3, sides, false), paint);
|
|
12089
12334
|
for (const m of [edges, lining]) {
|
|
12090
12335
|
m.userData.noFollow = true;
|
|
12091
12336
|
m.castShadow = false;
|
|
@@ -12142,26 +12387,14 @@ var VehicleVisuals = class {
|
|
|
12142
12387
|
const box = this.I.localBox(this.c, o, inv);
|
|
12143
12388
|
const size = box.getSize(new THREE.Vector3());
|
|
12144
12389
|
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) <
|
|
12390
|
+
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
12391
|
const toVehicle = new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld);
|
|
12147
|
-
const
|
|
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);
|
|
12392
|
+
const over = cutStripOverDoor(this.kit, { strip: o, toVehicle, x0, x1 });
|
|
12153
12393
|
over.position.copy(o.position);
|
|
12154
12394
|
over.quaternion.copy(o.quaternion);
|
|
12155
12395
|
over.scale.copy(o.scale);
|
|
12156
12396
|
this.c.vehicle.add(over);
|
|
12157
12397
|
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
12398
|
over.userData.noFollow = true;
|
|
12166
12399
|
piv.attach(over);
|
|
12167
12400
|
}
|
|
@@ -12742,11 +12975,18 @@ var VehicleVisuals = class {
|
|
|
12742
12975
|
}
|
|
12743
12976
|
}
|
|
12744
12977
|
}
|
|
12978
|
+
/** 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. */
|
|
12979
|
+
crackLight(p, pierced) {
|
|
12980
|
+
const z = this.zone(p);
|
|
12981
|
+
if (!z) return;
|
|
12982
|
+
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);
|
|
12983
|
+
}
|
|
12745
12984
|
/** The lens of a light breaks and its unit stays (damaged) or goes (destroyed). */
|
|
12746
12985
|
breakLight(p, gone) {
|
|
12747
12986
|
const z = this.zone(p);
|
|
12748
12987
|
if (!z) return;
|
|
12749
12988
|
for (const L of z.lights) {
|
|
12989
|
+
L.lensMaterial = p.material;
|
|
12750
12990
|
this.kit.breakLight(this.c, L, new this.THREE.Vector3(p.box.center[0] > 0 ? -1 : 1, 0, 0));
|
|
12751
12991
|
if (gone) {
|
|
12752
12992
|
if (L.group) L.group.visible = false;
|
|
@@ -13392,6 +13632,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
|
|
|
13392
13632
|
wear(p, amount, out);
|
|
13393
13633
|
syncZone(p);
|
|
13394
13634
|
if (p.hp <= 0) lose(p, "broken", out, cause);
|
|
13635
|
+
else if (p.def.role === "light") vis?.crackLight(p.def, dtype === "ballistic");
|
|
13395
13636
|
if (p.def.role === "tyre" && !p.flat && p.hp < p.def.hp * 0.6 && !p.lost) {
|
|
13396
13637
|
p.flat = true;
|
|
13397
13638
|
vis?.tyreFlat(p.def, 0.24);
|
|
@@ -14204,7 +14445,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
|
|
|
14204
14445
|
if (!list.length) return 1;
|
|
14205
14446
|
return Math.min(...list.map((p) => p.lost || !attached(p.def.id) ? 0 : p.hp / p.def.hp));
|
|
14206
14447
|
};
|
|
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) }));
|
|
14448
|
+
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
14449
|
const status = computeStatus();
|
|
14209
14450
|
const brakeSystem = [...ps.values()].find((p) => p.def.role === "brakes" && p.def.contains);
|
|
14210
14451
|
const fluidLeft = brakeSystem ? clamp((brakeSystem.liters ?? 0) / brakeSystem.def.contains.liters / BRAKE_FLUID_NEEDED, 0, 1) : 1;
|