@adatechnology/game-destruction 0.14.0 → 0.16.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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Vec3, Quat, MaterialId, FractureStyle, DamageType, DebrisKind, DestructionEvent, StructureRole, SoilProfile, WindExposure, IgnitionRules, BlastOptions, DestructibleSpec, ObjectState, DestructibleImpl, Hit, HitResult, RayHit, BallisticExtras, ToolKind, JointState, PartState, DestructionEventType, ConfinementOptions, FragmentHitOptions, ShelterOptions, ExplosionChain, PartSpec, JointKind, JointSpec, PartShape, SurfaceLayer, PartUnits, Reinforcement, PartBreach, DeepPartial, PileSpec, PartStatus, CostOptions } from '@adatechnology/game-contracts';
1
+ import { Vec3, Quat, MaterialId, FractureStyle, DamageType, DebrisKind, DestructionEvent, StructureRole, SoilProfile, WindExposure, IgnitionRules, BlastOptions, DestructibleSpec, ObjectState, DestructibleImpl, Hit, HitResult, ShelterOptions, RayHit, BallisticExtras, ToolKind, JointState, PartState, DestructionEventType, ConfinementOptions, FragmentHitOptions, ExplosionChain, PartSpec, JointKind, JointSpec, PartShape, SurfaceLayer, PartUnits, Reinforcement, PartBreach, DeepPartial, PileSpec, PartStatus, CostOptions } from '@adatechnology/game-contracts';
2
2
  export { BallisticExtras, BlastOptions, BlastReport, ChainDelay, ConfinementOptions, Crack, DamageStage, DamageType, DebrisKind, Dent, DestructibleImpl, DestructibleSpec, DestructionEvent, DestructionEventType, ExplosionChain, FluidKind, FractureStyle, Fragment, FragmentHitOptions, FragmentMark, Hit, HitIgnition, HitResult, Hole, IMPACT_CONTRACT, ImpactContract, JointKind, JointSpec, JointState, MaterialId, ObjectState, PartBreach, PartShape, PartSpec, PartState, PartStatus, PartUnits, PileSpec, Quat, RayHit, Reinforcement, ShelterOptions, SoilLayer, SoilProfile, SoilState, StructureRole, SurfaceLayer, TensionRebar, ToolKind, TunedMassDamper, Vec3, WindExposure, WindState, checkHit } from '@adatechnology/game-contracts';
3
3
 
4
4
  /**
@@ -457,6 +457,16 @@ interface DestructionWorld {
457
457
  * that target, and a `blast` event sums it up.
458
458
  */
459
459
  explode(point: Vec3, energy: number, radius: number, source?: string, options?: BlastOptions): HitResult[];
460
+ /**
461
+ * The share (0..1) of a blast at `point` that reaches `target` through what stands between them: every part the straight line crosses lets
462
+ * `exp(-r / k)` through (`ShelterOptions`; `r` the hit points of the slab per square metre), a broken part or a hole lets it all by. Always on, whatever
463
+ * `BlastOptions.shelter` says: the shove of a blast (people, the player, loose things) is blocked by cover in every room. `ignore` lists objects that never
464
+ * block (the explosive itself, the body being shoved).
465
+ */
466
+ blastReach(point: Vec3, target: Vec3, options?: {
467
+ readonly ignore?: readonly string[];
468
+ readonly shelter?: ShelterOptions;
469
+ }): number;
460
470
  /** Cost of the last `explode`: parts in range of the broad phase (`partsTested`) and those whose bounding sphere the blast reached (`partsNear`). */
