@adatechnology/game-vehicles 0.3.0 → 0.4.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 CHANGED
@@ -64,6 +64,9 @@ scene.remove(c.root); kit.dispose(c)
64
64
  const hurt = kit.build('meteor', 'intact', state)
65
65
  scene.add(hurt.root)
66
66
 
67
+ // ...or name a registered scenario and build it in one call (explicit health / flags given alongside win)
68
+ const wreck = kit.build('meteor', 'intact', { scenario: 'burnt' })
69
+
67
70
  // 4. animate (particles, fluids, hood, rollover, explosions)
68
71
  kit.tick(hurt, dt)
69
72
  ```
@@ -89,7 +92,7 @@ and a band decided by the health:
89
92
  | 3 | `destroyed` | 1 to 25 | Heavy deformation, parts fall off |
90
93
  | 4 | `torn_off` | 0 | The part is gone |
91
94
 
92
- Bands only change the look when the band changes; `kit.build(key, state, zoneState)` rebuilds the vehicle and reapplies every zone.
95
+ Bands only change the look when the band changes; `kit.build(key, state, zoneState)` rebuilds the vehicle and reapplies every zone. `zoneState` may also carry `scenario: 'id or name'`: the scenario is computed on the vehicle being built (no probe build) and any `health` / `flags` given with it are merged on top. An unknown scenario damages nothing, like `zoneScenario`.
93
96
 
94
97
  **Content packs.** A pack is a plugin: `{ name, configure, install(kit) }`. `configure` accepts everything in the configuration table below and
95
98
  `install` can call `kit.register(...)`, `kit.scenario(...)`, `kit.defineFluid(...)`, `kit.on(...)` and so on. The engine never imports a pack.
@@ -139,7 +142,7 @@ created with, so `kit.t('mypack.some.key')` reads from it, with English as the f
139
142
  | Area | Methods |
140
143
  |---|---|
141
144
  | Registry | `register(spec, build)`, `find(key)`, `fleet`, `configure`, `plugin`, `on`/`hook`, `scenario`, `defineFluid` |
142
- | Lifecycle | `build(key, state?, zoneState?)`, `dispose(c)`, `tick(c, dt)`, `settle(c, seconds)`, `defaultDamage(c)` |
145
+ | Lifecycle | `build(key, state?, zoneState?)` (`zoneState.scenario` runs a scenario), `dispose(c)`, `tick(c, dt)`, `settle(c, seconds)`, `defaultDamage(c)` |
143
146
  | Zones | `bandOf(health)`, `zonesOf(c)`, `applyZones(c, state)`, `zoneScenario(c, id)`, `zoneOverall(c)`, `zoneAt(c, point)`, `zoneDamageAt(c, point, dir, force)`, `zonePoint(c, zone)`, `zoneEffect(zone, health?)`, `zoneOwner` |
144
147
  | Events | `impact(c, point, dir, force, options?)`, `zoneImpact(c, side, force)` (side: `front`, `rear`, `left`, `right` or `rolled`), `explode(c, options?)`, `blastDamage(c, amount)`, `roll(c, side?)`, `setHood(c, open)` |
145
148
  | Fluids and particles | `splat`, `puddle`, `wipe(c, mode)` (`wiper` or `rain`), `spawn`, `burst`, `addEmitter` |
@@ -175,6 +178,7 @@ kit.scenario({
175
178
  })
176
179
  const result = kit.zoneScenario(c, 'front') // { health, flags, scenario }
177
180
  const hurt = kit.build('meteor', 'intact', result)
181
+ const same = kit.build('meteor', 'intact', { scenario: 'front' }) // the two steps in one call
178
182
  ```
179
183
 
