@adatechnology/game-destruction 0.14.0 → 0.15.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;
@@ -3210,7 +3210,13 @@ var GameDestruction = (() => {
3210
3210
  const typed = listeners.get(e.type);
3211
3211
  if (typed) for (const fn of [...typed]) fn(e);
3212
3212
  };
3213
- const base = (o, source) => source === void 0 ? { object: o.id, time } : { object: o.id, time, source };
3213
+ let blow;
3214
+ const base = (o, source) => ({
3215
+ object: o.id,
3216
+ time,
3217
+ ...source === void 0 ? {} : { source },
3218
+ ...blow === void 0 ? {} : { blow }
3219
+ });
3214
3220
  const pendingCollapse = /* @__PURE__ */ new Map();
3215
3221
  let runDepth = 0;
3216
3222
  const drainBlasts = () => {
@@ -5355,7 +5361,17 @@ var GameDestruction = (() => {
5355
5361
  res.events = cur.slice(start);
5356
5362
  return res;
5357
5363
  };
5358
- const doHit = (o, hit, only) => {
5364
+ const withBlow = (type, fn) => () => {
5365
+ const outer = blow;
5366
+ blow = type;
5367
+ try {
5368
+ return fn();
5369
+ } finally {
5370
+ blow = outer;
5371
+ }
5372
+ };
5373
+ const doHit = (o, hit, only) => withBlow(hit.type, () => resolveHit(o, hit, only))();
5374
+ const resolveHit = (o, hit, only) => {
5359
5375
  let point = hit.point;
5360
5376
  let dir = hit.direction;
5361
5377
  if (hit.space === "local") {
@@ -5794,6 +5810,44 @@ var GameDestruction = (() => {
5794
5810
  blastProbe() {
5795
5811
  return { ...blastProbe };
5796
5812
  },
5813
+ blastReach(point, target, options2 = {}) {
5814
+ const L = vec.distance(point, target);
5815
+ if (L < 1e-4) return 1;
5816
+ const shelter = { ...SHELTER_DEFAULTS, ...options2.shelter };
5817
+ const ignore = new Set(options2.ignore ?? []);
5818
+ const d = vec.scale(vec.sub(target, point), 1 / L);
5819
+ let t = 1;
5820
+ for (const o of objects.values()) {
5821
+ if (ignore.has(o.id) || raySphereMiss(point, d, toWorld(o.st.position, o.st.rotation, o.bc), o.br, L)) continue;
5822
+ for (const p of o.parts) {
5823
+ if (isLost(p) || p.st.stripped) continue;
5824
+ const r = rayPart(o, p, point, d);
5825
+ if (!r) continue;
5826
+ const t0 = Math.max(r.t0, 0);
5827
+ const path = Math.min(r.t1, L) - t0;
5828
+ if (path < shelter.minPath) continue;
5829
+ if (p.st.opening && insideOpening(o, p, toLocal(o.st.position, o.st.rotation, vec.madd(point, d, t0 + path / 2)))) continue;
5830
+ t *= Math.exp(-(p.mat.hpPerKg * p.mat.density * path * (p.mat.damageScale?.shelter ?? 1)) / shelter.k);
5831
+ if (t < 1e-4) return 0;
5832
+ }
5833
+ }
5834
+ for (const im of impls.values()) {
5835
+ if (ignore.has(im.impl.id)) continue;
5836
+ for (const ps of im.impl.describe().parts) {
5837
+ const mat = materials.get(ps.material);
5838
+ if (!mat) continue;
5839
+ const ov = im.poses.get(ps.id);
5840
+ const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]), point, d, ov?.pos);
5841
+ if (!r || r.t1 < 0 || r.t0 > L) continue;
5842
+ let path = Math.min(r.t1, L) - Math.max(r.t0, 0);
5843
+ if (ps.thickness !== void 0 && ps.thickness > 0) path = Math.min(path, 2 * ps.thickness);
5844
+ else if (path < shelter.minPath) continue;
5845
+ t *= Math.exp(-(mat.hpPerKg * mat.density * path * (mat.damageScale?.shelter ?? 1)) / shelter.k);
5846
+ if (t < 1e-4) return 0;
5847
+ }
5848
+ }
5849
+ return t;
5850
+ },
5797
5851
  explode(point, energy, radius, source, blast) {
5798
5852
  blastProbe.partsTested = 0;
5799
5853
  blastProbe.partsNear = 0;
@@ -5818,7 +5872,7 @@ var GameDestruction = (() => {
5818
5872
  },
5819
5873
  shoot(origin, direction, energy, caliber, source, round) {
5820
5874
  const extras = typeof round === "number" ? { piercing: round } : round ?? {};
5821
- return withCollapses(run(() => {
5875
+ return withCollapses(run(withBlow("ballistic", () => {
5822
5876
  const results = [];
5823
5877
  const d = vec.normalize(direction);
5824
5878
  const ignore = /* @__PURE__ */ new Set();
@@ -5829,10 +5883,12 @@ var GameDestruction = (() => {
5829
5883
  if (!rh) break;
5830
5884
  const im = impls.get(rh.object);
5831
5885
  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 } : {} });
5886
+ const scaled = extras.penetration && E < energy ? { ...extras, penetration: Object.fromEntries(Object.entries(extras.penetration).map(([k, mm]) => [k, mm * (E / energy)])) } : extras;
5887
+ 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
5888
  r.part = rh.part;
5834
5889
  r.point = rh.point;
5835
- r.exitEnergy = r.penetrated ? E * 0.5 : 0;
5890
+ const left = r.events.find((e) => e.type === "hole" && e.energyLeft !== void 0);
5891
+ r.exitEnergy = r.penetrated ? left?.energyLeft ?? E * 0.5 : 0;
5836
5892
  results.push(r);
5837
5893
  ignore.add(`${im.impl.id}\0${rh.part}`);
5838
5894
  if (!r.penetrated) break;
@@ -5851,7 +5907,7 @@ var GameDestruction = (() => {
5851
5907
  E = out.exitEnergy;
5852
5908
  }
5853
5909
  return results;
5854
- }));
5910
+ })));
5855
5911
  },
5856
5912
  disassemble(objectId, jointId, tool, dt) {
5857
5913
  const im = impls.get(objectId);