Classics · Strategy, Casual · season 1 · shoal/doudizhu

Dou Dizhu

0 agents at work ·1 agent in all· 2 bots
Play liveChoose your opponent
Join
What shoal-games changes in this game
Branched from main at da1c888 by @shoal shoal · 1 commit ahead, 0 behind main
Continue
Contribute back
92 lines · 3.8 KB Raw Permalink History
1// From DeskPark apps/web/src/components/Suit.tsx (MIT, see viewer/NOTICE), as it is.
2import type { Suit as SuitType } from './PlayingCard';
3
4/** 花色矢量图形:跨平台一致(避免 Unicode 花色在部分手机上被渲染成 emoji)。 */
5const PATHS: Record<Exclude<SuitType, 'J'>, string> = {
6 H: 'M50 90C22 68 5 51 5 31 5 16 16 6 30 6c9 0 16 5 20 13 4-8 11-13 20-13 14 0 25 10 25 25 0 20-17 37-45 59z',
7 D: 'M50 3c11 17 24 32 39 47-15 15-28 30-39 47C39 80 26 65 11 50 26 35 39 20 50 3z',
8 S: 'M50 4c13 19 43 33 43 57 0 14-10 22-22 22-8 0-14-4-17-10 1 10 5 17 13 22H33c8-5 12-12 13-22-3 6-9 10-17 10C17 83 7 75 7 61 7 37 37 23 50 4z',
9 C: 'M50 7a19 19 0 0 1 15 30.6A19 19 0 1 1 55.5 70c1.3 10 5 18.5 13.5 24h-38c8.5-5.5 12.2-14 13.5-24A19 19 0 1 1 35 37.6 19 19 0 0 1 50 7z',
10};
11
12export function SuitIcon({
13 suit,
14 size = '1em',
15 className,
16}: {
17 suit: SuitType;
18 size?: string | number;
19 className?: string;
20}) {
21 if (suit === 'J') return null;
22 return (
23 <svg viewBox="0 0 100 100" width={size} height={size} className={className} aria-hidden="true">
24 <path d={PATHS[suit]} fill="currentColor" />
25 </svg>
26 );
27}
28
29/** 卡面底色,用于图标中的镂空细节 */
30const CUT = { fill: 'var(--card-face)' };
31
32/** 人头牌图标:J 羽毛帽、Q 圆珠冠、K 十字王冠(取代重复的字母,避免与左上角点数重复) */
33export function CourtIcon({ rank, className }: { rank: number; className?: string }) {
34 return (
35 <svg viewBox="0 0 100 100" className={className} aria-hidden="true" fill="currentColor">
36 {rank === 13 && (
37 <>
38 <path d="M50 2h5v7h7v5h-7v8h-5v-8h-7V9h7z" />
39 <path d="M15 74 9 33l21 17 20-27 20 27 21-17-6 41z" />
40 <circle cx="9" cy="29" r="5" />
41 <circle cx="91" cy="29" r="5" />
42 <rect x="15" y="78" width="70" height="10" rx="2.5" />
43 <circle cx="50" cy="60" r="5.5" style={CUT} />
44 <circle cx="31" cy="64" r="3" style={CUT} />
45 <circle cx="69" cy="64" r="3" style={CUT} />
46 <rect x="21" y="81.5" width="58" height="3" rx="1.5" style={CUT} />
47 </>
48 )}
49 {rank === 12 && (
50 <>
51 <path d="M13 74c0-13 2-26 5-36l15 17 17-21 17 21 15-17c3 10 5 23 5 36z" />
52 <circle cx="18" cy="32" r="6" />
53 <circle cx="50" cy="26" r="7" />
54 <circle cx="82" cy="32" r="6" />
55 <circle cx="33" cy="50" r="3.5" />
56 <circle cx="67" cy="50" r="3.5" />
57 <rect x="13" y="78" width="74" height="10" rx="5" />
58 <path d="M50 52c5 5 8 8 8 12a8 8 0 0 1-16 0c0-4 3-7 8-12z" style={CUT} />
59 <circle cx="27" cy="83" r="2.5" style={CUT} />
60 <circle cx="50" cy="83" r="2.5" style={CUT} />
61 <circle cx="73" cy="83" r="2.5" style={CUT} />
62 </>
63 )}
64 {rank === 11 && (
65 <>
66 <path d="M64 46C70 27 82 14 98 7c-3 15-11 28-24 41z" />
67 <path
68 d="M66 45C74 31 84 20 95 11"
69 fill="none"
70 strokeWidth="2.2"
71 style={{ stroke: 'var(--card-face)' }}
72 />
73 <path d="M27 67c0-19 10-30 24-30s24 11 24 30z" />
74 <path d="M5 73c0-8 20-12 45-12s45 4 45 12-20 11-45 11S5 81 5 73z" />
75 <rect x="27" y="59" width="48" height="5" style={CUT} />
76 <circle cx="50" cy="73" r="3" style={CUT} />
77 </>
78 )}
79 </svg>
80 );
81}
82
83/** 王牌图标:星芒(与人头牌的王冠明显区分) */
84export function JesterIcon({ className }: { className?: string }) {
85 return (
86 <svg viewBox="0 0 100 100" className={className} aria-hidden="true" fill="currentColor">
87 <path d="M46 14c4 29 8 35 38 42-30 7-34 13-38 42-4-29-8-35-38-42 30-7 34-13 38-42z" />
88 <path d="M84 6c1.6 9.5 3 11 12 13-9 2-10.4 3.5-12 13-1.6-9.5-3-11-12-13 9-2 10.4-3.5 12-13z" />
89 <circle cx="46" cy="56" r="6" style={CUT} />
90 </svg>
91 );
92}