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:
Eric Wagoner
2026-08-23 20:07:41 -04:00
co-authored by Claude Fable 5
parent 045e868190
commit cd9ce9fddf
3 changed files with 44 additions and 19 deletions
+7 -6
View File
@@ -13,8 +13,8 @@ import type { GameEvent, GameView } from "@wizwar/engine";
export type FpFx =
| { id: number; kind: "projectile"; art: string; from: { x: number; y: number }; to: { x: number; y: number }; t0: number; dur: number;
/** This leg flies in a warp mouth's virtual space: visible only
* through the opening, on columns whose rays bent. */
warped?: boolean }
* through THAT warp's opening, beyond its mouth. */
warped?: boolean; warpId?: number }
| { id: number; kind: "impact"; art: string; at: { x: number; y: number }; t0: number; dur: number;
/** Lifted off the floor (fireworks burst overhead); default 0.3. */
rise?: number }
@@ -62,19 +62,20 @@ const IMPACT_ART: Record<string, string> = {
* clipped by the depth buffer to the side the camera stands on. */
function projectileLegs(
view: GameView, a: { x: number; y: number }, b: { x: number; y: number },
): { from: { x: number; y: number }; to: { x: number; y: number }; warped?: boolean }[] {
): { from: { x: number; y: number }; to: { x: number; y: number }; warped?: boolean; warpId?: number }[] {
const len = Math.hypot(b.x - a.x, b.y - a.y);
if (len < 0.05) return [{ from: a, to: b }];
const direct = castRay(view, a.x, a.y, Math.atan2(b.y - a.y, b.x - a.x));
if (!direct.warped && direct.dist >= len - 0.4) return [{ from: a, to: b }];
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 vt = motion.toVirtual(b);
const vlen = Math.hypot(vt.x - a.x, vt.y - a.y);
const hit = castRay(view, a.x, a.y, Math.atan2(vt.y - a.y, vt.x - a.x));
if (hit.warped && hit.dist >= vlen - 0.4) {
return [
{ from: a, to: vt, warped: true },
{ from: a, to: vt, warped: true, warpId: wi },
{ from: motion.toReal(a), to: b },
];
}
@@ -96,7 +97,7 @@ export function fpFxForEvents(
out.push({
fx: {
id: nextId++, kind: "projectile", art,
from: leg.from, to: leg.to, warped: leg.warped,
from: leg.from, to: leg.to, warped: leg.warped, warpId: leg.warpId,
t0: 0, dur: PROJECTILE_DUR[art]!,
},
delay,