@adatechnology/game-destruction 0.4.2 → 0.6.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
@@ -88,7 +88,7 @@ for 3 mm glass), so a 9 mm shatters a window pane in one shot while a 5 cm block
88
88
  ## Fire, melting and leaks
89
89
 
90
90
  `update(dt)` advances everything that is hot. Burning parts consume fuel (`burnRate * surface`), lose hit points, char and finally turn to ashes
91
- (`fuelFraction >= 0.8`) or stay charred. Heat flows through closed joints by conductivity and to parts of other objects within `fireSpread`. Heat
91
+ (`fuelFraction >= 0.8`) or stay charred. Heat flows through closed joints by conductivity and to parts of other objects within `fireSpread`. With `blastFrontSpeed` (m/s, 340 is the speed of sound; default off) the glass an explosion reaches breaks `distance / speed` seconds later, in `update`, instead of on the spot. Heat
92
92
  only moves towards a source temperature, so a wood fire (800 °C) never melts steel (1450 °C) but melts aluminium and plastic, and a 3000 °C torch
93
93
  melts anything. Melting emits `melt` and `drip`; a melted part is lost and its joints open. Pressurized gas that leaks near fire burns (or explodes
94
94
  when the part has `explodes`). `contains` parts leak through punctures: the `leak` event carries the hole's world point, the outward normal as
@@ -146,11 +146,40 @@ without any `structure` behave exactly as before.
146
146
  `splitGroup`.
147
147
  - `buildFrame({ spanX, spanZ, columnsX, columnsZ, height, floors, walls, roof, columnCapacity, ... })` returns a test structure: columns on
148
148
  foundations (`rebar`), beams, a slab per floor, optional brick walls (`mortar`) with a door opening, and a roof that rests (`resting`) on the top beams.
149
- - `sizeStructure(spec, { safety?, redundancy? })` returns a copy of the spec with `structure.capacity` set from the loads the world itself solves
149
+ - `sizeStructure(spec, { safety?, redundancy?, redundancyRoles?, safetyByRole? })` returns a copy of the spec with `structure.capacity` set from the loads the world itself solves
150
150
  for the intact spec, so every structural part rests at stress `safety` (default 0.65). With `redundancy` (e.g. 0.97) the capacities are also
151
151
  raised where needed so that losing any single `column` keeps every other part at or below that stress: the structure stands with one column
152
152
  gone and, without help, falls with two neighbours gone. Raw material capacities are rarely a sensible design (a 54 t roof on 20 cm columns is
153
153
  already over 1 at rest); size the structure instead of hand-tuning. Parts without `structure` or with role `none` are untouched.
