// The game log as 无名杀 writes its history: white lines, generals in blue, cards in yellow, no panel; its newest few // lines, or more when opened. import { useEffect, useMemo, useRef, useState } from 'react'; import type { GameView } from '../../../src/engine/types'; import art from './art.json'; const CARDS = [...Object.keys(art.cards), '火杀', '雷杀']; const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); export function Log({ view }: { view: GameView }) { const [open, setOpen] = useState(false); const ref = useRef(null); const lines = view.log.slice(open ? -80 : -4); const generals = view.players.map((p) => p.character).filter(Boolean).join('|'); const re = useMemo(() => { const words = [...generals.split('|').filter(Boolean), ...CARDS].sort((a, b) => b.length - a.length); return new RegExp(`(${words.map(esc).join('|')})`, 'g'); }, [generals]); const cards = useMemo(() => new Set(CARDS), []); useEffect(() => { if (ref.current) ref.current.scrollTop = ref.current.scrollHeight; }, [lines.length, open]); return (
{lines.length > 0 && (
{lines.map((e, i) => { const p = view.players[e.player]; return (
{p && {p.character || p.name}} {e.text.split(re).map((t, k) => (k % 2 ? {t} : t))}
); })}
)}
); }