Milestone two: the pane becomes the wand hand
Clicks in the first-person view now act. The renderer keeps what the last frame knew — per-column depth, warp bends, every wall hit's edge, every sprite's screen rect with its identity — and hitTest inverts the projection: the nearest visible sprite answers as its wizard or creature, a struck wall or door answers as its edge, and the ground answers as the square under the pixel, warp-bent columns mapping their virtual floor back to real cells through the warp's own rigid motion. The pane hands each target to the very functions the top-down board's clicks use, so pane and map can never disagree: click the floor ahead and you walk there; click a door with a key selected and the lock turns; click a wizard with an attack and the spell flies. The keymap token now wears a golden wedge showing the camera's compass quadrant — the cockpit and the chart always agree on which way you face. Verified live: a floor click became a real move command in the room ledger, edge clicks resolve and defer correctly, and the wedge tracks quarter-turns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
co-authored by
Claude Fable 5
parent
aac06dd2b5
commit
42d872df6d
@@ -8,13 +8,14 @@
|
||||
// instant replay uses, so spells, doors, and conjurations perform
|
||||
// here first.
|
||||
import FirstPerson from "./fpv/FirstPerson.svelte";
|
||||
import type { FpvTarget } from "./fpv/raycast";
|
||||
import { fpFxForEvents, type FpFx } from "./fpv/fx3d";
|
||||
import { castRay } from "./fpv/raycast";
|
||||
import { aimOfEvents, gatherGlides, hurledIn, shortestArc } from "./fpv/director";
|
||||
import { untrack } from "svelte";
|
||||
import type { GameEvent, GameView, Side } from "@wizwar/engine";
|
||||
|
||||
let { view, batch, onhide, onstride = null, canStride = false }: {
|
||||
let { view, batch, onhide, onstride = null, canStride = false, ontarget = null, onfacing = null }: {
|
||||
view: GameView;
|
||||
/** The latest live event batch, numbered so each plays once, with
|
||||
* the view the server sent alongside it. */
|
||||
@@ -23,6 +24,12 @@
|
||||
/** Issue a real move command: the pane is the cockpit on your turn. */
|
||||
onstride?: ((side: Side) => void) | null;
|
||||
canStride?: boolean;
|
||||
/** Clicks in the pane resolve to board targets: the pane is not just
|
||||
* the window but the wand hand. */
|
||||
ontarget?: ((t: FpvTarget) => void) | null;
|
||||
/** Reports the camera's compass quadrant whenever it settles on a new
|
||||
* one — the keymap wears it as a wedge on your token. */
|
||||
onfacing?: ((side: Side) => void) | null;
|
||||
} = $props();
|
||||
|
||||
const povId = $derived(view.you);
|
||||
@@ -225,6 +232,14 @@
|
||||
raf = requestAnimationFrame(tick);
|
||||
return () => cancelAnimationFrame(raf);
|
||||
});
|
||||
let lastQuad = -99;
|
||||
$effect(() => {
|
||||
const q = ((Math.round(cam.facing / (Math.PI / 2)) % 4) + 4) % 4;
|
||||
if (q !== lastQuad) {
|
||||
lastQuad = q;
|
||||
onfacing?.(SIDES4[q]!);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={onKey} />
|
||||
@@ -233,7 +248,8 @@
|
||||
<div class="live-fp">
|
||||
<FirstPerson {view} povId={cutawayShot ? "" : povId}
|
||||
x={cam.x} y={cam.y} facing={cam.facing} width={960} height={400}
|
||||
fx={fpFx} posOverride={actorPos} />
|
||||
fx={fpFx} posOverride={actorPos}
|
||||
ontarget={ontarget ?? undefined} />
|
||||
<button class="live-fp-hide" onclick={onhide} title="hide (re-enable in preferences)">✕</button>
|
||||
<!-- The helm, tappable: edge strips turn and stride. -->
|
||||
<button class="drive drive-left" onclick={() => manualTurn(-1)} aria-label="turn left">‹</button>
|
||||
|
||||
Reference in New Issue
Block a user