@adatechnology/game-vehicles 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.d.ts +42 -2
- package/dist/index.global.js +109 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +109 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -258,6 +258,12 @@ come in `Hit.ignition` (the weapons build them from the core and jacket of the a
|
|
|
258
258
|
easiest, a full one mostly leaks); diesel takes a spark far worse than petrol. `createVehicleDestructible(kit, vehicle, { fuel: 0.1, ignition: { fillCurve, fireNear, sparkTarget } })`;
|
|
259
259
|
the defaults and every number are `IgnitionRules` of `@adatechnology/game-contracts`.
|
|
260
260
|
|
|
261
|
+
Cabin and smoke: `cabinEnclosure(car, { frame })` is the cabin (`meta.cabin`, from `kit.interior`) as an `Enclosure` in world space, for the host's
|
|
262
|
+
`EffectSink.enclose`: a shut car keeps the smoke out and seeps only at `CABIN_LEAK` (2 % per second), more with cracked glass. It is open where a door
|
|
263
|
+
or lid stands open (past 6 % of its swing), where a window shattered and where a door, glass, roof or panel was torn off or burnt away; a bullet hole
|
|
264
|
+
lets smoke in at the hole at a rate that grows with its area (a 9 mm hole about 0.2 a second). `null` for a vehicle with no cabin (a bike) or one
|
|
265
|
+
destroyed. `createCabinSmoke()` of `game-vehicles-three` keeps a host told every frame.
|
|
266
|
+
|
|
261
267
|
## Português
|
|
262
268
|
|
|
263
269
|
Motor genérico de veículos procedurais para three.js: carrocerias, vidros, faróis, rodas, interiores, capô e compartimento do motor, **zonas de dano
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MaterialId, Vec3, JointKind, ToolKind, DestructibleImpl, AnimationClipSpec, IgnitionRules, ObjectState, Enclosure } from '@adatechnology/game-contracts';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Light mode ("lite"): the same vehicles with fewer triangles, for a whole street of them. Port of the `[LOD]` hooks of the team's Frota Leve
|
|
@@ -917,4 +917,44 @@ interface VehicleItem {
|
|
|
917
917
|
*/
|
|
918
918
|
declare function buildVehicleItem(kit: VehicleKit, key: string, options?: VehicleItemOptions): VehicleItem;
|
|
919
919
|
|
|
920
|
-
|
|
920
|
+
/**
|
|
921
|
+
* The cabin of a vehicle as a closed space for the smoke, dust and steam of the host's effects (`EffectSink.enclose` of the contracts): a shut car
|
|
922
|
+
* keeps the smoke out. It is open where it is in the real world: a door standing open, a window shattered, a door, glass or roof torn off or burnt
|
|
923
|
+
* away; a bullet hole lets a little in at a rate that grows with its size, and the gaps of a shut door let a trace in.
|
|
924
|
+
*/
|
|
925
|
+
|
|
926
|
+
/** The cabin box a vehicle with an interior records (`kit.interior`), in the vehicle's own space: x along it, `z` the inner half width. */
|
|
927
|
+
interface CabinBox {
|
|
928
|
+
readonly x0: number;
|
|
929
|
+
readonly x1: number;
|
|
930
|
+
readonly y0: number;
|
|
931
|
+
readonly y1: number;
|
|
932
|
+
readonly z: number;
|
|
933
|
+
}
|
|
934
|
+
/** What the cabin enclosure reads of a vehicle: its parts and their state (any `DestructibleImpl` of a vehicle has these). */
|
|
935
|
+
interface CabinSource {
|
|
936
|
+
readonly vehicle: VehicleContext;
|
|
937
|
+
readonly model: VehicleModel;
|
|
938
|
+
state(): ObjectState;
|
|
939
|
+
}
|
|
940
|
+
interface CabinEnclosureOptions {
|
|
941
|
+
/** The object the destructible answers in (its `frame` option, where the host placed the vehicle): the holes of `state()` are in its space. Default `vehicle.root`. */
|
|
942
|
+
readonly frame?: any;
|
|
943
|
+
/** Gaps of the shut doors and vents, share per second of the smoke pressing on the closed shell (default {@link CABIN_LEAK}). */
|
|
944
|
+
readonly leak?: number;
|
|
945
|
+
/** How open a door or lid has to be (share of its full swing) to count as open (default 0.06: ajar). */
|
|
946
|
+
readonly ajar?: number;
|
|
947
|
+
}
|
|
948
|
+
/** The gaps of a shut car (door seals, the vents, the dash): about 2 % per second of what presses on it seeps in. */
|
|
949
|
+
declare const CABIN_LEAK = 0.02;
|
|
950
|
+
/** The cabin box of a vehicle, or `null` when it has none (a bike, a vehicle with no interior). */
|
|
951
|
+
declare function cabinBoxOf(vehicle: VehicleContext): CabinBox | null;
|
|
952
|
+
/** How far a door or lid stands open, 0 shut to 1 at the end of its clip: the turn of its drawn hinge from rest (the room poses it by the part id). */
|
|
953
|
+
declare function swingOf(vehicle: VehicleContext, partId: string): number;
|
|
954
|
+
/**
|
|
955
|
+
* The cabin of `source` as an `Enclosure` in world space, or `null` when there is no closed cabin (no interior, the vehicle exploded, every shell
|
|
956
|
+
* part gone). Call it each frame, or when the vehicle moves or changes, and hand it to `EffectSink.enclose`.
|
|
957
|
+
*/
|
|
958
|
+
declare function cabinEnclosure(source: CabinSource, options?: CabinEnclosureOptions): Enclosure | null;
|
|
959
|
+
|
|
960
|
+
export { type Band, type BandId, type BandIndex, type BandOverride, type BuildFunction, type BuildOptions, type BuildState, type BuildZoneState, CABIN_LEAK, type CabinBox, type CabinEnclosureOptions, type CabinSource, type DamageMapOptions, type DamageZone, type Damageable, type DamageableKind, type DamageableMaterial, type DamageableSide, 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 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, buildVehicleItem, buildVehicleModel, cabinBoxOf, cabinEnclosure, clamp, 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
|
@@ -23,12 +23,15 @@ var GameVehicles = (() => {
|
|
|
23
23
|
// src/index.ts
|
|
24
24
|
var index_exports = {};
|
|
25
25
|
__export(index_exports, {
|
|
26
|
+
CABIN_LEAK: () => CABIN_LEAK,
|
|
26
27
|
JOINT_RULES: () => JOINT_RULES,
|
|
27
28
|
LEGACY_STATES: () => LEGACY_STATES,
|
|
28
29
|
LEGACY_ZONE_IDS: () => LEGACY_ZONE_IDS,
|
|
29
30
|
SEED_SALTS: () => SEED_SALTS,
|
|
30
31
|
buildVehicleItem: () => buildVehicleItem,
|
|
31
32
|
buildVehicleModel: () => buildVehicleModel,
|
|
33
|
+
cabinBoxOf: () => cabinBoxOf,
|
|
34
|
+
cabinEnclosure: () => cabinEnclosure,
|
|
32
35
|
clamp: () => clamp,
|
|
33
36
|
createLod: () => createLod,
|
|
34
37
|
createTranslate: () => createTranslate,
|
|
@@ -48,6 +51,7 @@ var GameVehicles = (() => {
|
|
|
48
51
|
rng: () => rng,
|
|
49
52
|
smooth: () => smooth,
|
|
50
53
|
strHash: () => strHash,
|
|
54
|
+
swingOf: () => swingOf,
|
|
51
55
|
wrapThreeForLod: () => wrapThreeForLod
|
|
52
56
|
});
|
|
53
57
|
|
|
@@ -6661,8 +6665,15 @@ var GameVehicles = (() => {
|
|
|
6661
6665
|
x.arc(cx, cy, 26 * s, 0, PI * 2);
|
|
6662
6666
|
x.fill();
|
|
6663
6667
|
}
|
|
6668
|
+
const NO_CANVAS = new Proxy(function() {
|
|
6669
|
+
}, { get: () => NO_CANVAS, apply: () => NO_CANVAS, set: () => true });
|
|
6664
6670
|
function crackGlass(c, msh, p, force, sc) {
|
|
6665
6671
|
const G = msh.userData.glass;
|
|
6672
|
+
if (G.cracksFreed) {
|
|
6673
|
+
drawWeb(NO_CANVAS, 0, 0, sc != null ? sc : 0.6 + force * 0.5, Math.max(0.4, G.aspect || 1), c.rand);
|
|
6674
|
+
G.hits++;
|
|
6675
|
+
return;
|
|
6676
|
+
}
|
|
6666
6677
|
if (!G.cv) {
|
|
6667
6678
|
G.cv = document2.createElement("canvas");
|
|
6668
6679
|
G.cv.width = G.cv.height = 256;
|
|
@@ -6745,6 +6756,14 @@ var GameVehicles = (() => {
|
|
|
6745
6756
|
const G = msh.userData.glass;
|
|
6746
6757
|
if (G.broken) return;
|
|
6747
6758
|
G.broken = true;
|
|
6759
|
+
const freeCracks = function() {
|
|
6760
|
+
if (!G.tex) return;
|
|
6761
|
+
if (msh.material !== MATERIALS.glassEdge && msh.material?.map === G.tex) msh.material.dispose();
|
|
6762
|
+
G.tex.dispose();
|
|
6763
|
+
G.tex = null;
|
|
6764
|
+
G.cv = null;
|
|
6765
|
+
G.cracksFreed = true;
|
|
6766
|
+
};
|
|
6748
6767
|
const cubesAt = [];
|
|
6749
6768
|
if (G.outline && G.T !== void 0) {
|
|
6750
6769
|
const out = G.outline;
|
|
@@ -6778,6 +6797,7 @@ var GameVehicles = (() => {
|
|
|
6778
6797
|
geo.computeVertexNormals();
|
|
6779
6798
|
msh.geometry.dispose();
|
|
6780
6799
|
msh.geometry = geo;
|
|
6800
|
+
freeCracks();
|
|
6781
6801
|
msh.material = MATERIALS.glassEdge;
|
|
6782
6802
|
let minx = 1e9;
|
|
6783
6803
|
let maxx = -1e9;
|
|
@@ -6801,8 +6821,12 @@ var GameVehicles = (() => {
|
|
|
6801
6821
|
if (rim) {
|
|
6802
6822
|
msh.geometry.dispose();
|
|
6803
6823
|
msh.geometry = rim;
|
|
6824
|
+
freeCracks();
|
|
6804
6825
|
msh.material = MATERIALS.glassEdge;
|
|
6805
|
-
} else
|
|
6826
|
+
} else {
|
|
6827
|
+
freeCracks();
|
|
6828
|
+
msh.visible = false;
|
|
6829
|
+
}
|
|
6806
6830
|
for (let j = 0; j < 36; j++)
|
|
6807
6831
|
cubesAt.push(
|
|
6808
6832
|
new V3(
|
|
@@ -14496,6 +14520,90 @@ var GameVehicles = (() => {
|
|
|
14496
14520
|
root.userData.vehicle = vehicle;
|
|
14497
14521
|
return { root, vehicle };
|
|
14498
14522
|
}
|
|
14523
|
+
|
|
14524
|
+
// src/destructible/enclosure.ts
|
|
14525
|
+
var CABIN_LEAK = 0.02;
|
|
14526
|
+
var HOLE_RATE_PER_AREA = 3e3;
|
|
14527
|
+
var CRACK_GAP_PER_METRE = 0.5;
|
|
14528
|
+
var THROUGH = 0.18;
|
|
14529
|
+
var HOLE_PATCH = 0.12;
|
|
14530
|
+
var FULL_SWING = 1.15;
|
|
14531
|
+
var SHELL_ROLES = /* @__PURE__ */ new Set(["door", "glass", "roof", "lid", "panel", "pillar"]);
|
|
14532
|
+
var GONE = /* @__PURE__ */ new Set(["broken", "melted", "ashes"]);
|
|
14533
|
+
function cabinBoxOf(vehicle) {
|
|
14534
|
+
const cabin = vehicle.meta.cabin;
|
|
14535
|
+
if (!cabin || !(cabin.x1 > cabin.x0) || !(cabin.y1 > cabin.y0) || !(cabin.z > 0)) return null;
|
|
14536
|
+
return cabin;
|
|
14537
|
+
}
|
|
14538
|
+
function swingOf(vehicle, partId) {
|
|
14539
|
+
const pivot = vehicle.vehicle?.getObjectByName?.(partId);
|
|
14540
|
+
const q = pivot?.quaternion;
|
|
14541
|
+
if (!q) return 0;
|
|
14542
|
+
const angle = 2 * Math.acos(Math.min(1, Math.abs(q.w)));
|
|
14543
|
+
return Math.min(1, angle / FULL_SWING);
|
|
14544
|
+
}
|
|
14545
|
+
var overlaps = (b, c, margin) => Math.abs(b.center[0] - (c.x0 + c.x1) / 2) <= b.size[0] / 2 + (c.x1 - c.x0) / 2 + margin && Math.abs(b.center[1] - (c.y0 + c.y1) / 2) <= b.size[1] / 2 + (c.y1 - c.y0) / 2 + margin && Math.abs(b.center[2]) <= b.size[2] / 2 + c.z + margin;
|
|
14546
|
+
function cabinEnclosure(source, options = {}) {
|
|
14547
|
+
const c = source.vehicle;
|
|
14548
|
+
const cabin = cabinBoxOf(c);
|
|
14549
|
+
const node = c.vehicle;
|
|
14550
|
+
if (!cabin || !node?.localToWorld) return null;
|
|
14551
|
+
const state = source.state();
|
|
14552
|
+
if (state.destroyed) return null;
|
|
14553
|
+
const V3 = node.position.constructor;
|
|
14554
|
+
const Quat = node.quaternion.constructor;
|
|
14555
|
+
const mid = [(cabin.x0 + cabin.x1) / 2, (cabin.y0 + cabin.y1) / 2, 0];
|
|
14556
|
+
const half = [(cabin.x1 - cabin.x0) / 2, (cabin.y1 - cabin.y0) / 2, cabin.z];
|
|
14557
|
+
const ajar = options.ajar ?? 0.06;
|
|
14558
|
+
const byId = new Map(state.parts.map((p) => [p.id, p]));
|
|
14559
|
+
const mainGroup = state.groups[0] ? new Set(state.groups[0]) : null;
|
|
14560
|
+
const openings = [];
|
|
14561
|
+
let crackLength = 0;
|
|
14562
|
+
const toCabin = (b) => ({
|
|
14563
|
+
center: [b.center[0] - mid[0], b.center[1] - mid[1], b.center[2] - mid[2]],
|
|
14564
|
+
halfSize: [b.size[0] / 2 + THROUGH, b.size[1] / 2 + THROUGH, b.size[2] / 2 + THROUGH]
|
|
14565
|
+
});
|
|
14566
|
+
node.updateWorldMatrix?.(true, false);
|
|
14567
|
+
const frame = options.frame ?? c.root;
|
|
14568
|
+
frame?.updateWorldMatrix?.(true, false);
|
|
14569
|
+
const inverse = node.matrixWorld.clone().invert();
|
|
14570
|
+
for (const def of source.model.parts) {
|
|
14571
|
+
if (!SHELL_ROLES.has(def.role) || !overlaps(def.box, cabin, 0.12)) continue;
|
|
14572
|
+
const part = byId.get(def.id);
|
|
14573
|
+
if (!part) continue;
|
|
14574
|
+
const gone = GONE.has(part.status) || mainGroup !== null && !mainGroup.has(def.id);
|
|
14575
|
+
const swung = (def.role === "door" || def.role === "lid") && swingOf(c, def.id) > ajar;
|
|
14576
|
+
if (gone || swung) {
|
|
14577
|
+
openings.push(toCabin(def.box));
|
|
14578
|
+
continue;
|
|
14579
|
+
}
|
|
14580
|
+
for (const hole of part.holes) {
|
|
14581
|
+
if (!hole.through && def.material !== "glass") continue;
|
|
14582
|
+
const area3 = Math.PI * (hole.diameter / 2) ** 2;
|
|
14583
|
+
const p = new V3(hole.point[0], hole.point[1], hole.point[2]);
|
|
14584
|
+
if (frame?.localToWorld) frame.localToWorld(p);
|
|
14585
|
+
p.applyMatrix4(inverse);
|
|
14586
|
+
openings.push({
|
|
14587
|
+
center: [p.x - mid[0], p.y - mid[1], p.z - mid[2]],
|
|
14588
|
+
halfSize: [HOLE_PATCH, HOLE_PATCH, HOLE_PATCH],
|
|
14589
|
+
// the patch is far bigger than the hole: only a share of what crosses it passes, more for a bigger hole
|
|
14590
|
+
rate: Math.min(4, area3 * HOLE_RATE_PER_AREA)
|
|
14591
|
+
});
|
|
14592
|
+
}
|
|
14593
|
+
for (const crack of part.cracks) crackLength += crack.length;
|
|
14594
|
+
}
|
|
14595
|
+
const center = node.localToWorld(new V3(mid[0], mid[1], mid[2]));
|
|
14596
|
+
const q = node.getWorldQuaternion(new Quat());
|
|
14597
|
+
const scale = node.getWorldScale(new V3());
|
|
14598
|
+
const leak = (options.leak ?? CABIN_LEAK) * (1 + crackLength * CRACK_GAP_PER_METRE);
|
|
14599
|
+
return {
|
|
14600
|
+
center: [center.x, center.y, center.z],
|
|
14601
|
+
halfSize: [half[0] * Math.abs(scale.x), half[1] * Math.abs(scale.y), half[2] * Math.abs(scale.z)],
|
|
14602
|
+
orientation: [q.x, q.y, q.z, q.w],
|
|
14603
|
+
openings: openings.map((o) => ({ ...o, center: [o.center[0] * scale.x, o.center[1] * scale.y, o.center[2] * scale.z], halfSize: [o.halfSize[0] * Math.abs(scale.x), o.halfSize[1] * Math.abs(scale.y), o.halfSize[2] * Math.abs(scale.z)] })),
|
|
14604
|
+
leak
|
|
14605
|
+
};
|
|
14606
|
+
}
|
|
14499
14607
|
return __toCommonJS(index_exports);
|
|
14500
14608
|
})();
|
|
14501
14609
|
//# sourceMappingURL=index.global.js.map
|