Log inGet started
World

Fencing with Fences

Branchmain
Branchesmain
Commitc0197a6317

README.md

Fencer

One combatant in the fence duel. Attaches to a bare entity — it asks fencer_body for its rig in awake, so it needs no Model or mesh of its own.

Owns footwork along the piste (world X, depth and height held) and the windup → active → recover state machine behind the four commitments: thrust, sweep, lunge and parry. Every commitment carries the body over its own advance, so distance and commitment can be made in one action; the step stops at the fencer's own minReach rather than carrying it inside its own measure.

Footwork is bounded by the other fencer, not by the property line: a man who has drawn back into his own garden is still reachable, which is the whole reason to hold the measure. Breaking ground is slower than taking it.

A commitment that reached nobody — short of its reach, or crowded inside it, or a guard that turned nothing aside — enters recovery from a debt. So does one that was parried. The debt is the commitment's own (duel.whiffOf / duel.parriedOf), so it scales with what the commitment bought: a thrust turned aside costs half a second and a lunge turned aside costs the better part of two. That debt is the opening the other fencer is meant to punish, and phaseTime < 0 in recover is how a reader tells a real opening from an ordinary recovery.

The lunge is recovered to the line it was thrown from. It is the one commitment marked recoil: whatever of its 1.30 m of advance it managed to cover, it gives back over its own recovery, travelling backwards at the pace it went out and unable to answer anything while it does. recoilLeft is the ground still owed. So the lunge buys half a metre of reach nothing else has, and buys no position at all — which is what stops it being a free way to close the measure.

A commitment made while riposte is open comes out at RIPOSTE_QUICK of its wind-up, reaches RIPOSTE_REACH further, cannot be turned aside, and plants RIPOSTE_POSTS posts instead of one.

The component is input-agnostic except for one case: a role = "player" fencer on a local entity reads the move axis and the thrust / sweep / lunge / parry actions itself. A role = "rival" fencer is driven by FencerAI through the same public methods a human's input reaches.

Fields worth reading from outside: role, action, phase, phaseTime, riposte, ripostePending, whiffed, frozen, touches, facing. action is "" while idle, not "guard"; the idle phase is "guard".

Methods: commit(name), walk(dir, dt, line), resetTo(line), receiveTouch(), beParried(), clash(), openRiposte(), markContact(), freeze(on), isStriking(), currentReach(), currentMinReach().

Requires the duel and fencer_body modules and @builtin::modules.zinput. DuelMatch umpires between two of these; it is what calls receiveTouch / beParried / clash / openRiposte / markContact.