@adatechnology/game-vehicles 0.1.12 → 0.3.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/dist/index.js CHANGED
@@ -2,6 +2,9 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
+ // src/kit.ts
6
+ import { shapeGeometries } from "@adatechnology/game-shapes-three";
7
+
5
8
  // src/locales/en.json
6
9
  var en_default = {
7
10
  plate: {
@@ -837,9 +840,11 @@ function installGeometry(kit) {
837
840
  const V3 = THREE.Vector3;
838
841
  const PI = Math.PI;
839
842
  const I = kit._;
843
+ const lod = kit.lod;
840
844
  function roundedBoxGeometry(w, h, d, r) {
841
845
  r = Math.max(2e-3, Math.min(r, w * 0.45, h * 0.45, d * 0.45));
842
- const nb = 3;
846
+ if (lod.on && (r < 0.012 || Math.max(w, h, d) < lod.flatBoxEdge || Math.min(w, h, d) < 0.08)) return new THREE.BoxGeometry(w, h, d);
847
+ const nb = lod.on ? lod.roundingRings : 3;
843
848
  const n = 2 * nb + 1;
844
849
  const g = new THREE.BoxGeometry(1, 1, 1, n, n, n);
845
850
  const p = g.attributes.position;
@@ -884,6 +889,7 @@ function installGeometry(kit) {
884
889
  geo.computeVertexNormals();
885
890
  }
886
891
  function subdivide(geo, maxLen) {
892
+ if (lod.on) maxLen = maxLen * lod.subdivisionFactor;
887
893
  const src = geo.index ? geo.toNonIndexed() : geo;
888
894
  const a = src.attributes.position.array;
889
895
  const out = [];
@@ -1613,6 +1619,7 @@ function installWarp(kit) {
1613
1619
  const V3 = THREE.Vector3;
1614
1620
  const PI = Math.PI;
1615
1621
  const I = kit._;
1622
+ const lod = kit.lod;
1616
1623
  const { crease } = I;
1617
1624
  function makeWarp(o) {
1618
1625
  function sec(y) {
@@ -1710,6 +1717,7 @@ function installWarp(kit) {
1710
1717
  });
1711
1718
  }
1712
1719
  function resample(st, n) {
1720
+ if (lod.on) n = Math.max(lod.minStations, Math.round(n * lod.stationFraction));
1713
1721
  const out = [];
1714
1722
  const L = st.length;
1715
1723
  function P(i) {
@@ -1741,6 +1749,7 @@ function installWarp(kit) {
1741
1749
  }
1742
1750
  function smoothLoft(st, o) {
1743
1751
  o = o || {};
1752
+ if (lod.on) o = Object.assign({}, o, { seg: Math.max(lod.minLoftSegments, Math.round((o.seg || 24) * lod.loftSegmentFraction)) });
1744
1753
  if (o.smooth) st = resample(st, o.smooth);
1745
1754
  const seg = o.seg || 24;
1746
1755
  const pos = [];
@@ -9755,16 +9764,167 @@ function installUi(kit) {
9755
9764
  kit.ui = { damageMap, bandButtons, legend, healthBar, scenarioButtons, zoneLabel, bandRange, css, injectStyles };
9756
9765
  }
9757
9766
 
9767
+ // src/lod.ts
9768
+ var defaultLodSettings = {
9769
+ on: false,
9770
+ segmentFraction: 0.5,
9771
+ roundingRings: 1,
9772
+ subdivisionFactor: 2.2,
9773
+ flatBoxEdge: 0.22,
9774
+ minLoftSegments: 14,
9775
+ loftSegmentFraction: 0.45,
9776
+ minStations: 20,
9777
+ stationFraction: 0.55,
9778
+ gridFactor: 2.6,
9779
+ hiddenMaterials: ["seat", "carpet", "headliner", "gauge", "screen", "bay", "hoodLiner", "block", "cover", "radiator", "coolant", "coolantLiq", "washer", "hv", "hose", "leather"]
9780
+ };
9781
+ function createLod(options) {
9782
+ return { ...defaultLodSettings, ...options || {}, on: false };
9783
+ }
9784
+ var fewer = (lod, n, min) => Math.max(min, Math.round(n * lod.segmentFraction));
9785
+ function wrapThreeForLod(TH, lod) {
9786
+ const T = Object.assign({}, TH);
9787
+ if (TH.SRGBColorSpace) {
9788
+ T.CanvasTexture = class extends TH.CanvasTexture {
9789
+ constructor(a, b, c, d, e, f, g, h, i) {
9790
+ super(a, b, c, d, e, f, g, h, i);
9791
+ this.colorSpace = TH.SRGBColorSpace;
9792
+ }
9793
+ };
9794
+ }
9795
+ T.CylinderGeometry = class extends TH.CylinderGeometry {
9796
+ constructor(rt, rb, h, rs, hs, open, ts, tl) {
9797
+ super(rt, rb, h, lod.on && rs != null ? fewer(lod, rs, 6) : rs, lod.on ? 1 : hs, open, ts, tl);
9798
+ }
9799
+ };
9800
+ T.SphereGeometry = class extends TH.SphereGeometry {
9801
+ constructor(r, ws, hs, ps, pl, ts, tl) {
9802
+ super(r, lod.on && ws != null ? fewer(lod, ws, 8) : ws, lod.on && hs != null ? fewer(lod, hs, 5) : hs, ps, pl, ts, tl);
9803
+ }
9804
+ };
9805
+ T.TorusGeometry = class extends TH.TorusGeometry {
9806
+ constructor(r, tube, rs, ts, arc) {
9807
+ super(r, tube, lod.on && rs != null ? fewer(lod, rs, 4) : rs, lod.on && ts != null ? fewer(lod, ts, 8) : ts, arc);
9808
+ }
9809
+ };
9810
+ T.LatheGeometry = class extends TH.LatheGeometry {
9811
+ constructor(pts, seg, a, b) {
9812
+ super(pts, lod.on && seg != null ? fewer(lod, seg, 10) : seg, a, b);
9813
+ }
9814
+ };
9815
+ T.TubeGeometry = class extends TH.TubeGeometry {
9816
+ constructor(path, ts, r, rs, closed) {
9817
+ if (lod.on) {
9818
+ let len = 1;
9819
+ try {
9820
+ len = path.getLength();
9821
+ } catch (e) {
9822
+ }
9823
+ ts = Math.max(6, Math.min(ts == null ? 64 : ts, Math.ceil(len / 0.14)));
9824
+ rs = r < 0.025 ? 3 : Math.max(4, fewer(lod, rs == null ? 8 : rs, 4));
9825
+ }
9826
+ super(path, ts, r, rs, closed);
9827
+ }
9828
+ };
9829
+ T.ExtrudeGeometry = class extends TH.ExtrudeGeometry {
9830
+ constructor(shape, o) {
9831
+ if (lod.on && o) {
9832
+ o = Object.assign({}, o);
9833
+ if (o.curveSegments) o.curveSegments = fewer(lod, o.curveSegments, 5);
9834
+ if (o.bevelSegments) o.bevelSegments = Math.max(1, Math.round(o.bevelSegments * 0.5));
9835
+ }
9836
+ super(shape, o);
9837
+ }
9838
+ };
9839
+ T.CircleGeometry = class extends TH.CircleGeometry {
9840
+ constructor(r, seg, a, b) {
9841
+ super(r, lod.on && seg != null ? fewer(lod, seg, 8) : seg, a, b);
9842
+ }
9843
+ };
9844
+ T.RingGeometry = class extends TH.RingGeometry {
9845
+ constructor(a, b, ts, ps, c, d) {
9846
+ super(a, b, lod.on && ts != null ? fewer(lod, ts, 8) : ts, ps, c, d);
9847
+ }
9848
+ };
9849
+ return T;
9850
+ }
9851
+ var INTERIOR_FUNCTIONS = ["interior", "engineBay", "seat", "dashboard", "steeringWheel", "yoke", "busRows"];
9852
+ function installLod(kit) {
9853
+ const lod = kit.lod;
9854
+ const meshesOf = (c) => {
9855
+ const set = /* @__PURE__ */ new Set();
9856
+ c.vehicle.traverse((o) => {
9857
+ if (o.isMesh) set.add(o);
9858
+ });
9859
+ return set;
9860
+ };
9861
+ for (const name of INTERIOR_FUNCTIONS) {
9862
+ const original = kit[name];
9863
+ if (typeof original !== "function") continue;
9864
+ kit[name] = function(...args) {
9865
+ const c = args[0];
9866
+ if (!lod.on || !c || !c.vehicle) return original.apply(this, args);
9867
+ const before = meshesOf(c);
9868
+ const result = original.apply(this, args);
9869
+ c.vehicle.traverse((o) => {
9870
+ if (o.isMesh && !before.has(o)) o.userData.skipLite = true;
9871
+ });
9872
+ return result;
9873
+ };
9874
+ }
9875
+ kit.lodHiddenMaterials = function() {
9876
+ const materials = kit.materials;
9877
+ return new Set(lod.hiddenMaterials.map((name) => materials[name]).filter(Boolean));
9878
+ };
9879
+ }
9880
+
9881
+ // src/damageables.ts
9882
+ var defaultDamageableKinds = {
9883
+ door: { material: "metal", part: "door" },
9884
+ hood: { material: "metal", part: "hood" },
9885
+ lid: { material: "metal", part: "lid" },
9886
+ bumper: { material: "metal", part: "bumper" },
9887
+ glass: { material: "glass", part: "window" },
9888
+ "glass:windshield": { material: "glass", part: "windshield" },
9889
+ light: { material: "glass", part: "headlight" },
9890
+ "light:taillight": { material: "glass", part: "taillight" },
9891
+ wheel: { material: "rubber", part: "wheel" },
9892
+ "internal:fuel_tank": { material: "fuel", part: "fuel_tank" }
9893
+ };
9894
+ function installDamageables(kit) {
9895
+ const THREE = kit.THREE;
9896
+ kit.damageableKinds = { ...defaultDamageableKinds };
9897
+ const kindOf2 = (zone) => {
9898
+ const kinds = kit.damageableKinds;
9899
+ if (zone.type === "internal") return zone.sub ? kinds[`internal:${zone.sub}`] || null : null;
9900
+ return kinds[`${zone.type}:${zone.fxKey}`] || kinds[zone.type] || null;
9901
+ };
9902
+ kit.damageables = function(c, matrix) {
9903
+ const out = [];
9904
+ for (const zone of c.zones || []) {
9905
+ const kind = kindOf2(zone);
9906
+ if (!kind || !zone.box) continue;
9907
+ const b = zone.box;
9908
+ const box = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
9909
+ if (matrix) box.applyMatrix4(matrix);
9910
+ out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box });
9911
+ }
9912
+ return out;
9913
+ };
9914
+ }
9915
+
9758
9916
  // src/kit.ts
9759
9917
  function createVehicleKit(options) {
9760
9918
  const opts = options || {};
9761
- const THREE = opts.THREE;
9762
- if (!THREE || !THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
9919
+ if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
9920
+ const lod = createLod(opts.lod);
9921
+ const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
9763
9922
  const strings = resolveStrings(opts.locale);
9764
9923
  const t = createTranslate(strings);
9765
9924
  const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
9766
9925
  const kit = {
9767
9926
  THREE,
9927
+ lod,
9768
9928
  V3: THREE.Vector3,
9769
9929
  PI: Math.PI,
9770
9930
  document: doc,
@@ -9836,6 +9996,8 @@ function createVehicleKit(options) {
9836
9996
  installZoneApply(kit);
9837
9997
  installScenarios(kit);
9838
9998
  installUi(kit);
9999
+ installLod(kit);
10000
+ installDamageables(kit);
9839
10001
  const I = kit._;
9840
10002
  const HOOKS = {};
9841
10003
  kit.on = function(ev, fn) {
@@ -9930,9 +10092,18 @@ function createVehicleKit(options) {
9930
10092
  }
9931
10093
  }
9932
10094
  kit.defaultDamage = defaultDamage;
9933
- kit.build = function(key, state, zoneState) {
10095
+ kit.build = function(key, state, zoneState, options2) {
9934
10096
  const spec = kit.find(key);
9935
10097
  if (!spec) throw new Error("Unknown vehicle: " + key);
10098
+ const lodBefore = lod.on;
10099
+ if (options2 && options2.lod != null) lod.on = !!options2.lod;
10100
+ try {
10101
+ return buildVehicle(spec, key, state, zoneState);
10102
+ } finally {
10103
+ lod.on = lodBefore;
10104
+ }
10105
+ };
10106
+ function buildVehicle(spec, key, state, zoneState) {
9936
10107
  const st = state || "intact";
9937
10108
  const seedKey = spec.seedKey || key;
9938
10109
  const c = new kit.BuildContext(st, strHash(seedKey + (LEGACY_STATES[st] || st)));
@@ -9981,7 +10152,7 @@ function createVehicleKit(options) {
9981
10152
  if (o.isMesh && !(o.material && o.material.transparent)) o.receiveShadow = true;
9982
10153
  });
9983
10154
  return c;
9984
- };
10155
+ }
9985
10156
  kit.dispose = function(c) {
9986
10157
  const shared = Object.values(I.PARTICLE_GEOMETRIES);
9987
10158
  c.root.traverse(function(o) {
@@ -9998,8 +10169,9 @@ function createVehicleKit(options) {
9998
10169
  assignDeep(strings, resolveStrings(opts.locale));
9999
10170
  return kit;
10000
10171
  };
10001
- const { THREE: _three, locale: _locale, document: _document, plugins, ...config } = opts;
10172
+ const { THREE: _three, lod: _lod, locale: _locale, document: _document, plugins, ...config } = opts;
10002
10173
  void _three;
10174
+ void _lod;
10003
10175
  void _locale;
10004
10176
  void _document;
10005
10177
  kit.configure(config);
@@ -10007,6 +10179,9 @@ function createVehicleKit(options) {
10007
10179
  return kit;
10008
10180
  }
10009
10181
 
10182
+ // src/destructible/impl.ts
10183
+ import { ignitionChance, resolveIgnitionRules, sparkTargetFactor } from "@adatechnology/game-contracts";
10184
+
10010
10185
  // src/destructible/model.ts
10011
10186
  var JOINT_RULES = {
10012
10187
  hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
@@ -11119,6 +11294,9 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11119
11294
  const headless = !!options.headless;
11120
11295
  const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
11121
11296
  let rand = rng(seedBase);
11297
+ const ignitionRules = resolveIgnitionRules(options.ignition);
11298
+ const fuelFraction = clamp(options.fuel ?? 1, 0, 1);
11299
+ let igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11122
11300
  let clock = 0;
11123
11301
  let model;
11124
11302
  let vis = null;
@@ -11186,7 +11364,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11186
11364
  dents: [],
11187
11365
  holes: [],
11188
11366
  cracks: [],
11189
- liters: d.contains?.liters,
11367
+ liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
11190
11368
  lost: false,
11191
11369
  status: "intact",
11192
11370
  flat: false,
@@ -11204,6 +11382,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11204
11382
  wasLit = true;
11205
11383
  deadEngine = false;
11206
11384
  rand = rng(seedBase);
11385
+ igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
11207
11386
  steerSign = rand() < 0.5 ? -1 : 1;
11208
11387
  vis = headless ? null : new VehicleVisuals(kit, c, model);
11209
11388
  recomputeGroups();
@@ -11498,8 +11677,19 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11498
11677
  if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
11499
11678
  }
11500
11679
  for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
11680
+ fireNearLeak(flames, dt, out);
11501
11681
  thermal(out);
11502
11682
  };
11683
+ const fireNearLeak = (flames, dt, out) => {
11684
+ const tank = tankPart();
11685
+ const leak = tank ? leaks.get(tank.def.id) : void 0;
11686
+ if (!tank || !leak || tank.lost || tank.burning || exploded || leak.fluid !== "fuel" && leak.fluid !== "diesel") return;
11687
+ if (leak.pud?.fire) return light(tank, out);
11688
+ const { radius, chancePerSecond } = ignitionRules.fireNear;
11689
+ if (chancePerSecond <= 0) return;
11690
+ const near = flames.some((f) => f !== tank && distToBox(tank.def, centreOf(f.def)) - Math.max(...f.def.box.size) / 2 <= radius);
11691
+ if (near && igniteRand() < 1 - Math.exp(-chancePerSecond * dt)) light(tank, out);
11692
+ };
11503
11693
  const normalOf = (d, v) => faceNormal(d, v);
11504
11694
  const ballistic = (h, out) => {
11505
11695
  sync();
@@ -11567,10 +11757,21 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11567
11757
  let amount = through ? E * 0.6 : E * 0.25;
11568
11758
  if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
11569
11759
  damage(p, amount, "ballistic", h.source, out, worldPoint);
11570
- if (d.role === "fuel_tank" && E > 1500 && (p.liters ?? 0) > 1) p.temp = Math.max(p.temp, d.ignitionC ?? 260);
11760
+ if (through && d.role === "fuel_tank") rollIgnition(p, h, metal, out);
11571
11761
  }
11572
11762
  }
11573
11763
  };
11764
+ const light = (tank, out) => {
11765
+ tank.temp = Math.max(tank.temp, (tank.def.ignitionC ?? 260) + 1);
11766
+ if ((tank.liters ?? 0) > 0.5) ignite(tank, out);
11767
+ };
11768
+ const rollIgnition = (tank, h, metal, out) => {
11769
+ const d = tank.def;
11770
+ if (!d.contains || tank.burning || (tank.liters ?? 0) <= 0.05) return;
11771
+ const fill = d.contains.liters > 0 ? (tank.liters ?? 0) / d.contains.liters : 0;
11772
+ const chance = ignitionChance(h, { sparkTarget: sparkTargetFactor(d.material, metal, ignitionRules), fill, fluid: d.contains.fluid }, ignitionRules);
11773
+ if (chance > 0 && igniteRand() < chance) light(tank, out);
11774
+ };
11574
11775
  const blunt = (h, out) => {
11575
11776
  sync();
11576
11777
  const M = toFrameMatrix();
@@ -11634,7 +11835,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11634
11835
  const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
11635
11836
  if (verdict === "explosion") explode(tank, h.source, out);
11636
11837
  else if (verdict === "fire") {
11637
- tank.temp = Math.max(tank.temp, tank.def.ignitionC ?? 260);
11838
+ light(tank, out);
11638
11839
  startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
11639
11840
  }
11640
11841
  }
@@ -11953,7 +12154,7 @@ function createVehicleDestructible(kit, vehicleCtx, options = {}) {
11953
12154
  // src/destructible/item.ts
11954
12155
  function buildVehicleItem(kit, key, options = {}) {
11955
12156
  const THREE = kit.THREE;
11956
- const vehicle = kit.build(key, options.state ?? "intact");
12157
+ const vehicle = options.lod ? kit.build(key, options.state ?? "intact", void 0, { lod: true }) : kit.build(key, options.state ?? "intact");
11957
12158
  const m = vehicle.meta;
11958
12159
  const root = new THREE.Group();
11959
12160
  root.name = key;
@@ -11973,9 +12174,12 @@ export {
11973
12174
  buildVehicleItem,
11974
12175
  buildVehicleModel,
11975
12176
  clamp,
12177
+ createLod,
11976
12178
  createTranslate,
11977
12179
  createVehicleDestructible,
11978
12180
  createVehicleKit,
12181
+ defaultDamageableKinds,
12182
+ defaultLodSettings,
11979
12183
  format,
11980
12184
  hash,
11981
12185
  legacyGlassId,
@@ -11987,6 +12191,7 @@ export {
11987
12191
  resolveStrings,
11988
12192
  rng,
11989
12193
  smooth,
11990
- strHash
12194
+ strHash,
12195
+ wrapThreeForLod
11991
12196
  };
11992
12197
  //# sourceMappingURL=index.js.map