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:
co-authored by
Claude Fable 5
parent
765828700d
commit
59c25f42f0
@@ -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;
|
||||
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 h = castRay(v, tx, ty, a);
|
||||
if (h.dist > deepest) { deepest = h.dist; targetFacing = a; }
|
||||
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)) {
|
||||
|
||||
Reference in New Issue
Block a user