# FlashFire
FlashFire fires an effect from in front of the player, away from the camera the player looks
through, while the player presses or holds `fire`. It sits on the player's body: the peer that owns
the body reads the control and aims the shot, and every peer plays it, because `shoot` is a synced
function.
The gun has one effect of its own, standing at its muzzle as a child of the body. A shot moves it to
where the muzzle is, turns it down the aim and plays it again from its start, so a body firing any
number of shots holds one effect, and each body in the world holds its own.
The main scene uses it on the `body` entity of `DefaultSocialPlayer`, the PlayerPrototype under
`PlayerSetups`, with `effect` set to `effects.combat.muzzleFlash` and `inputMap` set to
`weapon.weapon`. It aims with the `Camera` of the `CameraRig` entity whose `follow` is this body.
## Fields
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `effect` | assetRef (procGraph) | `effects.combat.muzzleFlash` | The effect fired on each press. |
| `inputMap` | assetRef (inputMap) | `weapon.weapon` | The controls live while this peer's player owns the body. `fire` is the one that shoots. |
| `ahead` | number | 0.8 | How far in front of the body, along the aim, the effect fires from, in metres. |
| `height` | number | 1.4 | How far above the body's origin the effect fires from, in metres. |
| `side` | number | 0.35 | How far to the right of the body, across the aim, the effect fires from, in metres: where a gun in the right hand is. |
| `rate` | number | 11 | Shots per second while `fire` is held down. A press fires at once; holding it keeps firing at this rate. |
All fields are `Sync`.
## Methods
- `:shoot(at, dir)`: plays the effect at `at` (a world position `{ x, y, z }`), pointing along `dir`
(a world direction `{ x, y, z }`), on this peer. It is declared in `syncedFunctions`, so a call
plays the shot on every peer. It makes the muzzle effect on first use (a temporary child entity
named `muzzle` with a Generator for `effect`, `playOn = "manual"`), and makes it again when
`effect` names another asset. It moves the muzzle to `at`, turns its forward (-Z) onto `dir`,
seeks the Generator to the first frame of its playback and plays it.
## Lifecycle
- `update(dt)`: on the peer that owns the body, while the engine runs unpaused, it activates
`inputMap` and listens to its `fire` control: a press fires one shot at once and holds the
trigger, a release lets go. Elsewhere, and while paused, it deactivates the map and lets go of the
trigger. While the trigger is held and `rate` is above 0, it accumulates `dt * rate` shots (held at
most 2, so a hitch adds at most one extra shot) and fires one per whole shot owed.
- `onDestroy()`: deactivates the input map and despawns the muzzle effect.
A shot finds the aim camera and places the effect at the body's position plus `ahead` along the
camera's forward, `side` along its right and `height` up, then calls `shoot` with that position and
the camera's forward.
If the input map raises an error on activation or deactivation, FlashFire reports it with `warn`.
# muzzleFlash
The bloom at a barrel the instant a weapon fires: a ragged star of light and a
tongue of flame down the bore, gone within three frames; a spray of burning
powder that streaks out, falls and cools; a few puffs of smoke that swell, rise
and thin; and a light that throws the flash on whatever stands near.
The bore runs along the effect's -Z. Stand it on a child of the weapon at the
muzzle, so it follows the weapon, and play it from the start on each shot:
```lua
-- `graph` is this graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local function mountFlash(weapon, muzzleOffset)
local muzzle = entity.spawn("muzzle", { parent = weapon.id, position = muzzleOffset })
muzzle.component.add("Generator", { graph = graph, playOn = "manual", ending = "hold" })
local flash = muzzle.component.get("Generator")
return function()
flash:seek(0)
flash:play()
end
end
```
| Input | Default | What it does |
|---|---|---|
| `size` | 0.28 | Width of the flash in metres: a pistol at 0.12, a rifle at 0.28, a tank gun at 1.2. Every layer scales with it. |
| `light` | 1 | Strength of the light it throws, as a multiple of the default. |
It plays 54 frames at 60 fps (0.9 s). Played again, it plays the frames its
first play cooked, so a weapon firing on full auto cooks it once.
# weapon
The `weapon` control scheme: an input map named `weapon` that holds the controls for firing a
weapon. It declares one control, `fire`, defined in its `fire.inputBinding` child: a button labelled
"Fire" bound to the left mouse button, the gamepad's right trigger, and a touch button in the
`right-lower` touch zone.
`FlashFire` activates it. The main scene's `body` entity carries a FlashFire whose `inputMap` field
references this map as `weapon.weapon`, which is also the field's default. On the peer that owns the
body, FlashFire calls `activate()` on the map, listens to the claimed `fire` control's
`onPressed` and `onReleased`, and calls `deactivate` with the claim when it gives the controls up.
## Controls
| Control | Kind | Keyboard and mouse | Gamepad | Touch |
|---------|------|--------------------|---------|-------|
| `fire` | button | left mouse button | `right_trigger` | touch button, zone `right-lower`, priority 5 |
# effectShowcase
The procedural effects of the Procgen Effects Showcase world. Each one is a `.procGraph` that plays
through a `Generator`, and every shader, material and module a graph draws with sits beside it in
this package, so the package carries everything its graphs need. Each graph's README says what it
makes, how to place it and what each of its inputs changes.
## Contents
| Folder | Graphs | What they make |
|---|---|---|
| `combat/` | `impact`, `muzzleFlash`, `tracer` | A round striking a hard surface, the bloom at a barrel as a weapon fires, and the glowing streak of a round in flight. They draw with `fxFlash`, `fxSmoke` and `fxSpark` in `combat/shaders/`. |
| `blast/` | `wall_blast` | A charge blowing a breach through a stone block wall, on a 9.6 s loop. |
| `storm/` | `storm`, `tower` | A thunderstorm around a lone tower: wind-driven rain, lightning striking on the schedule `schedule.module` keeps, and sparks and smoke where strikes land. `tower` is the stone keep it stands around. |
| `rift/` | `arcane_rift` | A rift tearing open in the ground, its motes and rubble moved by the compute shaders in `rift/compute/`. |
| `dragon/` | `dragon_breath` | A dragon's fire breath sweeping a courtyard: a flame jet, rolling smoke and drifting embers. |
| `launch/` | `launch` | A heavy-lift rocket liftoff on one looping timeline, with the heat-haze material `rk_haze_mat`. |
| `waterfall/` | `wf_env`, `wf_water`, `wf_spray`, `wf_foam`, `wf_mist` | A waterfall built in parts: the cliff, basin and boulders, the falling and still water, the droplets, the foam where the falls land, and the mist with its rainbow. Its water materials are in `waterfall/materials/`. |
| `shaders/` | | `fx_noise`, the noise functions the waterfall and wall blast shaders include. |
## Playing one
A graph plays on a `Generator` component bound to it:
```lua
-- `graph` is the graph's reference: the AssetRef `asset.resolve` or a search returns for it.
local e = entity.spawn("impact", { position = { 0, 1, 0 } })
e.component.add("Generator", { graph = graph, playOn = "load" })
```
Set a graph's inputs with the Generator's `params`, e.g. `params = { size = 3 }`.
# fire
The `Fire` control of the `weapon` input map: a button control bound on every device class
(keyboard and mouse, gamepad and touch), which activation requires of every control. It builds its
bindings with `@builtin::modules.zinput.bindings`.
`FlashFire` reads it through the map: once the map is active, pressing `fire` fires one shot and
starts held fire at the component's `rate`, and releasing it ends the held fire.
## Bindings
| Field | Value |
|-------|-------|
| `label` | `"Fire"` |
| `kind` | `"button"` |
| `kbm` | `B.mouse("left")`: the left mouse button |
| `gamepad` | `B.padButton("right_trigger")`: the right trigger |
| `touch` | `B.touchButton({ zone = "right-lower", priority = 5 })`: a touch button in the `right-lower` zone, priority 5 |
`B` is `require("@builtin::modules.zinput.bindings")`.