v0.4.8 — the Limits bound the whole district, the nine spots really are nine, and an action points at its square
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YQAJ4dND7enLj54eLyiF2C
This commit is contained in:
+44
-1
@@ -383,6 +383,16 @@ export function officeSvg(
|
||||
* offered without a word. Reported as the Depot lighting up for no visible reason.
|
||||
*/
|
||||
legal: { row: number; col: number; label: string }[] = [],
|
||||
/**
|
||||
* The columns the Limits signs stand in, drawn as the district's east and west edges.
|
||||
*
|
||||
* Omitted by the previews and by the replay viewers' single-card renders, which have no district
|
||||
* to bound. Where it is given, the boundary is drawn down the WHOLE canvas: track may not be laid
|
||||
* outside these columns at any row (§2.1), and two signs sitting on one row could not say that —
|
||||
* a player looking at open ground past a sign had no way to know nothing of his could ever go
|
||||
* there until the square failed to light up.
|
||||
*/
|
||||
limits: { west: number; east: number } | null = null,
|
||||
): string {
|
||||
const W = 166;
|
||||
const H = 96;
|
||||
@@ -447,7 +457,15 @@ export function officeSvg(
|
||||
return { x: W / 2, y: H };
|
||||
};
|
||||
|
||||
let out = `<svg class="bs" viewBox="0 0 ${width} ${height}" preserveAspectRatio="xMinYMin meet">`;
|
||||
/**
|
||||
* ROOM FOR THE BOUNDARY. The Limits edge is drawn just outside the sign's own column, and the west
|
||||
* sign is usually the leftmost card on the canvas — so at x = −1.5 the line lands outside a viewBox
|
||||
* starting at 0 and is simply not there. The margin is added only when there is a boundary to draw.
|
||||
*/
|
||||
const margin = limits ? 3 : 0;
|
||||
let out =
|
||||
`<svg class="bs" viewBox="${-margin} 0 ${width + margin * 2} ${height}" ` +
|
||||
`preserveAspectRatio="xMinYMin meet">`;
|
||||
for (const cell of cells) {
|
||||
const x = px(cell.col);
|
||||
const y = py(cell.row);
|
||||
@@ -804,6 +822,22 @@ export function officeSvg(
|
||||
`<text x="${W / 2}" y="${H - 36}" text-anchor="middle">${esc(l.label)}</text></g>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* THE LIMITS, AS THE EDGE OF THE BUILDABLE DISTRICT.
|
||||
*
|
||||
* Drawn just OUTSIDE the sign's own column, because the sign stands on the boundary rather than
|
||||
* beyond it: a siding may run under the sign, and the line has to agree with the rule or it
|
||||
* teaches the wrong one. It moves outward on its own as the Running Track is extended, which is
|
||||
* the reward for extending made visible.
|
||||
*/
|
||||
if (limits) {
|
||||
const edge = (x: number, side: string): string =>
|
||||
`<line class="bs-limitline" x1="${x}" y1="0" x2="${x}" y2="${height}"/>` +
|
||||
`<text class="bs-limitlab" x="${x + (side === 'w' ? 4 : -4)}" y="${height - 4}" ` +
|
||||
`text-anchor="${side === 'w' ? 'start' : 'end'}">LIMITS</text>`;
|
||||
out += edge(px(limits.west) - PAD / 2, 'w') + edge(px(limits.east) + W + PAD / 2, 'e');
|
||||
}
|
||||
|
||||
// Mark the Running Track so the spine of the district is unmistakable.
|
||||
out += `<text class="bs-rowlab" x="2" y="${py(runningRow) + 92}">RUNNING TRACK</text>`;
|
||||
out += '</svg>';
|
||||
@@ -914,6 +948,10 @@ export const BOARD_CSS = `
|
||||
text.bs-mod{fill:#c8a04a}
|
||||
.bs-enh{fill:#7fb0e6;font:9px ui-monospace,monospace}
|
||||
.bs-rowlab{fill:#5f6b7a;font:600 9px ui-monospace,monospace;letter-spacing:.1em}
|
||||
/* THE EDGE OF THE DISTRICT. Deliberately quiet — it is a boundary, not an action — and dashed, so it
|
||||
reads as a line on the table rather than as rail. Nothing is laid outside it (§2.1). */
|
||||
.bs-limitline{stroke:#5f6b7a;stroke-width:1.5;stroke-dasharray:3 5;opacity:.7}
|
||||
.bs-limitlab{fill:#5f6b7a;font:600 8px ui-monospace,monospace;letter-spacing:.12em}
|
||||
.bs-ghost rect{fill:rgba(90,169,230,.07);stroke:#5aa9e6;stroke-width:2;stroke-dasharray:5 4}
|
||||
.bs-ghost text{fill:#5aa9e6;font:11px ui-monospace,monospace}
|
||||
/* The caption on a legal square that already carries a card. Same blue as the ghost squares and the
|
||||
@@ -924,6 +962,11 @@ text.bs-mod{fill:#c8a04a}
|
||||
.bs-ghost,g[data-cell].bs-legal{cursor:pointer}
|
||||
g[data-cell].bs-legal .bs-card{stroke:#5aa9e6;stroke-width:2.5}
|
||||
g[data-cell].bs-legal:hover .bs-card,.bs-ghost:hover rect{fill:#233246}
|
||||
/* THE SQUARE THE ACTION UNDER THE CURSOR MEANS. White, and brighter than anything else the board
|
||||
marks, because it answers a question being asked RIGHT NOW rather than describing a standing
|
||||
state — and it has to win when it lands on a square that is already legal, focused or blocked. */
|
||||
g[data-cell].bs-point .bs-card,g[data-ghost].bs-point rect{stroke:#f2f5f8;stroke-width:3.5;stroke-dasharray:none}
|
||||
g[data-cell].bs-point .bs-card{fill:#2b3b52}
|
||||
/* WHERE THE CREW CAN GO. Amber, the colour this game spends on "you can do this", so a reachable
|
||||
card reads the same way an action button does. bs-from is where it is standing now. */
|
||||
g[data-cell].bs-focus .bs-card{stroke:#e0c060;stroke-width:2.5}
|
||||
|
||||
+1
-1
@@ -499,7 +499,7 @@ function render() {
|
||||
|
||||
$('division').innerHTML = divisionSvg(DIV);
|
||||
// The same office renderer the playable app uses, so replay and game draw one board.
|
||||
$('grid').innerHTML = officeSvg(CELLS, f.runningRow);
|
||||
$('grid').innerHTML = officeSvg(CELLS, f.runningRow, [], [], f.limits);
|
||||
|
||||
/* Auto-hide: the district is worth its space during the phases that change it, and folded
|
||||
otherwise. A summary stays, because a panel that vanishes reads as broken. */
|
||||
|
||||
@@ -356,6 +356,15 @@ export type Frame = {
|
||||
cells: CellView[];
|
||||
/** Which grid row is the Running Track — the spine the district hangs beneath. */
|
||||
runningRow: number;
|
||||
/**
|
||||
* The columns the Limits signs stand in — the district's east and west edges, at EVERY row.
|
||||
*
|
||||
* Track may not be laid outside them (§2.1), so a player looking at open ground beyond a sign is
|
||||
* looking at ground no card of his will ever go on. Two signs on one row could not say that; the
|
||||
* board draws the boundary down the whole district instead. Carried on the Frame rather than read
|
||||
* off the area for the usual reason: a remote client holds no `GameState`.
|
||||
*/
|
||||
limits: { west: number; east: number };
|
||||
/**
|
||||
* Moves left in this Local Operations turn, or null outside a switching turn.
|
||||
*
|
||||
@@ -1201,6 +1210,7 @@ export function snapshot(
|
||||
handCount: (s.decks.hands.get(viewer) ?? []).length,
|
||||
objective: objectiveOf(s, viewer),
|
||||
runningRow: area.runningRow,
|
||||
limits: { west: area.limitsWest.col, east: area.limitsEast.col },
|
||||
movesLeft: s.clock.phase === 'localOps' && turnOf(s, viewer).option === 'switch' ? turnOf(s, viewer).movesRemaining : null,
|
||||
moves: switchingMoves(s, viewer),
|
||||
blocked: impediments(s, viewer),
|
||||
|
||||
Reference in New Issue
Block a user