154
+ `redundancyRoles` (default `['column']`) says which parts the N-1 check removes one at a time: a house uses `['column', 'wall']` so it also
155
+ stands with a bearing wall gone. `safetyByRole` overrides `safety` per role: `{ beam: 0.45, slab: 0.45, roof: 0.45, stair: 0.45 }` keeps the
156
+ floors well under their limit so they outlast the member that fails.
157
+
158
+ ## Walls: layers, stages, breach and reinforcement
159
+ A wall is one part that opts in with `layers`, `units`, `reinforcement` or `breach` (`PartSpec`, all optional; parts without them behave as before).
160
+ `assemblyPart(id, { id, width, height, position, structure? })` builds one from the eight built-in **assemblies** (`ASSEMBLIES`, data in
161
+ `src/data/assemblies.json`; add or replace with `registerAssembly`): `brick_wall_plastered` (0.09 m brick, 2 x 0.02 m plaster, 0.23 x 0.075 x 0.11 m
162
+ bricks in running bond), `concrete_block_wall` (0.39 x 0.19 x 0.14 m hollow blocks), `reinforced_concrete` (0.2 m, rebar every 0.18 m, 0.6 x 0.42 m
163
+ panels), `drywall`, `wood_frame_wall`, `log_wall`, `sheet_metal_shack`, `glass_pane`.
164
+
165
+ - **Layers and spall.** A bullet spends `penetration x thickness` of every coat still on the faces it crosses, plus the core's. The outermost coat at the
166
+ impact comes off first (`spall` event, `area`), and a patch that spalled no longer stops anything: the same round that stops at fresh plaster can
167
+ go through once it has come off. A coat only spalls once per patch. Blunt hits spall the coat too.
168
+ - **Stages** (`PartState.stage`, `stage` event). 0 intact; 1 at the first hole, crack or spall; 2 when through damage within 0.55 m adds up to
169
+ `breach.area` (0.25 m²): the units around the hit come out as fragments the size of one unit (`stage.fragments`, laid in the assembly's bond), the part
170
+ stays with an `opening` (shots and rays pass through it, its mass and capacity drop) and nothing is hidden; 3 when the part fractures or collapses.
171
+ Through damage is the area of through holes plus, for blunt, slash and explosive damage, the volume the damage takes out (`damage / (hpPerKg x
172
+ density)`) over the thickness it crosses, times `breach.blastFactor` (6) for blasts. An opening as wide as the part cuts it instead (a pilaster, a column):
173
+ that is a fracture. So a 9 mm burst never leaves stage 1 on plastered brick (it does not even go through), a rifle never breaches reinforced
174
+ concrete, a sledgehammer opens plastered brick after ~47 swings and a C4 or RPG opens reinforced concrete at once. The engine stays in joules: the
175
+ game tunes energies, `breach: { radius, blastFactor }` and each assembly's `breach.area`.
176
+ - **Reinforced concrete.** At 0 hp the concrete crumbles (`fracture`, in panels when `units` is set) but the part does not go: it becomes its rebar cage
177
+ (`PartState.stripped`, `expose` with the bars in world space, stage 3), still part of the structure with `capacity x residualCapacity` (default 0.15).
178
+ A bare cage under load is overloaded in seconds (`overloadRate`), so the frame fails progressively: `expose`, `stress`, the cage is lost (`expose` with
179
+ no bars), then the neighbours and the slab (`collapse`). Bullets pass through a cage. The `rebar` joints of a reinforced part are not worn by blows:
180
+ only the cutting torch or a saw cuts them (or the cage giving way). A breach at stage 2 exposes only the bars inside the opening.
181
+ - **Holes stay on the pieces.** Every `fracture` and breach fragment carries `marks` (`FragmentMark`: point, normal and diameter in the fragment's own
182
+ axes) for the holes that fell inside it, at most 12 per fragment.
154
183
 
155
184
  ## Contracts: implement your own or use the default world
156
185
 
@@ -186,7 +215,7 @@ valid (unique ids, known materials, joints reference parts), a 500 J ballistic h
186
215
  ## Events
187
216
 
188
217
  `damage`, `dent`, `hole`, `crack`, `ricochet`, `debris`, `fracture` (with oriented `fragments` in world space), `detach`, `disassemble_progress`,
189
- `ignite`, `extinguish`, `burnout`, `melt`, `drip`, `leak`, `explode`, `primed`, `stress`, `collapse`, `destroyed`. All points are in world space. Subscribe with
218
+ `ignite`, `extinguish`, `burnout`, `melt`, `drip`, `leak`, `explode`, `primed`, `stress`, `collapse`, `spall`, `stage`, `expose`, `destroyed`. All points are in world space. Subscribe with
190
219
  `world.on(type | '*', fn)` or the `onEvent` option; `hit` and `update` also return their events.
191
220
 
192
221
  ## Determinism and LAN
@@ -201,7 +230,7 @@ world continues with the same events as the original. The loading world must use
201
230
  `raycast`, `shoot`, `disassemble`, `workableJoints`, `nearestJoint`, `extinguish`, `setLiters`, `update`, `state`, `part`, `reset`, `on`,
202
231
  `register`, `serialize`, `load`, `time`. `state()` and `part()` return live views; do not mutate them.
203
232
 
204
- Also exported: `buildFrame`, `sizeStructure`, `partCapacity`, `checkDestructible`, `checkHit`, `IMPACT_CONTRACT`, `MATERIALS`, `JOINT_DEFAULTS`, `JOINT_BY_MATERIALS`, `inferJoints`, `guessMaterial`, `partVolume`, `partMass`, `partThickness`, the
233
+ Also exported: `buildFrame`, `sizeStructure`, `assemblyPart`, `registerAssembly`, `getAssembly`, `ASSEMBLIES`, `assemblyDepth`, `partCapacity`, `checkDestructible`, `checkHit`, `IMPACT_CONTRACT`, `MATERIALS`, `JOINT_DEFAULTS`, `JOINT_BY_MATERIALS`, `inferJoints`, `guessMaterial`, `partVolume`, `partMass`, `partThickness`, the
205
234
  helpers `vec`, `quat`, `quatFromEuler`, `rotateVec`, `toLocal`, `toWorld`, the locale (`locales`, `resolveStrings`, `DestructionStrings`) and all types.
206
235
 
207
236
  The locale table has `en` and `pt-BR` strings for material names, part statuses, joint kinds, tools, damage types, event labels and fracture styles:
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Vec3, Quat, MaterialId, FractureStyle, DamageType, DebrisKind, DestructionEvent, DestructibleSpec, ObjectState, DestructibleImpl, Hit, HitResult, RayHit, ToolKind, JointState, PartState, DestructionEventType, ExplosionChain, PartSpec, JointKind, JointSpec, PartShape, PartStatus, StructureRole } from '@adatechnology/game-contracts';
2
- export { ChainDelay, Crack, DamageType, DebrisKind, Dent, DestructibleImpl, DestructibleSpec, DestructionEvent, DestructionEventType, ExplosionChain, FluidKind, FractureStyle, Fragment, Hit, HitResult, Hole, IMPACT_CONTRACT, ImpactContract, JointKind, JointSpec, JointState, MaterialId, ObjectState, PartShape, PartSpec, PartState, PartStatus, Quat, RayHit, StructureRole, ToolKind, Vec3, checkHit } from '@adatechnology/game-contracts';
1
+ import { Vec3, Quat, MaterialId, FractureStyle, DamageType, DebrisKind, DestructionEvent, DestructibleSpec, ObjectState, DestructibleImpl, Hit, HitResult, RayHit, ToolKind, JointState, PartState, DestructionEventType, StructureRole, ExplosionChain, PartSpec, JointKind, JointSpec, PartShape, SurfaceLayer, PartUnits, Reinforcement, PartBreach, PartStatus } from '@adatechnology/game-contracts';
2
+ export { ChainDelay, Crack, DamageStage, DamageType, DebrisKind, Dent, DestructibleImpl, DestructibleSpec, DestructionEvent, DestructionEventType, ExplosionChain, FluidKind, FractureStyle, Fragment, FragmentMark, Hit, HitResult, Hole, IMPACT_CONTRACT, ImpactContract, JointKind, JointSpec, JointState, MaterialId, ObjectState, PartBreach, PartShape, PartSpec, PartState, PartStatus, PartUnits, Quat, RayHit, Reinforcement, StructureRole, SurfaceLayer, ToolKind, Vec3, checkHit } from '@adatechnology/game-contracts';
3
3
 
