@adatechnology/game-destruction 0.13.2 → 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/README.md +5 -0
- package/dist/index.d.ts +46 -7
- package/dist/index.global.js +240 -20
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +115 -20
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -383,6 +383,11 @@ helpers `vec`, `quat`, `quatFromEuler`, `rotateVec`, `toLocal`, `toWorld`, the l
|
|
|
383
383
|
The locale table has `en` and `pt-BR` strings for material names, part statuses, joint kinds, tools, damage types, event labels and fracture styles:
|
|
384
384
|
`resolveStrings('pt-BR').materialNames.glass === 'vidro'`. Unknown locales fall back to `en`.
|
|
385
385
|
|
|
386
|
+
## Assemblies by instance
|
|
387
|
+
|
|
388
|
+
`createAssemblies({ packs, patch, remove })` makes a set of assemblies of its own: the built-in ones plus `packs`. `packs` is a list of lists of `AssemblyDef` (`[[wallDef, doorDef], [extraDef]]`); a def with the id of a built-in replaces it.
|
|
389
|
+
`patch` merges fields by assembly id, `remove` lists ids to take out. The global `ASSEMBLIES`, `registerAssembly` and the rest are deprecated delegates of `defaultAssemblies()`.
|
|
390
|
+
|
|
386
391
|
## Português
|
|
387
392
|
|
|
388
393
|
Modelo de destruição independente de engine. Um objeto é um conjunto de peças (caixa, cilindro ou esfera com um material) unidas por juntas. O
|
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,
|
|
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
|
/**
|
|
@@ -324,6 +324,11 @@ interface DestructionOptions {
|
|
|
324
324
|
maxDents?: number;
|
|
325
325
|
/** Maximum holes kept per part (default 32). */
|
|
326
326
|
maxHoles?: number;
|
|
327
|
+
/**
|
|
328
|
+
* Most primed explosives that go off in one `update` (default: no cap). The rest wait, ordered by due time, then older object, then part
|
|
329
|
+
* order, and go off in the next updates, so a long chain spreads over steps by count and not by the speed of the machine.
|
|
330
|
+
*/
|
|
331
|
+
chainLinksPerUpdate?: number;
|
|
327
332
|
/** Distance at which a burning part heats parts of other objects, in metres (default 0.6). */
|
|
328
333
|
fireSpread?: number;
|
|
329
334
|
/**
|
|
@@ -452,6 +457,16 @@ interface DestructionWorld {
|
|
|
452
457
|
* that target, and a `blast` event sums it up.
|
|
453
458
|
*/
|
|
454
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;
|
|
455
470
|
/** Cost of the last `explode`: parts in range of the broad phase (`partsTested`) and those whose bounding sphere the blast reached (`partsNear`). */
|
|
456
471
|
blastProbe(): {
|
|
457
472
|
partsTested: number;
|
|
@@ -711,16 +726,38 @@ interface AssemblyPartOptions {
|
|
|
711
726
|
/** Materials to read densities from (default the built-in `MATERIALS`). */
|
|
712
727
|
materials?: ReadonlyMap<MaterialId, MaterialDef>;
|
|
713
728
|
}
|
|
714
|
-
/**
|
|
729
|
+
/**
|
|
730
|
+
* Built-in assemblies; `registerAssembly` adds to this list.
|
|
731
|
+
* @deprecated the list every page shares. Use `createAssemblies({ packs, patch, remove })`, or `defaultAssemblies()` for this one.
|
|
732
|
+
*/
|
|
715
733
|
declare const ASSEMBLIES: AssemblyDef[];
|
|
734
|
+
interface AssembliesOptions {
|
|
735
|
+
/** Assemblies added to the built-in ones; one with the id of a built-in replaces it. */
|
|
736
|
+
readonly packs?: readonly (readonly AssemblyDef[])[];
|
|
737
|
+
/** Changes by id, merged field by field over what is there. */
|
|
738
|
+
readonly patch?: Readonly<Record<string, DeepPartial<AssemblyDef>>>;
|
|
739
|
+
/** Ids taken out. */
|
|
740
|
+
readonly remove?: readonly string[];
|
|
741
|
+
}
|
|
742
|
+
interface Assemblies {
|
|
743
|
+
list(): readonly AssemblyDef[];
|
|
744
|
+
get(id: string): AssemblyDef | undefined;
|
|
745
|
+
/** Adds an assembly, or replaces the one with the same id. */
|
|
746
|
+
register(def: AssemblyDef): void;
|
|
747
|
+
/** The wall as a `PartSpec`; throws for an id this set does not have. */
|
|
748
|
+
part(assemblyId: string, options: AssemblyPartOptions): PartSpec;
|
|
749
|
+
}
|
|
750
|
+
/** A set of assemblies of its own: the built-in ones (copied) plus `packs`, `patch` and `remove`. Nothing is shared with any other set on the page. */
|
|
751
|
+
declare const createAssemblies: (options?: AssembliesOptions) => Assemblies;
|
|
752
|
+
/** The set behind `ASSEMBLIES`, `registerAssembly`, `getAssembly` and `assemblyPart`, created on first use. */
|
|
753
|
+
declare const defaultAssemblies: () => Assemblies;
|
|
754
|
+
/** @deprecated use `createAssemblies(...).register`. */
|
|
716
755
|
declare const registerAssembly: (def: AssemblyDef) => void;
|
|
756
|
+
/** @deprecated use `createAssemblies(...).get`. */
|
|
717
757
|
declare const getAssembly: (id: string) => AssemblyDef | undefined;
|
|
718
758
|
/** Physical depth of the wall in metres. */
|
|
719
759
|
declare const assemblyDepth: (def: AssemblyDef) => number;
|
|
720
|
-
/**
|
|
721
|
-
* The wall as a box `[width, height, depth]` (thin along local z) with the assembly's layers, units, rebar and breach. Mass is the core (times `fill`)
|
|
722
|
-
* plus the coats; `thickness` is the core's, so each coat adds its own `penetration x thickness` to what a bullet must cross.
|
|
723
|
-
*/
|
|
760
|
+
/** @deprecated use `createAssemblies(...).part`. */
|
|
724
761
|
declare const assemblyPart: (assemblyId: string, options: AssemblyPartOptions) => PartSpec;
|
|
725
762
|
|
|
726
763
|
interface FrameOptions {
|
|
@@ -1086,6 +1123,8 @@ declare const resolveStrings: (locale: string | undefined) => DestructionStrings
|
|
|
1086
1123
|
interface CheckOptions {
|
|
1087
1124
|
/** Ids of custom materials the impl uses besides the built-in `MATERIALS`. */
|
|
1088
1125
|
materials?: string[];
|
|
1126
|
+
/** Opt-in cost measurement of `state()` and `describe()` (D8). */
|
|
1127
|
+
cost?: boolean | CostOptions;
|
|
1089
1128
|
}
|
|
1090
1129
|
/**
|
|
1091
1130
|
* `checkDestructible` of `@adatechnology/game-contracts`, with the built-in `MATERIALS` (plus `options.materials`) as the known material ids.
|
|
@@ -1361,4 +1400,4 @@ declare const tensionCapacity: (dims: Vec3, o: {
|
|
|
1361
1400
|
tensileMPa?: number;
|
|
1362
1401
|
}) => number;
|
|
1363
1402
|
|
|
1364
|
-
export { ASSEMBLIES, type AddOptions, type AssemblyDef, type AssemblyPartOptions, type BearingWallOptions, type BendGeometry, type BendSection, CONFINEMENT_DEFAULTS, type ChainTrigger, type CheckOptions, type DestructionLocale, type DestructionOptions, type DestructionSnapshot, type DestructionStrings, type DestructionWorld, type ExplosivePartOptions, FIRE_PRIME_C, FLEXURE_DEFAULTS, FRAGMENT_HIT_DEFAULTS, type FlexureOptions, type FoundationSolve, type FrameOptions, GRAVITY, type Gust, JOINT_BY_MATERIALS, JOINT_DEFAULTS, MASONRY_DEFAULTS, MATERIALS, type MasonryOptions, type MaterialDef, type MaterialHit, type MaterialStrength, type MaterialThermal, type ObjectRuntime, type PartRuntime, type ResolvedWind, SHELTER_DEFAULTS, SOILS, SOIL_DEFAULTS, type SectionMoments, type SizeStructureOptions, type SoilDef, type SoilKind, type SoilOptions, type SoilSpring, type SwayState, type SwaySystem, type TensionSteel, WIND_DEFAULTS, WIND_TERRAIN, type WallFrameSolution, type WindAnalysis, type WindCase, type WindOptions, type WindPartInput, type WindTerrainDef, type WorldExplosive, analyzeWind, assemblyDepth, assemblyPart, bearingWallThickness, bendGeometry, blastImpulse, buildFrame, checkDestructible, crackingMoment, createDestruction, damperAmplification, damperTuning, elasticEnergy, explosivePart, exposureOf, footingArea, footingSoil, fragility, getAssembly, guessMaterial, gustAt, heightFactor, impulseEnergy, inferJoints, layersBetween, locales, makeGust, meanHeightFactor, momentNodes, momentOfEnergy, partCapacity, partMass, partThickness, partVolume, pileCapacity, pileLength, pileStiffness, profileOf, quat, quatFromEuler, registerAssembly, resolveStrings, resolveWind, rotateVec, sectionMoments, shapeAt, shearNodes, sizeRebar, sizeStructure, slendernessFactor, soilAt, solveFoundation, solveWallFrame, spanMoment, spanOf, steelMoment, stepSway, stiffnessCentre, subgradeModulus, tBeamInertia, tensionCapacity, tensionSteel, toLocal, toWorld, turbulenceIntensity, vec, windClass, windPressure };
|
|
1403
|
+
export { ASSEMBLIES, type AddOptions, type Assemblies, type AssembliesOptions, type AssemblyDef, type AssemblyPartOptions, type BearingWallOptions, type BendGeometry, type BendSection, CONFINEMENT_DEFAULTS, type ChainTrigger, type CheckOptions, type DestructionLocale, type DestructionOptions, type DestructionSnapshot, type DestructionStrings, type DestructionWorld, type ExplosivePartOptions, FIRE_PRIME_C, FLEXURE_DEFAULTS, FRAGMENT_HIT_DEFAULTS, type FlexureOptions, type FoundationSolve, type FrameOptions, GRAVITY, type Gust, JOINT_BY_MATERIALS, JOINT_DEFAULTS, MASONRY_DEFAULTS, MATERIALS, type MasonryOptions, type MaterialDef, type MaterialHit, type MaterialStrength, type MaterialThermal, type ObjectRuntime, type PartRuntime, type ResolvedWind, SHELTER_DEFAULTS, SOILS, SOIL_DEFAULTS, type SectionMoments, type SizeStructureOptions, type SoilDef, type SoilKind, type SoilOptions, type SoilSpring, type SwayState, type SwaySystem, type TensionSteel, WIND_DEFAULTS, WIND_TERRAIN, type WallFrameSolution, type WindAnalysis, type WindCase, type WindOptions, type WindPartInput, type WindTerrainDef, type WorldExplosive, analyzeWind, assemblyDepth, assemblyPart, bearingWallThickness, bendGeometry, blastImpulse, buildFrame, checkDestructible, crackingMoment, createAssemblies, createDestruction, damperAmplification, damperTuning, defaultAssemblies, elasticEnergy, explosivePart, exposureOf, footingArea, footingSoil, fragility, getAssembly, guessMaterial, gustAt, heightFactor, impulseEnergy, inferJoints, layersBetween, locales, makeGust, meanHeightFactor, momentNodes, momentOfEnergy, partCapacity, partMass, partThickness, partVolume, pileCapacity, pileLength, pileStiffness, profileOf, quat, quatFromEuler, registerAssembly, resolveStrings, resolveWind, rotateVec, sectionMoments, shapeAt, shearNodes, sizeRebar, sizeStructure, slendernessFactor, soilAt, solveFoundation, solveWallFrame, spanMoment, spanOf, steelMoment, stepSway, stiffnessCentre, subgradeModulus, tBeamInertia, tensionCapacity, tensionSteel, toLocal, toWorld, turbulenceIntensity, vec, windClass, windPressure };
|
package/dist/index.global.js
CHANGED
|
@@ -47,9 +47,11 @@ var GameDestruction = (() => {
|
|
|
47
47
|
checkDestructible: () => checkDestructible2,
|
|
48
48
|
checkHit: () => checkHit,
|
|
49
49
|
crackingMoment: () => crackingMoment,
|
|
50
|
+
createAssemblies: () => createAssemblies,
|
|
50
51
|
createDestruction: () => createDestruction,
|
|
51
52
|
damperAmplification: () => damperAmplification,
|
|
52
53
|
damperTuning: () => damperTuning,
|
|
54
|
+
defaultAssemblies: () => defaultAssemblies,
|
|
53
55
|
elasticEnergy: () => elasticEnergy,
|
|
54
56
|
explosivePart: () => explosivePart,
|
|
55
57
|
exposureOf: () => exposureOf,
|
|
@@ -2780,6 +2782,28 @@ var GameDestruction = (() => {
|
|
|
2780
2782
|
};
|
|
2781
2783
|
|
|
2782
2784
|
// ../game-contracts/dist/index.js
|
|
2785
|
+
var DEFAULT_COST_READS = 1e4;
|
|
2786
|
+
var DEFAULT_COST_CEILING_MS = 50;
|
|
2787
|
+
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
2788
|
+
var heapUsedBytes = () => {
|
|
2789
|
+
const nodeProcess = globalThis.process;
|
|
2790
|
+
return nodeProcess?.memoryUsage?.().heapUsed;
|
|
2791
|
+
};
|
|
2792
|
+
var checkReadCost = ({ name, read, options, problems }) => {
|
|
2793
|
+
const reads = options.reads ?? DEFAULT_COST_READS;
|
|
2794
|
+
const ceilingMs = options.ceilingMs ?? DEFAULT_COST_CEILING_MS;
|
|
2795
|
+
const allocationBytes = options.allocationBytes ?? DEFAULT_COST_ALLOCATION_BYTES;
|
|
2796
|
+
if (read() !== read()) problems.push(`${name}() returns a new object on each read: cache it between changes (cost contract)`);
|
|
2797
|
+
for (let i = 0; i < reads; i++) read();
|
|
2798
|
+
const heapBefore = heapUsedBytes();
|
|
2799
|
+
const started = performance.now();
|
|
2800
|
+
for (let i = 0; i < reads; i++) read();
|
|
2801
|
+
const elapsedMs = performance.now() - started;
|
|
2802
|
+
const heapAfter = heapUsedBytes();
|
|
2803
|
+
if (elapsedMs > ceilingMs) problems.push(`${name}() took ${elapsedMs.toFixed(1)} ms for ${reads} reads, over the ${ceilingMs} ms ceiling (cost contract)`);
|
|
2804
|
+
if (heapBefore !== void 0 && heapAfter !== void 0 && heapAfter - heapBefore > allocationBytes)
|
|
2805
|
+
problems.push(`${name}() grew the heap by ${heapAfter - heapBefore} bytes over ${reads} reads, over the ${allocationBytes} byte ceiling (cost contract)`);
|
|
2806
|
+
};
|
|
2783
2807
|
var IMPACT_CONTRACT = {
|
|
2784
2808
|
ballistic: { required: ["type", "point", "direction", "energy", "caliber"], optional: ["space", "source"], note: "bullets and pellets: joules and diameter in millimetres" },
|
|
2785
2809
|
blunt: { required: ["type", "point", "energy"], optional: ["direction", "radius", "space", "source"], note: "contact radius in metres (default 3 cm)" },
|
|
@@ -2885,6 +2909,15 @@ var GameDestruction = (() => {
|
|
|
2885
2909
|
} catch (e) {
|
|
2886
2910
|
problems.push(`reset() threw: ${String(e)}`);
|
|
2887
2911
|
}
|
|
2912
|
+
if (options.cost) {
|
|
2913
|
+
const cost = options.cost === true ? {} : options.cost;
|
|
2914
|
+
try {
|
|
2915
|
+
checkReadCost({ name: "state", read: () => impl.state(), options: cost, problems });
|
|
2916
|
+
checkReadCost({ name: "describe", read: () => impl.describe(), options: cost, problems });
|
|
2917
|
+
} catch (e) {
|
|
2918
|
+
problems.push(`cost measurement threw: ${String(e)}`);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2888
2921
|
return { ok: problems.length === 0, problems };
|
|
2889
2922
|
};
|
|
2890
2923
|
var DEFAULT_IGNITION_RULES = {
|
|
@@ -2930,6 +2963,103 @@ var GameDestruction = (() => {
|
|
|
2930
2963
|
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
2931
2964
|
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
2932
2965
|
};
|
|
2966
|
+
var DEFERRED_QUEUE_ID = {
|
|
2967
|
+
DESTRUCTION_EVENTS: "destruction.events",
|
|
2968
|
+
BREACH_PAINT: "floorplan.breach-paint",
|
|
2969
|
+
AUDIO_VOICES: "audio.voices",
|
|
2970
|
+
VEHICLE_CREASE: "vehicles.crease"
|
|
2971
|
+
};
|
|
2972
|
+
var DEFERRED_PRIORITY = {
|
|
2973
|
+
[DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
|
|
2974
|
+
[DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
|
|
2975
|
+
[DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
|
|
2976
|
+
[DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
|
|
2977
|
+
};
|
|
2978
|
+
var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
|
|
2979
|
+
var warnedKeys = () => {
|
|
2980
|
+
const holder = globalThis;
|
|
2981
|
+
return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
|
|
2982
|
+
};
|
|
2983
|
+
var isProduction = () => {
|
|
2984
|
+
try {
|
|
2985
|
+
return process.env.NODE_ENV === "production";
|
|
2986
|
+
} catch {
|
|
2987
|
+
return false;
|
|
2988
|
+
}
|
|
2989
|
+
};
|
|
2990
|
+
var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
|
|
2991
|
+
var warnOnce = (key, message) => {
|
|
2992
|
+
if (isQuiet() || isProduction()) return;
|
|
2993
|
+
const seen = warnedKeys();
|
|
2994
|
+
if (seen.has(key)) return;
|
|
2995
|
+
seen.add(key);
|
|
2996
|
+
console.warn(`[game-kit] ${message}`);
|
|
2997
|
+
};
|
|
2998
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
2999
|
+
var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
3000
|
+
var deepMerge = (base, patch) => {
|
|
3001
|
+
if (!patch) return base;
|
|
3002
|
+
const result = { ...base };
|
|
3003
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
3004
|
+
if (value === void 0 || UNSAFE_KEYS.has(key)) continue;
|
|
3005
|
+
const current = result[key];
|
|
3006
|
+
result[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value;
|
|
3007
|
+
}
|
|
3008
|
+
return result;
|
|
3009
|
+
};
|
|
3010
|
+
var mergeById = (shipped, extra) => {
|
|
3011
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3012
|
+
for (const entry of [...shipped, ...extra ?? []]) entries.set(entry.id, entry);
|
|
3013
|
+
return entries;
|
|
3014
|
+
};
|
|
3015
|
+
var applyPatches = (entries, patches) => {
|
|
3016
|
+
for (const [id, patch] of Object.entries(patches ?? {})) {
|
|
3017
|
+
const entry = entries.get(id);
|
|
3018
|
+
if (entry) entries.set(id, deepMerge(entry, patch));
|
|
3019
|
+
else warnOnce(`registry.patch.${id}`, `patch for "${id}" ignored: no entry has that id`);
|
|
3020
|
+
}
|
|
3021
|
+
};
|
|
3022
|
+
var removeIds = (entries, ids) => {
|
|
3023
|
+
for (const id of ids ?? []) {
|
|
3024
|
+
if (!entries.delete(id)) warnOnce(`registry.remove.${id}`, `remove of "${id}" ignored: no entry has that id`);
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
|
|
3028
|
+
var GAIT_IDS = ["walk", "run"];
|
|
3029
|
+
var LANDING_IDS = ["light", "heavy"];
|
|
3030
|
+
var BODY_SOUND_IDS = [
|
|
3031
|
+
"jump",
|
|
3032
|
+
"cloth_rustle",
|
|
3033
|
+
"body_fall",
|
|
3034
|
+
"fall_damage",
|
|
3035
|
+
"player_hurt",
|
|
3036
|
+
"player_breath_run",
|
|
3037
|
+
"player_death",
|
|
3038
|
+
"bite",
|
|
3039
|
+
"grab",
|
|
3040
|
+
"punch_swing",
|
|
3041
|
+
"punch_hit_body",
|
|
3042
|
+
"punch_hit_face",
|
|
3043
|
+
"punch_hit_object",
|
|
3044
|
+
"kick_swing",
|
|
3045
|
+
"kick_hit",
|
|
3046
|
+
"kick_hit_object",
|
|
3047
|
+
"shove_two_hands",
|
|
3048
|
+
"knife_swing",
|
|
3049
|
+
"knife_stab",
|
|
3050
|
+
"knife_slash",
|
|
3051
|
+
"knife_hit_object",
|
|
3052
|
+
"blunt_swing",
|
|
3053
|
+
"blunt_hit",
|
|
3054
|
+
"blunt_hit_object",
|
|
3055
|
+
"axe_swing",
|
|
3056
|
+
"axe_chop",
|
|
3057
|
+
"axe_hit_object"
|
|
3058
|
+
];
|
|
3059
|
+
var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
|
|
3060
|
+
var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
|
|
3061
|
+
var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing) => `land_${surface}_${landing}`));
|
|
3062
|
+
var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
2933
3063
|
|
|
2934
3064
|
// src/world.ts
|
|
2935
3065
|
var THIN_SHEET_DISHING = 5e-3;
|
|
@@ -3057,6 +3187,7 @@ var GameDestruction = (() => {
|
|
|
3057
3187
|
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
3058
3188
|
const breachBlastFactor = options.breach?.blastFactor ?? BREACH_BLAST_FACTOR;
|
|
3059
3189
|
const chipEnergy = options.breach?.chipEnergy ?? CHIP_ENERGY;
|
|
3190
|
+
const chainLinkCap = options.chainLinksPerUpdate !== void 0 && options.chainLinksPerUpdate >= 1 ? Math.floor(options.chainLinksPerUpdate) : Infinity;
|
|
3060
3191
|
const rng = createRng(options.seed ?? 1);
|
|
3061
3192
|
const rnd = () => rng.next();
|
|
3062
3193
|
let time = 0;
|
|
@@ -3079,7 +3210,13 @@ var GameDestruction = (() => {
|
|
|
3079
3210
|
const typed = listeners.get(e.type);
|
|
3080
3211
|
if (typed) for (const fn of [...typed]) fn(e);
|
|
3081
3212
|
};
|
|
3082
|
-
|
|
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
|
+
});
|
|
3083
3220
|
const pendingCollapse = /* @__PURE__ */ new Map();
|
|
3084
3221
|
let runDepth = 0;
|
|
3085
3222
|
const drainBlasts = () => {
|
|
@@ -4004,7 +4141,7 @@ var GameDestruction = (() => {
|
|
|
4004
4141
|
if (disc < 0) return true;
|
|
4005
4142
|
return -b - Math.sqrt(disc) > maxDist;
|
|
4006
4143
|
};
|
|
4007
|
-
const raycastInternal = (origin, dir, maxDist, ignore, worldOnly = false) => {
|
|
4144
|
+
const raycastInternal = (origin, dir, maxDist, ignore, worldOnly = false, precise = false) => {
|
|
4008
4145
|
const d = vec.normalize(dir);
|
|
4009
4146
|
let best = null;
|
|
4010
4147
|
for (const o of objects.values()) {
|
|
@@ -4019,13 +4156,25 @@ var GameDestruction = (() => {
|
|
|
4019
4156
|
}
|
|
4020
4157
|
}
|
|
4021
4158
|
for (const im of worldOnly ? [] : impls.values()) {
|
|
4159
|
+
let boxHit = null;
|
|
4022
4160
|
for (const ps of im.impl.describe().parts) {
|
|
4023
4161
|
if (ignore?.has(`${im.impl.id}\0${ps.id}`)) continue;
|
|
4024
4162
|
const ov = im.poses.get(ps.id);
|
|
4025
4163
|
const r = rayPartRaw(im.position, im.rotation, ps, ov?.q ?? quatFromEuler(...ps.rotation ?? [0, 0, 0]), origin, d, ov?.pos);
|
|
4026
|
-
if (!r || r.
|
|
4027
|
-
if (
|
|
4164
|
+
if (!r || r.t1 < 0 || r.t0 > maxDist) continue;
|
|
4165
|
+
if (r.t0 < 0) {
|
|
4166
|
+
if (precise && im.impl.surfaceRay) boxHit = boxHit ?? { object: im.impl.id, part: ps.id, point: origin, normal: r.n, distance: 0, pathLen: r.t1 };
|
|
4167
|
+
continue;
|
|
4168
|
+
}
|
|
4169
|
+
if (!boxHit || r.t0 < boxHit.distance) boxHit = { object: im.impl.id, part: ps.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 };
|
|
4028
4170
|
}
|
|
4171
|
+
if (!boxHit) continue;
|
|
4172
|
+
if (precise && im.impl.surfaceRay) {
|
|
4173
|
+
const far = best ? Math.min(maxDist, best.distance) : maxDist;
|
|
4174
|
+
const implId = im.impl.id;
|
|
4175
|
+
const s = im.impl.surfaceRay({ origin, direction: d, maxDistance: far, ...ignore ? { ignored: (part) => ignore.has(`${implId}\0${part}`) } : {} });
|
|
4176
|
+
if (s && (!best || s.distance < best.distance)) best = { object: implId, part: s.part, point: s.point, normal: s.normal, distance: s.distance, pathLen: s.pathLen };
|
|
4177
|
+
} else if (!best || boxHit.distance < best.distance) best = boxHit;
|
|
4029
4178
|
}
|
|
4030
4179
|
return best;
|
|
4031
4180
|
};
|
|
@@ -5212,7 +5361,17 @@ var GameDestruction = (() => {
|
|
|
5212
5361
|
res.events = cur.slice(start);
|
|
5213
5362
|
return res;
|
|
5214
5363
|
};
|
|
5215
|
-
const
|
|
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) => {
|
|
5216
5375
|
let point = hit.point;
|
|
5217
5376
|
let dir = hit.direction;
|
|
5218
5377
|
if (hit.space === "local") {
|
|
@@ -5509,7 +5668,9 @@ var GameDestruction = (() => {
|
|
|
5509
5668
|
};
|
|
5510
5669
|
const detonatePrimed = (until) => {
|
|
5511
5670
|
const end = time;
|
|
5512
|
-
|
|
5671
|
+
let fired = 0;
|
|
5672
|
+
for (let next = nextPrimed(until); next && fired < chainLinkCap; next = nextPrimed(until)) {
|
|
5673
|
+
fired++;
|
|
5513
5674
|
primed.delete(next.p);
|
|
5514
5675
|
time = Math.max(0, Math.min(end, next.p.rt.chainDue));
|
|
5515
5676
|
next.p.rt.chainDue = void 0;
|
|
@@ -5579,6 +5740,7 @@ var GameDestruction = (() => {
|
|
|
5579
5740
|
updateBound(o);
|
|
5580
5741
|
},
|
|
5581
5742
|
remove(id) {
|
|
5743
|
+
impls.get(id)?.impl.dispose?.();
|
|
5582
5744
|
objects.delete(id);
|
|
5583
5745
|
impls.delete(id);
|
|
5584
5746
|
},
|
|
@@ -5648,13 +5810,51 @@ var GameDestruction = (() => {
|
|
|
5648
5810
|
blastProbe() {
|
|
5649
5811
|
return { ...blastProbe };
|
|
5650
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
|
+
},
|
|
5651
5851
|
explode(point, energy, radius, source, blast) {
|
|
5652
5852
|
blastProbe.partsTested = 0;
|
|
5653
5853
|
blastProbe.partsNear = 0;
|
|
5654
5854
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
5655
5855
|
},
|
|
5656
5856
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
5657
|
-
const r = raycastInternal(origin, direction, maxDistance);
|
|
5857
|
+
const r = raycastInternal(origin, direction, maxDistance, void 0, false, true);
|
|
5658
5858
|
return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null;
|
|
5659
5859
|
},
|
|
5660
5860
|
sweepBox(box, direction, distance, options2 = {}) {
|
|
@@ -5672,21 +5872,23 @@ var GameDestruction = (() => {
|
|
|
5672
5872
|
},
|
|
5673
5873
|
shoot(origin, direction, energy, caliber, source, round) {
|
|
5674
5874
|
const extras = typeof round === "number" ? { piercing: round } : round ?? {};
|
|
5675
|
-
return withCollapses(run(() => {
|
|
5875
|
+
return withCollapses(run(withBlow("ballistic", () => {
|
|
5676
5876
|
const results = [];
|
|
5677
5877
|
const d = vec.normalize(direction);
|
|
5678
5878
|
const ignore = /* @__PURE__ */ new Set();
|
|
5679
5879
|
let org = origin;
|
|
5680
5880
|
let E = energy;
|
|
5681
5881
|
for (let i = 0; i < 4 && E > 0; i++) {
|
|
5682
|
-
const rh = raycastInternal(org, d, Infinity, ignore);
|
|
5882
|
+
const rh = raycastInternal(org, d, Infinity, ignore, false, true);
|
|
5683
5883
|
if (!rh) break;
|
|
5684
5884
|
const im = impls.get(rh.object);
|
|
5685
5885
|
if (im) {
|
|
5686
|
-
const
|
|
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 } : {} });
|
|
5687
5888
|
r.part = rh.part;
|
|
5688
5889
|
r.point = rh.point;
|
|
5689
|
-
|
|
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;
|
|
5690
5892
|
results.push(r);
|
|
5691
5893
|
ignore.add(`${im.impl.id}\0${rh.part}`);
|
|
5692
5894
|
if (!r.penetrated) break;
|
|
@@ -5705,7 +5907,7 @@ var GameDestruction = (() => {
|
|
|
5705
5907
|
E = out.exitEnergy;
|
|
5706
5908
|
}
|
|
5707
5909
|
return results;
|
|
5708
|
-
}));
|
|
5910
|
+
})));
|
|
5709
5911
|
},
|
|
5710
5912
|
disassemble(objectId, jointId, tool, dt) {
|
|
5711
5913
|
const im = impls.get(objectId);
|
|
@@ -6009,17 +6211,34 @@ var GameDestruction = (() => {
|
|
|
6009
6211
|
];
|
|
6010
6212
|
|
|
6011
6213
|
// src/assemblies.ts
|
|
6214
|
+
var SHIPPED = structuredClone(assemblies_default);
|
|
6012
6215
|
var ASSEMBLIES = assemblies_default;
|
|
6013
|
-
var
|
|
6014
|
-
const
|
|
6015
|
-
|
|
6016
|
-
|
|
6216
|
+
var assembliesOver = (defs) => {
|
|
6217
|
+
const get = (id) => defs.find((a) => a.id === id);
|
|
6218
|
+
return {
|
|
6219
|
+
list: () => defs,
|
|
6220
|
+
get,
|
|
6221
|
+
register(def) {
|
|
6222
|
+
const at = defs.findIndex((a) => a.id === def.id);
|
|
6223
|
+
if (at >= 0) defs[at] = def;
|
|
6224
|
+
else defs.push(def);
|
|
6225
|
+
},
|
|
6226
|
+
part: (assemblyId, options) => buildAssemblyPart(get(assemblyId), assemblyId, options)
|
|
6227
|
+
};
|
|
6228
|
+
};
|
|
6229
|
+
var createAssemblies = (options = {}) => {
|
|
6230
|
+
const entries = mergeById(SHIPPED.map((def) => structuredClone(def)), (options.packs ?? []).flat());
|
|
6231
|
+
applyPatches(entries, options.patch);
|
|
6232
|
+
removeIds(entries, options.remove);
|
|
6233
|
+
return assembliesOver([...entries.values()]);
|
|
6017
6234
|
};
|
|
6018
|
-
var
|
|
6235
|
+
var defaultSet;
|
|
6236
|
+
var defaultAssemblies = () => defaultSet ?? (defaultSet = assembliesOver(ASSEMBLIES));
|
|
6237
|
+
var registerAssembly = (def) => defaultAssemblies().register(def);
|
|
6238
|
+
var getAssembly = (id) => defaultAssemblies().get(id);
|
|
6019
6239
|
var faceCount = (layer) => layer.faces === "both" ? 2 : 1;
|
|
6020
6240
|
var assemblyDepth = (def) => def.depth ?? def.core.thickness + (def.layers ?? []).reduce((sum, l) => sum + l.thickness * faceCount(l), 0);
|
|
6021
|
-
var
|
|
6022
|
-
const def = getAssembly(assemblyId);
|
|
6241
|
+
var buildAssemblyPart = (def, assemblyId, options) => {
|
|
6023
6242
|
if (!def) throw new Error(`unknown assembly "${assemblyId}"`);
|
|
6024
6243
|
const densityOf = (id) => {
|
|
6025
6244
|
const m = options.materials?.get(id) ?? MATERIALS.find((x) => x.id === id);
|
|
@@ -6046,6 +6265,7 @@ var GameDestruction = (() => {
|
|
|
6046
6265
|
...options.extra
|
|
6047
6266
|
};
|
|
6048
6267
|
};
|
|
6268
|
+
var assemblyPart = (assemblyId, options) => defaultAssemblies().part(assemblyId, options);
|
|
6049
6269
|
|
|
6050
6270
|
// src/frame.ts
|
|
6051
6271
|
var buildFrame = (o = {}) => {
|
|
@@ -6483,7 +6703,7 @@ var GameDestruction = (() => {
|
|
|
6483
6703
|
|
|
6484
6704
|
// src/contracts.ts
|
|
6485
6705
|
var checkDestructible2 = (impl, options = {}) => {
|
|
6486
|
-
const contractOptions = { materials: [...MATERIALS.map((m) => m.id), ...options.materials ?? []] };
|
|
6706
|
+
const contractOptions = { materials: [...MATERIALS.map((m) => m.id), ...options.materials ?? []], ...options.cost === void 0 ? {} : { cost: options.cost } };
|
|
6487
6707
|
return checkDestructible(impl, contractOptions);
|
|
6488
6708
|
};
|
|
6489
6709
|
return __toCommonJS(index_exports);
|