Abilities, Effects and beatable platformer foes
The October audit's Wave 4 item "Abilities and Effects on every mover; beatable platformer enemies" (docs/design/audit-2026-10.md). Before it, every body had one attack (kit.melee's blow or kit.ranged's volley), nothing could slow, stun, shield or poison anything, and a platformer foe was a hazard to jump over: it could never be beaten.
Effects (Effect, ids effect.*, in effects/)
A status a body is under for a while: slowed, poisoned, shielded, hasted, stunned, invulnerable.
- Fields:
durationMs(0 lasts until taken off),stacking(refreshstarts its time over,extendadds to it,stackadds a stack up tomaxStacks,keepdoes nothing), and what it changes:speedMul,damageTakenMul,damageDealtMul(each once per stack),canMove,canAct,invulnerable, andeveryactions run eacheveryMsonce per stack (regeneration, poison), withselfthe body andsourcewhoever put it on. - Put on by
apply_effect, an ability,blastandfire(theireffect), and a shooter'seffect(every shot that hurts). Taken off byremove_effector its time. - One place for everyone (
GwEffects): kits never keep a slow or a stun of their own. They askspeed_mul,can_move,can_act,damage_taken_mul,damage_dealt_mulandinvulnerable:- kit.move scales a walk and holds a root;
- kit.melee multiplies harm both ways, turns every hit away from an invulnerable body, and stops a body that can't act from striking or dodging;
- kit.ranged stops it shooting;
- kit.platformer scales a body's running and climbing, holds a root, lets an invulnerable player pass spikes and foes, and makes a foe that can't act harmless to the touch.
- State on the entity (
comp.effects.on, absolute ticks), so saves and replays carry it exactly. An entity nothing was put on has none, and one whose last effect ends loses it: games without effects keep every state hash. kit.core's first system takes off what has ended and runs theeveryactions due, before any other system steps. - Events
effect.started,effect.renewed,effect.ended; latcheseffect:<id>,renewed:<id>,ended:<id>. Pure functionshas_effect(e, id),effect_stacks,effect_secs,can_act,can_move, so a HUD shows "SLIMED" and juice answerseffect.started.
Abilities (Ability, ids ability.*, in abilities/)
Something a body does on demand: a dash, a fireball, a shield, a slam, a swipe.
- Carried in kit.core's
abilitiescomponent (any mover). Used by the player'saction, by any other body by itself (auto, whilewhenholds), or byuse_abilityfrom a state machine or rule. - A use: the body is alive and can act, the ability is off its
cooldownMs,whenholds and the body has itscost(a stat). Then the cost is paid, the cooldown starts,ability.usedgoes out anddoruns, withselfthe body andtargetwhat it was used at (a click's target for the player). A refused press oruse_abilitysays why inability.refused;ability.readycomes when a cooldown ends. - What it does is any actions, and three new ones built for it: kit.move's
dash, kit.melee'sblast(an area hit that may put an effect on what it hurts), kit.ranged'sfire(a volley from any body, paced by the ability's cooldown), and kit.platformer'splatformer_strike. - Cooldowns are absolute ticks in
comp.abilities.ready, written on the first use.
Beatable platformer foes
A platformer_touch with beatable is beaten by:
- a stomp: the player comes down on it from above (falling, its feet the tick before at the
thing's top or over it). The player bounces off at
stompBounceof its jump; - a strike:
platformer_strike, a box ahead of a body (an ability's swipe).
It takes its touch's hits to beat (1 unless it says; a strike counts its damage). Hits are
counted on the touch, never in a stat: every entity carries each declared stat's default, so a
health stat would have made a plain foe as tough as the player (the first test of it found that).
platformer_hits_left(e) reads what is left, for a boss's bar. A thing just hit stays harmless, and
can't be hit again, for 400 ms, so the landing that stomped it doesn't hurt the player on the way
back up. Beaten, it is gone from the level (platformer.beaten, latch beaten:<id>); short of
that, platformer.struck. Starting the level over brings it back. A side touch still hurts.
The levels' witnesses (the core's reachability search) never stomp, so beatable foes only add ways through: every route still holds.
Checks (core/src/game/abilities.ts)
An ability's cost names a declared stat (error); its action is in the game's InputMap (error);
something carries every Ability and something uses it (warnings); something puts every Effect on
(warning); maxStacks only with stacking stack (warning). Expressions in an Ability read self
and target; in an Effect, self and source.
In the samples and the starter
- Burrow Run: its crawlers are beatable; a stomp bursts sparks, stretches the hero and plays a squash and a boing. Its look tour stomps the meadow's crawler. The platformer starter's crawler is beatable too, so a new platformer game's foes can be stomped from the start.
- Saltwell: a spitter's glob slimes the keeper (
effect.slimed: seven tenths of the speed for 1.5 s, refreshed by each hit), with green droplets and SLIMED on the HUD.
Not yet
- An ability bar in the HUD (cooldowns drawn), and touch buttons for ability actions.
- A boss built from these and a Timeline in a sample.
- Effects that change a body's look (a tint while slowed) beyond juice on
effect.started.