# Guard, parry and dodge (combat.defense) ## What it is for `combat.defense` decides what happens when an attack reaches a defender: `blocked`, `parried`, `dodged`, `hit` or `guard_break`, and how much damage gets through. It owns the guard points, the parry and dodge windows and the guard-break recovery of each defender. It owns no health, no contact detection, no animation and no knockback: the game detects the contact, sends it, and applies the returned damage exactly once. It is a driver you step yourself, not a hidden tick. It is the guard logic that `combat.duel` shares, and it works with `combat.moveset` ([moveset guide](guide:combat-moveset)) and `combat.hit_reactions` ([reactions guide](guide:combat-hit-reactions)). ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "combat.defense" } gripforge_gamekit_configure { project_id: "gkp_…", id: "combat.defense", config: { "guardMax": 100, "chipRatio": 0.1, "parry": { "active": 3 } } } ``` The kit requires `game.engine` (it is added with it). Call `step` once per fixed simulation frame (normally 60 Hz); durations are whole frames. ## First example Each defender is an instance, `default` or one created with `create`. Hold the standing guard for a frame, then submit a front mid attack: the default instance blocks it, another defender without a guard takes it. ```json { "service": "combat.defense", "method": "create", "args": ["other", {}] } { "service": "combat.defense", "method": "request", "args": ["default", { "op": "step", "guard": "standing" }] } { "service": "combat.defense", "method": "request", "args": ["default", { "op": "contact", "sequence": 1, "level": "mid", "front": true, "damage": 10, "guardDamage": 10 }] } ``` The answer holds `contact: { sequence, outcome: "blocked", damage }`. The same contact sent to `other` answers `hit`. A repeated or out-of-order contact `sequence` is refused without changing anything. ## Settings and defaults All optional and fixed when the instance is created; unknown keys are refused. | Setting | Default | Meaning | |---|---|---| | `guardMax` | `100` | Guard points (1 to 1,000,000); also restored at the end of a guard break. | | `guardRegen` | `1` | Guard points restored per eligible frame. | | `regenDelayFrames` | `60` | Frames without regeneration after a block (0 to 600). | | `guardBreakFrames` | `45` | Frames without defense once guard points run out (1 to 600). | | `chipRatio` | `0.1` | Fraction (0 to 1) of the incoming damage that passes a successful block. | | `airDefense` | `false` | Allow guard, parry and dodge while airborne. | | `parry` | `{ startup: 0, active: 3, recovery: 18 }` | Frames of the parry action. | | `dodge` | `{ startup: 2, active: 8, recovery: 16 }` | Frames of the dodge action. | ## Services Service `combat.defense` (at most 128 named instances): - `create(id, config)`, `remove(id)`, `ids()`. - `request(id, command)`: the commands are `step { guard?, action?, grounded?, enabled?, frozen? }` (`guard` is `none`, `standing` or `crouching`; `action` is a press edge `parry` or `dodge`), `contact { sequence, level, front, damage, guardDamage, blockStunFrames?, parryable?, dodgeable? }` (`level` is `mid`, `low`, `overhead`, `unblockable` or `throw`), `interrupt`, `reset`, `status`, `config`, `save`, `load { save }`, `preview { command }`. Rules in short: an active dodge avoids any dodgeable strike; an active parry stops parryable strikes from the front; a standing guard blocks mid and overhead, a crouching one mid and low; throws and unblockable strikes bypass guard and parry. A block spends guard points; at zero the outcome is `guard_break` with full damage. Script verbs: a request verb and an exists condition named after the kit id with dots turned into underscores (`_request`, `_exists`). ## Events - Bus events, named `.`: `.created` and `.removed` when an instance appears or goes away. - `.changed`: a decided command applied (`instance`, `op`, `result`); the result lists `started`, `active`, `ended`, `cancelled`, `contact` and `guard_restored` events of that request only. - `.refused`: a tap cancelled a command. ## Hooks and modding | Hook | Effect | |---|---| | `combat.defense.create` | Before an instance is created; may change its `config` or cancel. | | `combat.defense.request` | Before a decided command (not `step`, `status`, `config`, `save`, `preview`, `load`); may change the `command` or cancel. | `create(id, { preset: "name" })` reads a document of the presets collection of the kit (its id with dots turned into underscores, then `_presets`), which mods can extend. ## Engines The same Rust core runs in the web player, the native player, and as a C library and WebAssembly module for the engine wrappers. The wrappers detect contacts and apply damage; the core owns timing and rules. ## When it does not do what you expect - Contact refused: the `sequence` must grow strictly for that defender, over all attackers. - A parry does nothing against a throw: throws are handled by `combat.grabs`. - Time does not pass: nothing steps the instance; send `step` every frame (`frozen: true` pauses clocks for hit stop). ## Limits No health, no hit stop, no animation, no world coordinates: `front` comes from the game. Parry and dodge reactions on the attacker belong to the game. ## Versions Kit 1.1.0. Save format `gripforge.motion/1`.