Weak points
Usage guide of combat.weakpoints: localized armor, exposure windows and part health for bosses and monsters, the hit and resolve protocol, settings and defaults, service, events, hooks and a worked example.
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.
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 #
<!-- test: kits/combat_weakpoints/tests/combat_weakpoints.rs::armor_absorbs_first_and_the_rest_is_split_between_part_and_body --> 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.
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: <action.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 instancedefaultuses the installed settings; other instances are created with their ownconfig(or a preset).- Commands of
request:hit(part,seq,amount;seqmust rise with every hit),expose(part,seconds),restoreArmor(part,amount),step(dt),resolve(token,success),preview(command),status,config,save,load. AhitorrestoreArmoranswers anactionwith atokenand theeffectto carry out; it stays pending, and blocks other commands, untilresolve(token, success). A failed resolution drops the hit and does not consume itsseq.previewshows an outcome without a pending action. - The
effectof a hit carriesarmorDamage,partDamage,bodyDamage(the part damage timesbodyShare),disabledanddisabledCapabilities. The engine appliesbodyDamagethrough 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
opandresult; never a read or astep), combat.weakpoints.refused (a hook cancelled a command, withbyandreason). The kit listens to no event.
Hooks and modding #
combat.weakpoints.create: an instance is about to be created; a tap may change itsconfigor cancel, andcreatethen fails with the reason.combat.weakpoints.request: a decided command (everyopexcept the reads andstep) is about to apply; a tap may change thecommandor cancel, andrequestthen 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: "<id>" }) takes it (own keys win). A hook document of a game or a mod looks like this:
{ "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.
{ "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.