Skip to content

Commit 8014ac9

Browse files
committed
replay wip
1 parent 8b7bdbd commit 8014ac9

17 files changed

Lines changed: 1705 additions & 1177 deletions

File tree

src/dashboard/src/api/types.gen.ts

Lines changed: 963 additions & 885 deletions
Large diffs are not rendered by default.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import { useTranslation } from '@/lib/i18n';
3+
import { getRoleConfig } from '@/constants/gameData';
4+
5+
interface RoleTagProps {
6+
roleName: string;
7+
index?: number;
8+
showIndex?: boolean;
9+
isDead?: boolean;
10+
onClick?: () => void;
11+
className?: string;
12+
}
13+
14+
export const RoleTag: React.FC<RoleTagProps> = ({
15+
roleName,
16+
index,
17+
showIndex = false,
18+
isDead = false,
19+
onClick,
20+
className = '',
21+
}) => {
22+
const { t } = useTranslation();
23+
const config = getRoleConfig(roleName);
24+
25+
const baseStyles =
26+
'text-[10px] uppercase tracking-wider font-bold px-1.5 py-0.5 rounded border transition-all flex items-center gap-1';
27+
28+
const colorStyles =
29+
config.camp === 'WEREWOLF'
30+
? 'bg-red-100 dark:bg-red-900/40 border-red-300 dark:border-red-800 text-red-700 dark:text-red-300'
31+
: config.camp === 'GOD'
32+
? 'bg-indigo-100 dark:bg-indigo-900/40 border-indigo-300 dark:border-indigo-800 text-indigo-700 dark:text-indigo-300'
33+
: config.camp === 'VILLAGER'
34+
? 'bg-emerald-100 dark:bg-emerald-900/40 border-emerald-300 dark:border-emerald-800 text-emerald-700 dark:text-emerald-300'
35+
: 'bg-slate-100 dark:bg-slate-800/40 border-slate-300 dark:border-slate-700 text-slate-700 dark:text-slate-300';
36+
37+
const statusStyles = isDead ? 'line-through opacity-60 decoration-2 decoration-slate-500' : '';
38+
39+
const interactionStyles = onClick ? 'cursor-pointer hover:opacity-100' : '';
40+
41+
return (
42+
<span
43+
onClick={onClick}
44+
className={`${baseStyles} ${colorStyles} ${statusStyles} ${interactionStyles} ${className}`}
45+
>
46+
<config.icon className="w-3 h-3" />
47+
{showIndex && index !== undefined && `${index + 1}. `}
48+
{t(config.translationKey, config.id.replace(/_/g, ' '))}
49+
</span>
50+
);
51+
};
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
import {
2+
Crosshair,
3+
Dna,
4+
Eye,
5+
Flame,
6+
Gavel,
7+
Ghost,
8+
LucideIcon,
9+
Moon,
10+
Pill,
11+
Search,
12+
Shield,
13+
ShoppingBag,
14+
Skull,
15+
Sparkles,
16+
Sword,
17+
Swords,
18+
User,
19+
Wine,
20+
Zap,
21+
} from 'lucide-react';
22+
23+
export interface GameEntityConfig {
24+
id: string;
25+
color: string;
26+
icon: LucideIcon;
27+
translationKey: string;
28+
camp?: 'WEREWOLF' | 'GOD' | 'VILLAGER' | 'OTHER';
29+
}
30+
31+
export const GAME_ROLES: Record<string, GameEntityConfig> = {
32+
WEREWOLF: {
33+
id: 'WEREWOLF',
34+
color: '#ef4444', // Red-500
35+
icon: Skull,
36+
translationKey: 'roles.labels.WEREWOLF',
37+
camp: 'WEREWOLF',
38+
},
39+
WOLF_KING: {
40+
id: 'WOLF_KING',
41+
color: '#b91c1c', // Red-700
42+
icon: Flame,
43+
translationKey: 'roles.labels.WOLF_KING',
44+
camp: 'WEREWOLF',
45+
},
46+
WOLF_YOUNGER_BROTHER: {
47+
id: 'WOLF_YOUNGER_BROTHER',
48+
color: '#f87171', // Red-400
49+
icon: Zap,
50+
translationKey: 'roles.labels.WOLF_YOUNGER_BROTHER',
51+
camp: 'WEREWOLF',
52+
},
53+
WOLF_BROTHER: {
54+
id: 'WOLF_BROTHER',
55+
color: '#dc2626', // Red-600
56+
icon: Skull,
57+
translationKey: 'roles.labels.WOLF_BROTHER',
58+
camp: 'WEREWOLF',
59+
},
60+
SEER: {
61+
id: 'SEER',
62+
color: '#06b6d4', // Cyan-500
63+
icon: Eye,
64+
translationKey: 'roles.labels.SEER',
65+
camp: 'GOD',
66+
},
67+
WITCH: {
68+
id: 'WITCH',
69+
color: '#a855f7', // Purple-500
70+
icon: Wine,
71+
translationKey: 'roles.labels.WITCH',
72+
camp: 'GOD',
73+
},
74+
GUARD: {
75+
id: 'GUARD',
76+
color: '#3b82f6', // Blue-500
77+
icon: Shield,
78+
translationKey: 'roles.labels.GUARD',
79+
camp: 'GOD',
80+
},
81+
HUNTER: {
82+
id: 'HUNTER',
83+
color: '#f97316', // Orange-500
84+
icon: Swords,
85+
translationKey: 'roles.labels.HUNTER',
86+
camp: 'GOD',
87+
},
88+
VILLAGER: {
89+
id: 'VILLAGER',
90+
color: '#10b981', // Emerald-500
91+
icon: User,
92+
translationKey: 'roles.labels.VILLAGER',
93+
camp: 'VILLAGER',
94+
},
95+
IDIOT: {
96+
id: 'IDIOT',
97+
color: '#facc15', // Yellow-400
98+
icon: Gavel,
99+
translationKey: 'roles.labels.IDIOT',
100+
camp: 'GOD',
101+
},
102+
KNIGHT: {
103+
id: 'KNIGHT',
104+
color: '#6366f1', // Indigo-500
105+
icon: Sword,
106+
translationKey: 'roles.labels.KNIGHT',
107+
camp: 'GOD',
108+
},
109+
GRAVE_KEEPER: {
110+
id: 'GRAVE_KEEPER',
111+
color: '#475569', // Slate-600
112+
icon: Skull,
113+
translationKey: 'roles.labels.GRAVE_KEEPER',
114+
camp: 'GOD',
115+
},
116+
DREAM_EATER: {
117+
id: 'DREAM_EATER',
118+
color: '#ec4899', // Pink-500
119+
icon: Moon,
120+
translationKey: 'roles.labels.DREAM_EATER',
121+
camp: 'GOD',
122+
},
123+
MAGICIAN: {
124+
id: 'MAGICIAN',
125+
color: '#8b5cf6', // Violet-500
126+
icon: Sparkles,
127+
translationKey: 'roles.labels.MAGICIAN',
128+
camp: 'GOD',
129+
},
130+
WHITE_WOLF_KING: {
131+
id: 'WHITE_WOLF_KING',
132+
color: '#ef4444', // Red-500
133+
icon: Flame,
134+
translationKey: 'roles.labels.WHITE_WOLF_KING',
135+
camp: 'WEREWOLF',
136+
},
137+
HIDDEN_WOLF: {
138+
id: 'HIDDEN_WOLF',
139+
color: '#dc2626', // Red-600
140+
icon: Ghost,
141+
translationKey: 'roles.labels.HIDDEN_WOLF',
142+
camp: 'WEREWOLF',
143+
},
144+
GARGOYLE: {
145+
id: 'GARGOYLE',
146+
color: '#991b1b', // Red-800
147+
icon: Eye,
148+
translationKey: 'roles.labels.GARGOYLE',
149+
camp: 'WEREWOLF',
150+
},
151+
NIGHT_KNIGHT: {
152+
id: 'NIGHT_KNIGHT',
153+
color: '#7f1d1d', // Red-900
154+
icon: Crosshair,
155+
translationKey: 'roles.labels.NIGHT_KNIGHT',
156+
camp: 'WEREWOLF',
157+
},
158+
BLOOD_MOON: {
159+
id: 'BLOOD_MOON',
160+
color: '#991b1b', // Red-800
161+
icon: Moon,
162+
translationKey: 'roles.labels.BLOOD_MOON',
163+
camp: 'WEREWOLF',
164+
},
165+
ROBOT_WOLF: {
166+
id: 'ROBOT_WOLF',
167+
color: '#dc2626', // Red-600
168+
icon: Zap,
169+
translationKey: 'roles.labels.ROBOT_WOLF',
170+
camp: 'WEREWOLF',
171+
},
172+
CLONE: {
173+
id: 'CLONE',
174+
color: '#8b5cf6', // Violet-500
175+
icon: Dna,
176+
translationKey: 'roles.labels.CLONE',
177+
camp: 'GOD',
178+
},
179+
DARK_MERCHANT: {
180+
id: 'DARK_MERCHANT',
181+
color: '#eab308', // Gold-500
182+
icon: ShoppingBag,
183+
translationKey: 'roles.labels.DARK_MERCHANT',
184+
camp: 'GOD',
185+
},
186+
HIDDEN: {
187+
id: 'HIDDEN',
188+
color: '#64748b', // Slate-500
189+
icon: Ghost,
190+
translationKey: 'roles.labels.HIDDEN',
191+
camp: 'OTHER',
192+
},
193+
};
194+
195+
export const GAME_ACTIONS: Record<string, GameEntityConfig> = {
196+
WEREWOLF_KILL: {
197+
id: 'WEREWOLF_KILL',
198+
color: '#ef4444',
199+
icon: Sword,
200+
translationKey: 'roles.actions.WEREWOLF_KILL',
201+
},
202+
WOLF_YOUNGER_BROTHER_EXTRA_KILL: {
203+
id: 'WOLF_YOUNGER_BROTHER_EXTRA_KILL',
204+
color: '#f87171',
205+
icon: Flame,
206+
translationKey: 'roles.actions.WOLF_YOUNGER_BROTHER_EXTRA_KILL',
207+
},
208+
WITCH_ANTIDOTE: {
209+
id: 'WITCH_ANTIDOTE',
210+
color: '#10b981',
211+
icon: Pill,
212+
translationKey: 'roles.actions.WITCH_ANTIDOTE',
213+
},
214+
WITCH_POISON: {
215+
id: 'WITCH_POISON',
216+
color: '#a855f7',
217+
icon: Pill,
218+
translationKey: 'roles.actions.WITCH_POISON',
219+
},
220+
SEER_CHECK: {
221+
id: 'SEER_CHECK',
222+
color: '#06b6d4',
223+
icon: Search,
224+
translationKey: 'roles.actions.SEER_CHECK',
225+
},
226+
GUARD_PROTECT: {
227+
id: 'GUARD_PROTECT',
228+
color: '#f59e0b',
229+
icon: Shield,
230+
translationKey: 'roles.actions.GUARD_PROTECT',
231+
},
232+
HUNTER_REVENGE: {
233+
id: 'HUNTER_REVENGE',
234+
color: '#ec4899',
235+
icon: Crosshair,
236+
translationKey: 'roles.actions.HUNTER_REVENGE',
237+
},
238+
WOLF_KING_REVENGE: {
239+
id: 'WOLF_KING_REVENGE',
240+
color: '#b91c1c',
241+
icon: Flame,
242+
translationKey: 'roles.actions.WOLF_KING_REVENGE',
243+
},
244+
DARK_MERCHANT_TRADE: {
245+
id: 'DARK_MERCHANT_TRADE',
246+
color: '#eab308',
247+
icon: ShoppingBag,
248+
translationKey: 'roles.actions.DARK_MERCHANT_TRADE',
249+
},
250+
MERCHANT_SEER_CHECK: {
251+
id: 'MERCHANT_SEER_CHECK',
252+
color: '#06b6d4',
253+
icon: Search,
254+
translationKey: 'roles.actions.MERCHANT_SEER_CHECK',
255+
},
256+
MERCHANT_POISON: {
257+
id: 'MERCHANT_POISON',
258+
color: '#a855f7',
259+
icon: Pill,
260+
translationKey: 'roles.actions.MERCHANT_POISON',
261+
},
262+
MERCHANT_GUN: {
263+
id: 'MERCHANT_GUN',
264+
color: '#ec4899',
265+
icon: Crosshair,
266+
translationKey: 'roles.actions.MERCHANT_GUN',
267+
},
268+
DEATH: {
269+
id: 'DEATH',
270+
color: '#6b7280',
271+
icon: Skull,
272+
translationKey: 'roles.actions.DEATH',
273+
},
274+
};
275+
276+
// Helper function to get role config by name (handling English/Chinese variants if needed)
277+
export const getRoleConfig = (roleName: string): GameEntityConfig => {
278+
const upper = roleName.toUpperCase();
279+
if (GAME_ROLES[upper]) return GAME_ROLES[upper];
280+
281+
// Fallback for Chinese names if they come from backend directly
282+
if (roleName.includes('白狼王')) return GAME_ROLES.WHITE_WOLF_KING;
283+
if (roleName.includes('狼王')) return GAME_ROLES.WOLF_KING;
284+
if (roleName.includes('狼弟')) return GAME_ROLES.WOLF_YOUNGER_BROTHER;
285+
if (roleName.includes('狼兄')) return GAME_ROLES.WOLF_BROTHER;
286+
if (roleName.includes('隱狼')) return GAME_ROLES.HIDDEN_WOLF;
287+
if (roleName.includes('石像鬼')) return GAME_ROLES.GARGOYLE;
288+
if (roleName.includes('惡靈騎士')) return GAME_ROLES.NIGHT_KNIGHT;
289+
if (roleName.includes('血月使者')) return GAME_ROLES.BLOOD_MOON;
290+
if (roleName.includes('機械狼')) return GAME_ROLES.ROBOT_WOLF;
291+
if (roleName.includes('狼')) return GAME_ROLES.WEREWOLF;
292+
if (roleName.includes('預言')) return GAME_ROLES.SEER;
293+
if (roleName.includes('女巫')) return GAME_ROLES.WITCH;
294+
if (roleName.includes('守衛')) return GAME_ROLES.GUARD;
295+
if (roleName.includes('獵人')) return GAME_ROLES.HUNTER;
296+
if (roleName.includes('白痴')) return GAME_ROLES.IDIOT;
297+
if (roleName.includes('騎士')) return GAME_ROLES.KNIGHT;
298+
if (roleName.includes('守墓人')) return GAME_ROLES.GRAVE_KEEPER;
299+
if (roleName.includes('攝夢人')) return GAME_ROLES.DREAM_EATER;
300+
if (roleName.includes('魔術師')) return GAME_ROLES.MAGICIAN;
301+
if (roleName.includes('平民') || roleName.includes('村民')) return GAME_ROLES.VILLAGER;
302+
if (roleName.includes('黑市')) return GAME_ROLES.DARK_MERCHANT;
303+
if (roleName.includes('複製')) return GAME_ROLES.CLONE;
304+
305+
return GAME_ROLES.HIDDEN;
306+
};
307+
308+
// Helper function to get action config by ID
309+
export const getActionConfig = (actionId: string): GameEntityConfig => {
310+
const config = Object.values(GAME_ACTIONS).find((a) => actionId.includes(a.id));
311+
if (config) return config;
312+
313+
return {
314+
id: 'UNKNOWN',
315+
color: '#6b7280',
316+
icon: Gavel,
317+
translationKey: 'roles.actions.UNKNOWN',
318+
};
319+
};

0 commit comments

Comments
 (0)