实时对局选你的对手
加入
43 行 · 1.8 KB 原始文件 永久链接 历史
1// The game log as 无名杀 writes its history: white lines, generals in blue, cards in yellow, no panel; its newest few
2// lines, or more when opened.
3import { useEffect, useMemo, useRef, useState } from 'react';
4import type { GameView } from '../../../src/engine/types';
5import art from './art.json';
6
7const CARDS = [...Object.keys(art.cards), '火杀', '雷杀'];
8const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
10export function Log({ view }: { view: GameView }) {
11 const [open, setOpen] = useState(false);
12 const ref = useRef<HTMLDivElement>(null);
13 const lines = view.log.slice(open ? -80 : -4);
14 const generals = view.players.map((p) => p.character).filter(Boolean).join('|');
15 const re = useMemo(() => {
16 const words = [...generals.split('|').filter(Boolean), ...CARDS].sort((a, b) => b.length - a.length);
17 return new RegExp(`(${words.map(esc).join('|')})`, 'g');
18 }, [generals]);
19 const cards = useMemo(() => new Set(CARDS), []);
20 useEffect(() => {
21 if (ref.current) ref.current.scrollTop = ref.current.scrollHeight;
22 }, [lines.length, open]);
23 return (
24 <div className={`nm-log${open ? ' open' : ''}`}>
25 <button className="nm-log-toggle" onClick={() => setOpen(!open)} title={open ? '收起记录' : '展开记录'}>
26 记录
27 </button>
28 {lines.length > 0 && (
29 <div className="nm-log-lines" ref={ref}>
30 {lines.map((e, i) => {
31 const p = view.players[e.player];
32 return (
33 <div key={view.log.length - lines.length + i} className="nm-log-line">
34 {p && <span className="who">{p.character || p.name}</span>}
35 {e.text.split(re).map((t, k) => (k % 2 ? <span key={k} className={cards.has(t) ? 'card' : 'who'}>{t}</span> : t))}
36 </div>
37 );
38 })}
39 </div>
40 )}
41 </div>
42 );
43}