@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.d.ts
CHANGED
|
@@ -391,8 +391,11 @@ interface VehicleSpec {
|
|
|
391
391
|
/** Fuel id (a key of `configure({ fuels })`) or `'electric'` / `'none'`. */
|
|
392
392
|
fuel?: string;
|
|
393
393
|
build?: BuildFunction;
|
|
394
|
+
/** What the headlights' lenses are made of (default `polycarbonate`, the lens of cars since the 1990s; `glass` on older ones): glass shatters, polycarbonate cracks. */
|
|
395
|
+
headlightLens?: HeadlightLens;
|
|
394
396
|
[key: string]: any;
|
|
395
397
|
}
|
|
398
|
+
type HeadlightLens = 'glass' | 'polycarbonate';
|
|
396
399
|
/** Builds the geometry of a vehicle into the context. */
|
|
397
400
|
type BuildFunction = (c: VehicleContext, kit: VehicleKit) => void;
|
|
398
401
|
interface VehiclePart {
|
|
@@ -999,10 +1002,12 @@ interface VehicleDestructible extends DestructibleImpl {
|
|
|
999
1002
|
crumple(point: Vec3, direction: Vec3, depth: number): void;
|
|
1000
1003
|
}
|
|
1001
1004
|
type DriveReport = {
|
|
1005
|
+
/** `rim`: the tyre is torn off and the bare rim is on the road. */
|
|
1002
1006
|
readonly tyres: ReadonlyArray<{
|
|
1003
1007
|
readonly x: number;
|
|
1004
1008
|
readonly z: number;
|
|
1005
1009
|
readonly health: number;
|
|
1010
|
+
readonly rim: boolean;
|
|
1006
1011
|
}>;
|
|
1007
1012
|
readonly brakes: number;
|
|
1008
1013
|
readonly engine: number;
|
|
@@ -1122,4 +1127,4 @@ declare function cabinEnclosure(source: CabinSource, options?: CabinEnclosureOpt
|
|
|
1122
1127
|
/** The material of a pane with cracks drawn on it. A host warms its program at load with any texture: the first crack is in the middle of a blast. */
|
|
1123
1128
|
declare function crackedGlassMaterial(THREE: any, texture: unknown): any;
|
|
1124
1129
|
|
|
1125
|
-
export { ARMOUR_LEVELS, type ArmourLevel, type ArmourLevelId, type Band, type BandId, type BandIndex, type BandOverride, type BuildFunction, type BuildOptions, type BuildState, type BuildZoneState, CABIN_LEAK, CRASH_RULES, type CabinBox, type CabinEnclosureOptions, type CabinSource, type CrashOptions, type CrashResult, type CrashRules, type CreaseQueue, type CreaseWork, type DamageMapOptions, type DamageZone, type Damageable, type DamageableKind, type DamageableMaterial, type DamageableSide, type DriveReport, type ExplodeOptions, type FlagEffect, type FluidDefinition, type ImpactOptions, JOINT_RULES, type JointDef, type KitConfiguration, type KitInternals, type KitTexts, LEGACY_STATES, LEGACY_ZONE_IDS, type LocalBox, type LocaleStrings, type LodSettings, type OverallHealth, type PartDef, type PartLabel, type PartRole, type PartialLocaleStrings, type ParticleEffectRequest, type ParticleEffects, type ParticleMaterials, type Plugin, type ResolvedConfiguration, SEED_SALTS, type ScenarioContext, type ScenarioDefinition, type ScenarioResult, type SplatOptions, type ThreeObject, type ThreeVector, type Translate, type UiHelpers, type VehicleContext, type VehicleDestructible, type VehicleDestructibleOptions, type VehicleItem, type VehicleItemOptions, type VehicleKit, type VehicleKitOptions, type VehicleMeta, type VehicleModel, type VehiclePart, type VehicleSpec, type VehicleStatus, type ZoneDamageEntry, type ZoneEffect, type ZoneState, armourLevelOf, buildVehicleItem, buildVehicleModel, cabinBoxOf, cabinEnclosure, clamp, crackedGlassMaterial, crashDeltaV, crashVehicle, createCreaseQueue, createLod, createTranslate, createVehicleDestructible, createVehicleKit, defaultDamageableKinds, defaultLodSettings, format, hash, legacyGlassId, legacyLightId, legacyZoneId, listLocales, registerLegacySeeds, registerLocale, resolveStrings, rng, smooth, strHash, swingOf, wrapThreeForLod };
|
|
1130
|
+
export { ARMOUR_LEVELS, type ArmourLevel, type ArmourLevelId, type Band, type BandId, type BandIndex, type BandOverride, type BuildFunction, type BuildOptions, type BuildState, type BuildZoneState, CABIN_LEAK, CRASH_RULES, type CabinBox, type CabinEnclosureOptions, type CabinSource, type CrashOptions, type CrashResult, type CrashRules, type CreaseQueue, type CreaseWork, type DamageMapOptions, type DamageZone, type Damageable, type DamageableKind, type DamageableMaterial, type DamageableSide, type DriveReport, type ExplodeOptions, type FlagEffect, type FluidDefinition, type HeadlightLens, type ImpactOptions, JOINT_RULES, type JointDef, type KitConfiguration, type KitInternals, type KitTexts, LEGACY_STATES, LEGACY_ZONE_IDS, type LocalBox, type LocaleStrings, type LodSettings, type OverallHealth, type PartDef, type PartLabel, type PartRole, type PartialLocaleStrings, type ParticleEffectRequest, type ParticleEffects, type ParticleMaterials, type Plugin, type ResolvedConfiguration, SEED_SALTS, type ScenarioContext, type ScenarioDefinition, type ScenarioResult, type SplatOptions, type ThreeObject, type ThreeVector, type Translate, type UiHelpers, type VehicleContext, type VehicleDestructible, type VehicleDestructibleOptions, type VehicleItem, type VehicleItemOptions, type VehicleKit, type VehicleKitOptions, type VehicleMeta, type VehicleModel, type VehiclePart, type VehicleSpec, type VehicleStatus, type ZoneDamageEntry, type ZoneEffect, type ZoneState, armourLevelOf, buildVehicleItem, buildVehicleModel, cabinBoxOf, cabinEnclosure, clamp, crackedGlassMaterial, crashDeltaV, crashVehicle, createCreaseQueue, createLod, createTranslate, createVehicleDestructible, createVehicleKit, defaultDamageableKinds, defaultLodSettings, format, hash, legacyGlassId, legacyLightId, legacyZoneId, listLocales, registerLegacySeeds, registerLocale, resolveStrings, rng, smooth, strHash, swingOf, wrapThreeForLod };
|
package/dist/index.global.js
CHANGED
|
@@ -7163,8 +7163,20 @@ var GameVehicles = (() => {
|
|
|
7163
7163
|
if (!L || L.broken) return;
|
|
7164
7164
|
L.broken = true;
|
|
7165
7165
|
L.lens.forEach(function(m) {
|
|
7166
|
-
m.
|
|
7166
|
+
const rim = L.lensMaterial ? glassRim(m.geometry, c.rand) : null;
|
|
7167
|
+
if (!rim) {
|
|
7168
|
+
m.visible = false;
|
|
7169
|
+
return;
|
|
7170
|
+
}
|
|
7171
|
+
m.geometry.dispose();
|
|
7172
|
+
m.geometry = rim;
|
|
7173
|
+
m.material = MATERIALS.glassEdge;
|
|
7174
|
+
m.userData.lensRim = true;
|
|
7167
7175
|
});
|
|
7176
|
+
if (L.lensMaterial)
|
|
7177
|
+
(L.reflector || []).forEach(function(m) {
|
|
7178
|
+
m.material = MATERIALS.broken;
|
|
7179
|
+
});
|
|
7168
7180
|
L.inner.forEach(function(m) {
|
|
7169
7181
|
m.material = MATERIALS.bulbBroken;
|
|
7170
7182
|
});
|
|
@@ -7172,11 +7184,38 @@ var GameVehicles = (() => {
|
|
|
7172
7184
|
h.visible = false;
|
|
7173
7185
|
});
|
|
7174
7186
|
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
7175
|
-
|
|
7176
|
-
I.burst(c, "
|
|
7187
|
+
const glass = L.lensMaterial !== "plastic";
|
|
7188
|
+
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" });
|
|
7189
|
+
I.burst(c, "plastic", c.toEffects(p), glass ? 2 : 5, { dir: c.dirToEffects(dir.clone().negate()), speed: 1 });
|
|
7177
7190
|
}
|
|
7178
7191
|
kit.breakLight = breakLight;
|
|
7179
|
-
|
|
7192
|
+
const crazedCache = /* @__PURE__ */ new Map();
|
|
7193
|
+
const CRAZED_OPACITY = 0.6;
|
|
7194
|
+
function crazed(material) {
|
|
7195
|
+
const known = crazedCache.get(material);
|
|
7196
|
+
if (known) return known;
|
|
7197
|
+
const m = I.crackLens(material).clone();
|
|
7198
|
+
m.opacity = Math.max(m.opacity ?? 1, CRAZED_OPACITY);
|
|
7199
|
+
m.transparent = true;
|
|
7200
|
+
m.userData.shared = true;
|
|
7201
|
+
crazedCache.set(material, m);
|
|
7202
|
+
return m;
|
|
7203
|
+
}
|
|
7204
|
+
function crackLight(c, L, dir, pierced) {
|
|
7205
|
+
if (!L || L.broken) return;
|
|
7206
|
+
if (!L.cracked) {
|
|
7207
|
+
L.cracked = true;
|
|
7208
|
+
for (const m of L.lens) if (m.material && !Array.isArray(m.material)) m.material = crazed(m.material);
|
|
7209
|
+
}
|
|
7210
|
+
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
7211
|
+
I.burst(c, "lens", c.toEffects(p), 3, { dir: c.dirToEffects(dir.clone().negate()), speed: 0.9, variant: L.tint === "red" ? "red" : "clear" });
|
|
7212
|
+
if (!pierced || L.dark) return;
|
|
7213
|
+
L.dark = true;
|
|
7214
|
+
for (const m of L.inner) m.material = MATERIALS.bulbBroken;
|
|
7215
|
+
for (const h of L.halo ?? []) h.visible = false;
|
|
7216
|
+
}
|
|
7217
|
+
kit.crackLight = crackLight;
|
|
7218
|
+
Object.assign(I, { glassUV, drawWeb, crackGlass, glassRim, shatterGlass, hitGlass, breakLight, crackLight });
|
|
7180
7219
|
}
|
|
7181
7220
|
|
|
7182
7221
|
// src/impact.ts
|
|
@@ -12726,6 +12765,8 @@ var GameVehicles = (() => {
|
|
|
12726
12765
|
heli: ["battery", "alternator", "gearbox"]
|
|
12727
12766
|
};
|
|
12728
12767
|
var HITCH_CHANCE = { car: 30, truck: 60, bus: 0, tractor: 100, quad: 40, bike: 0, tank: 0, boat: 0, plane: 0, heli: 0 };
|
|
12768
|
+
var GLASS_LENS_HP = 320;
|
|
12769
|
+
var PLASTIC_LENS_HP = 900;
|
|
12729
12770
|
var STEEL_KG_M2 = 7;
|
|
12730
12771
|
var ALU_KG_M2 = 3.2;
|
|
12731
12772
|
var kindOf = (c) => {
|
|
@@ -12883,7 +12924,9 @@ var GameVehicles = (() => {
|
|
|
12883
12924
|
continue;
|
|
12884
12925
|
}
|
|
12885
12926
|
if (role === "light") {
|
|
12886
|
-
|
|
12927
|
+
const lens = /^head/.test(id) ? spec.headlightLens ?? "polycarbonate" : "acrylic";
|
|
12928
|
+
const glassLens = lens === "glass";
|
|
12929
|
+
add({ 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] });
|
|
12887
12930
|
continue;
|
|
12888
12931
|
}
|
|
12889
12932
|
if (role === "bumper") {
|
|
@@ -13340,21 +13383,188 @@ var GameVehicles = (() => {
|
|
|
13340
13383
|
mesh2.geometry = g;
|
|
13341
13384
|
}
|
|
13342
13385
|
|
|
13386
|
+
// src/destructible/door-open.ts
|
|
13387
|
+
var STRIP_SMOOTH_DEG = 180;
|
|
13388
|
+
var SKIN_BAND = 0.05;
|
|
13389
|
+
var BODY_FACING = 0.5;
|
|
13390
|
+
var FIELDS = 4;
|
|
13391
|
+
var SEAM_THIN = 0.03;
|
|
13392
|
+
var SEAM_AT_EDGE = 0.04;
|
|
13393
|
+
var SEAM_TALL = 0.4;
|
|
13394
|
+
var SLIVER_AREA = 1e-10;
|
|
13395
|
+
function cutStripOverDoor(vehicleKit, params) {
|
|
13396
|
+
const kit = vehicleKit;
|
|
13397
|
+
const strip = params.strip;
|
|
13398
|
+
const toVehicle = params.toVehicle;
|
|
13399
|
+
const { x0, x1 } = params;
|
|
13400
|
+
const THREE = kit.THREE;
|
|
13401
|
+
const at = new THREE.Vector3();
|
|
13402
|
+
const along = (x, y, z) => at.set(x, y, z).applyMatrix4(toVehicle).x;
|
|
13403
|
+
const piece = (tests) => {
|
|
13404
|
+
const mesh2 = new THREE.Mesh(strip.geometry.clone(), strip.material);
|
|
13405
|
+
kit.cutMesh(mesh2, tests, STRIP_SMOOTH_DEG, {});
|
|
13406
|
+
return mesh2;
|
|
13407
|
+
};
|
|
13408
|
+
const over = piece([{ sdf: (x, y, z) => along(x, y, z) - x0 }, { sdf: (x, y, z) => x1 - along(x, y, z) }]);
|
|
13409
|
+
const after = piece([{ sdf: (x, y, z) => along(x, y, z) - x1 }]);
|
|
13410
|
+
kit.cutMesh(strip, [{ sdf: (x, y, z) => x0 - along(x, y, z) }], STRIP_SMOOTH_DEG, {});
|
|
13411
|
+
const joined = joinTriangles(THREE, strip.geometry, after.geometry);
|
|
13412
|
+
strip.geometry.dispose();
|
|
13413
|
+
after.geometry.dispose();
|
|
13414
|
+
strip.geometry = joined;
|
|
13415
|
+
return over;
|
|
13416
|
+
}
|
|
13417
|
+
function joinTriangles(THREE, first, second) {
|
|
13418
|
+
const a = first.index ? first.toNonIndexed() : first;
|
|
13419
|
+
const b = second.index ? second.toNonIndexed() : second;
|
|
13420
|
+
const flags = [...solidTriangles(THREE, a), ...solidTriangles(THREE, b)];
|
|
13421
|
+
const both = new THREE.BufferGeometry();
|
|
13422
|
+
for (const name of Object.keys(a.attributes)) {
|
|
13423
|
+
const p = a.getAttribute(name);
|
|
13424
|
+
const q = b.getAttribute(name);
|
|
13425
|
+
if (!q || q.itemSize !== p.itemSize) continue;
|
|
13426
|
+
const array = new Float32Array(p.array.length + q.array.length);
|
|
13427
|
+
array.set(p.array, 0);
|
|
13428
|
+
array.set(q.array, p.array.length);
|
|
13429
|
+
both.setAttribute(name, new THREE.Float32BufferAttribute(array, p.itemSize));
|
|
13430
|
+
}
|
|
13431
|
+
if (a !== first) a.dispose();
|
|
13432
|
+
if (b !== second) b.dispose();
|
|
13433
|
+
const out = pickTriangles(THREE, both, flags, true) ?? new THREE.BufferGeometry();
|
|
13434
|
+
both.dispose();
|
|
13435
|
+
return out;
|
|
13436
|
+
}
|
|
13437
|
+
function solidTriangles(THREE, geometry) {
|
|
13438
|
+
const pos = geometry.getAttribute("position");
|
|
13439
|
+
const triangle = new THREE.Triangle();
|
|
13440
|
+
const flags = [];
|
|
13441
|
+
for (let t = 0; t * 3 + 2 < pos.count; t++) {
|
|
13442
|
+
triangle.a.fromBufferAttribute(pos, t * 3);
|
|
13443
|
+
triangle.b.fromBufferAttribute(pos, t * 3 + 1);
|
|
13444
|
+
triangle.c.fromBufferAttribute(pos, t * 3 + 2);
|
|
13445
|
+
flags.push(triangle.getArea() > SLIVER_AREA);
|
|
13446
|
+
}
|
|
13447
|
+
return flags;
|
|
13448
|
+
}
|
|
13449
|
+
function splitDoorSkin(three, meshes, toVehicleOf, side2, depth) {
|
|
13450
|
+
const THREE = three;
|
|
13451
|
+
const pieces = meshes;
|
|
13452
|
+
const toVehicle = toVehicleOf;
|
|
13453
|
+
const v = new THREE.Vector3();
|
|
13454
|
+
const corners = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()];
|
|
13455
|
+
const triangle = new THREE.Triangle();
|
|
13456
|
+
const centres = pieces.map((mesh2) => {
|
|
13457
|
+
const geometry = mesh2.geometry.index ? mesh2.geometry.toNonIndexed() : mesh2.geometry;
|
|
13458
|
+
const matrix = toVehicle(mesh2);
|
|
13459
|
+
const pos = geometry.getAttribute("position");
|
|
13460
|
+
const out = new Float32Array(pos.count / 3 * FIELDS);
|
|
13461
|
+
for (let t = 0; t * 3 + 2 < pos.count; t++) {
|
|
13462
|
+
let y = 0;
|
|
13463
|
+
let across = 0;
|
|
13464
|
+
for (let k = 0; k < 3; k++) {
|
|
13465
|
+
corners[k].fromBufferAttribute(pos, t * 3 + k).applyMatrix4(matrix);
|
|
13466
|
+
y += corners[k].y / 3;
|
|
13467
|
+
across += side2 * corners[k].z / 3;
|
|
13468
|
+
}
|
|
13469
|
+
out[t * FIELDS] = y;
|
|
13470
|
+
out[t * FIELDS + 1] = across;
|
|
13471
|
+
triangle.set(corners[0], corners[1], corners[2]).getNormal(v);
|
|
13472
|
+
out[t * FIELDS + 2] = v.y;
|
|
13473
|
+
out[t * FIELDS + 3] = v.x;
|
|
13474
|
+
}
|
|
13475
|
+
return { geometry, out };
|
|
13476
|
+
});
|
|
13477
|
+
const outer = /* @__PURE__ */ new Map();
|
|
13478
|
+
for (const { out } of centres)
|
|
13479
|
+
for (let t = 0; t * FIELDS < out.length; t++) {
|
|
13480
|
+
const band = Math.floor(out[t * FIELDS] / SKIN_BAND);
|
|
13481
|
+
outer.set(band, Math.max(outer.get(band) ?? -Infinity, out[t * FIELDS + 1]));
|
|
13482
|
+
}
|
|
13483
|
+
const surfaceAt = (y) => {
|
|
13484
|
+
const band = Math.floor(y / SKIN_BAND);
|
|
13485
|
+
return Math.max(outer.get(band - 1) ?? -Infinity, outer.get(band) ?? -Infinity, outer.get(band + 1) ?? -Infinity);
|
|
13486
|
+
};
|
|
13487
|
+
return centres.map(({ geometry, out }, i) => {
|
|
13488
|
+
const near = [];
|
|
13489
|
+
for (let t = 0; t * FIELDS < out.length; t++) {
|
|
13490
|
+
const f = t * FIELDS;
|
|
13491
|
+
const body = out[f + 1] < surfaceAt(out[f]) - depth && (out[f + 2] < -BODY_FACING || Math.abs(out[f + 3]) > BODY_FACING);
|
|
13492
|
+
near.push(!body);
|
|
13493
|
+
}
|
|
13494
|
+
const result = { skin: pickTriangles(THREE, geometry, near, true), deep: pickTriangles(THREE, geometry, near, false) };
|
|
13495
|
+
if (geometry !== pieces[i].geometry) geometry.dispose();
|
|
13496
|
+
return result;
|
|
13497
|
+
});
|
|
13498
|
+
}
|
|
13499
|
+
function leaveDeepSkinOnBody(three, meshes, params) {
|
|
13500
|
+
const THREE = three;
|
|
13501
|
+
const pieces = meshes;
|
|
13502
|
+
const toVehicle = params.toVehicle;
|
|
13503
|
+
const { side: side2, depth } = params;
|
|
13504
|
+
const split = splitDoorSkin(THREE, pieces, (mesh2) => new THREE.Matrix4().multiplyMatrices(toVehicle, mesh2.matrixWorld), side2, depth);
|
|
13505
|
+
split.forEach(({ skin, deep }, i) => {
|
|
13506
|
+
const piece = pieces[i];
|
|
13507
|
+
if (!deep || !skin || !piece.parent) return;
|
|
13508
|
+
const body = new THREE.Mesh(deep, piece.material);
|
|
13509
|
+
body.position.copy(piece.position);
|
|
13510
|
+
body.quaternion.copy(piece.quaternion);
|
|
13511
|
+
body.scale.copy(piece.scale);
|
|
13512
|
+
body.castShadow = piece.castShadow;
|
|
13513
|
+
body.receiveShadow = piece.receiveShadow;
|
|
13514
|
+
piece.parent.add(body);
|
|
13515
|
+
piece.geometry.dispose();
|
|
13516
|
+
piece.geometry = skin;
|
|
13517
|
+
});
|
|
13518
|
+
}
|
|
13519
|
+
function pickTriangles(THREE, geometry, flags, wanted) {
|
|
13520
|
+
const count = flags.filter((flag) => flag === wanted).length;
|
|
13521
|
+
if (!count) return null;
|
|
13522
|
+
const out = new THREE.BufferGeometry();
|
|
13523
|
+
for (const name of Object.keys(geometry.attributes)) {
|
|
13524
|
+
const attribute = geometry.getAttribute(name);
|
|
13525
|
+
const size = attribute.itemSize;
|
|
13526
|
+
const source = attribute.array;
|
|
13527
|
+
const array = new Float32Array(count * 3 * size);
|
|
13528
|
+
let w = 0;
|
|
13529
|
+
flags.forEach((flag, t) => {
|
|
13530
|
+
if (flag !== wanted) return;
|
|
13531
|
+
array.set(source.subarray(t * 3 * size, (t + 1) * 3 * size), w);
|
|
13532
|
+
w += 3 * size;
|
|
13533
|
+
});
|
|
13534
|
+
out.setAttribute(name, new THREE.Float32BufferAttribute(array, size));
|
|
13535
|
+
}
|
|
13536
|
+
out.computeBoundingBox();
|
|
13537
|
+
out.computeBoundingSphere();
|
|
13538
|
+
return out;
|
|
13539
|
+
}
|
|
13540
|
+
function isDoorSeam(three, params) {
|
|
13541
|
+
const THREE = three;
|
|
13542
|
+
const box2 = params.box;
|
|
13543
|
+
const { zone, side: side2, dark } = params;
|
|
13544
|
+
if (!dark || box2.isEmpty()) return false;
|
|
13545
|
+
const size = box2.getSize(new THREE.Vector3());
|
|
13546
|
+
const centre = box2.getCenter(new THREE.Vector3());
|
|
13547
|
+
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;
|
|
13548
|
+
if (centre.y < zone.y0 || centre.y > zone.y1) return false;
|
|
13549
|
+
return Math.abs(centre.x - zone.x0) < SEAM_AT_EDGE || Math.abs(centre.x - zone.x1) < SEAM_AT_EDGE;
|
|
13550
|
+
}
|
|
13551
|
+
|
|
13343
13552
|
// src/destructible/visual.ts
|
|
13344
13553
|
var HOLE_DECAL_SCALE = 2.4;
|
|
13345
13554
|
var DOOR_DEPTH = 0.07;
|
|
13555
|
+
var DOOR_SKIN_DEPTH = 0.09;
|
|
13556
|
+
var DOOR_FLOOR_DEPTH = 0.2;
|
|
13346
13557
|
var DOOR_EDGE_PIECES = 8;
|
|
13347
13558
|
var DOOR_EDGE_REACH = 0.08;
|
|
13348
13559
|
var DOOR_CARD_COLOR = "#3b3e42";
|
|
13349
13560
|
var DOOR_LINER_COLOR = "#5a5e64";
|
|
13350
13561
|
var DOOR_LINER_DARK = 0.15;
|
|
13351
|
-
var DOOR_STRIP_THIN = 0.
|
|
13562
|
+
var DOOR_STRIP_THIN = 0.13;
|
|
13352
13563
|
var DOOR_STRIP_NEAR = 0.04;
|
|
13353
13564
|
var DOOR_STRIP_TALL = 0.22;
|
|
13354
|
-
var
|
|
13355
|
-
var DOOR_STRIP_VERTICES = 2e4;
|
|
13565
|
+
var DOOR_STRIP_VERTICES = 6e4;
|
|
13356
13566
|
var DOOR_SILL = 0.25;
|
|
13357
|
-
var DOOR_STRIP_OVER = 0.
|
|
13567
|
+
var DOOR_STRIP_OVER = 0.05;
|
|
13358
13568
|
var DOOR_TOP_OVER_PANE = 0.03;
|
|
13359
13569
|
var HOLE_TEXTURE_SIZE = 64;
|
|
13360
13570
|
var LAMINATED_HP = 1e3;
|
|
@@ -13463,6 +13673,10 @@ var GameVehicles = (() => {
|
|
|
13463
13673
|
__publicField(this, "skins", /* @__PURE__ */ new Map());
|
|
13464
13674
|
/** How high each door's cut reaches. */
|
|
13465
13675
|
__publicField(this, "doorTops", /* @__PURE__ */ new Map());
|
|
13676
|
+
/** 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. */
|
|
13677
|
+
__publicField(this, "seams", /* @__PURE__ */ new Map());
|
|
13678
|
+
/** Doors and lids standing open now. */
|
|
13679
|
+
__publicField(this, "openNow", /* @__PURE__ */ new Set());
|
|
13466
13680
|
__publicField(this, "lightsOn", true);
|
|
13467
13681
|
/** Heavy body meshes whose triangle index is still to build (null once done). */
|
|
13468
13682
|
__publicField(this, "warming");
|
|
@@ -13644,6 +13858,11 @@ var GameVehicles = (() => {
|
|
|
13644
13858
|
}
|
|
13645
13859
|
panel.visible = open && !this.skins.get(id);
|
|
13646
13860
|
recess.visible = open && !this.skins.get(id);
|
|
13861
|
+
if (open) this.openNow.add(id);
|
|
13862
|
+
else this.openNow.delete(id);
|
|
13863
|
+
this.seams.forEach((doors, seam) => {
|
|
13864
|
+
if (doors.has(id)) seam.visible = ![...doors].some((door) => this.openNow.has(door));
|
|
13865
|
+
});
|
|
13647
13866
|
};
|
|
13648
13867
|
const euler = piv.rotation;
|
|
13649
13868
|
const quaternion = piv.quaternion;
|
|
@@ -13686,11 +13905,13 @@ var GameVehicles = (() => {
|
|
|
13686
13905
|
const pcs = I.cutPanel(this.c, z, rect, true, dirty, { anyN: true }).map((x) => x.mesh).filter((m) => !!m);
|
|
13687
13906
|
this.mend(dirty);
|
|
13688
13907
|
if (pcs.length) {
|
|
13908
|
+
leaveDeepSkinOnBody(this.THREE, pcs, { toVehicle: inv, side: z.side < 0 ? -1 : 1, depth: DOOR_SKIN_DEPTH });
|
|
13689
13909
|
const panes = I.windowsOver(this.c, z, inv);
|
|
13690
13910
|
const seals = panes.map((g) => I.paneSeals.get(g)).filter((s) => !!s);
|
|
13691
13911
|
const mirrors = this.mirrorsOn(z);
|
|
13692
13912
|
const frames = this.framesAround(panes, inv);
|
|
13693
|
-
|
|
13913
|
+
const followers = I.followersIn(this.c, z, inv).filter((o) => !this.takeSeam(o, id, z, inv) && !this.inFloor(o, piv, z, inv));
|
|
13914
|
+
objs = [...pcs, ...followers, ...panes, ...seals, ...mirrors, ...frames];
|
|
13694
13915
|
}
|
|
13695
13916
|
} else {
|
|
13696
13917
|
const pcs = this.cutZone(z);
|
|
@@ -13718,6 +13939,25 @@ var GameVehicles = (() => {
|
|
|
13718
13939
|
this.c.root.updateMatrixWorld(true);
|
|
13719
13940
|
return objs.length > 0;
|
|
13720
13941
|
}
|
|
13942
|
+
/** 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. */
|
|
13943
|
+
inFloor(o, piv, z, inv) {
|
|
13944
|
+
const side2 = z.side < 0 ? -1 : 1;
|
|
13945
|
+
const centre = this.I.localBox(this.c, o, inv).getCenter(new this.THREE.Vector3());
|
|
13946
|
+
const skin = side2 * (piv.position.z - side2 * 0.016);
|
|
13947
|
+
return centre.y < z.box.y0 + DOOR_SILL && side2 * centre.z < skin - DOOR_FLOOR_DEPTH;
|
|
13948
|
+
}
|
|
13949
|
+
/** 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. */
|
|
13950
|
+
takeSeam(o, id, z, inv) {
|
|
13951
|
+
const B = z.box;
|
|
13952
|
+
const box2 = this.I.localBox(this.c, o, inv);
|
|
13953
|
+
const color2 = o.material?.color;
|
|
13954
|
+
const dark = !!color2 && color2.getHSL({ h: 0, s: 0, l: 0 }).l < DOOR_LINER_DARK;
|
|
13955
|
+
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;
|
|
13956
|
+
const doors = this.seams.get(o) ?? /* @__PURE__ */ new Set();
|
|
13957
|
+
doors.add(id);
|
|
13958
|
+
this.seams.set(o, doors);
|
|
13959
|
+
return true;
|
|
13960
|
+
}
|
|
13721
13961
|
/** 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. */
|
|
13722
13962
|
doorEdges(piv, z, y1) {
|
|
13723
13963
|
const THREE = this.THREE;
|
|
@@ -13739,13 +13979,15 @@ var GameVehicles = (() => {
|
|
|
13739
13979
|
const at = this.surface("z", side2, B, [[x, y]]);
|
|
13740
13980
|
return at != null && Math.abs(at - flat) < DOOR_EDGE_REACH ? at : flat;
|
|
13741
13981
|
});
|
|
13742
|
-
const band = (lift) => {
|
|
13982
|
+
const band = (lift, path, closed) => {
|
|
13743
13983
|
const position = [];
|
|
13744
13984
|
const index = [];
|
|
13745
|
-
|
|
13746
|
-
|
|
13985
|
+
path.forEach((at, i) => {
|
|
13986
|
+
const [x, y] = loop[at];
|
|
13987
|
+
position.push(x, y, heights[at] + side2 * lift, x, y, heights[at] + side2 * (lift - DOOR_DEPTH));
|
|
13988
|
+
if (!closed && i === path.length - 1) return;
|
|
13747
13989
|
const a = i * 2;
|
|
13748
|
-
const b = (i + 1) %
|
|
13990
|
+
const b = (i + 1) % path.length * 2;
|
|
13749
13991
|
index.push(a, a + 1, b, b, a + 1, b + 1);
|
|
13750
13992
|
});
|
|
13751
13993
|
const g = new THREE.BufferGeometry();
|
|
@@ -13773,8 +14015,11 @@ var GameVehicles = (() => {
|
|
|
13773
14015
|
card.castShadow = false;
|
|
13774
14016
|
this.c.vehicle.add(card);
|
|
13775
14017
|
piv.attach(card);
|
|
13776
|
-
const
|
|
13777
|
-
const
|
|
14018
|
+
const all = loop.map((_, i) => i);
|
|
14019
|
+
const top = 2 * DOOR_EDGE_PIECES;
|
|
14020
|
+
const sides = [...all.slice(3 * DOOR_EDGE_PIECES), ...all.slice(0, top + 1)];
|
|
14021
|
+
const edges = new THREE.Mesh(band(2e-3, all, true), paint);
|
|
14022
|
+
const lining = new THREE.Mesh(band(2e-3, sides, false), paint);
|
|
13778
14023
|
for (const m of [edges, lining]) {
|
|
13779
14024
|
m.userData.noFollow = true;
|
|
13780
14025
|
m.castShadow = false;
|
|
@@ -13831,26 +14076,14 @@ var GameVehicles = (() => {
|
|
|
13831
14076
|
const box2 = this.I.localBox(this.c, o, inv);
|
|
13832
14077
|
const size = box2.getSize(new THREE.Vector3());
|
|
13833
14078
|
const mid = box2.getCenter(new THREE.Vector3());
|
|
13834
|
-
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) <
|
|
14079
|
+
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;
|
|
13835
14080
|
const toVehicle = new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld);
|
|
13836
|
-
const
|
|
13837
|
-
const sdf = (x, y, zz) => {
|
|
13838
|
-
v.set(x, y, zz).applyMatrix4(toVehicle);
|
|
13839
|
-
return Math.max(x0 - v.x, v.x - x1);
|
|
13840
|
-
};
|
|
13841
|
-
const over = new THREE.Mesh(o.geometry.clone(), o.material);
|
|
14081
|
+
const over = cutStripOverDoor(this.kit, { strip: o, toVehicle, x0, x1 });
|
|
13842
14082
|
over.position.copy(o.position);
|
|
13843
14083
|
over.quaternion.copy(o.quaternion);
|
|
13844
14084
|
over.scale.copy(o.scale);
|
|
13845
14085
|
this.c.vehicle.add(over);
|
|
13846
14086
|
over.updateMatrixWorld(true);
|
|
13847
|
-
const overToVehicle = new THREE.Matrix4().multiplyMatrices(inv, over.matrixWorld);
|
|
13848
|
-
const w = new THREE.Vector3();
|
|
13849
|
-
this.kit.cutMesh(over, [{ sdf: (x, y, zz) => {
|
|
13850
|
-
w.set(x, y, zz).applyMatrix4(overToVehicle);
|
|
13851
|
-
return -Math.max(x0 - w.x, w.x - x1);
|
|
13852
|
-
} }], STRIP_SMOOTH_DEG, {});
|
|
13853
|
-
this.kit.cutMesh(o, [{ sdf }], STRIP_SMOOTH_DEG, {});
|
|
13854
14087
|
over.userData.noFollow = true;
|
|
13855
14088
|
piv.attach(over);
|
|
13856
14089
|
}
|
|
@@ -14431,11 +14664,18 @@ var GameVehicles = (() => {
|
|
|
14431
14664
|
}
|
|
14432
14665
|
}
|
|
14433
14666
|
}
|
|
14667
|
+
/** 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. */
|
|
14668
|
+
crackLight(p, pierced) {
|
|
14669
|
+
const z = this.zone(p);
|
|
14670
|
+
if (!z) return;
|
|
14671
|
+
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);
|
|
14672
|
+
}
|
|
14434
14673
|
/** The lens of a light breaks and its unit stays (damaged) or goes (destroyed). */
|
|
14435
14674
|
breakLight(p, gone) {
|
|
14436
14675
|
const z = this.zone(p);
|
|
14437
14676
|
if (!z) return;
|
|
14438
14677
|
for (const L of z.lights) {
|
|
14678
|
+
L.lensMaterial = p.material;
|
|
14439
14679
|
this.kit.breakLight(this.c, L, new this.THREE.Vector3(p.box.center[0] > 0 ? -1 : 1, 0, 0));
|
|
14440
14680
|
if (gone) {
|
|
14441
14681
|
if (L.group) L.group.visible = false;
|
|
@@ -15081,6 +15321,7 @@ var GameVehicles = (() => {
|
|
|
15081
15321
|
wear(p, amount, out);
|
|
15082
15322
|
syncZone(p);
|
|
15083
15323
|
if (p.hp <= 0) lose(p, "broken", out, cause);
|
|
15324
|
+
else if (p.def.role === "light") vis?.crackLight(p.def, dtype === "ballistic");
|
|
15084
15325
|
if (p.def.role === "tyre" && !p.flat && p.hp < p.def.hp * 0.6 && !p.lost) {
|
|
15085
15326
|
p.flat = true;
|
|
15086
15327
|
vis?.tyreFlat(p.def, 0.24);
|
|
@@ -15893,7 +16134,7 @@ var GameVehicles = (() => {
|
|
|
15893
16134
|
if (!list2.length) return 1;
|
|
15894
16135
|
return Math.min(...list2.map((p) => p.lost || !attached(p.def.id) ? 0 : p.hp / p.def.hp));
|
|
15895
16136
|
};
|
|
15896
|
-
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) }));
|
|
16137
|
+
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 }));
|
|
15897
16138
|
const status = computeStatus();
|
|
15898
16139
|
const brakeSystem = [...ps.values()].find((p) => p.def.role === "brakes" && p.def.contains);
|
|
15899
16140
|
const fluidLeft = brakeSystem ? clamp((brakeSystem.liters ?? 0) / brakeSystem.def.contains.liters / BRAKE_FLUID_NEEDED, 0, 1) : 1;
|