Emberward
The feel of the fight. Attaches to the local player's body; `BattleBoot` wires it there the moment the component exists.
Emberward
The feel of the fight. Attaches to the local player's body; BattleBoot wires
it there the moment the component exists.
What it does
| System | Behaviour |
|---|---|
| Locomotion | Gates the body's CharacterController speed by state — free, guarding, committed to a swing, rolling, hurt, dead. |
| Dodge-roll | Space. 0.62 s, invulnerable from 0.06 s to 0.40 s, carried by a decelerating curve so it has weight. Costs stamina. |
| Light attack | Left mouse. 0.16 s windup / 0.12 s active / 0.30 s recovery. |
| Heavy attack | Right mouse. 0.46 / 0.16 / 0.62 — a real commitment. |
| Guard + parry | Hold Shift to block at a stamina cost. Release it into an incoming strike inside 0.22 s and the saint rings. |
| Stamina | Everything spends it; it regenerates 0.55 s after the last spend and never while the guard is up. |
| Hit detection | Reach + arc from the Emberward's own facing to the saint's root. |
| Quench | E, only while the saint is staggered and within reach. 110 damage. |
| Lock-on | Tab. On by default. |
| Camera | Sets the live Camera's behavior to CombatCamera — from this file, so the asset literal resolves. |
Events
Raises player:attack player:roll player:guard player:parry
player:damaged player:died player:stamina hit boss:damaged shake
hitstop camera:beat. Consumes boss:strike.
Ownership
The saint's health, poise and stagger belong to the boss slice. This component
reports damage and lets that slice apply it. While no SlagSaint component
exists on boss_root it applies the numbers itself, so the fight is playable
and testable alone — and it stands that fallback down the instant the boss
slice appears, so a hit is never applied twice.
Driving it without a keyboard
Every verb is a method: :light() :heavy() :roll() :guard(down)
:quench() :toggleLock() :revive(). :status() returns the whole state
machine — state name, time in state, attack window, stamina, i-frames — which
is what a claim about the fight should be checked against.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
conforms to
zero/source-extract/v2Emberward — the feel of the fight. Everything the player DOES lives here: locomotion gating, the dodge-roll and its invulnerable window, light and heavy attacks with a real windup / active / recovery shape, the guard and the 0.22 s parry that rings the saint, the stamina that punishes greed, hit detection against the saint's body, and the Quench. It attaches to the LOCAL PLAYER'S BODY (BattleBoot puts it there), and it is also where the gameplay camera is handed over: it sets the live `Camera` component's `behavior` to `CombatCamera`, from this file, because an asset reference has to resolve in the engine that publishes the world and the wiring hub cannot name a component that did not exist when it was written. WHAT IT RAISES (see Rules.EVENTS): player:attack {kind} player:roll player:guard {down} player:parry {perfect} player:damaged {amount, health} player:died player:stamina {value, max} hit {point, surface, power} boss:damaged {amount, health, part} shake hitstop camera:beat WHAT IT CONSUMES: boss:strike {move, origin} — the only thing in the game that hurts you. WHO OWNS WHAT: the saint's health and poise belong to the boss slice. This component raises `boss:damaged` and lets that slice apply it. Until that slice exists, it applies the numbers itself so the fight is playable and testable on its own — and it stands down the moment a `SlagSaint` component appears on `boss_root`, so the two never both apply the same hit.
P( ) → any
bodyPos( ) → void
bossEnt( ) → any
bossSliceLive( ) → boolean
True once the boss slice is live. While it is, this component only REPORTS damage; the boss applies it.
cc( ) → any
pushStamina( ) → void
canSpend(cost: number) → boolean
| arg | type | description |
|---|---|---|
| cost | number |
spend(cost: number) → boolean
| arg | type | description |
|---|---|---|
| cost | number |
refund(amount: number) → void
| arg | type | description |
|---|---|---|
| amount | number |
enter(name: string, dur: number) → void
| arg | type | description |
|---|---|---|
| name | string | |
| dur | number |
busy( ) → boolean
attackWindows(kind: string) → void
The three windows of an attack, from the tuning in Rules.PLAYER.
| arg | type | description |
|---|---|---|
| kind | string |
attackPhase( ) → string
Which window an attack is in right now.
beginStaggerLocally( ) → void
endStaggerLocally( ) → void
strikeBoss(amount: number, poise: number, part: string, power: number) → void
Land a blow on the saint. `part` is one of the five keys in Rules.state.boss.damage, so the art slice can wear the body down.
| arg | type | description |
|---|---|---|
| amount | number | |
| poise | number | |
| part | string | |
| power | number |
saintInReach(reach: number, arc: number) → boolean
Is the saint inside the swing? Reach is measured to its root and the arc is taken from the Emberward's own facing, so a swing at nothing misses.
| arg | type | description |
|---|---|---|
| reach | number | |
| arc | number |
tryLandAttack( ) → void
die( ) → void
hurt(amount: number) → void
| arg | type | description |
|---|---|---|
| amount | number |
parry(move: string) → void
| arg | type | description |
|---|---|---|
| move | string |
block(amount: number) → void
| arg | type | description |
|---|---|---|
| amount | number |
onBossStrike(payload: any) → void
The one thing in the game that hurts the player.
| arg | type | description |
|---|---|---|
| payload | any |
moveDirFromInput( ) → void
resolveAnchor( ) → void
Facing is written on the BODY ROOT, not on the skinned mesh under it. The mesh is one child among several — the art slice hangs a cloak and its own pieces beside it — and turning only the mesh leaves everything else pointing the way it was. Turning the root turns the whole character, whatever anyone parents to it afterwards. Locomotion turns the mesh child toward the character's VELOCITY. Under a lock-on the facing is the saint rather than the velocity, so the two would fight every frame: stand its turn down, and level the child it may already have turned, so the root's rotation is the only one in play.
desiredYaw( ) → number
driveFacing(dt: number) → void
| arg | type | description |
|---|---|---|
| dt | number |
tuneController( ) → void
carryRoll(dt: number) → void
Carry the roll. The character controller is held at zero speed through it, so the displacement is this curve and nothing else: it starts fast and decelerates to a stop, which is what gives the roll its weight.
| arg | type | description |
|---|---|---|
| dt | number |
playerCameraEntity( ) → any
THIS player's camera rig — the sibling of the body inside the prototype clone the engine minted for this user. `layers.active.camera` is the wrong thing to ask: it answers with whatever camera is on screen right now, which in play mode is sometimes the EDITOR's fly camera and during a cutscene is staging's. Handing the fight camera to either of those puts a follow rig on a camera that is not the player's, and the symptom is a behaviour that appears to vanish and reappear on its own.
handOverCamera( ) → void
Staging takes the camera for the opening and the two endings by putting its own behavior on it, so the handover is not a one-shot latch: it is a poll that takes the camera back whenever the fight is the thing on screen and nothing of combat's is driving it. It never steals the camera mid-cutscene — during `intro` / `victory` / `defeat` it only claims a camera that has no behavior at all, which is the case where nobody is driving it.
tickFallbackBoss(dt: number) → void
Poise regeneration and the stagger timer belong to the boss slice. Run them here only while that slice is missing, so the fight is playable before it lands and completely untouched after.
| arg | type | description |
|---|---|---|
| dt | number |
armInput( ) → void
unsubscribeAll( ) → void
awake( ) → void
start( ) → void
tick(dt: number) → void
| arg | type | description |
|---|---|---|
| dt | number |
update(dt: number) → void
| arg | type | description |
|---|---|---|
| dt | number |
onDestroy( ) → void
light( ) → boolean
Swing the light attack: fast, cheap, small poise damage.
examples
emberward:light()
heavy( ) → boolean
Swing the heavy attack: slow to start, big damage, big commitment.
examples
emberward:heavy()
roll( ) → boolean
Dodge-roll. Invulnerable from 0.06 s to 0.40 s of a 0.62 s roll, and it costs stamina — an empty bar is why greed kills.
examples
emberward:roll()
guard(down: boolean) → void
Raise or drop the guard. Dropping it opens the parry window: a strike that arrives inside `Rules.PLAYER.parryWindow` of the release rings the saint instead of landing.
| arg | type | description |
|---|---|---|
| down | boolean | True while the control is held. |
examples
emberward:guard(true) ; emberward:guard(false)
quench( ) → boolean
Drive the quenching blade into the saint's exposed core. Only available while it is staggered and within reach; 110 damage.
examples
emberward:quench()
toggleLock( ) → boolean
Toggle the lock-on. Locked, the camera holds the Emberward and the saint in the tall frame and the body faces the saint.
examples
emberward:toggleLock()
revive( ) → void
Put the Emberward back on her feet at full health and stamina, in the free state — what a retry needs.
examples
emberward:revive()
status( ) →
Everything the fight is doing right now, in one table — the read-back behind any claim about i-frames, stamina or the state machine.
examples
print(emberward:status().state)
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Showing up to 3 levels deep. Open a part to see what it holds further down.
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
agent_score is exposed.+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
Scoped to this part · feeds back into the world's score.