The cockpit learns to aim: marked beings, verdict-bearing crosshair
While a targeted card is armed, wizards and creatures standing on castable ground wear a pulsing ember ring at their feet — drawn behind the body, only when the walls actually show it, and only when the card could land on a being (creations still light floor alone). The hover cue now carries a verdict: gold outline and bright name for what the cast can reach, cold slate for what stands beyond it. The workshop poses it all: ?aim=1 arms a pretend cast and ?rival=x,y stands the Rival wherever the shot needs them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
co-authored by
Claude Fable 5
parent
afa0e17483
commit
b996c2bc93
@@ -24,6 +24,7 @@
|
||||
rubble = [],
|
||||
ontarget,
|
||||
litCells = null,
|
||||
aimBeings = false,
|
||||
edgeSelect = false,
|
||||
}: {
|
||||
view: GameView;
|
||||
@@ -46,6 +47,9 @@
|
||||
/** Squares a selected cell-target card may aim at: the pane dims the
|
||||
* ineligible ground exactly as the board dims its squares. */
|
||||
litCells?: Set<string> | null;
|
||||
/** The armed card could land on a wizard or creature: beings on
|
||||
* castable ground wear the ember ring. Creations light floor only. */
|
||||
aimBeings?: boolean;
|
||||
/** An edge-target card is raised: every wall and door face glows
|
||||
* faintly, the pane's answer to the board's edge handles. */
|
||||
edgeSelect?: boolean;
|
||||
@@ -454,6 +458,32 @@
|
||||
: null);
|
||||
const iw = img instanceof HTMLImageElement ? img.naturalWidth : (img?.width ?? 0);
|
||||
const ih = img instanceof HTMLImageElement ? img.naturalHeight : (img?.height ?? 0);
|
||||
// A being standing on castable ground wears an ember ring while a
|
||||
// targeted card is armed: the cockpit's answer to the board's lit
|
||||
// squares, drawn behind the body so the art stays clean. Only a
|
||||
// sprite the walls actually show earns its ring.
|
||||
if (ontarget && aimBeings && litCells && s.hit && s.cell &&
|
||||
litCells.has(`${s.cell.x},${s.cell.y}`) &&
|
||||
!(s.hit.kind === "player" && s.hit.id === view.you)) {
|
||||
let seen = false;
|
||||
for (let col = Math.max(0, s.left | 0); col < Math.min(W, s.right); col++) {
|
||||
if (spriteVisibleInCol(s, col, zbuf, warpIdCol, warpDistCol)) { seen = true; break; }
|
||||
}
|
||||
if (seen) {
|
||||
const w = s.right - s.left;
|
||||
const cx = (s.left + s.right) / 2;
|
||||
const pulse = 0.55 + 0.25 * Math.sin(time / 220);
|
||||
ctx.save();
|
||||
ctx.strokeStyle = `rgba(232,160,60,${pulse})`;
|
||||
ctx.fillStyle = "rgba(232,160,60,0.16)";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, Math.min(H - 2, s.bottom), Math.max(7, w * 0.42), Math.max(3, w * 0.13), 0, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
// Painted art composites normally (inks stay true); light glows
|
||||
// additively. Either way translucency applies.
|
||||
if (s.glow) ctx.globalCompositeOperation = "lighter";
|
||||
@@ -594,9 +624,20 @@
|
||||
if (!f || !mouse) return;
|
||||
const found = resolveHover(mouse.x, mouse.y);
|
||||
if (!found) return;
|
||||
// The cue carries a verdict when a targeted card is armed: gold for
|
||||
// ground the cast can reach, cold slate for what stands beyond it.
|
||||
const t = found.target;
|
||||
const tCell = t.kind === "cell" || t.kind === "edge"
|
||||
? t.cell
|
||||
: t.kind === "player"
|
||||
? view.players.find((p) => p.id === t.id)?.position
|
||||
: view.creatures.find((c) => c.id === t.id)?.position;
|
||||
const eligible = litCells == null ||
|
||||
(t.kind === "edge" && edgeSelect) ||
|
||||
(tCell != null && litCells.has(`${tCell.x},${tCell.y}`));
|
||||
ctx.save();
|
||||
ctx.strokeStyle = "#c9a72a";
|
||||
ctx.fillStyle = "rgba(201,167,42,0.14)";
|
||||
ctx.strokeStyle = eligible ? "#c9a72a" : "rgba(150,150,155,0.55)";
|
||||
ctx.fillStyle = eligible ? "rgba(201,167,42,0.14)" : "rgba(120,120,125,0.10)";
|
||||
ctx.lineWidth = 1.5;
|
||||
let label = "";
|
||||
if (found.shape.kind === "rect") {
|
||||
@@ -643,7 +684,7 @@
|
||||
const ly = Math.max(16, mouse.y - 8);
|
||||
ctx.fillStyle = "rgba(13,12,18,0.85)";
|
||||
ctx.fillRect(lx - 4, ly - 12, wTxt + 8, 16);
|
||||
ctx.fillStyle = "#e9e1cb";
|
||||
ctx.fillStyle = eligible ? "#e9e1cb" : "#8a8a90";
|
||||
ctx.fillText(label, lx, ly);
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
Reference in New Issue
Block a user