# Tawpy
The goura the player is. Sits on the `GouraPlayer` PlayerPrototype's `body`, so
every peer that joins the world gets one and each reads only its own controls
(`entity:isLocal()`).
## What she does
A crowned pigeon is heavy and a poor flier, and she moves like it.
| State | What it is |
|---|---|
| `idle` | standing |
| `walk` | a deliberate 2.2 m/s, reached and lost over about a fifth of a second |
| `gather` | holding Flurry roots her and builds the charge, 0.3 → 1 over `gatherTime`; she can still swing round slowly to aim |
| `flurry` | released: a hop off the floor to 0.85 m at up to 12.5 m/s, committed to the heading she launched on, veering and beating sideways as she goes |
| `boom` | the answer to the flock; roots her for 1.15 s wherever she stands |
| `startled` | the eagle's gaze found her; frozen for 0.9 s, charge lost |
| `caught` | taken |
| `roosted` | home |
The **stagger** off a landing is a ninth reading and not a ninth state: `state()`
reports `walk` (she is on the floor and still moving), `phase()` reports
`stumble`, and for about half a second she cannot boom, gather or steer while
her speed decays and her heading wobbles. That is the price of the flurry, and
it is why a flurry across open floor is a gamble rather than a free sprint —
8.6 m in about 1.8 s door to door against 2.2 m/s of walking, at the cost of
being unable to answer or turn for the whole of it.
A **flurry into the trunks** at the lane's edge ends the flight where it hit.
A **boom pressed while she is committed** (mid-flurry, mid-stagger) is
remembered for 0.4 s and fires the moment she can act, so a landing on the beat
still answers the flock.
## Tuning
The feel numbers are the `FEEL` table at the top of `init.luau`, not public
fields. A public field is serialised with the entity, so a baked scene keeps
whatever default it was written against and that copy silently wins over
anything changed in the component — which is exactly what happened to this one.
`state`, `subState`, `power` and `speed` stay public because the camera and the
art read them off the component.
## The surface
`FL.provide("player", …)` — the spec's shape, plus four additive readings the
art and the camera can use:
| Call | |
|---|---|
| `position()` | `{ x, y, z }`; `y` lifts during a flurry |
| `heading()` | unit `{ x, z }` on the floor |
| `state()` | one of the eight above |
| `phase()` | the same, plus `stumble` |
| `speed()` | metres per second this frame |
| `power()` | gathered charge, 0..1 |
| `airborne()` | feet off the floor |
| `stumbling()` | staggering off a landing |
| `entity()` | her entity |
| `startle()` | the gaze found her |
| `catch()` | the eagle took her; emits `caught` |
| `reset()` | back to the lane start for a new run |
## Events emitted
`boom`, `flurry` (`{ position, power }`), `step` (every 0.62 m of walking),
`roost`, `caught`.
## Controls
`goura.inputMap` beside this file — move, flurry, boom, pause, each bound on
keyboard+mouse, gamepad and touch. The pause control toggles `playing ⇄ paused`
from the same press on every device.
# gouraart
Tawpy as a thing on screen: the meshes, the plumage maps, the materials, the
entity tree, and the pose clock that moves it. `game.hero.gouramesh` says what
shape every part is; this module uploads those shapes, paints the slate mantle,
the maroon breast shield, the white-tipped lace crest and the pale wing bar
against their UVs, hangs the parts from `gouramesh.ANCHOR`, and poses the
result.
Everything it spawns is temporary and unsynced. A rig is a picture of a body
entity, rebuilt wherever that body exists, never saved into a scene.
## Exports
- `M.ensure()` — builds every mesh and the ten runtime materials. Idempotent;
the first call costs a few tens of milliseconds and the rest are free.
- `M.make(body, opts?) -> rig` — dresses one body entity and hands back the rig.
`opts.scale` is her height to the crest tips in metres, `opts.seed` fixes the
phase of her breathing. Called again for a body that already carries a rig, it
returns the rig it already has.
- `M.setPose(rig, name)` — ask for one of `idle`, `walk`, `gather`, `flurry`,
`boom`, `startled`, `caught`, `roosted` (`M.POSE_NAMES`). Unknown names hold
whatever she had.
- `M.punch(rig, amount)` — a knock that decays over a beat: the pulse on a boom,
the pop of a launch.
- `M.tick(dt)` — poses every live rig. Safe to call more than once a frame; the
second call in the same frame does nothing.
- `M.owns(rig)` — whether THIS load of the module still holds the rig. A hot
reload empties the rig list, so a rig made under the previous load is an
orphan nothing poses any more; a component asks this before trusting the
handle it holds.
- `M.dispose(rig)`, `M.clear()`, `M.count()`, `M.report()`, `M.meshes()`.
## The poses
Each names a target set — lean, crouch, puff, stretch, roll, neck and head
pitch, head thrust, crest flare, wing spread and pitch, tail pitch and fan, leg
pitch, tremble — which the tick approaches exponentially, so a pose change reads
as a move rather than a cut. On top of the target sit the beats:
- **walk** — the cadence follows her ground speed, and the head-bob is the real
one: the head holds still in the world while the body carries under it, then
darts forward to catch up.
- **gather** — a crouch with a tremble that grows with how long she has held it.
- **flurry** — wings hammering at speed, the whole bird rolling on two beats at
once, legs trailing. She flies badly and it shows.
- **boom** — the chest pulses on the beat of the call, crest at full flare, head
drawn back and down.
- **startled** — frozen tall, crest flattened, a fine shake that decays.
- **caught** — rolled onto her side, the flail settling out over a beat.
- **idle** / **roosted** — a slow breath, and in idle a look about her every few
seconds, being a giddy bird.
## Painting
Each map is written against the part's own UVs, measured from the engine rather
than from the sweep's maths: on a lens or a lathe `u = 0` is the surface facing
her front, `0.25` her left flank, `0.5` her back and `0.75` her right flank, and
`v` runs from the low end of the sweep to the high end. On a ribbon `u` is only
the two edges and `v` runs base to tip, which is what carries the crest's white
tip and the tail's terminal band.
## Notes
- The materials are runtime GPU materials (`renderer.material.create`) rather
than `.material` assets: every colour and every painted map is code in this
file, so the bird is one artefact and there is nothing to keep in step with
it. `renderer.material.create` takes its values under `properties` and
`textures` — a `colors` or `floats` block is accepted and dropped.
- If a map fails to paint, every material renders its own base colour and the
bird is flat rather than absent.