# Weak points and armor parts (combat.weakpoints) ## What it is for `combat.weakpoints` gives a boss or a monster body parts that each have their own armor, health and vulnerability window. A hit on a part is absorbed by its armor first, the rest is multiplied (more while the part is exposed) and split between the part and the body; a part that reaches zero is disabled and names the capabilities it takes away (flight, a weapon). The kit only computes: the engine finds which part was hit, applies the damage to the body through the combat system, then confirms with `resolve`. ## Turn it on Install the kit in a project and set its settings with the tools below; `gripforge kit api combat.weakpoints` lists every setting, and `gripforge docs combat-weakpoints` prints this guide offline. ```text gripforge_gamekit_install { project_id: "gkp_…", id: "combat.weakpoints" } gripforge_gamekit_configure { project_id: "gkp_…", id: "combat.weakpoints", config: { "maxExposure": 10, "parts": [ { "id": "core", "maxHealth": 100, "armor": 20, "bodyShare": 0.5, "capabilities": ["flight"] }, { "id": "arm", "maxHealth": 50, "armor": 0, "exposedMultiplier": 3 } ] } } ``` ## First example A boss with a `core` (armor 20, half of the damage goes to the body) takes a 60-point hit. Nothing is applied until the engine resolves the hit. ```text combat.weakpoints.request("default", { op: "hit", part: "core", seq: 1, amount: 60 }) // action.effect: { kind: "hit", armorDamage: 20, partDamage: 40, bodyDamage: 20, disabled: false } combat.weakpoints.request("default", { op: "resolve", token: , success: true }) // core: health 60, armor 0 ``` ## Settings and defaults | Setting | Default | Meaning | | --- | --- | --- | | `maxExposure` | 30 | {{s_maxExposure}} | | `parts` | `[{ "id": "core" }]` | 1 to 64 parts with unique ids; the default is one part `core` (fields below) | | `parts[]` | Default | Meaning | | --- | --- | --- | | `id` | required | the hit region id your engine's collision query returns | | `maxHealth` | 100 | {{p_maxHealth}} | | `armor` | 25 | armor points absorbed before any multiplier; they come back only through `restoreArmor`, up to this value | | `normalMultiplier` | 1 | {{p_normalMultiplier}} | | `exposedMultiplier` | 2 | {{p_exposedMultiplier}} | | `bodyShare` | 1 | {{p_bodyShare}} | | `capabilities` | `[]` | up to 16 capability ids removed when the part reaches zero health | ## Services - `combat.weakpoints` (service): `create(id, config)`, `request(id, command)`, `remove(id)`, `ids()`. The instance `default` uses the installed settings; other instances are created with their own `config` (or a preset). - Commands of `request`: `hit` (`part`, `seq`, `amount`; `seq` must rise with every hit), `expose` (`part`, `seconds`), `restoreArmor` (`part`, `amount`), `step` (`dt`), `resolve` (`token`, `success`), `preview` (`command`), `status`, `config`, `save`, `load`. A `hit` or `restoreArmor` answers an `action` with a `token` and the `effect` to carry out; it stays pending, and blocks other commands, until `resolve(token, success)`. A failed resolution drops the hit and does not consume its `seq`. `preview` shows an outcome without a pending action. - The `effect` of a hit carries `armorDamage`, `partDamage`, `bodyDamage` (the part damage times `bodyShare`), `disabled` and `disabledCapabilities`. The engine applies `bodyDamage` through the combat system and disables the capabilities itself. ## Events - combat.weakpoints.created and combat.weakpoints.removed (an instance appeared or went), combat.weakpoints.changed (a decided command applied, with its `op` and `result`; never a read or a `step`), combat.weakpoints.refused (a hook cancelled a command, with `by` and `reason`). The kit listens to no event. ## Hooks and modding - `combat.weakpoints.create`: an instance is about to be created; a tap may change its `config` or cancel, and `create` then fails with the reason. - `combat.weakpoints.request`: a decided command (every `op` except the reads and `step`) is about to apply; a tap may change the `command` or cancel, and `request` then answers `{ ok: false, cancelled: true, by, reason }` with the instance unchanged. ## Using it from content Every setting is moddable. A `combat_weakpoints_presets` document `{ id, extends?, config }` names a ready-made set of parts, and `create(id, { preset: "" })` takes it (own keys win). A hook document of a game or a mod looks like this: ```json { "id": "no_armor_repair", "hook": "combat.weakpoints.request", "match": { "op": "restoreArmor" }, "cancel": true, "reason": "no_repair" } ``` Script verbs: the action `combat_weakpoints_request` sends a `command` to an instance (default `default`) through the request hook, and the condition `combat_weakpoints_exists` tells whether an instance exists. ```json { "combat_weakpoints_request": { "command": { "op": "expose", "part": "arm", "seconds": 4 } } } { "combat_weakpoints_exists": "default" } ``` ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | A hit changes nothing | The action is still pending: call `resolve` with its `token` (the engine confirms what it applied). | | A command is refused with "part disabled", "pending" or "stale" | The part is at zero health, another action is pending, or the `token` or `seq` is old. A `seq` must be higher than the last accepted hit. | | Damage does not use the exposure multiplier | The window ran out: send `expose` again, and advance time with `step` (`dt` in seconds) every fixed frame. | ## Limits - It does not find which part was hit: your engine's collision query names the part id. - It does not damage the body, drive animations or remove abilities by itself: the engine carries out the `effect`. - It has no display module and emits no event of its own beyond the instance events above. ## Versions Kit `combat.weakpoints` 1.1.0 (since 2026-10-19: hooks, presets, events and script verbs; 1.0.0 on 2026-10-16). It runs the same Rust rules on KitHost, in WebAssembly and through a native C library.