---
name: ferret-combat
description: Use to make two armed characters fight in Ferret, by the player or by the AI on the same rules: Fighter, MoveSet, Duel, weapon sweeps against hit capsules (BoneGeometry, ClipPoseGeometry), blocks, parries, stamina, health, reactions, death, FighterAI levels and FighterBody in the scene.
---

# Melee combat

## When to use
A sword, axe or other melee fight between two bodies: a duel mini-game, a boss, sparring NPCs, a player against the AI.

## The model
A `Fighter` is one body's state machine (`free`, `attack`, `block`, `blockImpact`, `parry`, `react`, `stagger`, `dodge`, `dead`) driven by
commands (`move`, `face`, `run`, `attack`, `heavy`, `block`, `dodge`). The player's input and `FighterAI` give the same commands. Its
`MoveSet` names the clips; their timings (length, `hit-start`/`hit-end` notifies, additive) come from the library's clip set items. A
`Duel` steps both fighters, sweeps the striking part of a weapon inside its window against the opponent's capsules and decides parry,
block or hit, then the end and the restart. The rules are plain numbers: the hit shapes come from a `BodyGeometry` (`BoneGeometry` in the
scene, `ClipPoseGeometry` in Node).

## Steps
1. Make the clips: attacks that start and end in the guard, with `hit-start`/`hit-end` notifies; a block loop, a block impact, reactions,
   deaths, walks (`ferret-animation`, `edit.clip.*`).
2. Measure the body: capsules from the skinned mesh (`tools/fight-clips/capsules.mjs`), the weapon's grip and striking part
   (`tools/fight-clips/grip.mjs`), into a `BodySpec`.
3. Build each `MoveSet` from the clip timings of the bound clip sets; one `Fighter` per body.
4. `new Duel([a, b], geometry, { starts, restartAfter })`; measure each attack's reach on the body (`attackReach`, or on the bones). Damage from outside the duel (fire, a trap): `duel.scorch(fighter, damage)`; without `restartAfter` the game calls `duel.restart()` itself.
5. In the scene: one `FighterBody` per body (`update` before the animations), then `duel.step(dt, [cmdA, cmdB])` after the animations
   and IK. A player's command comes from input, the AI's from `FighterAI.think(dt)`.
6. React to the events (`hit`, `blocked`, `parried`, `over`, `restart`): HUD, sparks, sound.

## Check afterwards
Run fights unattended in Node with `ClipPoseGeometry` (as `packages/combat/test/demo-duel.test.ts`): every fight ends, blows land only
inside the windows, `bodiesOverlap()` stays 0, both sides win, a harder AI wins more. In the browser draw the shapes (`?capsules` in
`examples/fight`) and see that the red striking part meets the green capsules where the blade meets the body.

## What usually goes wrong
- Scene and combat frames mixed: a scene point is `(-X, Y, Z)` in combat and the yaw is `-rotation.y` (`fromScene`, `toScene`).
- Unreal additive clips (hit reactions, leans) played as poses fold the body to a point: they must be marked `additive` in the clip set
  and layered on the guard.
- Hit capsules read before the frame's animations and IK: the rules see last frame's pose.
- A reach guessed instead of measured: the AI stands too far to land or too close to swing.
- One AI random source for both fighters: give each its own `seededRandom` so a seed replays the same fight.

## Files

- `reference/combat.md` — Fighters driven by the player or the AI by the same rules, weapon sweeps against hit capsules, blocks, parries, stamina, health, reactions and the end of a fight.
- `reference/api-combat.md` — every export of `@ferret/combat` with signature and doc comment
