Sprites respect the window they are seen through
DUKK's floor-to-ceiling white sliver, decoded: the sprite gate only asked "did this column's ray warp?" — so a REAL body standing in front of a warp mouth was clipped out of the mouth's columns (leaving a baffling sliver of a huge close sprite), and a body virtualized through warp A could paint columns bent by warp B. Every column now remembers WHICH warp bent its ray and how far away that mouth stood; a real body paints a column only nearer than its mouth (in front of the window), and a virtual one only through its own warp's columns, beyond the mouth. Projectile legs carry the same identity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
045e868190
commit
cd9ce9fddf
@@ -26,6 +26,11 @@ export interface Hit {
|
||||
edge?: string;
|
||||
/** The eye reached this through a warp: haze it other-worldly. */
|
||||
warped?: boolean;
|
||||
/** Which warp (index in board.warps) the ray first bent through, and
|
||||
* how far along the ray that mouth stood — the gate a sprite must be
|
||||
* beyond (its own warp) or in front of (any) to paint this column. */
|
||||
warpId?: number;
|
||||
warpDist?: number;
|
||||
/** The first known-illusion edge the ray crossed before its solid hit:
|
||||
* the eye passes, but a translucent ghost of a wall stands there. */
|
||||
ghost?: { dist: number; u: number; worldU: number; axis: "x" | "y" };
|
||||
@@ -108,6 +113,8 @@ export function castRay(
|
||||
// a mouth re-enters at the paired mouth and marches on, its hits hazed.
|
||||
let baseDist = 0;
|
||||
let warped = false;
|
||||
let warpId: number | undefined;
|
||||
let warpDist: number | undefined;
|
||||
let ghost: Hit["ghost"];
|
||||
let doorway: Hit["doorway"];
|
||||
for (let traversal = 0; traversal < 3; traversal++) {
|
||||
@@ -171,7 +178,7 @@ export function castRay(
|
||||
// A sliding door carries its texture with it: sample past the gap.
|
||||
return {
|
||||
dist: baseDist + b.t, kind: b.kind, u: Math.max(0, u - b.slide),
|
||||
axis: b.axis, worldU: along, edge: b.edge, warped, ghost, doorway,
|
||||
axis: b.axis, worldU: along, edge: b.edge, warped, warpId, warpDist, ghost, doorway,
|
||||
...(b.frame ? { frame: true } : {}),
|
||||
};
|
||||
}
|
||||
@@ -194,10 +201,12 @@ export function castRay(
|
||||
const offBoard = !view.board.cells[cellKey({ x: nx, y: ny })];
|
||||
if (offBoard) {
|
||||
const side: Side = crossSide;
|
||||
const warp = view.board.warps.find(
|
||||
const wIdx = view.board.warps.findIndex(
|
||||
(w) => cellKey(w.from.cell) === cellKey({ x: cx, y: cy }) && w.from.side === side,
|
||||
);
|
||||
if (!warp) return { dist: baseDist + dist, kind: "rim", u: texU, axis, worldU: along, warped, ghost, doorway };
|
||||
const warp = wIdx >= 0 ? view.board.warps[wIdx]! : undefined;
|
||||
if (!warp) return { dist: baseDist + dist, kind: "rim", u: texU, axis, worldU: along, warped, warpId, warpDist, ghost, doorway };
|
||||
if (warpId === undefined) { warpId = wIdx; warpDist = baseDist + dist; }
|
||||
// Step through: re-enter at the paired mouth, heading inward, the
|
||||
// lane carried by the same proper rotation the heading turns by —
|
||||
// mirrored for the pairings whose axes land head-to-head.
|
||||
@@ -215,14 +224,14 @@ export function castRay(
|
||||
break;
|
||||
}
|
||||
if (view.squareContents[cellKey({ x: nx, y: ny })]?.kind === "stone") {
|
||||
return { dist: baseDist + dist, kind: "stone", u: texU, axis, worldU: along, warped, ghost, doorway };
|
||||
return { dist: baseDist + dist, kind: "stone", u: texU, axis, worldU: along, warped, warpId, warpDist, ghost, doorway };
|
||||
}
|
||||
cx = nx;
|
||||
cy = ny;
|
||||
}
|
||||
if (!warped || traversal === 2) break;
|
||||
}
|
||||
return { dist: baseDist + 64, kind: "rim", u: 0, axis: "x", worldU: 0, warped, ghost, doorway };
|
||||
return { dist: baseDist + 64, kind: "rim", u: 0, axis: "x", worldU: 0, warped, warpId, warpDist, ghost, doorway };
|
||||
}
|
||||
|
||||
/** Can a wizard's body (not just their eye) cross this edge? Workshop
|
||||
@@ -277,8 +286,10 @@ export interface Billboard {
|
||||
bias?: number;
|
||||
/** Procedural stand-in name while the painted file loads. */
|
||||
fallback?: string;
|
||||
/** A reflection seen through a warp mouth, not the thing itself. */
|
||||
/** A reflection seen through a warp mouth, not the thing itself —
|
||||
* visible only through THAT warp's columns, beyond its mouth. */
|
||||
warped?: boolean;
|
||||
warpId?: number;
|
||||
}
|
||||
|
||||
/** The along=0 corner of a warp mouth — the anchor castRay's lane
|
||||
@@ -407,7 +418,8 @@ export function billboards(
|
||||
// the same rigid motion the rays use — the zbuffer clips it to the
|
||||
// opening, since everything around the mouth is nearer wall.
|
||||
const real = out.slice();
|
||||
for (const w of view.board.warps) {
|
||||
for (let wi = 0; wi < view.board.warps.length; wi++) {
|
||||
const w = view.board.warps[wi]!;
|
||||
const motion = warpMotion(w);
|
||||
const a2 = mouthAnchor(w.to);
|
||||
const inward = SIDE_ANGLE[OPPOSITE[w.to.side]];
|
||||
@@ -415,7 +427,7 @@ export function billboards(
|
||||
for (const b of real) {
|
||||
const rx = b.x - a2.x, ry = b.y - a2.y;
|
||||
if (rx * inX + ry * inY < -0.2 || Math.hypot(rx, ry) > 8) continue;
|
||||
out.push({ ...b, ...motion.toVirtual(b), warped: true });
|
||||
out.push({ ...b, ...motion.toVirtual(b), warped: true, warpId: wi });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
||||
Reference in New Issue
Block a user