import { useEffect, useRef, useState } from 'react' import type { Card, ClientMessage, GameView, PlayerView } from '../types' import { CardView } from './CardView' interface Props { view: GameView you: PlayerView send: (msg: ClientMessage) => void } // ArrangePhase lets the player order their deck for battle. Rightmost card // fights first; food cards buff the next pet to their left... i.e. foods // apply to the next pet later in the play order. Internally `order` stays in // play order (index 0 fights first) to match the backend; we only reverse for // display. Drag cards or use the arrow buttons to reorder, then lock in. export function ArrangePhase({ view, you, send }: Props) { const [order, setOrder] = useState(you.deck ?? []) const dragIndex = useRef(null) const [dragging, setDragging] = useState(false) const locked = you.ready // Resync only if the deck's actual contents changed — every broadcast // creates a fresh array, and blindly resetting would wipe an in-progress // ordering whenever the opponent acts. useEffect(() => { setOrder((prev) => { const deck = you.deck ?? [] const ids = new Set(deck.map((c) => c.id)) if (prev.length === deck.length && prev.every((c) => ids.has(c.id))) { return prev } return deck }) }, [you.deck]) function move(from: number, to: number) { if (to < 0 || to >= order.length) return setOrder((o) => { const next = [...o] const [c] = next.splice(from, 1) next.splice(to, 0, c) return next }) } // Which pets do the foods land on? Compute buff per card for preview. const bonuses = new Map() { let pendingApples = 0 for (const c of order) { if (c.kind === 'food') { if (c.food === 'apple') pendingApples++ } else { bonuses.set(c.id, pendingApples) pendingApples = 0 } } } const trailingFoods = (() => { let n = 0 for (let i = order.length - 1; i >= 0 && order[i].kind === 'food'; i--) n++ return n })() const opponent = view.players.find((p) => p.seat !== view.youSeat) if (locked) { return (

Order locked in ⚔️

Waiting for {opponent?.name ?? 'your opponent'} to arrange their deck…

) } return (
Arrange your battle line

The rightmost card fights first. Food cards power up the next pet to their left. {trailingFoods > 0 && ( {' '} ⚠️ {trailingFoods} food card{trailingFoods > 1 ? 's' : ''} on the far left will be wasted! )}

{order .map((c, i) => ({ c, i })) .reverse() .map(({ c, i }) => (
{ dragIndex.current = i setDragging(true) }} onDragOver={(e) => { e.preventDefault() if (dragIndex.current !== null && dragIndex.current !== i) { move(dragIndex.current, i) dragIndex.current = i } }} onDragEnd={() => { dragIndex.current = null setDragging(false) }} >
))}
⚔️ first
) }