@adatechnology/game-destruction 0.7.2 → 0.8.1
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 +111 -13
- package/dist/index.d.ts +805 -25
- package/dist/index.global.js +1934 -207
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1783 -98
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -94,6 +94,22 @@ melts anything. Melting emits `melt` and `drip`; a melted part is lost and its j
|
|
|
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
|
|
95
95
|
`direction` and the rate in litres per second, and `PartState.liters` drops in `update` (`setLiters` fills or empties a container).
|
|
96
96
|
|
|
97
|
+
### Ignition of a leak
|
|
98
|
+
|
|
99
|
+
A round that goes through a `contains` part with a flammable fluid (`gas`, `fuel`: a tank, a jerrycan, a gas bottle) **leaks and does not light by itself**. The leak lights
|
|
100
|
+
(a jet that burns, or the explosion of a part with `explodes`) when:
|
|
101
|
+
|
|
102
|
+
- the round carries a **spark** and hits **metal**: `Hit.ignition.spark` (the weapons build it from the core and the jacket of the round) times the factor of the struck
|
|
103
|
+
material (`ignition.sparkTarget`: steel and iron 1, aluminium 0.25, other metals `sparkMetalDefault` 0.5, a part that is not metal never sparks);
|
|
104
|
+
- the round carries a **flame**: `Hit.ignition.flame`, or the flags `Hit.incendiary` / `Hit.tracer` (0.7 and 0.2 when the host sends only the flags), whatever the wall;
|
|
105
|
+
- a **fire is near**: a burning part or a jet within `ignition.fireNear.radius` (1.5 m) lights the leak at `chancePerSecond` (0.5); or
|
|
106
|
+
- a **blast** (`explode`) goes off within its radius of the leaking container.
|
|
107
|
+
|
|
108
|
+
Both chances are weighed by how full the container is, `ignition.fillCurve` (points `[filled, factor]`; a full container is too rich to burn and mostly leaks, a nearly empty one lights
|
|
109
|
+
easiest; default 0.6 empty, 1 at 10 percent, 0.6 at 35, 0.3 at 65, 0.15 at 90, 0.12 full; the flame feels half of it, `flameFillInfluence` 0.5) and rolled with the seeded generator of the
|
|
110
|
+
world. `createDestruction({ ignition })` changes any number (`IgnitionRules` and `DEFAULT_IGNITION_RULES` of `@adatechnology/game-contracts`, which also export `ignitionChance` and
|
|
111
|
+
`fillFactor`). A round without `ignition`, `incendiary` and `tracer` never lights a leak; a part heated to 350 degrees C still does, as before.
|
|
112
|
+
|
|
97
113
|
### Chain reaction
|
|
98
114
|
|
|
99
115
|
A part with `explodes.chain` (fuel barrel, gas bottle, car tank, gas pump, station tank) answers to what happens around it: `shot` (a bullet, once it has taken off
|
|
@@ -103,6 +119,9 @@ instead of tearing it apart on the spot, and a part goes off once. Blasts resolv
|
|
|
103
119
|
part, so the timeline does not depend on the `dt` you pass to `update`. `explosivePart(explosive, part)` builds the part from an `ExplosiveDef` of `@adatechnology/game-weapons`
|
|
104
120
|
(`explosives.get('fuel_barrel')`), so energy, reach and delays come from one place and a game changes them with `patch.explosives`.
|
|
105
121
|
|
|
122
|
+
A fluid with `contains.outflow` (object-local direction) leaves a hole that way instead of out of the hole: the water on a roof membrane runs down
|
|
123
|
+
(`[0, -1, 0]`) into the floor below.
|
|
124
|
+
|
|
106
125
|
## Joints and tools
|
|
107
126
|
|
|
108
127
|
| Kind | Strength | Tools | Seconds |
|
|
@@ -132,9 +151,23 @@ solves loads: each structural part carries its own mass plus what rests on it, d
|
|
|
132
151
|
`PartState.load` is in kg and `PartState.stress` is `load / capacity`. Parts without a role (windows, doors, furniture) carry nothing. Objects
|
|
133
152
|
without any `structure` behave exactly as before.
|
|
134
153
|
|
|
135
|
-
- **
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
- **A slab or beam is held by the sum of its supports.** Its load is shared among the supports it rests on that are still there (columns, bearing
|
|
155
|
+
walls, beams), so a slab on four columns puts a quarter on each; lose one and the other three carry the load of the four, lose two and the
|
|
156
|
+
two left carry twice their share. The structure falls when the capacity that is left (the sum over the supports) is less than the load.
|
|
157
|
+
Everything is config: the capacity of a part is `structure.capacity` or `material.compressive` times its section (set `compressive` on a
|
|
158
|
+
`MaterialDef` passed in `materials`), `sizeStructure({ safety, redundancy })` sizes them (`safety` 0.72 with `redundancy` 0.97 on four columns:
|
|
159
|
+
rest 0.72, one lost 0.96, two lost 1.44), and the world option `structure: { safetyFactor }` (default 1) multiplies every load before it meets
|
|
160
|
+
the capacities (stress = `load * safetyFactor / capacity`): above 1 the whole building is loaded harder than it was sized for.
|
|
161
|
+
- **Capacity** is `structure.capacity` when the spec gives it. Otherwise, for a material with real strengths (`MaterialDef.strength`: concrete C25
|
|
162
|
+
and ceramic brick ship with them), it is the compressive strength times the section, divided by g: a 0.3 x 0.3 m plain concrete column of 3 m
|
|
163
|
+
carries 203 t, a 0.2 m brick wall 2.8 m tall 41 t per metre. Masonry and plain concrete walls and columns (`strength.slender`, default for
|
|
164
|
+
`masonry` materials without steel) get the NBR 16868-1 reduction `(1 - 2e)(1 - (h/t/40)^3)` (`structure.masonry`: `minEccentricity` 0.05,
|
|
165
|
+
`slendernessLimit` 40); a reinforced column carries `0.85 fck Ac + As fy` (`masonry.concreteFactor` 0.85). Materials without `strength` keep
|
|
166
|
+
`material.compressive` (kg per m²: softwood 100 000, steel 2 000 000). The section is the two smallest dimensions for columns, beams, roofs and
|
|
167
|
+
stairs (foundations x10), thickness times longest side for slabs, thickness times horizontal length for walls. Because each storey passes its
|
|
168
|
+
load down, a bearing wall that carries too many storeys crushes at the ground floor first. `bearingWallThickness({ storeys, storeyHeight,
|
|
169
|
+
floorLoad, material })` gives the thickness a designer would give each storey (γf 1.4, γm 2.0, minimum 0.14 m): 16 storeys of 3.75 m with
|
|
170
|
+
26 kN/m of floor per wall give 1.49 m at the base (the Monadnock Building has 1.8 m).
|
|
138
171
|
- **Joints** carry load when `bears` is true (default when both parts have a role). `bears: 'a_on_b'` means part `a` rests on `b`: load flows only
|
|
139
172
|
that way. A `partition` is supported but supports nothing, so breaking it never drops anything.
|
|
140
173
|
- **Overload**: above stress 1 a part loses `min(0.6, 0.12 + 0.8 * (stress - 1))` of its `maxHp` per second (stress 1.02 gives way in ~7 s, 1.1 in ~5 s,
|
|
@@ -146,6 +179,8 @@ without any `structure` behave exactly as before.
|
|
|
146
179
|
`splitGroup`.
|
|
147
180
|
- `buildFrame({ spanX, spanZ, columnsX, columnsZ, height, floors, walls, roof, columnCapacity, ... })` returns a test structure: columns on
|
|
148
181
|
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.
|
|
182
|
+
`continuousColumns` makes each column rest on the one below instead of on the slab (a tall frame needs it: otherwise the first-floor beams carry
|
|
183
|
+
the tower in bending), `columnCage` gives the columns a rebar cage and `roofThickness` sets the roof slab (default 0.75 x `slabThickness`).
|
|
149
184
|
- `sizeStructure(spec, { safety?, redundancy?, redundancyRoles?, safetyByRole? })` returns a copy of the spec with `structure.capacity` set from the loads the world itself solves
|
|
150
185
|
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
186
|
raised where needed so that losing any single `column` keeps every other part at or below that stress: the structure stands with one column
|
|
@@ -155,6 +190,61 @@ without any `structure` behave exactly as before.
|
|
|
155
190
|
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
191
|
floors well under their limit so they outlast the member that fails.
|
|
157
192
|
|
|
193
|
+
### Bending: concrete cracks in tension, the steel holds
|
|
194
|
+
|
|
195
|
+
Beams, slabs, roofs and stairs of a material with `strength.tensileMPa` that declare their steel (`structure.rebar: { ratio, yieldMPa?, cover? }`,
|
|
196
|
+
`ratio: 0` is plain concrete) also bend. The span comes from the supports (twice the farthest distance to one, so two walls 4 m apart give 4 m,
|
|
197
|
+
corner columns give the long side, a cantilever of c gives 2c, and losing a column doubles it); the bending load is the part's own weight plus
|
|
198
|
+
what rests on it away from the supports (a load within one depth of a support goes straight down, EN 1992-1-1 6.2.2(6)). `M = wL²/8` meets the
|
|
199
|
+
section: cracking moment `1.5 fct b h²/6` (NBR 6118 17.3.1) and the steel moment with the NBR 6118 rectangular block (`0.85 fck` over `0.8x`,
|
|
200
|
+
`x/d` at most 0.45, the rest as double reinforcement), CA-50 at 500 MPa, 3 cm cover. `PartState.flexure = M / capacity` (after cracking only the
|
|
201
|
+
steel counts) and `stress` is the larger of that and compression.
|
|
202
|
+
|
|
203
|
+
- **Blast**: a single-degree-of-freedom check (Biggs: KL 0.64, KM 0.5) with the Sadovsky impulse of the TNT-equivalent charge (4.184 MJ/kg),
|
|
204
|
+
reflected `2 + 4/Z`. Above the cracking moment the tension face cracks (`crack` on the face away from the charge, `PartState.cracked`); a part
|
|
205
|
+
without steel snaps (`fracture` style `snap`); with steel, yielding adds plastic rotation and at 10° (PDC-TR 06-08) the part hinges and falls in
|
|
206
|
+
one piece (`detach` from its supports, `collapse`).
|
|
207
|
+
- **At rest**: `flexure > 1` wears the part like a compression overload; a reinforced part then hinges and falls, a plain one snaps.
|
|
208
|
+
- **Joints**: `rebar` joints of a reinforced part are monolithic: blows and blasts do not wear them.
|
|
209
|
+
- **Options** (`structure.flexure`, `FLEXURE_DEFAULTS`): `enabled`, `roles`, `scope` (`'rebar'` default: only parts with `structure.rebar`;
|
|
210
|
+
`'reinforced'`: also parts with only a `reinforcement` cage; `'all'`: every concrete beam and slab, plain when they have no steel),
|
|
211
|
+
`crackFactor`, `rebarYieldMPa`, `cover`, `neutralAxisLimit`, `failRotationDeg`, `loadFactor`, `massFactor`, `tntJoules`, `impulse`,
|
|
212
|
+
`reflection`, `reflectionNear`, `nearScaled`. Helpers: `crackingMoment`, `steelMoment`, `sectionMoments`, `blastImpulse`, `slendernessFactor`,
|
|
213
|
+
`spanOf`, `bearingWallThickness`.
|
|
214
|
+
- **Sizing**: `sizeRebar(spec, { safety, redundancy, redundancyRoles, safetyByRole })` (also run at the end of `sizeStructure`) raises the steel
|
|
215
|
+
ratio of each reinforced beam or slab so it rests at `safety` and stays at or below `redundancy` after losing any part of `redundancyRoles`
|
|
216
|
+
(at most 4%, NBR 6118 17.3.5.2.4); plain concrete stays plain. `buildFrame({ rebar })` declares beams at 1% and slabs at 0.5% (`false` for
|
|
217
|
+
plain concrete).
|
|
218
|
+
|
|
219
|
+
### Soil and foundations
|
|
220
|
+
|
|
221
|
+
Give the spec a `soil` (a soil class id such as `clay_soft`, or `{ layers: [{ soil, thickness }, ...], surface?, footingWidth? }`) and its foundations
|
|
222
|
+
bear on the ground instead of being fixed. Without it nothing changes.
|
|
223
|
+
|
|
224
|
+
- **Soil classes** (`SOILS`, `soils.json`, extend with `soil.soils`): allowable footing pressure (NBR 6122 basic pressures: dense sand 400 kPa,
|
|
225
|
+
stiff clay 200, medium clay 100, soft clay 30, rock 3000), SPT N, Young's modulus (Teixeira & Godoy) and Poisson's ratio.
|
|
226
|
+
- **Footings** (a `foundation` part: pad, strip or raft): a grid of springs with the subgrade modulus of a rigid footing `E / (B (1 - nu^2) Iw)`
|
|
227
|
+
that give way at `safety` (3, NBR 6122) times the allowable pressure of the weakest layer within two widths below. Structural parts resting on a
|
|
228
|
+
foundation tagged `terrain` get a footing at least 0.6 m wide each.
|
|
229
|
+
- **Piles** (`structure.pile: { length, diameter, count?, method? }` on the cap): end bearing and skin friction by Décourt-Quaresma through the
|
|
230
|
+
layers. `pileLength(profile, { load, diameter })` gives the shortest pile that carries a load with safety 2 (down to firm ground).
|
|
231
|
+
- **The object is a rigid body on those springs**, out of plumb by 1/200 (EN 1992-1-1) the weak way, with P-delta. It settles at once where the
|
|
232
|
+
soil holds; where it yields it moves to the new equilibrium at `sinkRate`/`tiltRate` (game time), and with no stable equilibrium it keeps sinking
|
|
233
|
+
and leaning until its centre of mass passes the edge of its foundations: it tips over (`settle` with `status: 'toppled'`, then a `collapse` of
|
|
234
|
+
everything).
|
|
235
|
+
- `ObjectState.soil` has `settlement`, `tilt`, `direction`, `bearing` (weight over what the foundations carry), `edge`, `status` and `offset`
|
|
236
|
+
(the settled pose relative to the host's). `ObjectState.position`/`rotation` include it; `setTransform` sets the host's pose under it. A `settle`
|
|
237
|
+
event goes out when it moves (at most every 0.5 s) or changes status.
|
|
238
|
+
- Options (`soil`, `SOIL_DEFAULTS`): `soils`, `site`, `safety`, `pileSafety`, `rigidity`, `influence`, `imperfection`, `cells`, `sinkRate`,
|
|
239
|
+
`tiltRate`, `footingWidth`, `pileElasticGPa`. `buildFrame({ footing, raft, piles, soil })` builds test frames on the ground.
|
|
240
|
+
|
|
241
|
+
## Wind and sway
|
|
242
|
+
Off unless `createDestruction({ wind })` is given. Pressure `q = 0.613 V^2` grows with height by the NBR 6123 profile (terrain I-V, class by size), times a drag
|
|
243
|
+
coefficient, per 3 m band. The lateral system is the walls (cantilever) plus columns and T-beams (shear frame); the result is in `ObjectState.wind` (`deflection`,
|
|
244
|
+
`drift` against `driftLimit` H/500, `overturn`, `frequency`, `lean`, `sway`, `peak`, `status`) and a `sway` event. A gust (Davenport spectrum) shakes the first mode;
|
|
245
|
+
a part with `damper` (tuned mass damper, Den Hartog) reduces it. `world.setWind({ speed, direction })` changes it in play; `sizeStructure({ wind })` sizes for it;
|
|
246
|
+
on soft soil the wind moment tilts the building. Options and defaults: `wind`, `WIND_DEFAULTS`, `WIND_TERRAIN`. Sources in `specs/001-migracao-entregas-dos-times/design/T10-estrutura-real.md` section 10.
|
|
247
|
+
|
|
158
248
|
## Walls: layers, stages, breach and reinforcement
|
|
159
249
|
A wall is one part that opts in with `layers`, `units`, `reinforcement` or `breach` (`PartSpec`, all optional; parts without them behave as before).
|
|
160
250
|
`assemblyPart(id, { id, width, height, position, structure? })` builds one from the eight built-in **assemblies** (`ASSEMBLIES`, data in
|
|
@@ -224,15 +314,19 @@ the results for the push on pieces: `renderer.explode(point, energy, radius, { r
|
|
|
224
314
|
|
|
225
315
|
### Bullets and blasts against beams and columns
|
|
226
316
|
|
|
227
|
-
`tests/beams-and-columns.test.ts`
|
|
228
|
-
I-beam, a hardwood beam and a brick pillar. Two
|
|
317
|
+
`tests/beams-and-columns.test.ts` fires the loads of the ammo table of `@adatechnology/game-weapons` against a reinforced concrete column and beam, a steel
|
|
318
|
+
I-beam, a hardwood beam and a brick pillar (it prints the results table with `PRINT_TABLE=1`). Two corrections came out of it:
|
|
229
319
|
|
|
230
|
-
- `Hit.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
steel.
|
|
234
|
-
|
|
235
|
-
|
|
320
|
+
- `Hit.penetration` (the 6th argument of `shoot` is `{ penetration }` or a bare piercing factor): millimetres the round goes through, by material class
|
|
321
|
+
(`steel`, `concrete`, `brick`, `wood`). The weapons put it in the `Hit` (`shotToHit` / `shotExtras` of the contracts); the world reads no calibre names. The
|
|
322
|
+
material says which entry measures it (`MaterialDef.penetrationClass`, default its id) and how much more or less it resists
|
|
323
|
+
(`penetrationScale`: structural steel 1.3 against the RHA of the tables, softwood 1.5 against hardwood, aluminium 3 against steel). The round goes through when its
|
|
324
|
+
millimetres times that scale exceed the thickness of the part (slant counts); a material with no entry falls back to the energy rule, where `Hit.piercing`
|
|
325
|
+
(default 1) multiplies the energy against hard materials. M80 ball does not pierce an 8.5 mm structural flange, M993 AP does, and the .50 BMG M33 ball
|
|
326
|
+
pierces a 30 cm reinforced column. `PIERCING_BY_KIND`, `PIERCING_BY_CALIBER` and `piercingOf` are gone: the table lives in game-weapons.
|
|
327
|
+
- Blast reach of a breach (no option, it is the physics): a blast only opens a breach within 0.12 m per cube root of kilojoule of the part
|
|
328
|
+
(`BREACH_BLAST_REACH`). A hand grenade (200 kJ) at 1 m does not cut a reinforced column in two (at 0.2 m it strips it). Farther away the blast still
|
|
329
|
+
damages, dents and cracks. The old `breach.blastReach` option (default `Infinity`) is gone: it was a second path for a correction.
|
|
236
330
|
|
|
237
331
|
## Contracts: implement your own or use the default world
|
|
238
332
|
|
|
@@ -281,9 +375,9 @@ world continues with the same events as the original. The loading world must use
|
|
|
281
375
|
|
|
282
376
|
`createDestruction(options)` returns a `DestructionWorld`: `add`, `remove`, `has`, `ids`, `setTransform`, `splitGroup`, `hit`, `hitPart`, `explode`,
|
|
283
377
|
`raycast`, `shoot`, `disassemble`, `workableJoints`, `nearestJoint`, `extinguish`, `setLiters`, `update`, `state`, `part`, `reset`, `on`,
|
|
284
|
-
`register`, `serialize`, `load`, `time`. `explode(point, energy, radius, source?, blastOptions?)`; `shoot(origin, direction, energy, caliber, source?,
|
|
378
|
+
`register`, `serialize`, `load`, `time`. `explode(point, energy, radius, source?, blastOptions?)`; `shoot(origin, direction, energy, caliber, source?, round?)` (`round`: `Hit.penetration`, `piercing`, `incendiary`, `tracer`, `ignition` or a bare piercing factor). `state()` and `part()` return live views; do not mutate them.
|
|
285
379
|
|
|
286
|
-
Also exported: `SHELTER_DEFAULTS`, `FRAGMENT_HIT_DEFAULTS`, `CONFINEMENT_DEFAULTS`, `
|
|
380
|
+
Also exported: `SHELTER_DEFAULTS`, `FRAGMENT_HIT_DEFAULTS`, `CONFINEMENT_DEFAULTS`, `buildFrame`, `sizeStructure`, `sizeRebar`, `FLEXURE_DEFAULTS`, `MASONRY_DEFAULTS`, `bearingWallThickness`, `SOILS`, `SOIL_DEFAULTS`, `pileCapacity`, `pileLength`, `pileStiffness`, `footingSoil`, `subgradeModulus`, `solveFoundation`, `assemblyPart`, `registerAssembly`, `getAssembly`, `ASSEMBLIES`, `assemblyDepth`, `partCapacity`, `checkDestructible`, `checkHit`, `IMPACT_CONTRACT`, `MATERIALS`, `JOINT_DEFAULTS`, `JOINT_BY_MATERIALS`, `inferJoints`, `guessMaterial`, `partVolume`, `partMass`, `partThickness`, the
|
|
287
381
|
helpers `vec`, `quat`, `quatFromEuler`, `rotateVec`, `toLocal`, `toWorld`, the locale (`locales`, `resolveStrings`, `DestructionStrings`) and all types.
|
|
288
382
|
|
|
289
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:
|
|
@@ -298,4 +392,8 @@ parafuso, chave inglesa para porca e parafuso, maçarico para solda). O mundo de
|
|
|
298
392
|
determinístico (semente fixa) e serializável, então um host de LAN pode rodar o mundo e enviar só as entradas. Os nomes em português
|
|
299
393
|
(materiais, ferramentas, juntas, eventos) estão em `resolveStrings('pt-BR')`.
|
|
300
394
|
|
|
395
|
+
Estrutura real: viga e laje de concreto com `structure.rebar` dobram. Na explosão a face tracionada racha e o aço segura; sem aço a peça parte; com o aço escoado até 10° de giro ela dobra e cai inteira (`structure.flexure`). Paredes e pilares de alvenaria resistem com a resistência real do tijolo e a redução por esbeltez da NBR 16868-1, então a parede portante do térreo esmaga quando há andares demais em cima (`bearingWallThickness` dá a espessura de projeto). Valores e fontes em `specs/001-migracao-entregas-dos-times/design/T10-estrutura-real.md`.
|
|
396
|
+
|
|
397
|
+
Solo e fundação: com `soil` na spec a fundação se apoia no solo (classes com a tensão admissível da NBR 6122). Prédio pesado em sapata sobre argila mole afunda e tomba; em estacas até o chão firme (`structure.pile`, Décourt-Quaresma, `pileLength`) fica de pé; casa em radier em solo firme não sente.
|
|
398
|
+
|
|
301
399
|
Explosões: `blast.shelter` (paredes e portas entre a explosão e o alvo ficam com a sua parte: tijolo de 20 cm deixa passar ~19%, porta de madeira ~53%, vidro ~83%; a parede que quebra na mesma explosão deixa passar metade do que segurou), `blast.fragmentHits` (os pedaços do que quebra viram golpes de baixa energia no que está atrás) e `blast.confinement` (pressão num ambiente fechado, de 1 a 3 vezes). Tudo desligado por padrão, por opção do mundo ou da chamada, com os números em `SHELTER_DEFAULTS`, `FRAGMENT_HIT_DEFAULTS` e `CONFINEMENT_DEFAULTS`.
|