Files
idle-xboct/docs/PLAN.md
T

222 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Idle-XBOCT — план игры для Twitch-чата
Смесь Stream Avatars и idle-игр (Melvor Idle / Idle Iktah) с кооп-задачами.
Аватары зрителей живут на экране стрима; команды чата управляют ими.
**Ключевой принцип:** весь геймплей через команды чата — клиенты только отображают состояние.
---
## Стек
| Слой | Выбор |
| -------- | ------------------------------------------------------------------------------------------- |
| Монорепа | pnpm workspaces + TypeScript strict |
| Клиенты | Svelte 5 + PixiJS 8 (8.x) + bitecs 0.4 |
| Сервер | Node.js, `node:http` + `ws` + `sirv` (без фреймворка), симуляция на bitecs |
| Twitch | `@twurple/easy-bot` (чтение команд + ответы в чат), `@twurple/eventsub-ws` (Channel Points) |
| БД | SQLite через тонкий слой persist. M1: встроенный `node:sqlite` (Node ≥24, без нативных сборок и install-скриптов); замена на `better-sqlite3`/др. — при необходимости, не трогая остальной код |
Решения по ECS: **bitecs, новый API 0.4** (в 0.4 API переписан; старый доступен как `bitecs/legacy`).
Запасной вариант — koota (pmndrs), если новый API окажется слишком зубодробительным.
miniplex отклонён (объектный оверхед + не развивается с 2023).
**Уточнение по факту M1:** вертикальный срез сделан на plain TS за узким интерфейсом
`SimWorld` (сущностей — десятки, выгод от ECS ноль, а 0.4 требует чтения исходников).
Решение по ECS — в M2 по факту роста: bitecs 0.4, koota или остаться на plain TS,
если масштабы не потребуют.
## Архитектура
```txt
Twitch IRC ──> [сервер: очередь команд] ──> тик 1 Гц
SQLite <── persist WS-снапшот+дельты ──> overlay (OBS browser source)
└──> web-страница-мир
```
**Сервер полностью авторитарный.** Все зрители видят одно и то же; OBS произвольно
перезагружает browser source; сейвы в одном месте.
### Два слоя состояния
Не тащить всё в ECS:
- **Симуляционный слой (bitecs)** — то, что тикает и рендерится:
аватары (позиция, цель, прогресс действия), ноды ресурсов, костёр на сцене.
- **Мета-слой (plain TS records + SQLite)** — инвентарь, XP/скиллы, рецепты,
вклад в костёр, подарки. Живёт вне ECS, сохраняется напрямую.
## Синхронизация
- Подключение → полный снапшот; далее дельты раз в тик + события («+XP», «скрафчено», «вклад в костёр»)
- **Команды обрабатываются сразу при приходе**, не ждут тика → мгновенная реакция на `!рубить`
- **Движение — декларативные твины**: сервер один раз шлёт «идёт из A в B, скорость v»,
клиент анимирует сам. Позиции и прогресс-бары дорисовываются локально между тиками
- Тик фиксированный 1 сек (захочется отзывчивее — константа → 2–4 Гц)
- Симуляция от wall-clock таймстемпов: при старте сервера пропущенное время
догоняется ретроактивно (декей костра копился, пока стрим был оффлайн)
## Хостинг-ready (почти бесплатно сейчас)
Локально для себя, но с прицелом на будущий мультистримерный хостинг:
1. **`channelId` везде с первого дня**: колонки в таблицах БД, поле в командах и WS-сообщениях
2. **Интерфейс `TwitchSource`**: локально = свой bot-токен через twurple;
на хостинге замена на OAuth-flow без изменений в остальном коде
3. **Тонкий слой persist** над SQLite — заменим при необходимости
4. **Контент как данные** (JSON-in-TS в `shared`) — баланс правится без кода
Протокол — контракт: клиенты зависят только от него, язык сервера можно сменить позже.
## Структура репозитория
```txt
idle-xboct/
├─ packages/
│ ├─ shared/ # протокол WS, контент (предметы/скиллы/ноды/рецепты), XP-кривые.
│ │ # Чистые данные, без DOM/canvas зависимостей — импортируется и сервером
│ ├─ server/ # node:http + ws + twurple-bot + bitecs-sim + sqlite
│ │ └─ src/{twitch, sim, game, net, persist}
│ ├─ render/ # pixi-зависимый общий рендер (аватары, ноды, интерполяция)
│ ├─ overlay/ # Svelte 5 + Pixi 8 — компактный, прозрачный фон, для OBS
│ └─ web/ # Svelte 5 + Pixi 8 — страница-мир: большой канвас + панели
```
`web` = богатый спектатор, OAuth зрителям не нужен: read-only панели
(свой инвентарь по нику, дерево рецептов, лидерборды, прогресс костра).
Кликабельные действия — возможное будущее расширение с Twitch OAuth.
## Игровые механики MVP
Навыки: Рубка леса, Добыча руды, Кузнечное дело, Рыбалка, Кулинария.
XP-кривая Melvor-подобная, уровни открывают ноды/рецепты.
- Действие = 15–60 сек с видимым прогрессом (зритель должен увидеть аватар за работой)
- Ноды ресурсов: тиры деревьев/руды/рыбных мест, респавн
- Крафт: топоры/кирки тирами; предметы — стаки, строковые slug-id
- Инструменты можно менять и дарить (`!подарить @ник предмет`); подарки только
**перемещают** предметы — нельзя подарить то, чего у тебя нет
- Аватар продолжает текущее действие, пока не придёт другая команда (`!стоп` — остановиться)
- Онбординг — в самом виджете (HUD-подсказки), чат ботом не засоряем
- Команды: `!рубить`, `!копать`, `!ковать`, `!рыбачить`, `!готовить`, `!отдых`,
`!стоп`, `!подарить`, `!топ`; спотлайт — не команда, а Channel Point награда.
M4: `!костёр` — жечь брёвна у костра (прогресс пулу + личный вклад).
RU сейчас, EN-алиасы потом через конфиг
### Камера и «показать меня»
Оверлей показывает не весь мир, а окно-видеоискатель. Мир — горизонтальная полоса зон:
лес | рудник | река | костёр/кузница | кухня.
- **Автотур**: камера сама плавно переезжает между зонами с активностью,
стоянка ~30–60 сек на зону; без активности — медленный дрейф
- **Спотлайт через Channel Points**: стример создаёт кастомную награду «Показать меня»
в твитч-панели → зритель выкупает за свои очки канала (Twitch сам начисляет и списывает,
своя экономика не нужна) → сервер ловит redemption по EventSub → камера едет к аватару
на ~1 минуту → возврат в тур. Очередь спотлайтов; балансировка спроса — позже,
другими наградами за те же очки
- Реализация (M5): имя награды настраивается (`SPOTLIGHT_REWARD`, по умолчанию
«Показать меня»), длительность — `SPOTLIGHT_MS`; веб-страница получает спотлайт
как обычную камеру. Дашборд стримера — `/dashboard` на порту сервера
(состояние, сброс костра/зрителя, объявления в чат; опциональный `ADMIN_KEY`)
- Камера стрима **авторитарна на сервере** — все клиенты стрима видят одно и то же;
веб-страница-мир получает свободную камеру + «следовать за мной»
### Кооп-костёр
- Общий пул прогресса + ledger персональных вкладов
- Декей съедает вклады → визуальный откат, если долго никто не работает
- Уровни костра 15
- Реализация (M4): `!костёр` сжигает бревно за цикл (10 очков), декей ~4 очка/мин
и догоняется ретроактивно за оффлайн; уровень усиливает бафф отдыха (+25% к бонусу
за уровень), размер пламени растёт с уровнем
### Отдых (бафф за активность, не за силу)
- `!отдых` у костра тратит рыбу → **+20% добычи на 5 минут за 1 минуту отдыха**
- Стакается ограниченно; влияет почти незаметно — механика вовлечения, не прогресса
### Антиспам
Кулдаун на смену действия, троттлинг ответов бота (пишет только при значимых событиях).
## Милстоуны
- **M0 — каркас.** Монорепа, overlay+web скелеты, WS-эхо.
✅ аватар-заглушка виден в OBS
- **M1 — вертикальный срез.** `!рубить`/`!стоп`, спавн аватара (спрайт-человечек, ник
над головой в цвете чата), ходьба к дереву, брёвна + XP + уровень, автосейв,
WS-снапшот+дельты, камера-окно + простейший автотур, FakeTwitchSource — отладка
команд без живого чата.
✅ зритель пишет `!рубить` и видит себя рубящим в лесу
- **M2 — руда и кузница.** `!копать` + `!ковать` + тиры топоров/кирок.
✅ скрафтил железный топор → рубишь заметно быстрее
- **M3 — рыба и кухня.** `!рыбачить` + `!готовить` + `!отдых` у костра.
✅ рыба → жареная рыба → бафф +20%/5 мин
- **M4 — кооп-костёр.** Ledger вкладов, декей с откатом, уровни 1–5.
✅ растёт общими силами, без активности откатывается
- **M5 — экономика и полировка.** Спотлайт через Channel Points (EventSub, очередь),
`!подарить`, `!топ`, дашборд стримера (баланс-конфиги, сбросы).
✅ полная экономика + управление без правки кода
## Риски / известные ловушки
- **bitecs 0.4**: мало примеров под новый API — время на чтение исходников;
системы оборачиваем тонким интерфейсом для дешёвого fallback
- **OBS browser source кэширует**: dev через отдельный порт, версионирование протокола
в рукопожатии клиента/сервера
- **Free-версии паков (Sprout Lands, Mystic Woods) — только некоммерческое**:
для монетизированного канала — CC0 или платная лицензия
- **Rate limits чата**: бот отвечает дозированно
- **EventSub**: нужен Twitch-апп (client id/secret) у стримера; EventSub WebSocket
работает локально без публичного webhook-URL — ок для локального MVP
## Ассеты
Аватар зрителя = обычный спрайт-человечек целиком — лицо и тело из пака, фото профиля
Twitch не используем. На старте все зрители с одним и тем же спрайтом; личность читается
по нику над головой в цвете ника чата. Разные скины — на будущее (донатеры/сабы), поэтому
пак желателен с запасом: генератор персонажей или палитрные свопы.
### Человечки + скины
- **LimeZu — Modern Interiors / Modern Exteriors** (itch.io, name-your-own-price ≈ $5,
лицензия CC-BY: коммерция ок с указанием авторства) — основной кандидат: домики, мебель,
здания уровня «ратуша», предметы — всё в одном стиле; генератор персонажей в платной
версии пригодится, когда дойдёт до скинов донатеров
- **Universal LPC Spritesheet Generator** (бесплатно, онлайн) — сборка спрайт-листов
с ходьбой/анимациями из тысяч деталей. Лицензия GPL/CC-BY-SA — проверить перед коммерциализацией
- **Kenney.nl** — CC0 (тайлы, предметы, простые человечки) — **текущая база проекта**;
конкретные паки подбираем при M1 (нужны: персонаж с ходьбой, деревья/камни, вода)
### Домики, костёр, ратуша
- **Sprout Lands** (Cup Nooble, itch.io) — милый фермерский стиль; free-версия только
для некоммерческого, premium от ~$4 снимает ограничение
- **Mystic Woods** (Game Endeavor, itch.io) — есть бесплатная lite-версия: домики, костёр,
природа; free-версия тоже только для некоммерческого
- **OpenGameArt.org** — много RPG-паков зданий, лицензии смешанные — читать каждую
- **CraftPix freebies** — бесплатные наборы зданий/персонажей
### Правила выбора
1. Для стрим-игры (канал может быть монетизирован) — CC0 или платная лицензия;
CC-BY только с готовностью указать авторство (можно в дашборде)
2. Один стиль важнее качества каждого пака — брать линейку у одного автора
(LimeZu закрывает людей, дома, ратушу и предметы)
3. **Решено: идём полностью на бесплатном.** База — Kenney (CC0); OpenGameArt — с проверкой
лицензии каждого ассета. Платные паки (LimeZu ≈ $5) — опция на потом, если захочется
единого стиля
4. **M1 — процедурные спрайты в коде** (человечек, топор, деревья рисуются Pixi-графикой):
ноль лицензионных рисков и мгновенные правки; паки подключаем с M2, заменив
`AvatarView`/`TreeView` в `render`
## Идеи на потом
- Скины для донатеров/сабов: разные внешности аватаров (генератор персонажей LimeZu
или палитрные свопы)
- Событие усталости: аватар временно перестаёт работать, зритель реагирует
- Совместная работа: несколько аватаров у одной ноды дают небольшой бонус (социальная связка)
- Рандомные события для удержания: золотая жила, дух леса и т.п.