EmberwardBody — the Emberward as an object.
BattleBoot attaches this to the local player's body. It does three things:
1. FORGES HER. Every part in `art/emberward/kit` is built as a runtime mesh
and glued to the bone it belongs to. The glue is exact rather than
eyeballed: the rig's own inverse bind matrix IS the transform from model
space to bone space, so a part authored in character-space metres is
parented to its bone with `localRotation`/`localPosition` read straight
out of that matrix and a uniform scale of 100 (the rig's bone space is
centimetres). Nothing has to be posed, and it is right on the frame it
is built regardless of where the animation happens to be.
2. HANGS THE CLOAK. A verlet cloth pinned to ten points on the back yoke,
simulated in world space and rewritten into its mesh every frame. Because
the pins ride the spine and the cloth does not, a hard turn leaves the
cloak behind and it swings back — the lag is the simulation, not a curve.
3. POSES HER. The rig ships with locomotion clips and nothing else, so
every fighting pose here is authored: an additive world-space rotation
per bone, applied parent-first on top of whatever the locomotion is
already doing, driven by the `player:*` events combat raises. Every pose
rides a smoothstep in and out and every attack winds back before it goes
forward, so nothing snaps.
It owns nothing outside the player's body and the cloak, and it puts
everything back on destroy.
resolveAssets( ) → void
Resolve every asset this component reaches for. Each name is a LITERAL so
the reference is statically pinned and the world publishes; the resolves
happen on first use rather than at module load, because the registry is not
necessarily populated while the module body is running.
qmul(a: ?, b: ?) → void
Small maths: quaternions, easing
qaxis(ax: ?, ay: ?, az: ?, deg: ?) → void
| arg | type | description |
|---|
| ax | ? | |
| ay | ? | |
| az | ? | |
| deg | ? | |
qeuler(ex: ?, ey: ?, ez: ?) → void
A small XYZ rotation, built as three axis rotations. Order barely matters
at pose amplitudes and keeps the maths readable.
| arg | type | description |
|---|
| ex | ? | |
| ey | ? | |
| ez | ? | |
quatFromColumnMajor(m: ?, o: ?) → void
Matrix→quaternion for the 3x3 rotation of a COLUMN-MAJOR 4x4 (which is how
the rig stores its inverse bind matrices — measured, not assumed).
qClose(a: { number }, b: { number }) → boolean
Two quaternions describing the same rotation, within float noise.
| arg | type | description |
|---|
| a | { number } | |
| b | { number } | |
dressBaseBody(body: any) → void
Building the body
Dress the rig's own skin. The humanoid ships in a pale default that reads
as a mannequin under the plate; under the Emberward's armour it is the mail
and gambeson she wears beneath, so it is nearly black and very rough.
| arg | type | description |
|---|
| body | any | |
undressBaseBody(body: any) → void
| arg | type | description |
|---|
| body | any | |
findBones(body: any) → void
| arg | type | description |
|---|
| body | any | |
bindTransforms( ) → void
The rig's inverse bind matrices, keyed by bone name. This is the exact
model-space→bone-space transform every part is glued with.
restoreBindPose( ) → void
Put every bone this layer drives back on its rest orientation. A previous
run of the pose layer, or a reload part-way through an action, can leave a
bone somewhere the rig will not itself correct while no clip is driving it;
this is the known-good state to start composing from again.
buildCloak(body: any) → void
The cloak
| arg | type | description |
|---|
| body | any | |
stepCloak(dt: number) → void
One verlet step plus constraint relaxation, in world space. The pins are
read off the rig, so all the motion the cloak has comes from the body.
| arg | type | description |
|---|
| dt | number | |
link(a: ?, b: ?, rest: ?, stiff: ?) → void
Constraints. Structural along both axes plus one bend link down the
length, which is what stops it folding into a rope.
| arg | type | description |
|---|
| a | ? | |
| b | ? | |
| rest | ? | |
| stiff | ? | |
restLen(a: ?, b: ?) → void
swing(t: number, windup: number, active: number, recover: number) → number
The pose layer
Anticipation → strike → follow-through as one signed curve. It goes
NEGATIVE first (the wind-back), crosses fast, overshoots, and settles.
| arg | type | description |
|---|
| t | number | |
| windup | number | |
| active | number | |
| recover | number | |
guardPose(w: number) → void
The guard: held, not timed. She turns the shoulder into the blow and gets
the blade up across her.
| arg | type | description |
|---|
| w | number | |
idlePose( ) → void
Always-on breathing and weight shift, so she is never a statue.
accumulate(into: ?, from: ?, scale: ?) → void
| arg | type | description |
|---|
| into | ? | |
| from | ? | |
| scale | ? | |
applyPose(dt: number) → void
| arg | type | description |
|---|
| dt | number | |
play(name: string, force: boolean?) → void
| arg | type | description |
|---|
| name | string | |
| force | boolean? | |
writeEmber(force: boolean?) → void
The ember — the one warm thing on her
| arg | type | description |
|---|
| force | boolean? | |
buildAll( ) → boolean
Lifecycle
update(dt: number) → void
| arg | type | description |
|---|
| dt | number | |
status( ) → void
What the body actually built, for a look from an execute.
preview(name: string) → void
Play one authored pose by name, so it can be looked at without a fight.
Names: light heavy crit roll parry hurt death.
| arg | type | description |
|---|
| name | string | |
reforge( ) → boolean
Rebuild the armour and the cloak from the kit, live.