The closing shot finds something worth looking at

A reel that ends nose-to-brick ends badly — especially recorded. On
the final step the idle-gaze rescue raises its standard (a wall inside
1.2 cells is a bad last frame) and scores the four cardinals by more
than corridor depth: a visible wizard or creature weighs most, loose
treasure half, each behind a real line-of-sight check. The camera
turns once to the best view and only if one clearly beats what it has
— a wizard walled in on every side keeps its poor view rather than
hunting forever.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-23 19:14:25 -04:00
co-authored by Claude Fable 5
parent 765828700d
commit 59c25f42f0
+33 -8
View File
@@ -294,14 +294,39 @@
aimed = true;
}
}
if (!aimed && castRay(v, tx, ty, targetFacing).dist < 0.8) {
// Nothing aims the eyes and they'd idle nose-to-brick (the opening
// cut, or a beat after pacing into a dead end): face the deepest
// corridor from where the wizard stands instead.
let deepest = -1;
for (const a of [0, Math.PI / 2, Math.PI, -Math.PI / 2]) {
const h = castRay(v, tx, ty, a);
if (h.dist > deepest) { deepest = h.dist; targetFacing = a; }
if (!aimed) {
// Nothing aims the eyes. If they'd idle nose-to-brick (the opening
// cut, a dead-end beat — or the reel's CLOSING shot, which gets a
// higher standard), find a better view: the deepest corridor, and
// on the final frame, preferably one with something in it.
const viewScore = (a: number): number => {
let score = Math.min(castRay(v, tx, ty, a).dist, 8);
if (!atEnd) return score;
const bonus = (cell: { x: number; y: number }, worth: number) => {
const bx = cell.x + 0.5, by = cell.y + 0.5;
const bearing = Math.atan2(by - ty, bx - tx);
if (Math.abs(shortestArc(a, bearing)) > 0.55) return 0;
const d = Math.hypot(bx - tx, by - ty);
if (d < 0.1) return 0;
const sight = castRay(v, tx, ty, bearing);
return !sight.warped && sight.dist >= d - 0.4 ? worth / (1 + d * 0.3) : 0;
};
for (const p of v.players) if (p.alive && p.id !== povId) score += bonus(p.position, 6);
for (const c of v.creatures) score += bonus(c.position, 5);
for (const t of v.treasures) if (t.position && !t.carriedBy) score += bonus(t.position, 3);
return score;
};
const staring = castRay(v, tx, ty, targetFacing).dist < (atEnd ? 1.2 : 0.8);
if (staring) {
const current = viewScore(targetFacing);
let bestA = targetFacing, bestScore = current;
for (const a of [0, Math.PI / 2, Math.PI, -Math.PI / 2]) {
const sc = viewScore(a);
if (sc > bestScore + 0.5) { bestScore = sc; bestA = a; }
}
// One evaluation, one turn — a wizard walled in on all sides
// keeps whatever view it has rather than hunting forever.
targetFacing = bestA;
}
}
if (!camReady || (dist > 1.6 && !hurled)) {