# Kuldvillak/Jeopardy Game Platform - Technical Documentation ## Table of Contents - [Overview](#overview) - [Tech Stack](#tech-stack) - [Architecture](#architecture) - [Directory Structure](#directory-structure) - [Game Flow](#game-flow) - [State Management](#state-management) - [Components](#components) - [Data Types](#data-types) - [Features](#features) - [Multi-Window Sync](#multi-window-sync) --- ## Overview **Ultimate Gaming** is a web-based platform for hosting trivia game shows, currently featuring **Kuldvillak** - a Jeopardy-style quiz game. The platform supports: - Full game editor with customizable settings - Dual-screen setup (Moderator + Projector views) - Real-time cross-tab synchronization - Theme customization - Internationalization (Estonian/English) - Audio management - Persistent game storage --- ## Tech Stack | Technology | Version | Purpose | |------------|---------|---------| | **SvelteKit** | 2.48.5 | Full-stack framework | | **Svelte 5** | 5.43.8 | UI with Runes reactivity | | **TailwindCSS** | 4.1.17 | Utility-first styling | | **TypeScript** | 5.9.3 | Type safety | | **Paraglide** | 2.5.0 | i18n (internationalization) | | **Vite** | 7.2.2 | Build tool | --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Ultimate Gaming │ ├─────────────────────────────────────────────────────────────┤ │ Routes │ │ ├── / (Home - Game Selection) │ │ ├── /kuldvillak (Game Menu) │ │ ├── /kuldvillak/edit (Game Editor) │ │ └── /kuldvillak/play (Game Play) │ │ ├── ModeratorView (Controls) │ │ └── ProjectorView (Display) │ ├─────────────────────────────────────────────────────────────┤ │ State Management (Svelte 5 Runes) │ │ ├── gameSession.svelte.ts (Live game state + BroadcastAPI) │ │ ├── kuldvillak.svelte.ts (Game store - editor) │ │ ├── theme.svelte.ts (Color theming) │ │ ├── audio.svelte.ts (Music/SFX volumes) │ │ └── persistence.ts (localStorage utilities) │ ├─────────────────────────────────────────────────────────────┤ │ Components │ │ ├── Shared (Settings, ColorPicker, Toast, etc.) │ │ └── Kuldvillak UI (Buttons, Cards, Logo, etc.) │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Directory Structure ``` src/ ├── lib/ │ ├── assets/ # Static assets (favicon) │ ├── components/ │ │ ├── kuldvillak/ │ │ │ └── ui/ # Game-specific UI components │ │ │ ├── KvButtonPrimary.svelte │ │ │ ├── KvButtonSecondary.svelte │ │ │ ├── KvEditCard.svelte │ │ │ ├── KvGameLogo.svelte │ │ │ ├── KvNumberInput.svelte │ │ │ ├── KvPlayerCard.svelte │ │ │ └── KvProjectorCard.svelte │ │ ├── ColorPicker.svelte │ │ ├── ConfirmDialog.svelte │ │ ├── LanguageSwitcher.svelte │ │ ├── Settings.svelte │ │ ├── Slider.svelte │ │ └── Toast.svelte │ ├── paraglide/ # Generated i18n code │ ├── stores/ │ │ ├── audio.svelte.ts │ │ ├── gameSession.svelte.ts │ │ ├── kuldvillak.svelte.ts │ │ ├── persistence.ts │ │ └── theme.svelte.ts │ ├── types/ │ │ └── kuldvillak.ts # TypeScript interfaces │ └── index.ts # Library exports ├── routes/ │ ├── kuldvillak/ │ │ ├── edit/ │ │ │ └── +page.svelte # Game editor │ │ ├── play/ │ │ │ ├── +page.svelte # Route handler │ │ │ ├── ModeratorView.svelte │ │ │ └── ProjectorView.svelte │ │ ├── +layout.svelte │ │ └── +page.svelte # Game menu │ ├── +error.svelte │ ├── +layout.svelte # Root layout │ ├── +page.svelte # Home page │ └── layout.css # Global styles + Tailwind ├── app.html ├── app.d.ts ├── hooks.server.ts └── hooks.ts ``` --- ## Game Flow ### 1. Game Creation (Editor) ``` /kuldvillak/edit │ ├── Configure Settings │ ├── Number of rounds (1-2) │ ├── Timer durations │ ├── Point values (preset or custom) │ ├── Daily doubles per round │ └── Enable/disable final round │ ├── Add Teams (2-6 players) │ ├── Fill Categories & Questions │ ├── 6 categories per round │ ├── 5 questions per category │ └── Mark daily doubles │ ├── Final Round Question (optional) │ └── Start Game → Opens Moderator + Projector views ``` ### 2. Game Phases ``` ┌─────────────┐ │ intro │ ← Kuldvillak logo screen └──────┬──────┘ │ ┌──────▼──────────────┐ │ intro-categories │ ← Category reveal animation └──────┬──────────────┘ │ ┌──────▼──────┐ │ board │ ← Question selection grid └──────┬──────┘ │ ┌──────▼──────────────┐ │ daily-double (if) │ ← Wager selection └──────┬──────────────┘ │ ┌──────▼──────┐ │ question │ ← Display question, teams answer └──────┬──────┘ │ ├── Correct → Award points ├── Wrong → Deduct points, next team └── Skip → Reveal answer │ └──────► Back to board │ │ (After all questions) │ ┌──────▼────────────────┐ │ final-intro │ ← Final round logo ├───────────────────────┤ │ final-category │ ← Reveal final category ├───────────────────────┤ │ final-question │ ← Teams write answers + wagers ├───────────────────────┤ │ final-scores │ ← Final standings └───────────────────────┘ │ ┌──────▼──────┐ │ finished │ └─────────────┘ ``` ### 3. Question Flow ``` Question Selected │ ├── Is Daily Double? │ │ │ ├── YES → Select team → Enter wager → Show question │ └── NO → Show question directly │ ▼ Display Question │ ├── Timer starts │ ├── Team clicks to answer │ │ │ ├── Timer pauses │ │ │ ├── Moderator marks Correct/Wrong │ │ │ │ │ ├── Correct → +points → Show answer → Return to board │ │ └── Wrong → -points → Continue (others can try) │ │ │ └── All teams wrong → Auto-reveal answer │ └── Timer expires → Auto-reveal answer ``` --- ## State Management ### GameSessionStore (`gameSession.svelte.ts`) The heart of the game - manages live game state with cross-tab synchronization. **Key Features:** - Uses `BroadcastChannel` API for real-time sync between Moderator and Projector - Persists to `localStorage` for session recovery - Single internal timer managed by moderator view **State Interface:** ```typescript interface GameSessionState { // Game data name: string; settings: GameSettings; teams: Team[]; rounds: Round[]; finalRound: FinalRound | null; // Game phase phase: GamePhase; currentRoundIndex: number; activeTeamId: string | null; // Animation state introCategoryIndex: number; categoriesIntroduced: boolean; boardRevealed: boolean; // Question state currentQuestion: { roundIndex, categoryIndex, questionIndex } | null; showAnswer: boolean; wrongTeamIds: string[]; lastAnsweredTeamId: string | null; lastAnswerCorrect: boolean | null; // Daily Double dailyDoubleWager: number | null; // Final Round finalCategoryRevealed: boolean; finalWagers: Record; finalAnswers: Record; finalRevealed: string[]; // Timer timerRunning: boolean; timerSeconds: number; timerMax: number; timeoutCountdown: number | null; revealCountdown: number | null; skippingQuestion: boolean; // Tracking questionsAnswered: number; currentQuestionNumber: number; questionResults: QuestionResult[]; } ``` ### ThemeStore (`theme.svelte.ts`) Manages color theming with cross-window sync. **CSS Variables controlled:** - `--kv-blue` (primary) - `--kv-yellow` (secondary) - `--kv-text` - `--kv-background` ### AudioStore (`audio.svelte.ts`) Manages background music and sound effects volumes. --- ## Components ### Shared Components | Component | Purpose | |-----------|---------| | `Settings.svelte` | Modal with audio, language, and color settings | | `ColorPicker.svelte` | Full HSL color picker with swatches | | `ConfirmDialog.svelte` | Confirmation modal | | `Toast.svelte` | Notification messages | | `Slider.svelte` | Volume control slider | | `LanguageSwitcher.svelte` | ET/EN language toggle | ### Kuldvillak UI Components | Component | Purpose | |-----------|---------| | `KvButtonPrimary.svelte` | Yellow primary action button | | `KvButtonSecondary.svelte` | Blue secondary action button | | `KvEditCard.svelte` | Team card for moderator (score adjust, correct/wrong) | | `KvPlayerCard.svelte` | Basic player display card | | `KvProjectorCard.svelte` | Player card for projector view | | `KvNumberInput.svelte` | Styled number input with +/- buttons | | `KvGameLogo.svelte` | SVG logo component with variants | --- ## Data Types ### Core Types (`types/kuldvillak.ts`) ```typescript interface Question { id: string; question: string; answer: string; points: number; isDailyDouble: boolean; isRevealed: boolean; imageUrl?: string; } interface Category { id: string; name: string; questions: Question[]; } interface Round { id: string; name: string; categories: Category[]; pointMultiplier: number; } interface Team { id: string; name: string; score: number; } interface GameSettings { numberOfRounds: 1 | 2; pointValuePreset: 'round1' | 'round2' | 'custom' | 'multiplier'; pointValues: number[]; basePointValue: number; categoriesPerRound: number; // Default: 6 questionsPerCategory: number; // Default: 5 dailyDoublesPerRound: number[]; enableFinalRound: boolean; enableSoundEffects: boolean; allowNegativeScores: boolean; maxTeams: number; // Max: 6 defaultTimerSeconds: number; answerRevealSeconds: number; } type GamePhase = | 'intro' // Logo screen | 'intro-categories' // Category reveal | 'lobby' | 'board' // Question grid | 'question' | 'answer' | 'daily-double' | 'final-intro' | 'final-category' | 'final-wagers' | 'final-question' | 'final-reveal' | 'final-scores' | 'finished'; ``` --- ## Features ### 1. Game Editor - Auto-save to localStorage - Import/Export JSON game files - Visual daily double indicators - Point value presets or custom - Final round configuration ### 2. Moderator View - Full game control - Score adjustments - Timer control (start/stop/reset) - Question skip functionality - Open projector window button ### 3. Projector View - Full-screen presentation - Animated category introductions - Staggered board reveal animation - Question expand animation - Team score display during questions - Final scores display ### 4. Animations (ProjectorView) - **Category Intro**: 500ms fade-in → 1500ms shown → 500ms push-out - **Board Reveal**: Staggered 50ms per cell - **Question Expand**: From grid cell position to fullscreen ### 5. Cross-Tab Sync - Real-time state sync via BroadcastChannel - localStorage persistence for recovery - Single timer owner (moderator) --- ## Multi-Window Sync ``` ┌─────────────────┐ BroadcastChannel ┌─────────────────┐ │ Moderator Tab │ ◄─────────────────────────────► │ Projector Tab │ │ │ │ │ │ - Controls │ STATE_UPDATE │ - Display │ │ - Timer owner │ ─────────────────────────────────►│ - Animations │ │ - Score adjust │ │ - Read-only │ └────────┬────────┘ └─────────────────┘ │ │ localStorage ▼ ┌─────────────────┐ │ Session Store │ │ (persistence) │ └─────────────────┘ ``` --- ## Styling ### Design Tokens (CSS Variables) ```css :root { /* Colors */ --kv-blue: #003B9B; /* Primary */ --kv-yellow: #FFAB00; /* Secondary/Accent */ --kv-text: #FFFFFF; --kv-background: #000000; --kv-green: #009900; /* Correct */ --kv-red: #FF3333; /* Wrong */ /* Fonts */ --kv-font-title: 'Swiss 921'; --kv-font-body: 'Swiss 921'; --kv-font-price: 'Bebas Neue'; --kv-font-question: 'ITC Korinna'; /* Shadows */ --kv-shadow-text: 6px 6px 4px rgba(0,0,0,0.5); --kv-shadow-price: 8px 8px 4px rgba(0,0,0,0.5); --kv-shadow-button: 0 4px 4px rgba(0,0,0,0.25), 8px 8px 4px rgba(0,0,0,0.5); } ``` ### Tailwind Theme Extensions Custom Tailwind classes prefixed with `kv-`: - Colors: `bg-kv-blue`, `text-kv-yellow`, etc. - Fonts: `font-kv-body`, `font-kv-price`, etc. --- ## i18n (Internationalization) Using **Paraglide** for type-safe translations. **Supported Languages:** - Estonian (et) - Primary - English (en) **Message Files:** - `messages/et.json` - `messages/en.json` **Usage:** ```svelte {m.kv_play_daily_double()} ``` --- ## Development ```bash # Install dependencies npm install # Start dev server npm run dev # Build for production npm run build # Type check npm run check ``` --- ## Future Considerations 1. **Database Backend**: Replace localStorage with proper database for persistence 2. **WebSocket**: Real-time sync without BroadcastChannel limitations 3. **Sound Effects**: Implement actual SFX playback 4. **More Game Modes**: "Rooside Sõda" (Family Feud) placeholder exists 5. **User Accounts**: Save games to cloud 6. **Mobile Buzzer**: Player buzzer app for team selection