Grabs
One coordinator for all the paired grabs of an arena: hold, timed escape, throw and release, with exclusive participants and optional hold impacts.
Grabs and throws (combat.grabs) #
What it is for #
combat.grabs manages the relationship between a grabber and a target: acquire, hold, escape, throw, release. A participant belongs to at most one pair, as grabber or as target. One instance handles every grab of an arena, so do not create one per fighter. The game checks range, contacts, life, immunity and permissions, then sends grab. The kit owns the pair and its clocks; the game owns the bodies, poses, collisions, health and what happens after a release. It is a driver you step yourself: there is no automatic tick. It works with combat.moveset (guide), combat.defense (guide) and combat.hit_reactions (guide); the game composes them.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "combat.grabs" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "combat.grabs",
config: { "escapeFrames": 6, "minHoldFrames": 6, "maxHoldFrames": 60, "throwDamage": 20 } }
Durations are whole fixed simulation frames (normally 60 Hz); call step once per frame.
First example #
<!-- test: kits/combat_grabs/tests/combat_grabs.rs::a_grab_starts_a_holding_pair_with_a_sequence --> The game confirms the three permissions, and the kit answers a grabbed event with a strictly increasing sequence and a pair in phase holding. The same actors cannot be grabbed twice.
{ "service": "combat.grabs", "method": "request", "args": ["default",
{ "op": "grab", "attacker": "orc", "target": "elf",
"inRange": true, "attackerReady": true, "targetGrabbable": true }] }
Later, { "op": "throw", "sequence": 1, "actor": "orc", "velocity": [0, 5, 8] } (allowed from minHoldFrames) starts the throw (throw_started); throwFrames steps later one thrown event carries the velocity and damage and releases both actors. (kits/combat_grabs/tests/combat_grabs.rs::only_the_holder_can_throw_and_only_after_the_minimum_hold)
Settings and defaults #
Fixed at creation; unknown keys are refused. Keep escapeFrames <= minHoldFrames < maxHoldFrames.
| Setting | Default | Meaning |
|---|---|---|
maxPairs | 32 | Simultaneous pairs (1 to 64). |
escapeFrames | 6 | The target may escape during frames [0, escapeFrames) of the hold (0 to 600). |
minHoldFrames | 6 | First hold frame at which a throw is allowed (0 to 600). |
maxHoldFrames | 60 | The hold times out and releases both (1 to 600). |
throwFrames | 8 | Frames between the throw request and the release (1 to 600). |
throwDamage | 20 | Damage offered at release (0 to 1,000,000). |
holdImpacts | none | Up to 32 strictly ordered { frame, damage } impacts during the hold. |
Services #
Service combat.grabs (up to 128 named instances): create(id, config), remove(id), ids() and request(id, command) with the commands:
grab { attacker, target, inRange, attackerReady, targetGrabbable }: the three booleans must be true.throw { sequence, actor, velocity }:actormust be the grabber;velocityis a world vector in metres per second, Y up.escape { sequence, actor }: target only, inside the escape window; releases both.release { sequence, actor }: the grabber lets go, no damage.interrupt { actor }: ends the pair of that actor in either role.step { frozen?, invalidActors? }: advances every pair once; listed invalid actors release their pair even when frozen.reset,status,config,save,load { save },preview { command }.
Script verbs: a request verb and an exists condition named after the kit id with dots turned into underscores (<kit>_request, <kit>_exists).
Events #
- Core events in the answer of a request:
grabbed,throw_started,thrown,released(reasonescaped,voluntary,interrupted,invalid_actorortimeout) andhold_impact(impactIndex,damage). Each names both actors and the pairsequence. - Bus events, named
<kit id>.<suffix>:.created,.removed,.changed(a decided command applied) and.refused(a tap cancelled it).
On grabbed, interrupt the moveset and defense of both actors. On thrown, give the target a launch reaction in combat.hit_reactions with a new impact sequence and apply the throw damage only if the reaction is accepted. Apply damage once per pair sequence (and impact index).
Hooks and modding #
| Hook | Effect |
|---|---|
combat.grabs.create | Before an instance is created; may change its config or cancel. |
combat.grabs.request | Before 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; constraints between bodies are the wrapper's job.
When it does not do what you expect #
grabrefused with a message naming a field: that permission was false; oralready held or selfwhen an actor is already in a pair.throw unavailable: too early (beforeminHoldFrames), not the grabber, or already throwing.- Escape fails: the escape window ended or the throw started.
Limits #
No physics constraint, no animation, no health. A load does not prove the saved actors still exist: pass missing ones in invalidActors.
Versions #
Kit 1.2.0 (modding contract), 1.1.0 (holdImpacts). Save format gripforge.motion/1.