game/Command.component — how the commander plays.
OWNED BY THE "command" SLICE. The scene's build.luau attaches exactly one of
these, to an entity named "Command", and never changes again.
Everything between the player and the referee is here:
* picking — a ray from the cursor against the 37 cell addresses, so a
click lands on a tooth of a gear rather than on a collider
somebody else owns.
* the overlay — rings drawn on the metal: where the selected automaton can
step (green), where each of those cells will have TRAVELLED
once the machine turns (violet), and where the rust will be
(red). That violet ring is the telegraph, and it is the
whole game: you are not choosing a place, you are choosing
a place one tooth from now.
* the HUD — winding per automaton, the tooth count, and the cost of the
action under the cursor BEFORE it is spent.
* the framing — the live CameraRig, retuned for a board 34 units across.
It never writes game state itself while a referee will take the call. Every
action is offered to the Director first, by name, and only applied here when
the referee exposes no verb for it:
local d = entity.find("Director").component.get("Director")
d:spend(unitId, Core.COST.move)
d:endTurn()
The camera is the scene's PlayerPrototype CameraRig, which build.luau
authored and nothing may replace — there is one camera per joining player and
a second rig breaks the moment anyone else opens the world. This component
retunes the LIVE rig at runtime and never authors one.
ent(id: string?) → any
Small helpers
| arg | type | description |
|---|
| id | string? | |
show(id: string?, on: boolean) → void
| arg | type | description |
|---|
| id | string? | |
| on | boolean | |
place(id: string?, x: number, y: number, z: number, sx: number, sy: number, sz: number) → void
| arg | type | description |
|---|
| id | string? | |
| x | number | |
| y | number | |
| z | number | |
| sx | number | |
| sy | number | |
| sz | number | |
marker(root: any, name: string, mesh: any, mat: any) → string
The overlay — rings on the metal
| arg | type | description |
|---|
| root | any | |
| name | string | |
| mesh | any | |
| mat | any | |
tuneCamera( ) → void
The camera — retuned on the live rig, once per rig
rayThrough(sx: number, sy: number) → any
Picking — a ray from the cursor against the 37 addresses
The ray through one viewport pixel, in world space. Built from the live
camera's own pose and lens rather than from a collider, so picking does not
depend on geometry another slice owns.
| arg | type | description |
|---|
| sx | number | |
| sy | number | |
pointerOverHud(ray: any) → boolean
planeHit(ray: any, y: number) → void
Where a ray crosses the horizontal plane at height `y`, or nil if it never
does in front of the camera.
| arg | type | description |
|---|
| ray | any | |
| y | number | |
pickAlong(ray: any) → any
The cell a given ray lands on, nearest the camera first, or nil.
A platform is a disc lying flat at its ring's height, so the pick is the
disc's own test: cross the ring's plane, then measure in that plane. A
ray-to-centre distance would let a raking view confuse two teeth that are
nowhere near each other on the metal.
pickAtPixel(sx: number, sy: number) → any
The cell under one viewport pixel. Public so the pick can be exercised at an
exact pixel without a mouse in the loop.
| arg | type | description |
|---|
| sx | number | |
| sy | number | |
pickCell( ) → any
The cell under the live cursor, or nil when the cursor is over the HUD or
has never moved.
automatonAt(ring: number, sector: number) → any
Reading the board
| arg | type | description |
|---|
| ring | number | |
| sector | number | |
blightAt(ring: number, sector: number) → any
| arg | type | description |
|---|
| ring | number | |
| sector | number | |
displayName(unit: any) → string
| arg | type | description |
|---|
| unit | any | |
directorSurface(d: any) → void
directorMethod(d: any, names: { string }) → any
| arg | type | description |
|---|
| d | any | |
| names | { string } | |
readVerdict(v: any) → void
ask(d: any, names: { string }, ...: ?) → void
Put a question to the referee. Returns whether it said yes, why not, and
whether it answered at all — an unanswered question falls back to this
component's own reading of the state.
| arg | type | description |
|---|
| d | any | |
| names | { string } | |
| ... | ? | |
canAct(d: any, unit: any) → void
Can this automaton act at all right now? Mirrors the referee when it exposes
the question, and reads the shared state the same way when it does not.
| arg | type | description |
|---|
| d | any | |
| unit | any | |
resolveAction(cell: any) → any
verb: "move" | "strike" | "wind" | "hold" | "select"
| arg | type | description |
|---|
| cell | any | |
applyMove(d: any, unit: any, cell: any) → boolean
Acting — offered to the referee first, applied here only if it has no verb
| arg | type | description |
|---|
| d | any | |
| unit | any | |
| cell | any | |
applyStrike(d: any, unit: any, target: any) → boolean
| arg | type | description |
|---|
| d | any | |
| unit | any | |
| target | any | |
applyWind(d: any, unit: any, target: any) → boolean
| arg | type | description |
|---|
| d | any | |
| unit | any | |
| target | any | |
applyHold(d: any, unit: any) → boolean
| arg | type | description |
|---|
| d | any | |
| unit | any | |
director( ) → any
Public surface — what the rest of the game may call
select(unitId: string) → void
Pick an automaton. Passing "" clears the selection.
| arg | type | description |
|---|
| unitId | string | |
cycleSelection( ) → void
Step to the next automaton that can still act.
reachable( ) → void
Where the selected automaton could step to, this tick. Recomputed on every
call because the machine dissolves links as it turns — never cache it.
hovered( ) → any
The cell the cursor is over, as `{ ring, sector }` — nil off the board.
pickAt(sx: number, sy: number) → any
The cell under an exact viewport pixel. Same ray the cursor uses, stated
rather than sampled, so a pick is checkable in one call.
| arg | type | description |
|---|
| sx | number | |
| sy | number | |
previewAt(ring: number, sector: number) → any
What a click on `cell` would do right now. The HUD reads this; a test can too.
| arg | type | description |
|---|
| ring | number | |
| sector | number | |
commit(action: any) → boolean
Take the action the prompt describes. Returns whether anything was spent.
| arg | type | description |
|---|
| action | any | |
clickCell(ring: number, sector: number) → boolean
Click the cell at an address, exactly as the pointer would. The whole
command chain — resolve, judge, spend — in one call, so it is testable
without simulating a mouse.
| arg | type | description |
|---|
| ring | number | |
| sector | number | |
windTarget(targetId: string) → boolean
Spend 2 to give an adjacent ally 3. Offered in the bar only where the
referee says it is allowed, so this is the same call the button makes.
| arg | type | description |
|---|
| targetId | string | |
windTargets( ) → void
Every ally the selected automaton could wind right now, as unit records.
reachableNow( ) → void
Every cell the selected automaton could step onto this tooth. The referee
owns this answer — planks and occupancy are its business, not the HUD's.
strikeTargetsNow( ) → void
Every blight the selected automaton could strike this tooth.
restartMatch( ) → boolean
Wind the tower up again: a fresh hour on the same board. The referee musters
the roster from nothing, so the interface only has to forget what it was
looking at and let the end card go.
buildView(prompt: any) → any
The view the HUD is a function of
| arg | type | description |
|---|
| prompt | any | |
signature(view: any) → string
| arg | type | description |
|---|
| view | any | |
registerScreens(view: any) → void
Screens
| arg | type | description |
|---|
| view | any | |
refreshScreens(view: any) → void
| arg | type | description |
|---|
| view | any | |
layTrail(id: string?, ax: number, ay: number, az: number, bx: number, by: number, bz: number) → void
Drawing the overlay
Lay a rod between two world points, so the carry itself is drawn.
| arg | type | description |
|---|
| id | string? | |
| ax | number | |
| ay | number | |
| az | number | |
| bx | number | |
| by | number | |
| bz | number | |
drawOverlay(hover: any) → void
| arg | type | description |
|---|
| hover | any | |
onCallback(id: string, _data: any) → void
| arg | type | description |
|---|
| id | string | |
| _data | any | |
update(dt: number) → void
| arg | type | description |
|---|
| dt | number | |
editorUpdate(_dt: number) → void
| arg | type | description |
|---|
| _dt | number | |