"use client"; import { useEffect, useRef, useState } from "react"; import { getSocket } from "../lib/socket-client"; import { useGame } from "../lib/store"; const PALETTE = ["#2D2D2D","#FF5C5C","#FFD23F","#4ECDC4","#A593E0","#5BCEFA","#FF8FB1","#1AAE56"]; export default function GarticGame() { const room = useGame((s) => s.room); const [task, setTask] = useState<"prompt"|"drawing"|"guess"|null>(null); const [content, setContent] = useState(""); const [endsAt, setEndsAt] = useState(0); const [now, setNow] = useState(Date.now()); const [text, setText] = useState(""); const [submitted, setSubmitted] = useState(false); const [color, setColor] = useState("#2D2D2D"); const [size, setSize] = useState(5); const canvasRef = useRef(null); const stageRef = useRef(null); const drawingRef = useRef(false); const lastRef = useRef<{x:number;y:number}|null>(null); useEffect(() => { const socket = getSocket(); const onTurn = (d: any) => { setTask(d.task); setContent(d.content || ""); setEndsAt(Date.now() + d.durationMs); setText(""); setSubmitted(false); setTimeout(() => clearLocalCanvas(), 50); }; const onBook = () => { setTask(null); }; socket.on("gartic:turn", onTurn); socket.on("gartic:bookComplete", onBook); return () => { socket.off("gartic:turn", onTurn); socket.off("gartic:bookComplete", onBook); }; }, []); useEffect(() => { const t = setInterval(() => setNow(Date.now()), 250); return () => clearInterval(t); }, []); useEffect(() => { const resize = () => { const canvas = canvasRef.current; const stage = stageRef.current; if (!canvas || !stage) return; const rect = stage.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; canvas.width = Math.max(100, rect.width * dpr); canvas.height = Math.max(100, rect.height * dpr); canvas.style.width = rect.width + "px"; canvas.style.height = rect.height + "px"; const ctx = canvas.getContext("2d"); if (ctx) ctx.scale(dpr, dpr); }; resize(); window.addEventListener("resize", resize); return () => window.removeEventListener("resize", resize); }, [task]); const clearLocalCanvas = () => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, canvas.width, canvas.height); }; const drawSegment = (px:number, py:number, x:number, y:number) => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.strokeStyle = color; ctx.lineWidth = size; ctx.beginPath(); ctx.moveTo(px, py); ctx.lineTo(x, y); ctx.stroke(); }; const getPos = (e: React.PointerEvent) => { const canvas = canvasRef.current!; const rect = canvas.getBoundingClientRect(); return { x: e.clientX - rect.left, y: e.clientY - rect.top }; }; const onDown = (e: React.PointerEvent) => { if (task !== "drawing" || submitted) return; e.preventDefault(); drawingRef.current = true; const p = getPos(e); lastRef.current = p; drawSegment(p.x, p.y, p.x, p.y); }; const onMove = (e: React.PointerEvent) => { if (!drawingRef.current) return; const p = getPos(e); drawSegment(lastRef.current!.x, lastRef.current!.y, p.x, p.y); lastRef.current = p; }; const onUp = () => { drawingRef.current = false; lastRef.current = null; }; const submit = () => { if (submitted) return; let data: string = ""; if (task === "drawing") { const c = canvasRef.current; if (!c) return; data = c.toDataURL("image/png"); } else { data = text.trim(); if (!data) return; } getSocket().emit("gartic:submit", { type: task, data }, (resp: any) => { if (resp?.ok) setSubmitted(true); }); }; const remaining = Math.max(0, Math.ceil((endsAt - now) / 1000)); const totalTurns = room?.gartic?.totalTurns || 0; const turnIndex = room?.gartic?.turnIndex || 0; if (!task) { return
Get ready...
The first prompt is loading.
; } return (
Turn {turnIndex+1}/{totalTurns}
{remaining}s
{task === "prompt" ? "Write a prompt" : task === "drawing" ? "Draw it" : "What is this?"}
{task === "prompt" && (

Write a silly prompt

Other players will draw this. Be creative!

setText(e.target.value)} className="input-text max-w-[440px] text-center text-lg" placeholder="A penguin riding a skateboard..." disabled={submitted}/>
)} {task === "drawing" && ( <>
Draw this
{content || "..."}
{PALETTE.map(c => ( ))}
)} {task === "guess" && ( <>
What is this?
{content ? drawing : (no drawing)}
setText(e.target.value)} className="input-text max-w-[440px] mx-auto text-center text-lg" placeholder="What do you see?" disabled={submitted}/> )}
); }