Hit reactions

Stun, launch and knockdown clocks, landing, getting up and temporary invulnerability for fighting games, driven by explicit commands.

Source: module-kits:kits/combat_hit_reactions/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs combat-hit-reactionsMCP tool: gripforge_docs { "topic": "combat-hit-reactions" }

Hit reactions (combat.hit_reactions) #

What it is for #

combat.hit_reactions decides what a body does after it is hit: stunned, launched into the air, knocked down, then landing, lying down, getting up and a short protection window. It is a clock and a set of rules, one instance per actor. It owns no health, gravity, collision world or animation: the game says whether the body is grounded, applies the returned velocity exactly once, and plays the clips. It is a driver you step yourself, with no hidden tick. Use it after combat.defense (guide) has decided that a hit lands, with combat.moveset (guide) for the attacker and combat.grabs (guide) for throws.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "combat.hit_reactions" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "combat.hit_reactions",
                              config: { "downFrames": 24, "getupFrames": 18, "getupInvulnerabilityFrames": 18 } }

Durations are whole fixed simulation frames (normally 60 Hz); call step once per frame with the grounded flag from your collision code.

First example #

<!-- test: kits/combat_hit_reactions/tests/combat_hit_reactions.rs::a_knockdown_stays_down_then_gets_up_with_protection --> With downFrames: 3, getupFrames: 2 and getupInvulnerabilityFrames: 4, a knockdown goes to phase downed; after three grounded steps getup_started fires and the body is protected (canBeHit is false). A hit during the protection answers accepted: false with an ignored event (reason invulnerable); two more steps give recovered and the phase is ready again.

{ "service": "combat.hit_reactions", "method": "request", "args": ["default",
  { "op": "hit", "sequence": 1, "kind": "knockdown", "grounded": true }] }
{ "service": "combat.hit_reactions", "method": "request", "args": ["default",
  { "op": "step", "grounded": true }] }

Settings and defaults #

Fixed at creation; unknown keys are refused.

SettingDefaultMeaning
downFrames24Frames spent on the ground before getting up (1 to 600).
getupFrames18Frames of getting up before control returns (1 to 600).
getupInvulnerabilityFrames18Protected frames counted from the start of getting up (0 to 600).
maxAirFrames600Airborne steps allowed since the last accepted hit or transition (1 to 600).
recoveriesnoneUp to 16 getup choices { id, frames, invulnerabilityFrames, velocity } (a roll, a quick rise).

Services #

Service combat.hit_reactions (up to 128 named instances): create(id, config), remove(id), ids() and request(id, command) with the commands:

  • hit { sequence, kind, grounded, stunFrames?, velocity? }: kind is stun, launch or knockdown; sequence is an integer strictly above the last impact of that actor; a launch needs velocity (world metres per second, Y up). Answers accepted and the events.
  • step { grounded, frozen? }: advances the clocks; grounded is required. A frozen step changes nothing.
  • land { sequence }: the engine tells that the body landed during the current accepted reaction.
  • recovery { sequence, id }: picks one of the recoveries while down.
  • invulnerability { frames }: extends the protection (never shortens it).
  • interrupt, reset, status, config, save, load { save }, preview { command }.

Answers hold state, canAct (the phase is ready), canBeHit and events. Script verbs: a request verb and an exists condition named after the kit id with dots turned into underscores (<kit>_request, <kit>_exists).

Events #

  • In the answer of a request: reaction { sequence, kind, phase, velocity } (apply the velocity once), ignored, airborne, landed, getup_started (with recovery and velocity when a choice was made), recovered, air_timeout, interrupted.
  • Bus events, named <kit id>.<suffix>: .created, .removed, .changed (a decided command applied) and .refused (a tap cancelled it).

Hooks and modding #

HookEffect
combat.hit_reactions.createBefore an instance is created; may change its config or cancel.
combat.hit_reactions.requestBefore a decided command; may change the command or cancel.

create(id, { preset }) reads the presets collection of the kit (its id with dots turned into underscores, then _presets).

Engines #

The same Rust core runs on the web, the native player and as a C library and WebAssembly module for the engine wrappers. The engine provides grounded, applies velocities and plays the animation.

When it does not do what you expect #

  • A launch is refused: it needs a velocity.
  • A hit answers accepted: false: the body is protected (invulnerable) or the sequence is stale; the sequence is still consumed.
  • A launched body never lands: send grounded: false first, then grounded: true, or use land; otherwise air_timeout ends it after maxAirFrames.

Limits #

No health, gravity, ragdoll, root motion or clips. It does not decide whether a body is alive: gate new actions yourself.

Versions #

Kit 1.2.0 (modding contract), 1.1.0 (recoveries). Save format gripforge.motion/1.