141 lines
7.3 KiB
Markdown
141 lines
7.3 KiB
Markdown
# 🌙 Уютная викторина — Calm Family Quiz
|
||
|
||
Спокойная семейная игра-викторина в стиле «Своей игры», созданная для уютного
|
||
вечера перед сном. Все смотрят на одно табло, ведущий управляет игрой, команда
|
||
отвечает вместе — без таймеров, давления и спешки.
|
||
|
||
## 🚀 Запуск
|
||
|
||
```bash
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
Откройте адрес из консоли (обычно `http://localhost:5173`).
|
||
|
||
### Прочее
|
||
|
||
```bash
|
||
npm run build # production-сборка
|
||
npm run preview # предпросмотр сборки
|
||
npm run check # проверка типов (svelte-check)
|
||
```
|
||
|
||
## 🎮 Как играть
|
||
|
||
1. **Выбор набора** — выберите тему вопросов.
|
||
2. **Табло** — категории и карточки очков (100 / 200 / 300 / 500 / 1000).
|
||
Чем дороже карточка, тем сложнее вопрос.
|
||
3. Ведущий открывает вопрос, затем «Показать ответ», и решает:
|
||
- **Засчитать +N** — команде идут очки;
|
||
- **Засчитать частично +N/2** — для «почти правильного» ответа ведущий может
|
||
дать половину стоимости вопроса;
|
||
- **Не засчитывать** — без очков, но вопрос закрыт.
|
||
- Для обычных вопросов штрафов и минусов нет.
|
||
4. Иногда вместо обычного вопроса открывается **аукцион** — спецвопрос в стиле
|
||
«Своей игры». Ведущий назначает ставку и открывает случайный вопрос, затем
|
||
решает:
|
||
- **Засчитать +N** — команде идут очки;
|
||
- **Не засчитать** — с команды **снимается ставка**, но счёт не уходит в минус.
|
||
Пока аукцион не разрешён, ведущий может его отменить и вернуться к табло
|
||
без потерь.
|
||
5. У некоторых вопросов есть необязательная **подсказка** 💡. Ведущий может
|
||
показать её по желанию. Если подсказка показана — ответ засчитывается только
|
||
за половину стоимости: мягкая помощь без давления.
|
||
6. Для творческих вопросов (type `creative`) кнопка мягче:
|
||
«Засчитать творческий ответ +N».
|
||
7. Сыгранный вопрос затемняется и больше не выбирается.
|
||
8. Когда сыграны все вопросы — экран результата с мягкой финальной фразой.
|
||
|
||
Прогресс сохраняется автоматически — после перезагрузки игра продолжится с того же места.
|
||
|
||
## 🧩 Добавление нового набора вопросов
|
||
|
||
Архитектура задумана так, чтобы наборы добавлялись без правок UI.
|
||
|
||
**Структура набора:** ровно **5 категорий**, в каждой ровно **5 вопросов** с
|
||
номиналами **100 / 200 / 300 / 500 / 1000** (от простого к сложному). Вопросы
|
||
бывают `strict` (однозначный ответ) и `creative` (творческий). У вопроса может
|
||
быть необязательное поле `optionalHint` — подсказка для ведущего.
|
||
|
||
1. Создайте файл `src/lib/question-sets/<name>.ts`:
|
||
|
||
```ts
|
||
import type { QuestionSet } from '$lib/types';
|
||
|
||
export const mySet: QuestionSet = {
|
||
id: 'my-set', // уникальный
|
||
title: 'Моя тема',
|
||
description: 'Короткое описание',
|
||
ageRange: '6+ лет',
|
||
categories: [
|
||
{
|
||
id: 'cat-1', // уникально в пределах набора
|
||
title: 'Категория',
|
||
questions: [
|
||
{ id: 'q1', points: 100, question: '...', answer: '...', type: 'strict' },
|
||
{ id: 'q2', points: 200, question: '...', answer: '...', optionalHint: '...', type: 'strict' },
|
||
{ id: 'q3', points: 300, question: '...', answer: '...', type: 'strict' },
|
||
{ id: 'q4', points: 500, question: '...', answer: '...', type: 'creative' },
|
||
{ id: 'q5', points: 1000, question: '...', answer: '...', type: 'strict' }
|
||
// ровно 5 вопросов с очками 100/200/300/500/1000
|
||
]
|
||
}
|
||
// …ещё 4 категории (всего 5)
|
||
]
|
||
};
|
||
```
|
||
|
||
2. Зарегистрируйте набор в `src/lib/question-sets/index.ts`:
|
||
|
||
```ts
|
||
import { familyKnowledgeJourneySet } from './family-knowledge-journey';
|
||
import { mySet } from './my-set';
|
||
export const questionSets = [familyKnowledgeJourneySet, mySet];
|
||
```
|
||
|
||
Готово — набор автоматически появится на экране выбора.
|
||
|
||
## 🏗️ Архитектура
|
||
|
||
Код аккуратно разделён по слоям:
|
||
|
||
```
|
||
src/
|
||
├─ lib/
|
||
│ ├─ types.ts # Доменные типы (Question, Category, QuestionSet, GameState)
|
||
│ ├─ question-sets/ # ДАННЫЕ (отделены от UI)
|
||
│ │ ├─ family-knowledge-journey.ts # набор вопросов
|
||
│ │ └─ index.ts # реестр наборов + хелперы поиска
|
||
│ ├─ stores/
|
||
│ │ └─ game.ts # СОСТОЯНИЕ игры (Svelte store + localStorage)
|
||
│ └─ components/ # UI-компоненты
|
||
│ ├─ QuestionSetSelector.svelte
|
||
│ ├─ GameBoard.svelte
|
||
│ ├─ QuestionCard.svelte
|
||
│ ├─ ScorePanel.svelte
|
||
│ ├─ QuestionModal.svelte
|
||
│ ├─ HostControls.svelte
|
||
│ ├─ GameResult.svelte
|
||
│ └─ ConfirmDialog.svelte
|
||
└─ routes/
|
||
├─ +layout.svelte # подключение глобальных стилей
|
||
├─ +layout.ts # CSR-only (localStorage)
|
||
└─ +page.svelte # оркестрация экранов
|
||
```
|
||
|
||
**Принципы:**
|
||
- Данные вопросов полностью отделены от состояния и UI.
|
||
- Состояние игры живёт в Svelte store с автосохранением в `localStorage`
|
||
и восстанавливается после перезагрузки.
|
||
- Очки берутся из данных вопроса — ничего не хардкодится в UI.
|
||
- Никакого бэкенда, БД, авторизации, мультиплеера или редактора в MVP.
|
||
|
||
## 🎨 Стек
|
||
|
||
- **SvelteKit + TypeScript**
|
||
- **Tailwind CSS v4** (через `@tailwindcss/vite`)
|
||
- Без тяжёлых UI-библиотек — только лёгкие зависимости.
|
||
|
||
Лицензия: личный/семейный проект.
|