229 lines
No EOL
6.6 KiB
TypeScript
229 lines
No EOL
6.6 KiB
TypeScript
export type GameState =
|
|
| 'LANDING'
|
|
| 'CREATING'
|
|
| 'GENERATING'
|
|
| 'EDITING'
|
|
| 'LOBBY'
|
|
| 'COUNTDOWN'
|
|
| 'QUESTION'
|
|
| 'REVEAL'
|
|
| 'SCOREBOARD'
|
|
| 'PODIUM'
|
|
| 'DISCONNECTED'
|
|
| 'WAITING_TO_REJOIN'
|
|
| 'HOST_RECONNECTED';
|
|
|
|
export interface ColorScheme {
|
|
id: string;
|
|
name: string;
|
|
primary: string;
|
|
primaryDark: string;
|
|
primaryDarker: string;
|
|
}
|
|
|
|
export const COLOR_SCHEMES: ColorScheme[] = [
|
|
{ id: 'blue', name: 'Ocean Blue', primary: '#2563eb', primaryDark: '#1e40af', primaryDarker: '#1e3a5f' },
|
|
{ id: 'purple', name: 'Royal Purple', primary: '#7c3aed', primaryDark: '#5b21b6', primaryDarker: '#3b1a5f' },
|
|
{ id: 'pink', name: 'Hot Pink', primary: '#db2777', primaryDark: '#9d174d', primaryDarker: '#5f1a3b' },
|
|
{ id: 'red', name: 'Ruby Red', primary: '#dc2626', primaryDark: '#991b1b', primaryDarker: '#5f1a1a' },
|
|
{ id: 'orange', name: 'Sunset Orange', primary: '#ea580c', primaryDark: '#c2410c', primaryDarker: '#5f2a1a' },
|
|
{ id: 'yellow', name: 'Golden Yellow', primary: '#ca8a04', primaryDark: '#a16207', primaryDarker: '#5f4a1a' },
|
|
{ id: 'green', name: 'Emerald Green', primary: '#16a34a', primaryDark: '#15803d', primaryDarker: '#1a5f3b' },
|
|
{ id: 'teal', name: 'Teal', primary: '#0d9488', primaryDark: '#0f766e', primaryDarker: '#1a5f5a' },
|
|
{ id: 'cyan', name: 'Cyan', primary: '#0891b2', primaryDark: '#0e7490', primaryDarker: '#1a4a5f' },
|
|
{ id: 'indigo', name: 'Indigo', primary: '#4f46e5', primaryDark: '#4338ca', primaryDarker: '#2a2a5f' },
|
|
{ id: 'slate', name: 'Slate', primary: '#475569', primaryDark: '#334155', primaryDarker: '#1e293b' },
|
|
{ id: 'rose', name: 'Rose', primary: '#e11d48', primaryDark: '#be123c', primaryDarker: '#5f1a2a' },
|
|
];
|
|
|
|
export type AIProvider = 'gemini' | 'openrouter' | 'openai' | 'system';
|
|
|
|
export interface UserPreferences {
|
|
colorScheme: string;
|
|
aiProvider?: AIProvider;
|
|
geminiApiKey?: string;
|
|
geminiModel?: string;
|
|
openRouterApiKey?: string;
|
|
openRouterModel?: string;
|
|
openAIApiKey?: string;
|
|
openAIModel?: string;
|
|
}
|
|
|
|
export type GameRole = 'HOST' | 'CLIENT';
|
|
|
|
export interface AnswerOption {
|
|
text: string;
|
|
isCorrect: boolean;
|
|
shape: 'triangle' | 'diamond' | 'circle' | 'square';
|
|
color: 'red' | 'blue' | 'yellow' | 'green';
|
|
reason?: string; // Explanation for why this answer is correct or incorrect
|
|
}
|
|
|
|
export interface Question {
|
|
id: string;
|
|
text: string;
|
|
options: AnswerOption[];
|
|
timeLimit: number; // in seconds
|
|
}
|
|
|
|
export interface GameConfig {
|
|
shuffleQuestions: boolean;
|
|
shuffleAnswers: boolean;
|
|
hostParticipates: boolean;
|
|
randomNamesEnabled: boolean;
|
|
streakBonusEnabled: boolean;
|
|
streakThreshold: number;
|
|
streakMultiplier: number;
|
|
comebackBonusEnabled: boolean;
|
|
comebackBonusPoints: number;
|
|
penaltyForWrongAnswer: boolean;
|
|
penaltyPercent: number;
|
|
firstCorrectBonusEnabled: boolean;
|
|
firstCorrectBonusPoints: number;
|
|
}
|
|
|
|
export const DEFAULT_GAME_CONFIG: GameConfig = {
|
|
shuffleQuestions: false,
|
|
shuffleAnswers: false,
|
|
hostParticipates: true,
|
|
randomNamesEnabled: false,
|
|
streakBonusEnabled: false,
|
|
streakThreshold: 3,
|
|
streakMultiplier: 1.1,
|
|
comebackBonusEnabled: false,
|
|
comebackBonusPoints: 50,
|
|
penaltyForWrongAnswer: false,
|
|
penaltyPercent: 25,
|
|
firstCorrectBonusEnabled: false,
|
|
firstCorrectBonusPoints: 50,
|
|
};
|
|
|
|
export interface Quiz {
|
|
title: string;
|
|
questions: Question[];
|
|
config?: GameConfig;
|
|
}
|
|
|
|
export type QuizSource = 'manual' | 'ai_generated';
|
|
|
|
export interface SavedQuiz extends Quiz {
|
|
id: string;
|
|
source: QuizSource;
|
|
aiTopic?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
config?: GameConfig;
|
|
}
|
|
|
|
export interface QuizListItem {
|
|
id: string;
|
|
title: string;
|
|
source: QuizSource;
|
|
aiTopic?: string;
|
|
questionCount: number;
|
|
shareToken?: string;
|
|
isShared: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface ProcessedDocument {
|
|
type: 'native' | 'text';
|
|
content: string | Buffer;
|
|
mimeType?: string;
|
|
}
|
|
|
|
export interface GenerateQuizOptions {
|
|
topic?: string;
|
|
questionCount?: number;
|
|
documents?: ProcessedDocument[];
|
|
aiProvider?: AIProvider;
|
|
apiKey?: string;
|
|
geminiModel?: string;
|
|
openRouterModel?: string;
|
|
openAIModel?: string;
|
|
accessToken?: string;
|
|
}
|
|
|
|
export interface PointsBreakdown {
|
|
basePoints: number;
|
|
streakBonus: number;
|
|
comebackBonus: number;
|
|
firstCorrectBonus: number;
|
|
penalty: number;
|
|
total: number;
|
|
}
|
|
|
|
export interface Player {
|
|
id: string;
|
|
name: string;
|
|
score: number;
|
|
previousScore: number;
|
|
streak: number;
|
|
lastAnswerCorrect: boolean | null;
|
|
selectedShape: 'triangle' | 'diamond' | 'circle' | 'square' | null;
|
|
pointsBreakdown: PointsBreakdown | null;
|
|
isBot: boolean;
|
|
avatarSeed: number;
|
|
color: string;
|
|
}
|
|
|
|
// Network Types
|
|
export interface ExportedQuiz {
|
|
title: string;
|
|
source: QuizSource;
|
|
aiTopic?: string;
|
|
config?: GameConfig;
|
|
questions: Question[];
|
|
}
|
|
|
|
export interface QuizExportFile {
|
|
version: 1;
|
|
exportedAt: string;
|
|
quizzes: ExportedQuiz[];
|
|
}
|
|
|
|
export type NetworkMessage =
|
|
| { type: 'JOIN'; payload: { name: string; reconnect?: boolean; previousId?: string } }
|
|
| { type: 'WELCOME'; payload: {
|
|
playerId: string;
|
|
quizTitle: string;
|
|
players: Player[];
|
|
gameState?: GameState;
|
|
score?: number;
|
|
streak?: number;
|
|
hasAnswered?: boolean;
|
|
lastAnswerCorrect?: boolean | null;
|
|
lastPointsEarned?: number;
|
|
selectedShape?: 'triangle' | 'diamond' | 'circle' | 'square' | null;
|
|
currentQuestionIndex?: number;
|
|
totalQuestions?: number;
|
|
questionText?: string;
|
|
options?: AnswerOption[];
|
|
correctShape?: string;
|
|
timeLeft?: number;
|
|
assignedName?: string;
|
|
presenterId?: string | null;
|
|
} }
|
|
| { type: 'PLAYER_JOINED'; payload: { player: Player } }
|
|
| { type: 'GAME_START'; payload: {} }
|
|
| { type: 'START_COUNTDOWN'; payload: { duration: number } }
|
|
| {
|
|
type: 'QUESTION_START';
|
|
payload: {
|
|
totalQuestions: number;
|
|
currentQuestionIndex: number;
|
|
timeLimit: number;
|
|
correctShape: string;
|
|
questionText: string;
|
|
options: AnswerOption[];
|
|
}
|
|
}
|
|
| { type: 'ANSWER'; payload: { playerId: string; isCorrect: boolean; selectedShape: 'triangle' | 'diamond' | 'circle' | 'square' } }
|
|
| { type: 'RESULT'; payload: { isCorrect: boolean; scoreAdded: number; newScore: number; breakdown: PointsBreakdown } }
|
|
| { type: 'TIME_SYNC'; payload: { timeLeft: number } }
|
|
| { type: 'TIME_UP'; payload: {} }
|
|
| { type: 'SHOW_SCOREBOARD'; payload: { players: Player[] } }
|
|
| { type: 'GAME_OVER'; payload: { players: Player[] } }
|
|
| { type: 'PRESENTER_CHANGED'; payload: { presenterId: string | null } }
|
|
| { type: 'ADVANCE'; payload: { action: 'START' | 'NEXT' | 'SCOREBOARD' } }; |