@adatechnology/game-vehicles 0.6.1 → 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 +273 -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 +5 -5
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
|
@@ -1451,6 +1451,7 @@ var GameVehicles = (() => {
|
|
|
1451
1451
|
};
|
|
1452
1452
|
|
|
1453
1453
|
// ../game-contracts/dist/index.js
|
|
1454
|
+
var DEFAULT_BLAST_IMPULSE = { bodyArea: 0.7, lift: 0.15, minSpeed: 0.3, tntPerJoule: 0.24 / 2e5 };
|
|
1454
1455
|
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
1455
1456
|
var DEFAULT_IGNITION_RULES = {
|
|
1456
1457
|
fillCurve: [
|
|
@@ -7162,8 +7163,20 @@ var GameVehicles = (() => {
|
|
|
7162
7163
|
if (!L || L.broken) return;
|
|
7163
7164
|
L.broken = true;
|
|
7164
7165
|
L.lens.forEach(function(m) {
|
|
7165
|
-
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;
|
|
7166
7175
|
});
|
|
7176
|
+
if (L.lensMaterial)
|
|
7177
|
+
(L.reflector || []).forEach(function(m) {
|
|
7178
|
+
m.material = MATERIALS.broken;
|
|
7179
|
+
});
|
|
7167
7180
|
L.inner.forEach(function(m) {
|
|
7168
7181
|
m.material = MATERIALS.bulbBroken;
|
|
7169
7182
|
});
|
|
@@ -7171,11 +7184,38 @@ var GameVehicles = (() => {
|
|
|
7171
7184
|
h.visible = false;
|
|
7172
7185
|
});
|
|
7173
7186
|
const p = L.group ? L.group.position.clone() : L.inner[0] ? L.inner[0].position.clone() : new V3();
|
|
7174
|
-
|
|
7175
|
-
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 });
|
|
7176
7190
|
}
|
|
7177
7191
|
kit.breakLight = breakLight;
|
|
7178
|
-
|
|
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 });
|
|
7179
7219
|
}
|
|
7180
7220
|
|
|
7181
7221
|
// src/impact.ts
|
|
@@ -12725,6 +12765,8 @@ var GameVehicles = (() => {
|
|
|
12725
12765
|
heli: ["battery", "alternator", "gearbox"]
|
|
12726
12766
|
};
|
|
12727
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;
|
|
12728
12770
|
var STEEL_KG_M2 = 7;
|
|
12729
12771
|
var ALU_KG_M2 = 3.2;
|
|
12730
12772
|
var kindOf = (c) => {
|
|
@@ -12882,7 +12924,9 @@ var GameVehicles = (() => {
|
|
|
12882
12924
|
continue;
|
|
12883
12925
|
}
|
|
12884
12926
|
if (role === "light") {
|
|
12885
|
-
|
|
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] });
|
|
12886
12930
|
continue;
|
|
12887
12931
|
}
|
|
12888
12932
|
if (role === "bumper") {
|
|
@@ -13339,21 +13383,188 @@ var GameVehicles = (() => {
|
|
|
13339
13383
|
mesh2.geometry = g;
|
|
13340
13384
|
}
|
|
13341
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
|
+
|
|
13342
13552
|
// src/destructible/visual.ts
|
|
13343
13553
|
var HOLE_DECAL_SCALE = 2.4;
|
|
13344
13554
|
var DOOR_DEPTH = 0.07;
|
|
13555
|
+
var DOOR_SKIN_DEPTH = 0.09;
|
|
13556
|
+
var DOOR_FLOOR_DEPTH = 0.2;
|
|
13345
13557
|
var DOOR_EDGE_PIECES = 8;
|
|
13346
13558
|
var DOOR_EDGE_REACH = 0.08;
|
|
13347
13559
|
var DOOR_CARD_COLOR = "#3b3e42";
|
|
13348
13560
|
var DOOR_LINER_COLOR = "#5a5e64";
|
|
13349
13561
|
var DOOR_LINER_DARK = 0.15;
|
|
13350
|
-
var DOOR_STRIP_THIN = 0.
|
|
13562
|
+
var DOOR_STRIP_THIN = 0.13;
|
|
13351
13563
|
var DOOR_STRIP_NEAR = 0.04;
|
|
13352
13564
|
var DOOR_STRIP_TALL = 0.22;
|
|
13353
|
-
var
|
|
13354
|
-
var DOOR_STRIP_VERTICES = 2e4;
|
|
13565
|
+
var DOOR_STRIP_VERTICES = 6e4;
|
|
13355
13566
|
var DOOR_SILL = 0.25;
|
|
13356
|
-
var DOOR_STRIP_OVER = 0.
|
|
13567
|
+
var DOOR_STRIP_OVER = 0.05;
|
|
13357
13568
|
var DOOR_TOP_OVER_PANE = 0.03;
|
|
13358
13569
|
var HOLE_TEXTURE_SIZE = 64;
|
|
13359
13570
|
var LAMINATED_HP = 1e3;
|
|
@@ -13462,6 +13673,10 @@ var GameVehicles = (() => {
|
|
|
13462
13673
|
__publicField(this, "skins", /* @__PURE__ */ new Map());
|
|
13463
13674
|
/** How high each door's cut reaches. */
|
|
13464
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());
|
|
13465
13680
|
__publicField(this, "lightsOn", true);
|
|
13466
13681
|
/** Heavy body meshes whose triangle index is still to build (null once done). */
|
|
13467
13682
|
__publicField(this, "warming");
|
|
@@ -13643,6 +13858,11 @@ var GameVehicles = (() => {
|
|
|
13643
13858
|
}
|
|
13644
13859
|
panel.visible = open && !this.skins.get(id);
|
|
13645
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
|
+
});
|
|
13646
13866
|
};
|
|
13647
13867
|
const euler = piv.rotation;
|
|
13648
13868
|
const quaternion = piv.quaternion;
|
|
@@ -13685,11 +13905,13 @@ var GameVehicles = (() => {
|
|
|
13685
13905
|
const pcs = I.cutPanel(this.c, z, rect, true, dirty, { anyN: true }).map((x) => x.mesh).filter((m) => !!m);
|
|
13686
13906
|
this.mend(dirty);
|
|
13687
13907
|
if (pcs.length) {
|
|
13908
|
+
leaveDeepSkinOnBody(this.THREE, pcs, { toVehicle: inv, side: z.side < 0 ? -1 : 1, depth: DOOR_SKIN_DEPTH });
|
|
13688
13909
|
const panes = I.windowsOver(this.c, z, inv);
|
|
13689
13910
|
const seals = panes.map((g) => I.paneSeals.get(g)).filter((s) => !!s);
|
|
13690
13911
|
const mirrors = this.mirrorsOn(z);
|
|
13691
13912
|
const frames = this.framesAround(panes, inv);
|
|
13692
|
-
|
|
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];
|
|
13693
13915
|
}
|
|
13694
13916
|
} else {
|
|
13695
13917
|
const pcs = this.cutZone(z);
|
|
@@ -13717,6 +13939,25 @@ var GameVehicles = (() => {
|
|
|
13717
13939
|
this.c.root.updateMatrixWorld(true);
|
|
13718
13940
|
return objs.length > 0;
|
|
13719
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
|
+
}
|
|
13720
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. */
|
|
13721
13962
|
doorEdges(piv, z, y1) {
|
|
13722
13963
|
const THREE = this.THREE;
|
|
@@ -13738,13 +13979,15 @@ var GameVehicles = (() => {
|
|
|
13738
13979
|
const at = this.surface("z", side2, B, [[x, y]]);
|
|
13739
13980
|
return at != null && Math.abs(at - flat) < DOOR_EDGE_REACH ? at : flat;
|
|
13740
13981
|
});
|
|
13741
|
-
const band = (lift) => {
|
|
13982
|
+
const band = (lift, path, closed) => {
|
|
13742
13983
|
const position = [];
|
|
13743
13984
|
const index = [];
|
|
13744
|
-
|
|
13745
|
-
|
|
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;
|
|
13746
13989
|
const a = i * 2;
|
|
13747
|
-
const b = (i + 1) %
|
|
13990
|
+
const b = (i + 1) % path.length * 2;
|
|
13748
13991
|
index.push(a, a + 1, b, b, a + 1, b + 1);
|
|
13749
13992
|
});
|
|
13750
13993
|
const g = new THREE.BufferGeometry();
|
|
@@ -13772,8 +14015,11 @@ var GameVehicles = (() => {
|
|
|
13772
14015
|
card.castShadow = false;
|
|
13773
14016
|
this.c.vehicle.add(card);
|
|
13774
14017
|
piv.attach(card);
|
|
13775
|
-
const
|
|
13776
|
-
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);
|
|
13777
14023
|
for (const m of [edges, lining]) {
|
|
13778
14024
|
m.userData.noFollow = true;
|
|
13779
14025
|
m.castShadow = false;
|
|
@@ -13830,26 +14076,14 @@ var GameVehicles = (() => {
|
|
|
13830
14076
|
const box2 = this.I.localBox(this.c, o, inv);
|
|
13831
14077
|
const size = box2.getSize(new THREE.Vector3());
|
|
13832
14078
|
const mid = box2.getCenter(new THREE.Vector3());
|
|
13833
|
-
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;
|
|
13834
14080
|
const toVehicle = new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld);
|
|
13835
|
-
const
|
|
13836
|
-
const sdf = (x, y, zz) => {
|
|
13837
|
-
v.set(x, y, zz).applyMatrix4(toVehicle);
|
|
13838
|
-
return Math.max(x0 - v.x, v.x - x1);
|
|
13839
|
-
};
|
|
13840
|
-
const over = new THREE.Mesh(o.geometry.clone(), o.material);
|
|
14081
|
+
const over = cutStripOverDoor(this.kit, { strip: o, toVehicle, x0, x1 });
|
|
13841
14082
|
over.position.copy(o.position);
|
|
13842
14083
|
over.quaternion.copy(o.quaternion);
|
|
13843
14084
|
over.scale.copy(o.scale);
|
|
13844
14085
|
this.c.vehicle.add(over);
|
|
13845
14086
|
over.updateMatrixWorld(true);
|
|
13846
|
-
const overToVehicle = new THREE.Matrix4().multiplyMatrices(inv, over.matrixWorld);
|
|
13847
|
-
const w = new THREE.Vector3();
|
|
13848
|
-
this.kit.cutMesh(over, [{ sdf: (x, y, zz) => {
|
|
13849
|
-
w.set(x, y, zz).applyMatrix4(overToVehicle);
|
|
13850
|
-
return -Math.max(x0 - w.x, w.x - x1);
|
|
13851
|
-
} }], STRIP_SMOOTH_DEG, {});
|
|
13852
|
-
this.kit.cutMesh(o, [{ sdf }], STRIP_SMOOTH_DEG, {});
|
|
13853
14087
|
over.userData.noFollow = true;
|
|
13854
14088
|
piv.attach(over);
|
|
13855
14089
|
}
|
|
@@ -14430,11 +14664,18 @@ var GameVehicles = (() => {
|
|
|
14430
14664
|
}
|
|
14431
14665
|
}
|
|
14432
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
|
+
}
|
|
14433
14673
|
/** The lens of a light breaks and its unit stays (damaged) or goes (destroyed). */
|
|
14434
14674
|
breakLight(p, gone) {
|
|
14435
14675
|
const z = this.zone(p);
|
|
14436
14676
|
if (!z) return;
|
|
14437
14677
|
for (const L of z.lights) {
|
|
14678
|
+
L.lensMaterial = p.material;
|
|
14438
14679
|
this.kit.breakLight(this.c, L, new this.THREE.Vector3(p.box.center[0] > 0 ? -1 : 1, 0, 0));
|
|
14439
14680
|
if (gone) {
|
|
14440
14681
|
if (L.group) L.group.visible = false;
|
|
@@ -15080,6 +15321,7 @@ var GameVehicles = (() => {
|
|
|
15080
15321
|
wear(p, amount, out);
|
|
15081
15322
|
syncZone(p);
|
|
15082
15323
|
if (p.hp <= 0) lose(p, "broken", out, cause);
|
|
15324
|
+
else if (p.def.role === "light") vis?.crackLight(p.def, dtype === "ballistic");
|
|
15083
15325
|
if (p.def.role === "tyre" && !p.flat && p.hp < p.def.hp * 0.6 && !p.lost) {
|
|
15084
15326
|
p.flat = true;
|
|
15085
15327
|
vis?.tyreFlat(p.def, 0.24);
|
|
@@ -15892,7 +16134,7 @@ var GameVehicles = (() => {
|
|
|
15892
16134
|
if (!list2.length) return 1;
|
|
15893
16135
|
return Math.min(...list2.map((p) => p.lost || !attached(p.def.id) ? 0 : p.hp / p.def.hp));
|
|
15894
16136
|
};
|
|
15895
|
-
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 }));
|
|
15896
16138
|
const status = computeStatus();
|
|
15897
16139
|
const brakeSystem = [...ps.values()].find((p) => p.def.role === "brakes" && p.def.contains);
|
|
15898
16140
|
const fluidLeft = brakeSystem ? clamp((brakeSystem.liters ?? 0) / brakeSystem.def.contains.liters / BRAKE_FLUID_NEEDED, 0, 1) : 1;
|