diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 9ccb6b7..1626f48 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -56,6 +56,8 @@ 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(null); + /** The creature the pane is riding, if any: the ribbon moves to it. */ + let fpvBody = $state(null); let fxCancels: (() => void)[] = []; /** A trap drawn by YOU earns a modal; buried log lines get missed. */ let trapNotice = $state(null); @@ -1908,7 +1910,8 @@ ? { type: "pickUpTreasure", treasureId: w.id } : { type: "pickUpObject", instanceId: w.id })} onCreatureMove={(creatureId, direction) => dispatch({ type: "moveCreature", creatureId, direction })} - onCreatureAttack={(creatureId, targetId) => dispatch({ type: "creatureAttack", creatureId, targetId })} /> + onCreatureAttack={(creatureId, targetId) => dispatch({ type: "creatureAttack", creatureId, targetId })} + onbody={(id) => (fpvBody = id)} /> {/if} diff --git a/packages/web/src/Board.svelte b/packages/web/src/Board.svelte index a2c3b3c..77fd284 100644 --- a/packages/web/src/Board.svelte +++ b/packages/web/src/Board.svelte @@ -24,6 +24,7 @@ onEdgeClick, onPlayerClick, povFacing = null, + povCreatureId = null, onCreatureClick, onWarpClick, onCellPeek, @@ -47,6 +48,8 @@ /** Which way YOUR first-person camera faces: a wedge on your token * keeps the keymap and the pane pointing the same way. */ povFacing?: "N" | "E" | "S" | "W" | null; + /** While the pane rides a creature, the ribbon moves to ITS token. */ + povCreatureId?: string | null; onCreatureClick?: (creatureId: string) => void; onWarpClick?: (cell: { x: number; y: number }, side: Side) => void; /** Long-press on a square: read the card behind whatever occupies it. */ @@ -592,7 +595,7 @@ {/if} {/if} - {#if povFacing && p.id === view.you} + {#if povFacing && !povCreatureId && p.id === view.you} {@const fa = povFacing === "E" ? 0 : povFacing === "S" ? Math.PI / 2 : povFacing === "W" ? Math.PI : -Math.PI / 2} @@ -653,6 +656,19 @@ {c.kind === "fire-imp" ? "I" : c.kind === "democratic-monster" ? "D" : c.kind[0]?.toUpperCase()} {/if} + {#if povFacing && povCreatureId === c.id} + + {@const fa2 = povFacing === "E" ? 0 : povFacing === "S" ? Math.PI / 2 : povFacing === "W" ? Math.PI : -Math.PI / 2} + {@const ch = CELL * 0.26} + {@const cfx = Math.cos(fa2)} + {@const cfy = Math.sin(fa2)} + {@const cpx = -cfy} + {@const cpy = cfx} + + {/if} {/each} diff --git a/packages/web/src/LiveFirstPerson.svelte b/packages/web/src/LiveFirstPerson.svelte index 8c7336a..1841c7f 100644 --- a/packages/web/src/LiveFirstPerson.svelte +++ b/packages/web/src/LiveFirstPerson.svelte @@ -17,7 +17,7 @@ import { untrack } from "svelte"; import type { GameEvent, GameView, Side } from "@wizwar/engine"; - let { view, batch, onhide, onstride = null, canStride = false, ontarget = null, onfacing = null, litCells = null, onpickup = null, onCreatureMove = null, onCreatureAttack = null }: { + let { view, batch, onhide, onstride = null, canStride = false, ontarget = null, onfacing = null, litCells = null, onpickup = null, onCreatureMove = null, onCreatureAttack = null, onbody = null }: { view: GameView; /** The latest live event batch, numbered so each plays once, with * the view the server sent alongside it. */ @@ -40,6 +40,9 @@ /** Possession: ride a creature's eyes and drive it from the pane. */ onCreatureMove?: ((creatureId: string, side: Side) => void) | null; onCreatureAttack?: ((creatureId: string, targetId: string) => void) | null; + /** Reports which creature (if any) the pane is riding, so the keymap + * can move the facing ribbon to the mount's token. */ + onbody?: ((creatureId: string | null) => void) | null; } = $props(); const povId = $derived(view.you); @@ -363,6 +366,7 @@ raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }); + $effect(() => { onbody?.(possessedId); }); let lastQuad = -99; $effect(() => { const q = ((Math.round(cam.facing / (Math.PI / 2)) % 4) + 4) % 4;