4
4
  /**
5
5
  * Public types of @adatechnology/game-destruction.
@@ -95,12 +95,28 @@ interface DestructionOptions {
95
95
  maxHoles?: number;
96
96
  /** Distance at which a burning part heats parts of other objects, in metres (default 0.6). */
97
97
  fireSpread?: number;
98
+ /** Breach of staged parts (see `PartSpec.breach`). */
99
+ breach?: {
100
+ /** Metres around a hit within which through damage adds up (default 0.55). */
101
+ radius?: number;
102
+ /** Multiplier of the breach area a blast makes over a blow of the same damage: a blast loads the whole crater (default 6). */
103
+ blastFactor?: number;
104
+ };
105
+ /**
106
+ * Speed of the blast front in m/s. A glass part at distance `d` from an explosion breaks `d / blastFrontSpeed` seconds later, in `update`
107
+ * (the speed of sound is 340). Default off: every part takes the blast at once.
108
+ */
109
+ blastFrontSpeed?: number;
98
110
  }
99
111
  interface SizeStructureOptions {
100
112
  /** Stress every structural part has at rest after sizing (`load / capacity`), 0..1 (default 0.65). */
101
113
  safety?: number;
114
+ /** Stress at rest per structural role, over `safety` (a house: beams, slabs, roofs and stairs at 0.45 so they outlast a lost column). */
115
+ safetyByRole?: Partial<Record<StructureRole, number>>;
116
+ /** Roles whose parts are removed one at a time by the redundancy check (default `['column']`; a house adds `'wall'`). */
117
+ redundancyRoles?: readonly StructureRole[];
102
118
  /**
103
- * Redundancy target: when set, the capacity of a part is raised (never lowered) until losing any single `column` leaves every other
119
+ * Redundancy target: when set, the capacity of a part is raised (never lowered) until losing any single part of `redundancyRoles` leaves every other
104
120
  * structural part at this stress or below, e.g. 0.97 (an N-1 design: the building stands with one column gone). Omit to skip the check.
105
121
  * Should be above `safety` and, to let the structure fall when a second column goes, below 1.
106
122
  */
@@ -206,6 +222,12 @@ interface PartRuntime {
206
222
  stressT?: number;
207
223
  /** World time at which a primed explosive part goes off (`explodes.chain`). */
208
224
  chainDue?: number;
225
+ /** Patches of surface layers that came off (`layers.ts`). */
226
+ spalls?: Array<[number, number, number, number, number, number]>;
227
+ /** Through damage so far: `[x, y, z, area]` in object space, newest last. */
228
+ breach?: Array<[number, number, number, number]>;
229
+ /** Mass in kg that left the part in a breach. */
230
+ removed?: number;
209
231
  }
210
232
  /** Opaque bookkeeping of an object (see `PartRuntime`). `joints` holds the last disassembly step reported per joint id. */
211
233
  interface ObjectRuntime {
@@ -297,6 +319,54 @@ declare const partCapacity: (part: PartSpec, material: MaterialDef) => number;
297
319
  */
298
320
  declare const fragility: (ductility: number, thickness: number) => number;
299
321
 
322
+ /**
323
+ * A wall as a builder builds it: a core with coats, the masonry unit it is laid in, its rebar and when it breaches. Dimensions are metres (data in
324
+ * `src/data/assemblies.json`); `assemblyPart` turns one into a `PartSpec`. Extend or override with `registerAssembly`.
325
+ */
326
+ interface AssemblyDef {
327
+ id: string;
328
+ name: string;
329
+ /** `thickness` is the core's, in metres: what a bullet has to get through besides the coats. */
330
+ core: {
331
+ material: MaterialId;
332
+ thickness: number;
333
+ };
334
+ /** Coats, outermost first. */
335
+ layers?: readonly SurfaceLayer[];
336
+ /** Physical depth of the wall when it is not core plus coats (a drywall partition is mostly air between the boards). */
337
+ depth?: number;
338
+ /** Solid fraction of the core (hollow blocks, round logs): scales its mass and the thickness a bullet crosses. Default 1. */
339
+ fill?: number;
340
+ units?: PartUnits;
341
+ reinforcement?: Reinforcement;
342
+ breach?: PartBreach;
343
+ }
344
+ interface AssemblyPartOptions {
345
+ id: string;
346
+ /** Along the part's x axis. */
347
+ width: number;
348
+ /** Along the part's y axis. */
349
+ height: number;
350
+ position: Vec3;
351
+ rotation?: Vec3;
352
+ structure?: PartSpec['structure'];
353
+ /** Other `PartSpec` fields (tags, anchored, hp...). */
354
+ extra?: Partial<PartSpec>;
355
+ /** Materials to read densities from (default the built-in `MATERIALS`). */
356
+ materials?: ReadonlyMap<MaterialId, MaterialDef>;
357
+ }
358
+ /** Built-in assemblies; `registerAssembly` adds to this list. */
359
+ declare const ASSEMBLIES: AssemblyDef[];
360
+ declare const registerAssembly: (def: AssemblyDef) => void;
361
+ declare const getAssembly: (id: string) => AssemblyDef | undefined;
362
+ /** Physical depth of the wall in metres. */
363
+ declare const assemblyDepth: (def: AssemblyDef) => number;
364
+ /**
365
+ * 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`)
366
+ * plus the coats; `thickness` is the core's, so each coat adds its own `penetration x thickness` to what a bullet must cross.
367
+ */
368
+ declare const assemblyPart: (assemblyId: string, options: AssemblyPartOptions) => PartSpec;
369
+
300
370
  interface FrameOptions {
301
371
  /** Total length along x between the outer column centres, metres (default 8). */
302
372
  spanX?: number;
@@ -337,8 +407,9 @@ declare const buildFrame: (o?: FrameOptions) => DestructibleSpec;
337
407
  /**
338
408
  * Sizes the load capacity of a structure the way an engineer would, from the loads the world itself computes. The intact spec goes through the
339
409
  * same load solver `createDestruction` uses, and each structural part (`structure.role` other than `none`) gets `structure.capacity = load / safety`,
340
- * so its at-rest stress is exactly `safety`. With `redundancy` set, every `column` is also removed in turn and the capacity of the parts that
341
- * would carry more is raised so they stay at or below `redundancy`. Parts without `structure`, with role `none`, or with no support path to an
410
+ * so its at-rest stress is exactly `safety` (`safetyByRole` overrides it per role). With `redundancy` set, every part whose role is in
411
+ * `redundancyRoles` (default `column`) is also removed in turn and the capacity of the parts that would carry more is raised so they stay at or below
412
+ * `redundancy`. Parts without `structure`, with role `none`, or with no support path to an
342
413
  * anchored part are left as they are. The spec is not modified; a copy is returned.
343
414
  */
344
415
  declare const sizeStructure: (spec: DestructibleSpec, options?: SizeStructureOptions) => DestructibleSpec;
@@ -410,4 +481,4 @@ declare const checkDestructible: (impl: DestructibleImpl, options?: CheckOptions
410
481
  problems: string[];
411
482
  };
412
483
 
413
- export { type AddOptions, type ChainTrigger, type CheckOptions, type DestructionLocale, type DestructionOptions, type DestructionSnapshot, type DestructionStrings, type DestructionWorld, type ExplosivePartOptions, FIRE_PRIME_C, type FrameOptions, JOINT_BY_MATERIALS, JOINT_DEFAULTS, MATERIALS, type MaterialDef, type MaterialThermal, type ObjectRuntime, type PartRuntime, type SizeStructureOptions, type WorldExplosive, buildFrame, checkDestructible, createDestruction, explosivePart, fragility, guessMaterial, inferJoints, locales, partCapacity, partMass, partThickness, partVolume, quat, quatFromEuler, resolveStrings, rotateVec, sizeStructure, toLocal, toWorld, vec };
484
+ export { ASSEMBLIES, type AddOptions, type AssemblyDef, type AssemblyPartOptions, type ChainTrigger, type CheckOptions, type DestructionLocale, type DestructionOptions, type DestructionSnapshot, type DestructionStrings, type DestructionWorld, type ExplosivePartOptions, FIRE_PRIME_C, type FrameOptions, JOINT_BY_MATERIALS, JOINT_DEFAULTS, MATERIALS, type MaterialDef, type MaterialThermal, type ObjectRuntime, type PartRuntime, type SizeStructureOptions, type WorldExplosive, assemblyDepth, assemblyPart, buildFrame, checkDestructible, createDestruction, explosivePart, fragility, getAssembly, guessMaterial, inferJoints, locales, partCapacity, partMass, partThickness, partVolume, quat, quatFromEuler, registerAssembly, resolveStrings, rotateVec, sizeStructure, toLocal, toWorld, vec };