emberline.fight — the fight.
One fixed 60 Hz step drives everything: the state machine over Rules.STATES,
hitbox against hurtbox, the block table, hitstun and blockstun with the
frame advantage the roster's `onBlock` number PROMISES, combo scaling,
meter, hitstop, projectiles, the round clock and the best-of-three.
The rules that make it a fighting game rather than two boxes colliding:
* A block is a DIRECTION, never a button. Holding away blocks; the stance
you hold it in decides what it stops (Rules.BLOCK_TABLE), so a crouching
block eats an overhead and a standing block eats a low.
* A throw is unblockable and loses to anything that is not blocking.
* `onBlock` is enforced, not decorative: blockstun is set so the defender
leaves stun exactly `onBlock` frames before/after the attacker leaves
recovery. A blocked heavy really does leave you standing there.
* Hitstop freezes BOTH fighters on connect, and input keeps buffering
through it, which is what makes a link feel like a link.
Registers Bus.services.fight and emits every fight.* event. The roster slice
(Bus.registry.fighters) and the art slices (Bus.registry.art) win wherever
they have landed; emberline.fight.defaults answers where they have not.
pushVelocity(distance: number, weight: number) → number
| arg | type | description |
|---|
| distance | number | |
| weight | number | |
artFor(id: string) → any
| arg | type | description |
|---|
| id | string | |
defFor(id: string) → any
| arg | type | description |
|---|
| id | string | |
buildSheet(id: string) → any
| arg | type | description |
|---|
| id | string | |
newFighter(side: number, id: string, control: string) → any
| arg | type | description |
|---|
| side | number | |
| id | string | |
| control | string | |
showFrame(f: any, idx: number) → void
A mesh-mode PixelCanvas builds one mesh for the frame it is showing and
lets that mesh go once the frame stops being the shown one, so coming back
to an index that was displayed earlier binds a mesh that is no longer there
and the sprite draws nothing at all — while every query about the canvas,
the component and the Model still reads healthy. A freshly ADDED frame
always carries a live mesh, so every display goes through a duplicate of
the frame we actually want. The duplicates pile up above the authored sheet
and `pruneScratch` cuts them back between rounds, top-down, so no authored
index ever shifts.
| arg | type | description |
|---|
| f | any | |
| idx | number | |
pruneScratch(f: any) → void
setClip(self: any, name: string) → void
| arg | type | description |
|---|
| self | any | |
| name | string | |
clipRange(self: any) → any
| arg | type | description |
|---|
| self | any | |
isFree(self: any) → boolean
| arg | type | description |
|---|
| self | any | |
reset(self: any, x: number, facing: number) → void
| arg | type | description |
|---|
| self | any | |
| x | number | |
| facing | number | |
setState(self: any, state: string, clip: string?) → void
| arg | type | description |
|---|
| self | any | |
| state | string | |
| clip | string? | |
moveBox(f: any, m: any) → any
| arg | type | description |
|---|
| f | any | |
| m | any | |
overlaps(a: any, b: any) → boolean
| arg | type | description |
|---|
| a | any | |
| b | any | |
announce(line: string) → void
| arg | type | description |
|---|
| line | string | |
projectileHandle(f: any) → any
hexToRgb(hex: string) → void
| arg | type | description |
|---|
| hex | string | |
spawnProjectile(f: any, m: any) → void
| arg | type | description |
|---|
| f | any | |
| m | any | |
despawnProjectile(pr: any) → void
addMeter(f: any, amount: number) → void
| arg | type | description |
|---|
| f | any | |
| amount | number | |
hitstopFor(m: any) → number
knockOut(loser: any, winner: any) → void
| arg | type | description |
|---|
| loser | any | |
| winner | any | |
applyDamage(a: any, d: any, m: any, blocked: boolean, contactX: number, contactY: number, viaProjectile: boolean?) → void
| arg | type | description |
|---|
| a | any | |
| d | any | |
| m | any | |
| blocked | boolean | |
| contactX | number | |
| contactY | number | |
| viaProjectile | boolean? | |
resolveBlock(d: any, m: any) → boolean
Can `d` block `m` coming from `a` right now, and in which stance?
| arg | type | description |
|---|
| d | any | |
| m | any | |
startMove(f: any, slot: string) → boolean
| arg | type | description |
|---|
| f | any | |
| slot | string | |
tryAttack(f: any, cancelOnly: boolean) → boolean
Read the controller and start whatever it asked for. Ordered so a super
beats a special beats a normal: the double-quarter-circle contains a
quarter-circle, and mashing heavy underneath a super must not eat it.
| arg | type | description |
|---|
| f | any | |
| cancelOnly | boolean | |
beginJump(f: any, dir: number) → void
| arg | type | description |
|---|
| f | any | |
| dir | number | |
beginDash(f: any, dir: string) → void
| arg | type | description |
|---|
| f | any | |
| dir | string | |
stepFighter(f: any, opp: any) → void
| arg | type | description |
|---|
| f | any | |
| opp | any | |
resolveContact(a: any, d: any) → void
| arg | type | description |
|---|
| a | any | |
| d | any | |
stepProjectiles( ) → void
roundLine(round: number) → string
| arg | type | description |
|---|
| round | number | |
endMatch(winnerSide: number) → void
| arg | type | description |
|---|
| winnerSide | number | |
applyVisual(f: any) → void
pickControl(kind: string?, slot: number) → string
| arg | type | description |
|---|
| kind | string? | |
| slot | number | |
start(opts: any?) → any
| arg | type | description |
|---|
| opts | any? | |
update(dt: number) → void
| arg | type | description |
|---|
| dt | number | |
forceRoundEnd(winnerSide: number) → void
Force a round to end, used by the game driver when a phase change has to
interrupt a fight (a pause that becomes a quit, for instance).
| arg | type | description |
|---|
| winnerSide | number | |