# Explosions

# Explosions

Package `@ferret/physics-explosions`, capability `explosion`.

## What it is for

A grenade, a gas cylinder that bursts, a mine, a charge under a bridge, a shell. An explosion here is **energy delivered through scaling laws**. The other capabilities do the rest: bodies fly through the kernel, walls break through strength (skill `ferret-physics-strength`), the ground opens through terrain (skill `ferret-physics-terrain`), and fire is the job of [combustion](combustion.md) and heat (skill `ferret-physics-heat`).

## How a blast behaves

- **Scaling.** The blast depends on the distance divided by the cube root of the charge. The same effect at eight times the charge is twice as far away. The overpressure at a given distance comes from a standard curve, together with how long the pressure lasts; the push a body gets is the pressure times its duration, over a projected area.
- **The push.** A body gets its impulse as a shove along the line from the charge, when the front arrives (distance over the speed of the shock wave). Bodies behind other bodies get less.
- **Kinds of burst.** In open `air` the blast spreads freely. On the `surface` the ground reflects it, which counts as 1.8 times the charge. A `buried` charge loses much of its blast to the soil, and a deep enough one opens no crater at all (a camouflet). A `water` burst uses the underwater curve, and the front travels at 1480 m/s.
- **How big.** Give the charge as `tnt` (kilograms), as `energy` (joules), or as a `vessel` that bursts (`pressure`, `volume`, `gamma`), which takes the energy from the gas's expansion.
- **Casings.** A casing around the charge breaks into fragments of varied size which leave at a speed set by the ratio of casing to charge.
- **Walls and wind.** A wall reflects a weak wave to twice its pressure and a strong one up to eight times. A wind behind the front pushes as well. A wall can also spall: a slab flies off its back side when the pulse is strong and long enough.

## How far does it reach

These functions need no world, which makes them useful for danger rings in a game UI and for design.

```ts
import { blastAt, radiusFor, vesselTnt } from '@ferret/physics-explosions';

const grenade = 0.2;                                // kg of TNT
const at5m = blastAt(5, grenade);                   // { overpressure, duration, impulse, arrival, scaled }
const lethal = radiusFor(70e3, grenade);            // metres where the overpressure has fallen to 70 kPa
const glass = radiusFor(2e3, grenade);              // where windows break (2 kPa)
const cylinder = vesselTnt(150e5, 0.05);            // a 50 litre cylinder at 150 bar, as kg of TNT
```

In a world, `cap.preview(tnt, pressures)` and the command `explosion.preview` give the same rings.

## Blowing things up in a world

```ts
import { explosions } from '@ferret/physics-explosions';

const x = explosions();
host.register(x);
host.enable('explosion');

const result = x.detonate({ at: [0, 0, 0], tnt: 10 });    // { tnt, effective, bodies, fragments, crater?, camouflet }
```

A body within range receives the impulse of the blast times its exposed area. Command: `explosion.detonate { at, tnt | energy | vessel, burst?, burial?, casing?, seed? }`. The same `seed` gives the same fragments.

### Making things break

Set `rupture`, the impulse per area that a body can take (in Pa·s; `Infinity` means never). With strength (skill `ferret-physics-strength`) on, any body that receives at least that much breaks (`explosion:break`) and its pieces carry the momentum of the blast.

```ts
host.config('explosion').set('world', 'rupture', 200);                       // every body
host.config('explosion').set('entity', 'rupture', 20, 'glass-pane');         // one weak entity
```

For a wall, give `wall: { tensile, density, waveSpeed, thickness }`. A pulse that tears a slab off its back raises `explosion:spall`, even when the wall itself stays up.

### Hiding behind something

A body between the charge and a target blocks the blast. The target gets the share `transmission` (0.1 by default), which you can set per scope: 0.5 for a thin screen, 0.01 for a bunker wall.

`explosion:exposure { entity, overpressure, impulse, shielded, reflected, wind }` tells every exposed entity what it received, including entities that only carry the `exposure` tag, such as a player's character or a window, and have no body. That is the event to use for damage.

### A grenade with a casing

```ts
x.detonate({ at: [0, 1, 0], tnt: 0.2, casing: { mass: 0.4, shape: 'cylinder' }, seed: 7 });
```

Fragments are created over several steps (`perFrame`, 60), at most `maxFragments` (300) at once, each living `fragmentLife` seconds (8); the oldest are recycled. The event `explosion:fragments { at, count, speed, mass }` announces them, and a fragment that touches something raises `explosion:hit { fragment, target, speed, energy, point, outcome? }`.

### Armour

A fragment hits a plate: with the `armour` value `{ thickness, strength }` on the plate, `penetrate` compares the fragment's energy with the work of pushing a hole through. The outcome is `through` (with the speed it leaves at), `stopped`, or `ricochet` when it strikes at a grazing angle. A plate without `armour` simply makes fragments bounce.

### Craters

Give the capability a terrain (`x.setTerrain(terrain)` or `explosions({ terrain })`) and a `surface` or `buried` charge digs a crater, sized by the charge, the burial and the soil (sand, clay, silt, gravel, mud, rock). The event `explosion:crater` reports radius, depth, the volume removed and thrown, and how far the ejecta fly. A deep enough burial gives `camouflet: true` and no crater. Terrain (skill `ferret-physics-terrain`) says what the ground does next: slumping, flooding.

### A vessel that bursts

```ts
x.detonate({ at, vessel: { pressure: 150e5, volume: 0.05, gamma: 1.4 }, burst: 'air' });
```

The energy is the gas's expansion work. A boiler or a tank that goes over its limit in [combustion](combustion.md) calls this itself, with the mass of its casing for the fragments.

## Parameters

| Value | Default | Effect |
|---|---|---|
| `minPressure` | 500 Pa | smallest overpressure a body bothers with |
| `rupture` | `Infinity` | impulse per area that breaks a body (needs strength) |
| `area` | projected area | exposed area of a body, m² |
| `transmission` | 0.1 | share of the blast a shielding body lets through |
| `spread` | 0 | random tilt of the push, in radians |
| `armour` | none | a plate `{ thickness, strength }` that fragments meet |
| `wall` | none | `{ tensile, density, waveSpeed, thickness }` for spalling |

Options of `explosions()`: `terrain`, `curve`, `craters`, `maxFragments`, `perFrame`, `fragmentLife`.

## Cost

A table lookup and a few rays per body per blast. Fragments are capped and made over several steps. A blast with hundreds of bodies in range is a one-frame spike, not a continuing cost. Spread large scenes over a few frames, or reduce the number of targets with `minPressure`.

## When it does not behave

| Symptom | Cause |
|---|---|
| Nothing moves | the body is below `minPressure` at its distance; check `blastAt(r, tnt).overpressure` |
| A body moves less than expected | it is shielded; read `shielded` in `explosion:exposure` |
| Nothing breaks | `rupture` is `Infinity` (the default), or strength is not enabled |
| Wrong crater | no terrain, the burst is `air`, or the soil is missing from `craters` (sand is used) |
| Fragments do nothing to a plate | the plate has no `armour` value |
| Different fragments each time | no `seed`; pass one for a replay |

Worked numbers (scaling, impulse on a crate, Gurney velocity, penetration outcomes) are in `tests/unit/examples/physics-explosions.test.ts`.

## Reference

`explosions`, `ExplosionCapability`, `blastAt`, `radiusFor`, `vesselTnt`, `gurneyVelocity`, `mottMasses`, `craterRadius`, `penetrate`.
