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
@@ -54,6 +54,8 @@
|
||||
let boardFx = $state<BoardFx[]>([]);
|
||||
/** The live pane's feed: each event batch, numbered, with its view. */
|
||||
let fpBatch = $state<{ n: number; events: GameEvent[]; view: GameView } | null>(null);
|
||||
/** The pane camera's compass quadrant, worn by the keymap token. */
|
||||
let fpvFacing = $state<Side | null>(null);
|
||||
let fxCancels: (() => void)[] = [];
|
||||
/** A trap drawn by YOU earns a modal; buried log lines get missed. */
|
||||
let trapNotice = $state<string | null>(null);
|
||||
@@ -903,6 +905,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** A click in the first-person pane, resolved by the renderer to the
|
||||
* board's own vocabulary — then handled by the very same functions the
|
||||
* top-down board uses, so the pane and the map can never disagree. */
|
||||
function fpvTarget(t: import("./fpv/raycast").FpvTarget) {
|
||||
if (t.kind === "player") clickPlayer(t.id);
|
||||
else if (t.kind === "creature") clickCreature(t.id);
|
||||
else if (t.kind === "edge") clickEdge(t.cell, t.side);
|
||||
else clickCell(t.cell);
|
||||
}
|
||||
|
||||
function clickPlayer(playerId: string) {
|
||||
if (!view || !yourMoment) return;
|
||||
if (selectedCreature) {
|
||||
@@ -1888,7 +1900,8 @@
|
||||
<div class="table-stack" class:fpv-primary={prefs.liveFp && !!view.you && !net.spectating}>
|
||||
{#if prefs.liveFp && view.you && !net.spectating}
|
||||
<LiveFirstPerson {view} batch={fpBatch} onhide={() => setPref("liveFp", false)}
|
||||
onstride={(side) => tryMove(side)} canStride={yourMoment} />
|
||||
onstride={(side) => tryMove(side)} canStride={yourMoment}
|
||||
ontarget={fpvTarget} onfacing={(s) => (fpvFacing = s)} />
|
||||
{/if}
|
||||
<Board
|
||||
{view}
|
||||
@@ -1910,6 +1923,7 @@
|
||||
onEdgePeek={(tip) => (peekEdge = tip)}
|
||||
{litCells}
|
||||
{sightTrace}
|
||||
povFacing={prefs.liveFp && view.you && !net.spectating ? fpvFacing : null}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user