@adatechnology/game-destruction 0.4.2 → 0.5.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
@@ -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,23 @@ 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
+ };
98
105
  }
99
106
  interface SizeStructureOptions {
100
107
  /** Stress every structural part has at rest after sizing (`load / capacity`), 0..1 (default 0.65). */
101
108
  safety?: number;
109
+ /** Stress at rest per structural role, over `safety` (a house: beams, slabs, roofs and stairs at 0.45 so they outlast a lost column). */
110
+ safetyByRole?: Partial<Record<StructureRole, number>>;
111
+ /** Roles whose parts are removed one at a time by the redundancy check (default `['column']`; a house adds `'wall'`). */
112
+ redundancyRoles?: readonly StructureRole[];
102
113
  /**
103
- * Redundancy target: when set, the capacity of a part is raised (never lowered) until losing any single `column` leaves every other
114
+ * Redundancy target: when set, the capacity of a part is raised (never lowered) until losing any single part of `redundancyRoles` leaves every other
104
115
  * 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
116
  * Should be above `safety` and, to let the structure fall when a second column goes, below 1.
106
117
  */
@@ -206,6 +217,12 @@ interface PartRuntime {
206
217
  stressT?: number;
207
218
  /** World time at which a primed explosive part goes off (`explodes.chain`). */
208
219
  chainDue?: number;
220
+ /** Patches of surface layers that came off (`layers.ts`). */
221
+ spalls?: Array<[number, number, number, number, number, number]>;
222
+ /** Through damage so far: `[x, y, z, area]` in object space, newest last. */
223
+ breach?: Array<[number, number, number, number]>;
224
+ /** Mass in kg that left the part in a breach. */
225
+ removed?: number;
209
226
  }
210
227
  /** Opaque bookkeeping of an object (see `PartRuntime`). `joints` holds the last disassembly step reported per joint id. */
211
228
  interface ObjectRuntime {
@@ -297,6 +314,54 @@ declare const partCapacity: (part: PartSpec, material: MaterialDef) => number;
297
314
  */
298
315
  declare const fragility: (ductility: number, thickness: number) => number;
299
316
 
317
+ /**
318
+ * 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
319
+ * `src/data/assemblies.json`); `assemblyPart` turns one into a `PartSpec`. Extend or override with `registerAssembly`.
320
+ */
321
+ interface AssemblyDef {
322
+ id: string;
323
+ name: string;
324
+ /** `thickness` is the core's, in metres: what a bullet has to get through besides the coats. */
325
+ core: {
326
+ material: MaterialId;
327
+ thickness: number;
328
+ };
329
+ /** Coats, outermost first. */
330
+ layers?: readonly SurfaceLayer[];
331
+ /** Physical depth of the wall when it is not core plus coats (a drywall partition is mostly air between the boards). */
332
+ depth?: number;
333
+ /** Solid fraction of the core (hollow blocks, round logs): scales its mass and the thickness a bullet crosses. Default 1. */
334
+ fill?: number;
335
+ units?: PartUnits;
336
+ reinforcement?: Reinforcement;
337
+ breach?: PartBreach;
338
+ }
339
+ interface AssemblyPartOptions {
340
+ id: string;
341
+ /** Along the part's x axis. */
342
+ width: number;
343
+ /** Along the part's y axis. */
344
+ height: number;
345
+ position: Vec3;
346
+ rotation?: Vec3;
347
+ structure?: PartSpec['structure'];
348
+ /** Other `PartSpec` fields (tags, anchored, hp...). */
349
+ extra?: Partial<PartSpec>;
350
+ /** Materials to read densities from (default the built-in `MATERIALS`). */
351
+ materials?: ReadonlyMap<MaterialId, MaterialDef>;
352
+ }
353
+ /** Built-in assemblies; `registerAssembly` adds to this list. */
354
+ declare const ASSEMBLIES: AssemblyDef[];
355
+ declare const registerAssembly: (def: AssemblyDef) => void;
356
+ declare const getAssembly: (id: string) => AssemblyDef | undefined;
357
+ /** Physical depth of the wall in metres. */
358
+ declare const assemblyDepth: (def: AssemblyDef) => number;
359
+ /**
360
+ * 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`)
361
+ * plus the coats; `thickness` is the core's, so each coat adds its own `penetration x thickness` to what a bullet must cross.
362
+ */
363
+ declare const assemblyPart: (assemblyId: string, options: AssemblyPartOptions) => PartSpec;
364
+
300
365
  interface FrameOptions {
301
366
  /** Total length along x between the outer column centres, metres (default 8). */
302
367
  spanX?: number;
@@ -337,8 +402,9 @@ declare const buildFrame: (o?: FrameOptions) => DestructibleSpec;
337
402
  /**
338
403
  * 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
404
  * 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
405
+ * so its at-rest stress is exactly `safety` (`safetyByRole` overrides it per role). With `redundancy` set, every part whose role is in
406
+ * `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
407
+ * `redundancy`. Parts without `structure`, with role `none`, or with no support path to an
342
408
  * anchored part are left as they are. The spec is not modified; a copy is returned.
343
409
  */
344
410
  declare const sizeStructure: (spec: DestructibleSpec, options?: SizeStructureOptions) => DestructibleSpec;
@@ -410,4 +476,4 @@ declare const checkDestructible: (impl: DestructibleImpl, options?: CheckOptions
410
476
  problems: string[];
411
477
  };
412
478
 
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 };
479
+ 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 };