# 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 награда. RU сейчас, EN-алиасы потом через конфиг ### Камера и «показать меня» Оверлей показывает не весь мир, а окно-видеоискатель. Мир — горизонтальная полоса зон: лес | рудник | река | костёр/кузница | кухня. - **Автотур**: камера сама плавно переезжает между зонами с активностью, стоянка ~30–60 сек на зону; без активности — медленный дрейф - **Спотлайт через Channel Points**: стример создаёт кастомную награду «Показать меня» в твитч-панели → зритель выкупает за свои очки канала (Twitch сам начисляет и списывает, своя экономика не нужна) → сервер ловит redemption по EventSub → камера едет к аватару на ~1 минуту → возврат в тур. Очередь спотлайтов; балансировка спроса — позже, другими наградами за те же очки - Камера стрима **авторитарна на сервере** — все клиенты стрима видят одно и то же; веб-страница-мир получает свободную камеру + «следовать за мной» ### Кооп-костёр - Общий пул прогресса + ledger персональных вкладов - Декей съедает вклады → визуальный откат, если долго никто не работает - Уровни костра 1–5 ### Отдых (бафф за активность, не за силу) - `!отдых` у костра тратит рыбу → **+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 или палитрные свопы) - Событие усталости: аватар временно перестаёт работать, зритель реагирует - Совместная работа: несколько аватаров у одной ноды дают небольшой бонус (социальная связка) - Рандомные события для удержания: золотая жила, дух леса и т.п.