180
184
  `x` has `c`, `meta`, `front`, `rear`, `length`, `width`, `clearance`, `top`, `rand()`, `set(zone, health)`, `flags`, `health`, `center` (of the zone
package/dist/index.d.ts CHANGED
@@ -337,6 +337,8 @@ interface VehicleContext {
337
337
  meta: VehicleMeta;
338
338
  zones: DamageZone[];
339
339
  zoneState: ZoneState;
340
+ /** The scenario the vehicle was built in (`kit.build(key, state, { scenario })`), so a rebuild keeps it. */
341
+ scenario?: string;
340
342
  /** Deterministic random generator seeded from the vehicle key and state. */
341
343
  rand: () => number;
342
344
  parts: VehiclePart[];
@@ -418,6 +420,11 @@ interface ZoneState {
418
420
  health: Record<string, number>;
419
421
  flags: Record<string, boolean>;
420
422
  }
423
+ /** The zone-state argument of `kit.build`: a `ZoneState` plus an optional scenario to compute on the vehicle being built. */
424
+ interface BuildZoneState extends Partial<ZoneState> {
425
+ /** Id or name of a registered scenario (e.g. the fleet's `burnt`); explicit `health` / `flags` override its result. */
426
+ scenario?: string;
427
+ }
421
428
  interface ScenarioResult {
422
429
  health: Record<string, number>;
423
430
  flags: Record<string, boolean>;
@@ -662,7 +669,7 @@ interface VehicleKit {
662
669
  defineFluid(key: string, def: FluidDefinition): FluidDefinition;
663
670
  on(event: string, fn: (...args: any[]) => void): VehicleKit;
664
671
  hook(event: string, ...args: any[]): void;
665
- build(key: string, state?: BuildState, zoneState?: Partial<ZoneState>, options?: BuildOptions): VehicleContext;
672
+ build(key: string, state?: BuildState, zoneState?: BuildZoneState, options?: BuildOptions): VehicleContext;
666
673
  /** The damageable parts of a built vehicle as boxes (optionally in the frame of `matrix`). */
667
674
  damageables(c: VehicleContext, matrix?: ThreeObject): Damageable[];
668
675
  dispose(c: VehicleContext): void;
@@ -894,6 +901,8 @@ interface VehicleItemOptions {
894
901
  state?: BuildState;
895
902
  /** Build in the light mode of the kit (`kit.lod`): fewer segments and no interior, for crowds and for budgets. Default off. */
896
903
  lod?: boolean;
904
+ /** A registered damage scenario (id or name, e.g. the fleet's `burnt`) built into the vehicle, through `kit.build(key, state, { scenario })`. */
905
+ scenario?: string;
897
906
  }
898
907
  interface VehicleItem {
899
908
  /** Add this to the scene: origin at the centre of the footprint on the floor, front facing +z. */
@@ -908,4 +917,4 @@ interface VehicleItem {
908
917
  */
909
918
  declare function buildVehicleItem(kit: VehicleKit, key: string, options?: VehicleItemOptions): VehicleItem;
910
919
 
911
- export { type Band, type BandId, type BandIndex, type BandOverride, type BuildFunction, type BuildOptions, type BuildState, 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, clamp, createLod, createTranslate, createVehicleDestructible, createVehicleKit, defaultDamageableKinds, defaultLodSettings, format, hash, legacyGlassId, legacyLightId, legacyZoneId, listLocales, registerLegacySeeds, registerLocale, resolveStrings, rng, smooth, strHash, wrapThreeForLod };
920
+ export { type Band, type BandId, type BandIndex, type BandOverride, type BuildFunction, type BuildOptions, type BuildState, type BuildZoneState, 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, clamp, createLod, createTranslate, createVehicleDestructible, createVehicleKit, defaultDamageableKinds, defaultLodSettings, format, hash, legacyGlassId, legacyLightId, legacyZoneId, listLocales, registerLegacySeeds, registerLocale, resolveStrings, rng, smooth, strHash, wrapThreeForLod };
@@ -2501,46 +2501,79 @@ var GameVehicles = (() => {
2501
2501
  }
2502
2502
  const p = geo.attributes.position;
2503
2503
  const n = p.count;
2504
- const FN = [];
2505
- const map = {};
2506
- const A = new V3();
2507
- const B = new V3();
2508
- const C = new V3();
2509
- const e1 = new V3();
2510
- const e2 = new V3();
2504
+ const faces = n / 3;
2505
+ const FN = new Float64Array(faces * 3);
2506
+ const map = /* @__PURE__ */ new Map();
2507
+ const RANGE = 65536;
2511
2508
  function key(i) {
2512
- return Math.round(p.getX(i) * 2e3) + "," + Math.round(p.getY(i) * 2e3) + "," + Math.round(p.getZ(i) * 2e3);
2509
+ const x = Math.round(p.getX(i) * 2e3);
2510
+ const y = Math.round(p.getY(i) * 2e3);
2511
+ const z = Math.round(p.getZ(i) * 2e3);
2512
+ if (Math.abs(x) < RANGE && Math.abs(y) < RANGE && Math.abs(z) < RANGE) return ((x + RANGE) * 131072 + (y + RANGE)) * 131072 + (z + RANGE);
2513
+ return x + "," + y + "," + z;
2513
2514
  }
2514
2515
  const keys = new Array(n);
2515
- for (let f = 0; f < n / 3; f++) {
2516
- A.fromBufferAttribute(p, 3 * f);
2517
- B.fromBufferAttribute(p, 3 * f + 1);
2518
- C.fromBufferAttribute(p, 3 * f + 2);
2519
- const nn = new V3().crossVectors(e1.subVectors(C, B), e2.subVectors(A, B));
2520
- if (nn.lengthSq() > 1e-20) nn.normalize();
2521
- FN.push(nn);
2516
+ for (let f = 0; f < faces; f++) {
2517
+ const ax = p.getX(3 * f);
2518
+ const ay = p.getY(3 * f);
2519
+ const az = p.getZ(3 * f);
2520
+ const bx = p.getX(3 * f + 1);
2521
+ const by = p.getY(3 * f + 1);
2522
+ const bz = p.getZ(3 * f + 1);
2523
+ const cx = p.getX(3 * f + 2);
2524
+ const cy = p.getY(3 * f + 2);
2525
+ const cz = p.getZ(3 * f + 2);
2526
+ const e1x = cx - bx;
2527
+ const e1y = cy - by;
2528
+ const e1z = cz - bz;
2529
+ const e2x = ax - bx;
2530
+ const e2y = ay - by;
2531
+ const e2z = az - bz;
2532
+ let nx = e1y * e2z - e1z * e2y;
2533
+ let ny = e1z * e2x - e1x * e2z;
2534
+ let nz = e1x * e2y - e1y * e2x;
2535
+ if (nx * nx + ny * ny + nz * nz > 1e-20) {
2536
+ const inv = 1 / (Math.sqrt(nx * nx + ny * ny + nz * nz) || 1);
2537
+ nx *= inv;
2538
+ ny *= inv;
2539
+ nz *= inv;
2540
+ }
2541
+ FN[3 * f] = nx;
2542
+ FN[3 * f + 1] = ny;
2543
+ FN[3 * f + 2] = nz;
2522
2544
  for (let k = 0; k < 3; k++) {
2523
2545
  const kk = key(3 * f + k);
2524
2546
  keys[3 * f + k] = kk;
2525
- (map[kk] || (map[kk] = [])).push(f);
2547
+ const list2 = map.get(kk);
2548
+ if (list2) list2.push(f);
2549
+ else map.set(kk, [f]);
2526
2550
  }
2527
2551
  }
2528
2552
  const cs = Math.cos(deg * PI / 180);
2529
2553
  const out = new Float32Array(n * 3);
2530
- const s = new V3();
2531
2554
  for (let i = 0; i < n; i++) {
2532
- const fi = i / 3 | 0;
2533
- const list2 = map[keys[i]];
2534
- s.set(0, 0, 0);
2555
+ const fi = (i / 3 | 0) * 3;
2556
+ const list2 = map.get(keys[i]);
2557
+ let sx = 0;
2558
+ let sy = 0;
2559
+ let sz = 0;
2535
2560
  for (let j = 0; j < list2.length; j++) {
2536
- const g = FN[list2[j]];
2537
- if (g.dot(FN[fi]) >= cs) s.add(g);
2561
+ const g = list2[j] * 3;
2562
+ if (FN[g] * FN[fi] + FN[g + 1] * FN[fi + 1] + FN[g + 2] * FN[fi + 2] >= cs) {
2563
+ sx += FN[g];
2564
+ sy += FN[g + 1];
2565
+ sz += FN[g + 2];
2566
+ }
2538
2567
  }
2539
- if (s.lengthSq() < 1e-20) s.copy(FN[fi]);
2540
- s.normalize();
2541
- out[3 * i] = s.x;
2542
- out[3 * i + 1] = s.y;
2543
- out[3 * i + 2] = s.z;
2568
+ if (sx * sx + sy * sy + sz * sz < 1e-20) {
2569
+ sx = FN[fi];
2570
+ sy = FN[fi + 1];
2571
+ sz = FN[fi + 2];
2572
+ }
2573
+ const inv = 1 / (Math.sqrt(sx * sx + sy * sy + sz * sz) || 1);
2574
+ out[3 * i] = sx * inv;
2575
+ out[3 * i + 1] = sy * inv;
2576
+ out[3 * i + 2] = sz * inv;
2544
2577
  }
2545
2578
  geo.setAttribute("normal", new THREE.BufferAttribute(out, 3));
2546
2579
  geo.userData.crease = true;
@@ -8198,6 +8231,153 @@ var GameVehicles = (() => {
8198
8231
  return hole;
8199
8232
  }
8200
8233
  const _zr = new THREE.Raycaster();
8234
+ const PROXY_MIN_TRIANGLES = 500;
8235
+ const STROKE_REACH = 0.6;
8236
+ const _pv = new V3();
8237
+ const _pm = new THREE.Matrix4();
8238
+ function decalRegion(c, z) {
8239
+ const B = z.box;
8240
+ const lo = [0, 1, 2].map(function(k) {
8241
+ return I.boxLo(B, k) - STROKE_REACH;
8242
+ });
8243
+ const hi = [0, 1, 2].map(function(k) {
8244
+ return I.boxHi(B, k) + STROKE_REACH;
8245
+ });
8246
+ const a = axisOf(z.dir);
8247
+ const dn = z.dir.clone().normalize();
8248
+ const sg = z.dir.getComponent(a) > 0 ? 1 : -1;
8249
+ const outer = sg > 0 ? I.boxLo(B, a) - 0.6 : I.boxHi(B, a) + 0.6;
8250
+ const far = 0.6 + (I.boxHi(B, a) - I.boxLo(B, a)) + 0.05;
8251
+ for (let i = 0; i < 8; i++) {
8252
+ const p = new V3();
8253
+ p.setComponent(a, outer);
8254
+ [0, 1, 2].filter(function(k) {
8255
+ return k !== a;
8256
+ }).forEach(function(k, j) {
8257
+ p.setComponent(k, i >> j & 1 ? I.boxHi(B, k) : I.boxLo(B, k));
8258
+ });
8259
+ for (let e = 0; e < 2; e++) {
8260
+ const q = e ? p.clone().addScaledVector(dn, far) : p;
8261
+ for (let k = 0; k < 3; k++) {
8262
+ lo[k] = Math.min(lo[k], q.getComponent(k) - 0.05);
8263
+ hi[k] = Math.max(hi[k], q.getComponent(k) + 0.05);
8264
+ }
8265
+ }
8266
+ }
8267
+ return { lo, hi };
8268
+ }
8269
+ function regionCands(c, z, cands) {
8270
+ if (!cands.some(heavy)) return cands;
8271
+ const R = decalRegion(c, z);
8272
+ const Mw = c.vehicle.matrixWorld;
8273
+ return cands.map(function(o) {
8274
+ if (!heavy(o)) return o;
8275
+ if (!o.geometry.boundingSphere) o.geometry.computeBoundingSphere();
8276
+ if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
8277
+ return proxyIn(o, Mw, R);
8278
+ });
8279
+ }
8280
+ function heavy(o) {
8281
+ const g = o.geometry;
8282
+ const pos = g && g.attributes && g.attributes.position;
8283
+ return o.raycast === THREE.Mesh.prototype.raycast && !o.isSkinnedMesh && !Array.isArray(o.material) && pos && !pos.isInterleavedBufferAttribute && pos.itemSize === 3 && !g.groups.length && !(g.morphAttributes && g.morphAttributes.position) && (g.index ? g.index.count : pos.count) / 3 >= PROXY_MIN_TRIANGLES;
8284
+ }
8285
+ function proxyIn(o, Mw, R) {
8286
+ _pm.copy(o.matrixWorld).invert().multiply(Mw);
8287
+ const lo = [Infinity, Infinity, Infinity];
8288
+ const hi = [-Infinity, -Infinity, -Infinity];
8289
+ for (let i = 0; i < 8; i++) {
8290
+ _pv.set(i & 1 ? R.hi[0] : R.lo[0], i & 2 ? R.hi[1] : R.lo[1], i & 4 ? R.hi[2] : R.lo[2]).applyMatrix4(_pm);
8291
+ for (let k = 0; k < 3; k++) {
8292
+ lo[k] = Math.min(lo[k], _pv.getComponent(k));
8293
+ hi[k] = Math.max(hi[k], _pv.getComponent(k));
8294
+ }
8295
+ }
8296
+ const g = o.geometry;
8297
+ const P = g.attributes.position.array;
8298
+ const ix = g.index ? g.index.array : null;
8299
+ const n = ix ? ix.length : P.length / 3;
8300
+ const keep = new Uint32Array(n - n % 3);
8301
+ let nk = 0;
8302
+ for (let t = 0; t + 2 < n; t += 3) {
8303
+ const a = (ix ? ix[t] : t) * 3;
8304
+ const b = (ix ? ix[t + 1] : t + 1) * 3;
8305
+ const d = (ix ? ix[t + 2] : t + 2) * 3;
8306
+ let out = false;
8307
+ for (let k = 0; k < 3 && !out; k++) {
8308
+ const u = P[a + k];
8309
+ const v = P[b + k];
8310
+ const w = P[d + k];
8311
+ out = Math.max(u, v, w) < lo[k] || Math.min(u, v, w) > hi[k];
8312
+ }
8313
+ if (!out) {
8314
+ keep[nk++] = ix ? ix[t] : t;
8315
+ keep[nk++] = ix ? ix[t + 1] : t + 1;
8316
+ keep[nk++] = ix ? ix[t + 2] : t + 2;
8317
+ }
8318
+ }
8319
+ if (!nk) return null;
8320
+ const kept = keep.slice(0, nk);
8321
+ const box2 = new Float32Array(kept.length / 3 * 6);
8322
+ for (let t = 0, j = 0; t < kept.length; t += 3, j += 6) {
8323
+ const a = kept[t] * 3;
8324
+ const b = kept[t + 1] * 3;
8325
+ const d = kept[t + 2] * 3;
8326
+ for (let k = 0; k < 3; k++) {
8327
+ box2[j + k] = Math.min(P[a + k], P[b + k], P[d + k]);
8328
+ box2[j + 3 + k] = Math.max(P[a + k], P[b + k], P[d + k]);
8329
+ }
8330
+ }
8331
+ const pg = new THREE.BufferGeometry();
8332
+ pg.setAttribute("position", g.attributes.position);
8333
+ pg.setIndex(new THREE.BufferAttribute(new Uint32Array(kept.length), 1));
8334
+ pg.boundingSphere = g.boundingSphere;
8335
+ pg.boundingBox = g.boundingBox;
8336
+ const proxy = new THREE.Mesh(pg, o.material);
8337
+ proxy.layers.mask = o.layers.mask;
8338
+ proxy.proxyOf = o;
8339
+ proxy.kept = kept;
8340
+ proxy.keptBox = box2;
8341
+ return proxy;
8342
+ }
8343
+ function narrowTo(rc, proxy) {
8344
+ const o = proxy.proxyOf;
8345
+ const ray = rc.ray;
8346
+ _pm.copy(o.matrixWorld).invert();
8347
+ _pv.copy(ray.origin).applyMatrix4(_pm);
8348
+ const x0 = _pv.x;
8349
+ const y0 = _pv.y;
8350
+ const z0 = _pv.z;
8351
+ _pv.copy(ray.direction).multiplyScalar(rc.far).add(ray.origin).applyMatrix4(_pm);
8352
+ const lo0 = Math.min(x0, _pv.x) - 1e-4;
8353
+ const lo1 = Math.min(y0, _pv.y) - 1e-4;
8354
+ const lo2 = Math.min(z0, _pv.z) - 1e-4;
8355
+ const hi0 = Math.max(x0, _pv.x) + 1e-4;
8356
+ const hi1 = Math.max(y0, _pv.y) + 1e-4;
8357
+ const hi2 = Math.max(z0, _pv.z) + 1e-4;
8358
+ const K = proxy.kept;
8359
+ const X = proxy.keptBox;
8360
+ const dst = proxy.geometry.index.array;
8361
+ let m = 0;
8362
+ for (let t = 0, j = 0; t < K.length; t += 3, j += 6) {
8363
+ if (X[j + 3] < lo0 || X[j] > hi0 || X[j + 4] < lo1 || X[j + 1] > hi1 || X[j + 5] < lo2 || X[j + 2] > hi2) continue;
8364
+ dst[m++] = K[t];
8365
+ dst[m++] = K[t + 1];
8366
+ dst[m++] = K[t + 2];
8367
+ }
8368
+ proxy.geometry.setDrawRange(0, m);
8369
+ }
8370
+ function castAt(rc, cands) {
8371
+ for (let i = 0; i < cands.length; i++) {
8372
+ const q = cands[i];
8373
+ if (!q.proxyOf) continue;
8374
+ q.matrixWorld.copy(q.proxyOf.matrixWorld);
8375
+ narrowTo(rc, q);
8376
+ }
8377
+ const hits = rc.intersectObjects(cands, false);
8378
+ for (let i = 0; i < hits.length; i++) if (hits[i].object.proxyOf) hits[i].object = hits[i].object.proxyOf;
8379
+ return hits;
8380
+ }
8201
8381
  function zoneCands(c, z, inv, glassToo) {
8202
8382
  const out = [];
8203
8383
  c.vehicle.traverse(function(o) {
@@ -8250,7 +8430,7 @@ var GameVehicles = (() => {
8250
8430
  });
8251
8431
  _zr.set(org.clone().applyMatrix4(Mw), d.clone().transformDirection(Mw));
8252
8432
  _zr.far = 0.6 + (I.boxHi(B, a) - I.boxLo(B, a)) + 0.05;
8253
- const hits = _zr.intersectObjects(cands, false);
8433
+ const hits = castAt(_zr, cands);
8254
8434
  for (let i = 0; i < hits.length; i++) {
8255
8435
  const h = hits[i];
8256
8436
  if (!h.face) continue;
@@ -8278,7 +8458,7 @@ var GameVehicles = (() => {
8278
8458
  const q = s.p.clone().addScaledVector(t, len * 0.5 * k / M);
8279
8459
  _rt.set(q.clone().addScaledVector(n, 0.08).applyMatrix4(Mw), n.clone().negate().transformDirection(Mw));
8280
8460
  _rt.far = 0.2;
8281
- const h = _rt.intersectObjects(cands, false).find(function(x) {
8461
+ const h = castAt(_rt, cands).find(function(x) {
8282
8462
  return x.face;
8283
8463
  });
8284
8464
  if (!h) return null;
@@ -8349,6 +8529,7 @@ var GameVehicles = (() => {
8349
8529
  const R = c.rand;
8350
8530
  const need = (spec.fine || 0) + (spec.deep || 0) + (spec.chip || 0) + (spec.rust || 0) + (spec.tear || 0) + (spec.ding || 0) + (spec.soot || 0) + (spec.crater || 0);
8351
8531
  if (!need) return [];
8532
+ cands = regionCands(c, z, cands).filter(Boolean);
8352
8533
  const S = zoneSurf(c, z, need + 2, R, cands);
8353
8534
  let k = 0;
8354
8535
  const ang0 = R() * PI;
@@ -8429,8 +8610,25 @@ var GameVehicles = (() => {
8429
8610
  }
8430
8611
  function subdivIn(geo, rel, B, maxLen) {
8431
8612
  const a = geo.attributes.position.array;
8432
- const out = [];
8433
- const v = new V3();
8613
+ let out = new Float32Array(a.length + 4096);
8614
+ let used = 0;
8615
+ const push = function(a0, a1, a2, b0, b1, b2, c0, c1, c2) {
8616
+ if (used + 9 > out.length) {
8617
+ const bigger = new Float32Array(out.length * 2);
8618
+ bigger.set(out);
8619
+ out = bigger;
8620
+ }
8621
+ out[used] = a0;
8622
+ out[used + 1] = a1;
8623
+ out[used + 2] = a2;
8624
+ out[used + 3] = b0;
8625
+ out[used + 4] = b1;
8626
+ out[used + 5] = b2;
8627
+ out[used + 6] = c0;
8628
+ out[used + 7] = c1;
8629
+ out[used + 8] = c2;
8630
+ used += 9;
8631
+ };
8434
8632
  const e = 0.15;
8435
8633
  function d(p, q) {
8436
8634
  return Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
@@ -8444,7 +8642,7 @@ var GameVehicles = (() => {
8444
8642
  const ca = d(C, A);
8445
8643
  const mx = Math.max(ab, bc, ca);
8446
8644
  if (mx <= maxLen || depth > 9) {
8447
- out.push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
8645
+ push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
8448
8646
  return;
8449
8647
  }
8450
8648
  if (mx === ab) {
@@ -8461,31 +8659,26 @@ var GameVehicles = (() => {
8461
8659
  tri(F2, Bv, C, depth + 1);
8462
8660
  }
8463
8661
  }
8662
+ const M = rel.elements;
8663
+ const at = function(x, y, z, k) {
8664
+ const w = 1 / (M[3] * x + M[7] * y + M[11] * z + M[15]);
8665
+ return k === 0 ? (M[0] * x + M[4] * y + M[8] * z + M[12]) * w : k === 1 ? (M[1] * x + M[5] * y + M[9] * z + M[13]) * w : (M[2] * x + M[6] * y + M[10] * z + M[14]) * w;
8666
+ };
8667
+ const lo = [B.x0 - e, B.y0 - e, B.z0 - e];
8668
+ const hi = [B.x1 + e, B.y1 + e, B.z1 + e];
8464
8669
  for (let i = 0; i < a.length; i += 9) {
8465
- const A = [a[i], a[i + 1], a[i + 2]];
8466
- const Bv = [a[i + 3], a[i + 4], a[i + 5]];
8467
- const C = [a[i + 6], a[i + 7], a[i + 8]];
8468
- let x0 = 1e9;
8469
- let x1 = -1e9;
8470
- let y0 = 1e9;
8471
- let y1 = -1e9;
8472
- let z0 = 1e9;
8473
- let z1 = -1e9;
8474
- [A, Bv, C].forEach(function(p) {
8475
- v.set(p[0], p[1], p[2]).applyMatrix4(rel);
8476
- x0 = Math.min(x0, v.x);
8477
- x1 = Math.max(x1, v.x);
8478
- y0 = Math.min(y0, v.y);
8479
- y1 = Math.max(y1, v.y);
8480
- z0 = Math.min(z0, v.z);
8481
- z1 = Math.max(z1, v.z);
8482
- });
8483
- if (x1 < B.x0 - e || x0 > B.x1 + e || y1 < B.y0 - e || y0 > B.y1 + e || z1 < B.z0 - e || z0 > B.z1 + e)
8484
- out.push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
8485
- else tri(A, Bv, C, 0);
8670
+ let outside = false;
8671
+ for (let k = 0; k < 3 && !outside; k++) {
8672
+ const p = at(a[i], a[i + 1], a[i + 2], k);
8673
+ const q = at(a[i + 3], a[i + 4], a[i + 5], k);
8674
+ const r = at(a[i + 6], a[i + 7], a[i + 8], k);
8675
+ outside = Math.max(p, q, r) < lo[k] || Math.min(p, q, r) > hi[k];
8676
+ }
8677
+ if (outside) push(a[i], a[i + 1], a[i + 2], a[i + 3], a[i + 4], a[i + 5], a[i + 6], a[i + 7], a[i + 8]);
8678
+ else tri([a[i], a[i + 1], a[i + 2]], [a[i + 3], a[i + 4], a[i + 5]], [a[i + 6], a[i + 7], a[i + 8]], 0);
8486
8679
  }
8487
8680
  const g = new THREE.BufferGeometry();
8488
- g.setAttribute("position", new THREE.Float32BufferAttribute(out, 3));
8681
+ g.setAttribute("position", new THREE.BufferAttribute(out.slice(0, used), 3));
8489
8682
  g.userData.crease = true;
8490
8683
  g.userData.p0 = null;
8491
8684
  return g;
@@ -8508,6 +8701,17 @@ var GameVehicles = (() => {
8508
8701
  });
8509
8702
  return out;
8510
8703
  }
8704
+ function boxInFrame(B, m, tmp) {
8705
+ const r = [Infinity, Infinity, Infinity, -Infinity, -Infinity, -Infinity];
8706
+ for (let i = 0; i < 8; i++) {
8707
+ tmp.set(i & 1 ? B.x1 : B.x0, i & 2 ? B.y1 : B.y0, i & 4 ? B.z1 : B.z0).applyMatrix4(m);
8708
+ for (let k = 0; k < 3; k++) {
8709
+ r[k] = Math.min(r[k], tmp.getComponent(k) - 1e-4);
8710
+ r[k + 3] = Math.max(r[k + 3], tmp.getComponent(k) + 1e-4);
8711
+ }
8712
+ }
8713
+ return r;
8714
+ }
8511
8715
  function zoneDent(c, z, dents, dirty, opt) {
8512
8716
  opt = opt || {};
8513
8717
  const B = z.box;
@@ -8562,6 +8766,7 @@ var GameVehicles = (() => {
8562
8766
  g = msh.geometry = g.clone();
8563
8767
  msh.userData.zCloned = true;
8564
8768
  }
8769
+ let boundsDue = false;
8565
8770
  const nonIdx = !g.index;
8566
8771
  const onlyPos = Object.keys(g.attributes).every(function(k) {
8567
8772
  return k === "position" || k === "normal";
@@ -8570,15 +8775,17 @@ var GameVehicles = (() => {
8570
8775
  const ng = subdivIn(g, rel, B, opt.sub || 0.075);
8571
8776
  g.dispose();
8572
8777
  g = msh.geometry = ng;
8573
- ng.computeBoundingSphere();
8574
- ng.computeBoundingBox();
8778
+ boundsDue = true;
8575
8779
  dirty.add(msh);
8576
8780
  }
8577
8781
  irel.copy(rel).invert();
8578
8782
  const pos = g.attributes.position;
8579
8783
  let changed = false;
8784
+ const near = boxInFrame(B, irel, v);
8580
8785
  for (let i = 0; i < pos.count; i++) {
8581
- v.fromBufferAttribute(pos, i).applyMatrix4(rel);
8786
+ v.fromBufferAttribute(pos, i);
8787
+ if (v.x < near[0] || v.x > near[3] || v.y < near[1] || v.y > near[4] || v.z < near[2] || v.z > near[5]) continue;
8788
+ v.applyMatrix4(rel);
8582
8789
  const f = field(v);
8583
8790
  if (f <= 1e-5) continue;
8584
8791
  v.addScaledVector(d, f);
@@ -8599,9 +8806,11 @@ var GameVehicles = (() => {
8599
8806
  }
8600
8807
  if (changed) {
8601
8808
  pos.needsUpdate = true;
8809
+ dirty.add(msh);
8810
+ }
8811
+ if (changed || boundsDue) {
8602
8812
  g.computeBoundingSphere();
8603
8813
  g.computeBoundingBox();
8604
- dirty.add(msh);
8605
8814
  }
8606
8815
  });
8607
8816
  const dset = new Set(c.deform);
@@ -11704,6 +11913,20 @@ var GameVehicles = (() => {
11704
11913
  else kit.applyArmor(c);
11705
11914
  }
11706
11915
  if (zoneState == null && st === "damaged" && kit.config.damagedScenario) zoneState = kit.zoneScenario(c, kit.config.damagedScenario);
11916
+ else if (zoneState && zoneState.scenario != null) {
11917
+ c.scenario = zoneState.scenario;
11918
+ let fromScenario = { health: {}, flags: {} };
11919
+ try {
11920
+ fromScenario = kit.zoneScenario(c, zoneState.scenario);
11921
+ } catch (e) {
11922
+ ;
11923
+ (c.zoneErrors = c.zoneErrors || []).push("scenario: " + (e && e.message));
11924
+ }
11925
+ zoneState = {
11926
+ health: Object.assign({}, fromScenario.health, zoneState.health),
11927
+ flags: Object.assign({}, fromScenario.flags, zoneState.flags)
11928
+ };
11929
+ }
11707
11930
  if (zoneState) kit.applyZones(c, zoneState);
11708
11931
  else {
11709
11932
  c.zoneState = { health: {}, flags: {} };
@@ -13702,7 +13925,7 @@ var GameVehicles = (() => {
13702
13925
  const reset = () => {
13703
13926
  const old = c;
13704
13927
  const parent = old.root.parent;
13705
- const next = kit.build(old.key, old.state);
13928
+ const next = kit.build(old.key, old.state, old.scenario != null ? { scenario: old.scenario } : void 0);
13706
13929
  if (parent) {
13707
13930
  next.root.position.copy(old.root.position);
13708
13931
  next.root.quaternion.copy(old.root.quaternion);
@@ -13759,7 +13982,8 @@ var GameVehicles = (() => {
13759
13982
  // src/destructible/item.ts
13760
13983
  function buildVehicleItem(kit, key, options = {}) {
13761
13984
  const THREE = kit.THREE;
13762
- const vehicle = options.lod ? kit.build(key, options.state ?? "intact", void 0, { lod: true }) : kit.build(key, options.state ?? "intact");
13985
+ const zoneState = options.scenario != null ? { scenario: options.scenario } : void 0;
13986
+ const vehicle = options.lod ? kit.build(key, options.state ?? "intact", zoneState, { lod: true }) : kit.build(key, options.state ?? "intact", zoneState);
13763
13987
  const m = vehicle.meta;
13764
13988
  const root = new THREE.Group();
13765
13989
  root.name = key;