Fencing with Fences
Commits
Every published commit of this world, on every branch, newest first.
c0197a6317the-roster: un-invert the ramp, close the exploit with a rule, bound the bout
---
the-roster: un-invert the ramp, close the exploit with a rule, bound the bout4b3f2eaa50the-shell: a front door a played world cannot lock out, and a sitting that can be started fresh
The writ is raised from this component's own boot flag and from nothing that
persists. The sitting lives in a module-local table and the seven public fields
are written FROM it every tick, so the layer overlay restoring a docket counter
seconds after a load is undone inside a frame. assizes stops being a replicated
gate. Arriving in the middle of somebody's live bout raises the writ and leaves
their fight alone.
N rises the court from anywhere - mid-bout, on a verdict, on the court-rises
card - clearing the docket, putting the boundary to nought and naming the first
neighbour. It asks the scheme for a 'rise' action first and falls back to the
raw KeyN code, so declaring one in fencing_controls needs no edit here.
Three defects found by playing and fixed:
- A click on a neighbour's tile fenced the wrong neighbour. playerImpatient()
asked the action layer whether any commitment was pressed, and the scheme
binds the LEFT MOUSE BUTTON to thrust, so every pointer landing on the writ
read as a commitment and opened the standing assize under the tile that was
clicked. It now reads keys_pressed against each action's key bindings only.
- The card's button could half-restart the game. restart() rebuilds the garden,
the fence and the rival; run from onCallback it outran the 1000 ms script
watchdog and was cut off with the board at nought-all and state still 'over',
while clickElement reported ok. onCallback now records what was asked and the
component's update serves it, and a board that reads 'over' with nobody named
is a restart that did not finish - no card, no docket entry, asked again.
- assizes counted only the assizes the shell itself opened.
Verified by playing: two cold arrivals into a played world, two full six-assize
sittings from the writ to the court-rises card and back, all six neighbours
reached by name, four fresh sittings called from a bout and from a verdict, ten
verdicts answered by KeyR and by the card's button. Shell coverage unchanged at
top 5.3-9.2%, bottom 9.4%, painted 7.9-8.6% of a 1280x720 frame.1e5984549athe-roster: six neighbours, six manners, and the ceremony fields65f522211ccontact-and-ceremony: one frame of a touch says who struck whom, and the umpire stops repeating himself
The broadcast holds its side-on framing for every frame the two are fencing -
contactPct is 0 through all of state == fence and fencerFramePct never leaves
39.28781 there - and takes the umpire's own stop to make contact readable. For
the length of that stop the station swings 19 deg off the piste axis toward the
fencer who took the touch, cranes 0.22 m, closes to a distance solved from the
pair's own span, and holds 0.42 s past the stop. Every dial is a fraction or an
offset of the shot's own dials, because scene.json carries an authored
PisteCamera prop block and a station fixed in metres is a different shot in
every scene that loads. Over 120 samples of live play: fencerFramePct mean
40.575 (min 39.288) and lawnBelowPct mean 14.685, against 39.29 / 15.80.
A landed stroke leaves the panel's whitewash in the air for the length of the
hold, and the shape it leaves is which stroke it was: a thrust one level line,
a lunge a long low one off the back foot, a sweep a falling quarter circle, a
riposte the crossed guard and the answer out of it. It runs from the striker to
the struck, so which way the blow went is in the picture. Beside it,
contactHold pins recoil, spin, lean, overreach and jam at the values the blow
gave them, because a reaction that decays in 0.3 s is gone inside one frame of
this build.
The post is driven rather than moved: the surveyor's cord walks from where the
line stood to where it now runs, carrying a post held clear of the turf, and
three mallet blows seat it flush with the boundary's own post.
Fifteen new umpire calls through a new Fx.call, which writes a call as a list
of syllables rather than a stack of primitives. Each ceremony beat draws from
its own bag, narrowed by the bout: the score only when it is level, the last
post only when one post ends the assize, arret and touche only on a ground that
stands on form. Over one full bout of 67 lines the largest share is 13.4 %.
The bell counts one strike to open and three to close, both men breathe in the
pause, and each of the six neighbours has their own throat.
Measured: two KeyR-driven bouts to a verdict, engine.timeScale sampled 891 and
101 times, badRecovery 0 in both and max 1.0; busProduced - busTaken 0 on the
second. DuelFx 0.60 -> 0.91 ms and PisteCamera 0.23 -> 0.30 ms, medians of
eight samples each under the same load.46ea9ea9ddthe-garden: the garden takes light, and there are six of them
lib.hedge_leaf writes a real PBR G-buffer, so pass:albedo and pass:final differ
completely on every hedge, topiary, orchard and treeline surface and
pass:roughness reports a value instead of magenta. Every foliage material is
authored against a lit surface rather than an emitter, and the same hedge run
with the sun behind it is visibly darker than with the sun on it.
lib.garden_turf is new: the lawn is a lit surface whose mown bands wander
instead of meeting along a ruler, with a clump register for the sward, a blade
register so it does not go flat close up, and a wear register for the ground
either side of the piste. Ten lawn materials moved onto it, each carrying its
own roller width - 1.65m and almost no wander for the Major's parade ground,
7.5m and 3m of wander for a field mown twice a year. The stripe slabs and the
stripe style fields are gone with it.
Three more grounds and three more turnouts: Sixpenny Nursery, the Old Rectory
and Gallows Piece, with Mr Quilty, the Revd. Brasenose and Young Tibb. Mrs
Pettifer is a bottle-green bell under a wide pale brim now, because straw over
straw against a player in whites is one figure and not two.
The counter-fill is removed. A second directional light contributes nothing to
the shaded result - the same frame at fill 0.0 and fill 8.0 is pixel-identical,
filed as #8139 - so the shade side was always ambient alone, and the rig says
so now: one key and one hemisphere, five moods re-solved on it, and the shadow
re-arm hack that existed only to survive the second light went with it.
The treeline stands at 44m instead of 31m in eleven irregular crowns, because a
9m crown at 31m is twenty degrees of backdrop before anything in the game is
drawn. GardenDressing's repair loop runs twice a second instead of five times:
0.07ms a frame against 0.52ms.ba7a991685round 3 review: verdict good-base, expand+polish spec for round 4
Played 15 bouts to a verdict through the declared bindings, 8 back-to-back
restarts, 2 cold reloads and a 4-pass diagnostic sweep. Confirmed round 2's
one-button fix independently (4 of 4 closing one-button bots lost, one of them
18-13 on touches and still -10 on ground), the 33-41s passivity floor, the
no-leak restart, three grounds and three turnouts, 1.80ms total component cost
and 0 LSP diagnostics across 12 files.
Caught two reported-as-landed items that did not: lib.hedge_leaf writes no PBR
G-buffer (pass:albedo == pass:final on every foliage surface, roughness magenta)
so the largest surface in the game takes no light; and DuelHud.assizes is a
persisted Sync counter gating the writ, so the opening is permanently
unreachable and a cold layers.load returns a mid-bout board.
expand-r3.md splits round 4 into four parallel sections with disjoint paths:
the-garden, the-roster, the-shell, contact-and-ceremony.938bac12cfplaytest r2: re-checkpoint the edited sources after the garden rebuild5f0d6bdbc5playtest r2: close the triangle, de-fizz the garden, make the neighbours' grounds reachable922fa3fa01the-fight: round-2 journal entry
What landed, what was measured, and the two things that were not. The sweep is
now the one button that solves the game and the reasoning for it is written out,
because the guard that beats the other three is the one answer the triangle
forbids against a broadside.2c33888ad5impact-and-audio: journal note for card-and-ceremony on the scene.json rewritea47b9ee734impact-and-audio: correct the frame-cost claim to a measured range
zero profiler scripts prints last and avg as the same number and --reset does
not reset it, so every reading from that tool is one frame. The first write-up
quoted two single samples as a 3x improvement. Replaced with ten samples over a
live bout: median 2.47 ms, 0.62-0.70 ms in frames where nothing resolves,
2.5-4.4 ms in the frame an exchange lands, on an arena that has grown from 173
to 244 entities since the baseline. The whole-frame number is the one that did
not regress: p50 100.97 ms against 103.02 ms.a3d0174fffthe-fight: the panel goes up at a blade, not at a man
The last source of the losing line. A guard was still being offered to an
opponent standing at guard - by the weighted choice and by the old 'close the
line' rule, which existed because a 0.22 s tell used to be unanswerable. It is
answerable now that the read is latched, and a pre-emptive guard is strictly
worse than a read one: the opponent chooses when to start, so the window closes
before he does, and he attacks into a panel that has already come down. That is
'lunge vs parry/recover -> touch', which was 12 of 23 exchanges in the last
measured bout.
The panel now goes up only at a blade already on its way. Against a man who
does nothing but attack, the neighbour holds the edge of his measure and waits
for the wind-up.
Measured after the change, one-button lunge bot vs skill 0.5: lost 0-5, nine
exchanges, four lunges turned aside and four ripostes taken off them. No
occurrence of a guard being beaten in its wind-up or its recovery.c26f4de9e0impact-and-audio: four commitments that land differently, a lossless bus, and a lawn with places in it
The outline flash drew as an orange rectangle in the grass because a fencer is
eighteen separate boxes and every one of them was rimmed. A per-model rim on a
part that stands ON the lawn draws its rim IN the lawn, and the fence panel drew
a rectangle in the air beside the man. The flash marks the body now, skipping
what a fencer stands on and what they are holding, by name - a rig part's
position and scale are rewritten every frame by fencer_body.pose, so a
geometric gate answers differently depending on which frame you ask it. Over 12
seconds and five touches the only models that ever carried a rim were the
eleven body parts; panel, shoes, stockings and legs never once.
One PROFILE row per commitment drives recoil, spin, shove, hitstop, camera,
dust and sound. Only a sweep turns the struck fencer off the line and lands on
panel_crunch; only a lunge gives up three times the ground and tears a grit
trail from the back foot to the contact; a thrust is a narrow plume and is
over; a riposte rings. Which one landed is parsed out of lastExchange, so a
fifth commitment gets the default weight and says so rather than being
mis-sold.
SHORT and CROWDED never reach the bus - the umpire writes their record and
returns - so fx derives both. Their feedback is built from the opposite parts
of the vocabulary from a touch on purpose: the reaction is on the attacker,
there is no hitstop, no flash and no rim, and the shape left behind is a flat
scuff disc lying on the piste.
The reader drains fxQueue and counts fxSeq against what it took, so the
difference is exactly what never arrived: 990 missed before this round, 0 over
two clean bouts driven by real key presses.
Twelve new synthesised clips give the lawn places. The player's orchard at the
west end and the neighbour's walled end at the east crossfade on where the
fight has got to; the bell hangs on the garden wall and the wall gives it back
0.40 s later; the umpire walks the piste and has a voice, built from a new
formant primitive. Footfall knows the raked gravel of the piste from the spoil
thrown out around a driven post. probe_tone is gone.
Everything was tuned at a measured 104 ms frame, 9.6 fps, and every window is
in real seconds so it survives the frame rate coming up. DuelFx costs 0.48 ms
under driven play against 1.56 ms at the start of the round.c64e401de5light-and-shot: journal addendum — the zm failures that cost this section its own commit (#8092, #8093)53b992b599the-fight: the neighbour reads the wind-up, and never spends a guard on an empty tempo
Three defects the played logs turned up, all of which let a one-button lunge win.
- The read was being lost to frame sampling. It gated on the opponent's
phaseTime crossing a reaction threshold on some frame, and a 0.30 s wind-up
seen at 10 fps offers two samples - so at reaction 0.24 s the answer to a
lunge could not fire at all. The eye now latches onto a wind-up when it
appears, rolls the read once, and answers when it has had its time. The
threshold is 0.20 - 0.11 * skill, shorter than every tell it is meant to
beat; the difficulty lives in whether the read is taken, not in whether it
was physically possible.
- A guard was being thrown at a man who could not attack. Raising the panel
during an opponent's recovery spends its window on an empty tempo, and the
attack that follows meets nothing - which is exactly the losing line the log
kept showing, 'lunge vs parry/recover -> touch'. The guard is now offered
only when there is something it could turn aside, and against a proven
attacker the neighbour holds its measure at the edge of his reach and waits
for the wind-up rather than committing into his recovery.
- idleFor counted a man in the middle of a commitment as idle, so the courtesy
meant for a passive opponent was firing at one who was attacking. Only a
fencer standing at guard with nothing thrown is offering nothing.8be22a2dacgarden-and-surfaces: drop the authoring probe and the superseded treelines
probe_magenta was the three-cube test that proved a material authored during
play does reach the GPU through the file path. treeline_far/warm/cool were the
hedge-shader version of the estate beyond the wall; beyond_far/warm/cool replaced
them with a pbr material carrying an emissive floor, because a backdrop that is
lit by the garden's own sun is a black slab at whichever end of the lawn is
turned away from it.d44017adbelight-and-shot: the frame is spent on the fight, and both ends of the piste read alike
The shot is solved against the 60 deg frustum the renderer actually draws rather
than the one Camera.fov reports. lookHeight is the dial that decides the dead
lawn - the margin under the feet is exactly the frame not spent on the world
between the ground and the point the camera aims at - so aiming at the mask
instead of the chest takes the empty grass from 33.6 % to 15.8 % while the
fencer grows from 27.9 % to 39.3 %. Over 60 samples of live play the shot sat
at minDistance for every one of them.
The garden is lit by a mirrored pair. A single sun with a large component along
the piste lights one end of a side-on arena and backlights the other by
construction, so the game got harder to see as you lost. Key at azimuth A, fill
at 180 - A: reflecting an azimuth across the Z axis flips its X component and
keeps its Z one, so the pair cancels along the piste while both still come from
the camera's side of the hedge. End-to-end contrast falls from 2.86:1 to 1.72:1.
GardenDressing.MOODS ships five complete rigs - morning, noon, overcast,
gloaming, frost - and a public mood field chooses one. A mood sets key, fill,
ambient, sky, bloom, tone and haze by writing the live dials, so hand-tuning
afterwards still works. A name that is not a mood leaves the garden exactly as
it was and logs nothing. Every mood holds its exposure inside 1.22-1.34, because
the HUD is composited before post-process and is tonemapped with the scene.
One reflection probe over the piste gives the brass and the two masks the garden
to reflect instead of a sky they cannot see; without it the same mask is a flat
unlit dome.
Both components held their subjects with a full entity scan every frame, which
on a tripled arena had made PisteCamera the second most expensive script in the
world at 4.23 ms. Held by id now: PisteCamera 1.70 -> 0.21 ms, GardenDressing
1.08 -> 0.06 ms.
Adding a second directional light silently drops the scene's shadow map and
removing it does not restore it (#8089), so the fill is written only when a
value has moved and the shadow is re-armed across two frames when it has.
---
garden-and-surfaces: the lunge carries a panel, not a beam
At 3.10 m long and 0.30 thick the lunge's active pose read as a plank driven
through the frame rather than as a section of fence at full reach. Shorter,
deeper and a shade more square: still the longest reach of the four
commitments, still the flattest panel, but recognisably the same object the
fencer was holding a moment earlier.fbe22d3204garden-and-surfaces: four grounds, four turnouts, and a lawn that remembers
Garden.build(style) + Garden.STYLES and Body.build(owner, role, kit) + Body.KITS
ship four grounds and four turnouts for the same rite. Both are sticky per
caller (DuelMatch and Fencer rebuild with no argument every restart) and an
unknown name falls back to the standing one rather than erroring.
- assize keeps the neutral ground; pettifer is blossom, orchard and bee skeps
with cottages beyond the gates; wickham is cool clipped yew, pennants and
poplars; ashcombe is dark ancient topiary, a peacock, stone terrace and the
Hall standing at both ends. The plan, the piste and the dark band a fencer is
read against never move - only palette, skyline and furniture.
- Every turnout changes the SILHOUETTE, not just the palette: a brim, a shako, a
coat to the ankle. The player is unchanged in every assize by construction.
- The lawn is the score, so it carries the history: settled turf grown back,
four fresh bays brightening toward the line, a spoil ring per recent post
greening over with age, and a leading edge cut in three steps.
- The back hedge was wearing a full aerial-perspective step six metres behind
the fight. It rejoins the hedge family; the old values move to a treeline
thirty-one metres out that ends the lawn in every direction.
- Flat geometry is shelved ten millimetres apart, which is what the diagonal
seam across the annexed lawn actually was.
- The gate plates, the finial and the bell leave metallic behind, so they stop
being black holes cut in a whitewashed pier.
- The paling stops short of the line, so the boundary post the two fencers
straddle reads as a post and not as a crate.
- Every large surface re-authored under the bloom threshold, and turf and gravel
given roughness values that are actually different.ecfb9f2fd2card-and-ceremony: journal for round 2adffdc9f52card-and-ceremony: edge-anchored shell, a verdict that shows the board, and the docket
---
the-fight: the guard answers from its first frame, and a post is worth what the ground is
The lunge stops being the one button that solves the game, and the bout gets a
length that was chosen rather than inherited.
- resolveTouch treats a parry in windup exactly as one in active. A guard that
only worked once it had finished rising lost to any attack thrown from further
out than the wind-up is long, so a correct read lost to a committed attack -
the one outcome the triangle exists to prevent.
- The lunge is marked recoil: whatever of its 1.30 m advance it covered, it
gives back over its own recovery, travelling backwards and unable to answer.
It buys reach; it no longer buys position.
- whiff and parried debts are per commitment rather than one constant, so the
price scales with what was bought: a lunge turned aside costs 1.20 s. A guard
that turned nothing aside is an opening of its own, at 0.30 s.
- postDrive replaces the flat POST_STEP. Fresh lawn gives, the rooted feet by a
hedge do not, ground already conceded holds somebody else's posts, and every
post driven breaks the ground further. Rooted ground stops a garden falling to
a short run of luck; conceded ground stops two even fencers walking the line
back and forth all afternoon.
- duel.STYLES gives the three neighbours three manners of fencing rather than one
difficulty float: Mrs Pettifer stands off and waits, Major Wickham plays the
triangle's answer to a repeated stroke before it is thrown, Lady Ashcombe
fences hardest with her own hedge behind her.
- A neighbour waits for the invitation. An opponent who offers nothing is
indulged for as long as that neighbour's form requires, so passivity is still
punished but no longer instantly.82609e7f7ethe-fight: ship fxQueue, matchClock, neighbour, neighbourKey
The four fields the other sections are blocked on, ahead of the tuning.
- fxQueue is the lossless read of the frozen event vocabulary: DuelMatch
empties it at the top of its own update, before anything that reads it, so
a frame's whole list survives. The touch and the post it fires in the same
frame both arrive, which is what the one-slot bus drops.
- matchClock runs from the bout's first allez and stops at the verdict.
- neighbour / neighbourKey / FencerAI.style are one identity written three
ways. Writing any of index, slug or skill settles the other two off the
roster in duel.module, so a garden, a turnout and a verdict can name the
same person without guessing.5befd21c00design: round-2 review + expand-r2 spec (five parallel sections)0dc1071bfeplaytest r1: kill the phantom drivers, fix the placeholder ground and crushed lighting, frame against the fov the renderer actually uses7c7758c3ebduel-depth: four commitments with measure bands, a lunge, a riposte worth two posts, whiff punish, clash, and a rival that keeps a bookabb2b0e3c9feel-and-sound: the turf puff returns to zero and blooms instead of shrinking
The puff stopped being written once its life reached zero, so it kept the last
live frame's scale and a full-size dome sat on the lawn from the first post
onward. The fade now rides in the scale itself, and holds full size while the
puff spreads so it blooms rather than collapsing on frame one.38bd3231a1feel-and-sound: impact, camera reactions, and a synthesised sound bed
DuelFx turns the umpire's verdicts into something you feel: recoil, shove and
an outline flash on the struck fencer, follow-through on the striker, a contact
bloom, a turf puff when the post goes in, and a hitstop that always releases.
PisteCamera now frames by extent rather than by separation, so both fencers and
the boundary post stay in shot at the nearest distance that covers them. A
fencer reads 38% of frame height at measure, up from 31%, and 22% in the worst
case, up from 18%.
fx.module carries the event source over the umpire's bus (with a derivation
that recovers the events its one slot coalesces), the shared easing, and an
offline synthesiser. Twelve soundClips under /source/audio are rendered by it
from sines, noise and envelopes: the assize bell, panel on panel, the touch,
gravel underfoot, a tell for each of the four commitments, the riposte ring,
the post going in, a whiff, and the garden's own nine-second loop.494bebf1e9assize-shell: journal entry and the scene rewritten by the play-edit promotion (11 authored entities, unchanged)973a4dc182assize-shell: the umpire's card, the read strip and the way to go again
DuelHud becomes the whole shell around the fight: a compacted umpire's card
carrying the call, the boundary and the score; a read strip that names each
fencer's commitment the moment its wind-up starts and flags an open riposte;
and a measure gauge whose zones are computed live from Duel.ACTIONS. The
assize card carries the writ, the roster of three neighbours (which is the
difficulty choice) and the verdict, so a finished match is no longer a dead
end. The scene entrypoint attaches GardenDressing and DuelFx to the director
and keeps looking until both are on.c0cc8f612cgarden-and-light: the sunlit garden, real materials, and a silhouette that reads
Ships garden.module and fencer_body.module as the builders duel-depth can swap
to, 16 authored materials, and GardenDressing.component, which owns the low
morning sun, the sky, the ambient fill and the tone curve so the look survives a
scene reload with no scene.json edit.
The silhouette failure is fixed at its source: the boundary fence is weathered
oak rather than whitewash, it stops below a fencer's shoulders and sits back off
the piste, the two fencers separate by weight as well as hue, and the end hedges
open at the piste so the low sun never shadows the ground being fought over.7281d746fcreview: round-1 verdict and the expand + polish spec for the parallel phasee8423b152djournal: log the publish-gate frictioneb84fe3bdcbase: playable loop — the Hedgerow Assizes fence duel end to end
Scene, both fencers, controls, camera, core loop and win/lose. Placeholder
primitives and untuned numbers throughout.
- duel.module — the rules in one file: ground plan, frame data for thrust /
sweep / parry, the triangle (resolveTouch), and the placeholder palette.
- Fencer.component — one combatant: builds its own primitive body, footwork
locked to the piste, windup/active/recover state machine. The panel's pose per
phase is the tell. role=player reads input; role=rival is driven by FencerAI
through the same methods.
- FencerAI.component — the neighbour: closes the measure, reads a wind-up after
a reaction delay it cannot beat on a thrust, holds guard, takes its riposte.
- DuelMatch.component — the umpire: builds the garden and the boundary fence,
spawns the rival, resolves exchanges, plants a post, drives the line into the
loser's garden, runs the salute, calls the match at either hedge.
- DuelHud.component — the umpire's card.
- PisteCamera.component — the locked side-on broadcast view, on the
PlayerPrototype's CameraRig.
- fencing_controls.inputMap — A/D footwork on the canonical move axis, J thrust,
K sweep, L/Space parry, with touch buttons synthesised.
The fence runs ALONG the piste and its leading post IS the property line, so the
health bar is a physical fence growing across the lawn and the won ground
brightens behind it.
Validated in a running engine: two full matches to a player win driven with
synthetic input, both terminal branches (player and rival) reached, parry into
riposte into touch observed in play, clear-and-reload rebuilds the whole arena,
scene.json holds exactly 11 entities, zero component errors.
---
docs: describe the fencing_controls input map so the world is publishable56c004b1dcconjure: Fencing with Fences