You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
541 lines
16 KiB
541 lines
16 KiB
# 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<string, number>; |
|
finalAnswers: Record<string, string>; |
|
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 |
|
<script> |
|
import * as m from "$lib/paraglide/messages"; |
|
</script> |
|
|
|
{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
|
|
|