The eyes turn to meet magic thrown at them, even after a stride
Springing the Trap never showed its fireball: Rival stepped into the watched corridor facing the way he walked, and the ambush's fireball flew in from the mouth behind him while only the caption told of it. The director now aims at incoming magic and punches — the caster's or attacker's square, straight or through a mouth — and a stride that ends with something to watch turns once more after the walk, the slate holding the outcome until the eyes face it. The fireball comes through the wormhole at the camera, then the blow lands. The trap's first beat re-blessed for it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
86856781df
commit
86070b42a3
Binary file not shown.
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 321 KiB |
@@ -377,7 +377,17 @@
|
||||
const GROUNDWORK = new Set(["spellCast", "squareFilled", "creatureCreated",
|
||||
"treasurePickedUp", "treasureDropped", "objectDropped"]);
|
||||
const groundwork = aim?.self && step.events.some((e) => GROUNDWORK.has(e.type));
|
||||
if (camReady && !willCut && dist > 0.05 && !hurled) { targetFacing = Math.atan2(dy, dx); aimed = true; }
|
||||
/** A stride that ends with something to watch — an ambush that
|
||||
* fired as the step landed — turns once more after the walk. */
|
||||
let afterArc = 0;
|
||||
if (camReady && !willCut && dist > 0.05 && !hurled) {
|
||||
targetFacing = Math.atan2(dy, dx);
|
||||
aimed = true;
|
||||
if (aim && !aim.self) {
|
||||
const h = sightline(v, tx, ty, aim.x + 0.5, aim.y + 0.5);
|
||||
if (h !== null) afterArc = shortestArc(targetFacing, h);
|
||||
}
|
||||
}
|
||||
else if (groundwork || underfoot) {
|
||||
targetPitch = LOOK_DOWN;
|
||||
aimed = true;
|
||||
@@ -448,8 +458,8 @@
|
||||
// The reel's LAST step may be a stride into a corner: the walk keeps
|
||||
// its own facing, then the camera turns once more to a view worth
|
||||
// ending on.
|
||||
let closingArc = 0;
|
||||
if (atEnd && aimed && castRay(v, tx, ty, targetFacing).dist < 1.2) {
|
||||
let closingArc = afterArc;
|
||||
if (closingArc === 0 && atEnd && aimed && castRay(v, tx, ty, targetFacing).dist < 1.2) {
|
||||
const current = viewScoreAt(tx, ty, targetFacing);
|
||||
let bestA = targetFacing, bestScore = current;
|
||||
for (const a of [0, Math.PI / 2, Math.PI, -Math.PI / 2]) {
|
||||
@@ -498,8 +508,12 @@
|
||||
const turnMs = (hurled ? 0 : Math.min(260, Math.abs(arc) * 180)) / speed;
|
||||
const walkMs = (dist > 0.05 ? (hurled ? 260 : 420) : 0) / speed;
|
||||
const tiltMs = (Math.abs(targetPitch - fromP) > 0.01 ? 340 : 0) / speed;
|
||||
const clearSlate = setSlate(aimed && (aim || underfoot) ? Math.max(turnMs, tiltMs) + 120 / speed : 0);
|
||||
const closingMs = Math.min(300, Math.abs(closingArc) * 200) / speed;
|
||||
// The outcome waits until the eyes face it: after the turn (and,
|
||||
// for a stride that turns at its end, after the walk and that turn).
|
||||
const clearSlate = setSlate(aimed && (aim || underfoot)
|
||||
? Math.max(turnMs, tiltMs) + (afterArc !== 0 ? walkMs + closingMs : 0) + 120 / speed
|
||||
: 0);
|
||||
const t0 = performance.now() + dieMs;
|
||||
let raf = 0;
|
||||
const tick = (now: number) => {
|
||||
|
||||
@@ -41,6 +41,12 @@ export function aimOfEvents(
|
||||
cell = e.targetCell ?? view.players.find((p) => p.id === e.target)?.position ?? null;
|
||||
} else if (e.type === "attackResolved" && e.attacker === povId) {
|
||||
cell = view.players.find((p) => p.id === e.defender)?.position ?? null;
|
||||
} else if (e.type === "spellCast" && e.target === povId && e.caster !== povId) {
|
||||
// Magic thrown AT these eyes: they turn to where it came from —
|
||||
// the ambusher down the corridor, or through the mouth.
|
||||
cell = view.players.find((p) => p.id === e.caster)?.position ?? null;
|
||||
} else if (e.type === "punched" && e.target === povId) {
|
||||
cell = view.players.find((p) => p.id === e.attacker)?.position ?? null;
|
||||
} else if (e.type === "objectThrown" && e.attacker === povId) {
|
||||
cell = e.landedAt;
|
||||
} else if (
|
||||
|
||||
Reference in New Issue
Block a user