Log inGet started
World

Fencing with Fences

Branchmain
Branchesmain
Commitc0197a6317

README.md

DuelMatch

The umpire nobody sees. One instance, on the DuelDirector entity.

Asks garden for the walled garden and the boundary fence, spawns the rival, resolves every exchange between the two Fencers through duel.resolveTouch, plants a post and drives the property line into the loser's garden on a touch, resets both fencers and runs the salute, and calls the match when the line reaches a hedge.

The boundary is the health bar: line is the fence's leading post, and the fence is rebuilt from the player's hedge out to it whenever line changes, so the score is a physical object standing in the world.

What a touch is worth

How far a post drives the line is duel.postDrive, and it is a property of the ground rather than a constant: fresh lawn in the middle of a garden gives, the last feet before a hedge are rooted and stubborn, lawn already conceded holds somebody else's posts and has to be cleared before the line passes back over it, and every post driven breaks the ground a little further for the next one. postsDriven counts the posts this bout and lastDrive reports the metres the last one bought. Between them these are what give a bout a length rather than only a variance.

Resolution

An attack whose blade is live inside its measure is an exchange. Two live blades in the same tempo is a clash: neither scores and both pay for it, which is what stops the umpire's own resolution order from deciding the bout. An attack outside its measure is recorded as SHORT or CROWDED and never touches its man, so the fencer who threw it pays the whiff debt.

A touch taken inside a riposte window plants RIPOSTE_POSTS posts rather than one, and lastPosts reports which it was.

The event bus

fxEvent names the moment and fxSeq moves on every write, including a repeat of the same event — so a reader polls fxSeq against the last one it saw rather than diffing fields for edges. Vocabulary: salute, allez, short:player, short:rival, clash, parry:player, parry:rival, riposte:player, riposte:rival, touch:player, touch:rival, post, over:player, over:rival. ":player" means the player did it.

fxQueue is the lossless read of the same vocabulary: every event since the last frame, in the order it happened. DuelMatch empties it at the top of its own update, and it runs before everything that reads it, so a reader gets the whole of this frame's list — including a touch:player and the post that follows it in the same frame, which a single slot cannot carry.

The roster

neighbour (1..3), neighbourKey ("pettifer" | "wickham" | "ashcombe") and skill are one identity written three ways. Writing any of them settles the other two off duel.NEIGHBOURS and pushes the matching style onto the live rival's FencerAI, so a garden, a turnout and a verdict can all name the same person without any of them guessing. DuelHud keeps ownership of the prose.

Fields

line, state (salute | fence | touch | over), lastTouch, winner, playerTouches, rivalTouches, skill (the rival's quality — writing it retunes the live rival), announce, lastPosts, postsDriven, lastDrive, matchClock (seconds since this bout's first allez), neighbour, neighbourKey, fxEvent / fxSeq / fxQueue, and lastExchange / lastExchangeSeq / exchanges — the tuning record, naming who struck into what and what the triangle made of it, including attacks that fell short of their reach or were smothered inside it.

Method

restart() — a fresh assize, the line back to nought. Callable at any time, including from state == "over", where the restart action also reaches it.

Pairs with DuelHud on the same entity. Requires the duel and garden modules and @builtin::modules.zinput.