461
471
  blastProbe(): {
462
472
  partsTested: number;
@@ -2782,6 +2782,7 @@ var GameDestruction = (() => {
2782
2782
  };
2783
2783
 
2784
2784
  // ../game-contracts/dist/index.js
2785
+ var DEFAULT_BLAST_IMPULSE = { bodyArea: 0.7, lift: 0.15, minSpeed: 0.3, tntPerJoule: 0.24 / 2e5 };
2785
2786
  var DEFAULT_COST_READS = 1e4;
2786
2787
  var DEFAULT_COST_CEILING_MS = 50;
2787
2788
  var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
@@ -3210,7 +3211,13 @@ var GameDestruction = (() => {
3210
3211
  const typed = listeners.get(e.type);
3211
3212
  if (typed) for (const fn of [...typed]) fn(e);
3212
3213
  };
3213
- const base = (o, source) => source === void 0 ? { object: o.id, time } : { object: o.id, time, source };
3214
+ let blow;
3215
+ const base = (o, source) => ({
3216
+ object: o.id,
3217
+ time,
3218
+ ...source === void 0 ? {} : { source },
3219
+ ...blow === void 0 ? {} : { blow }
3220
+ });
3214
3221
  const pendingCollapse = /* @__PURE__ */ new Map();
3215
3222
  let runDepth = 0;
3216
3223
  const drainBlasts = () => {
@@ -4761,7 +4768,7 @@ var GameDestruction = (() => {
4761
4768
  return;
4762
4769
  }
4763
4770
  if (ex) {
4764
- emit({ ...base(o, src), type: "explode", part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, ...ex.effect !== void 0 ? { effect: ex.effect } : {} });
4771
+ emit({ ...base(o, src), type: "explode", part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, ...ex.tnt !== void 0 ? { tnt: ex.tnt } : {}, ...ex.effect !== void 0 ? { effect: ex.effect } : {} });
4765
4772
  blasts.push({ point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, source: src });
4766
4773
  }
4767
4774
  partLost(o, p, src);
@@ -5355,7 +5362,17 @@ var GameDestruction = (() => {
5355
5362
  res.events = cur.slice(start);
5356
5363
  return res;
5357
5364
  };
5358
- const doHit = (o, hit, only) => {
5365
+ const withBlow = (type, fn) => () => {
5366
+ const outer = blow;
5367
+ blow = type;
5368
+ try {
5369
+ return fn();
5370
+ } finally {
5371
+ blow = outer;
5372
+ }
5373
+ };
5374
+ const doHit = (o, hit, only) => withBlow(hit.type, () => resolveHit(o, hit, only))();
5375
+ const resolveHit = (o, hit, only) => {
5359
5376
  let point = hit.point;
5360
5377
  let dir = hit.direction;
5361
5378
  if (hit.space === "local") {
@@ -5794,6 +5811,44 @@ var GameDestruction = (() => {
5794
5811
  blastProbe() {
5795
5812
  return { ...blastProbe };
5796
5813
  },
5814
+ blastReach(point, target, options2 = {}) {
5815
+ const L = vec.distance(point, target);
5816
+ if (L < 1e-4) return 1;
5817
+ const shelter = { ...SHELTER_DEFAULTS, ...options2.shelter };
5818
+ const ignore = new Set(options2.ignore ?? []);
5819
+ const d = vec.scale(vec.sub(target, point), 1 / L);
5820
+ let t = 1;
5821
+ for (const o of objects.values()) {
5822
+ if (ignore.has(o.id) || raySphereMiss(point, d, toWorld(o.st.position, o.st.rotation, o.bc), o.br, L)) continue;
5823
+ for (const p of o.parts) {
5824
+ if (isLost(p) || p.st.stripped) continue;
5825
+ const r = rayPart(o, p, point, d);
5826
+ if (!r) continue;
5827
+ const t0 = Math.max(r.t0, 0);
5828
+ const path = Math.min(r.t1, L) - t0;
5829
+ if (path < shelter.minPath) continue;
5830
+ if (p.st.opening && insideOpening(o, p, toLocal(o.st.position, o.st.rotation, vec.madd(point, d, t0 + path / 2)))) continue;
5831
+ t *= Math.exp(-(p.mat.hpPerKg * p.mat.density * path * (p.mat.damageScale?.shelter ?? 1)) / shelter.k);
5832
+ if (t < 1e-4) return 0;
5833
+ }
5834
+ }
5835
+ for (const im of impls.values()) {
5836
+ if (ignore.has(im.impl.id)) continue;
5837
+ for (const ps of im.impl.describe().parts) {
5838
+ const mat = materials.get(ps.material);
5839
+ if (!mat) continue;
5840
+ const ov = im.poses.get(ps.id);
5841
+ const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]), point, d, ov?.pos);
5842
+ if (!r || r.t1 < 0 || r.t0 > L) continue;
5843
+ let path = Math.min(r.t1, L) - Math.max(r.t0, 0);
5844
+ if (ps.thickness !== void 0 && ps.thickness > 0) path = Math.min(path, 2 * ps.thickness);
5845
+ else if (path < shelter.minPath) continue;
5846
+ t *= Math.exp(-(mat.hpPerKg * mat.density * path * (mat.damageScale?.shelter ?? 1)) / shelter.k);
5847
+ if (t < 1e-4) return 0;
5848
+ }
5849
+ }
5850
+ return t;
5851
+ },
5797
5852
  explode(point, energy, radius, source, blast) {
5798
5853
  blastProbe.partsTested = 0;
5799
5854
  blastProbe.partsNear = 0;
@@ -5818,7 +5873,7 @@ var GameDestruction = (() => {
5818
5873
  },
5819
5874
  shoot(origin, direction, energy, caliber, source, round) {
5820
5875
  const extras = typeof round === "number" ? { piercing: round } : round ?? {};
5821
- return withCollapses(run(() => {
5876
+ return withCollapses(run(withBlow("ballistic", () => {
5822
5877
  const results = [];
5823
5878
  const d = vec.normalize(direction);
5824
5879
  const ignore = /* @__PURE__ */ new Set();
@@ -5829,10 +5884,12 @@ var GameDestruction = (() => {
5829
5884
  if (!rh) break;
5830
5885
  const im = impls.get(rh.object);
5831
5886
  if (im) {
5832
- const r = implHit(im, { type: "ballistic", point: rh.point, part: rh.part, normal: rh.normal, direction: d, energy: E, caliber, ...extras, ...source !== void 0 ? { source } : {} });
5887
+ const scaled = extras.penetration && E < energy ? { ...extras, penetration: Object.fromEntries(Object.entries(extras.penetration).map(([k, mm]) => [k, mm * (E / energy)])) } : extras;
5888
+ const r = implHit(im, { type: "ballistic", point: rh.point, part: rh.part, normal: rh.normal, direction: d, energy: E, caliber, ...scaled, ...source !== void 0 ? { source } : {} });
5833
5889
  r.part = rh.part;
5834
5890
  r.point = rh.point;
5835
- r.exitEnergy = r.penetrated ? E * 0.5 : 0;
5891
+ const left = r.events.find((e) => e.type === "hole" && e.energyLeft !== void 0);
5892
+ r.exitEnergy = r.penetrated ? left?.energyLeft ?? E * 0.5 : 0;
5836
5893
  results.push(r);
5837
5894
  ignore.add(`${im.impl.id}\0${rh.part}`);
5838
5895
  if (!r.penetrated) break;
@@ -5851,7 +5908,7 @@ var GameDestruction = (() => {
5851
5908
  E = out.exitEnergy;
5852
5909
  }
5853
5910
  return results;
5854
- }));
5911
+ })));
5855
5912
  },
5856
5913
  disassemble(objectId, jointId, tool, dt) {
5857
5914
  const im = impls.get(objectId);