From 10bd67c951202247947d52c69d1a52823d674971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BD=D0=B8=D1=81=20=D0=A8=D0=BA=D0=B0=D0=B1?= =?UTF-8?q?=D0=B0=D1=82=D1=83=D1=80?= Date: Thu, 12 Feb 2026 11:59:41 +0300 Subject: [PATCH] Phase 0: Project setup for agent-driven development - Phaser 3 + bitECS 0.4 + TypeScript + Vite stack - BootScene with title screen - 6 cursor rules (project context, agent workflow, ECS, chemistry, Phaser, data) - Vitest configured with happy-dom - GDD, engine analysis, implementation plan, progress tracking Co-authored-by: Cursor --- .cursor/rules/agent-workflow.mdc | 30 + .cursor/rules/chemistry-engine.mdc | 40 + .cursor/rules/data-files.mdc | 36 + .cursor/rules/ecs-patterns.mdc | 65 ++ .cursor/rules/phaser-scenes.mdc | 44 + .cursor/rules/project-context.mdc | 28 + .gitignore | 5 + IMPLEMENTATION-PLAN.md | 248 +++++ PROGRESS.md | 52 + engine-analysis.md | 328 ++++++ index.html | 22 + package-lock.json | 1560 ++++++++++++++++++++++++++++ package.json | 23 + src/config.ts | 29 + src/main.ts | 4 + src/scenes/BootScene.ts | 58 ++ synthesis-gdd.md | 606 +++++++++++ tests/setup.test.ts | 38 + tsconfig.json | 17 + vite.config.ts | 23 + 20 files changed, 3256 insertions(+) create mode 100644 .cursor/rules/agent-workflow.mdc create mode 100644 .cursor/rules/chemistry-engine.mdc create mode 100644 .cursor/rules/data-files.mdc create mode 100644 .cursor/rules/ecs-patterns.mdc create mode 100644 .cursor/rules/phaser-scenes.mdc create mode 100644 .cursor/rules/project-context.mdc create mode 100644 .gitignore create mode 100644 IMPLEMENTATION-PLAN.md create mode 100644 PROGRESS.md create mode 100644 engine-analysis.md create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/config.ts create mode 100644 src/main.ts create mode 100644 src/scenes/BootScene.ts create mode 100644 synthesis-gdd.md create mode 100644 tests/setup.test.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.cursor/rules/agent-workflow.mdc b/.cursor/rules/agent-workflow.mdc new file mode 100644 index 0000000..e4aabd8 --- /dev/null +++ b/.cursor/rules/agent-workflow.mdc @@ -0,0 +1,30 @@ +--- +description: Agent development workflow — visual feedback, testing, progress tracking +alwaysApply: true +--- + +# Agent Workflow + +## Visual Verification +After ANY visual change, verify via Playwright browser tools: +1. Ensure dev server is running (`npm run dev`) +2. Navigate to `http://localhost:5173` +3. Take screenshot to see result +4. Check browser console for errors +5. Fix issues before moving on + +## Testing Strategy +- **Chemistry, ecology, math** — write vitest tests FIRST, then implement +- **Visual/rendering** — verify via Playwright screenshots +- Run `npm run test:run` after logic changes + +## After Completing a Task +1. Verify it works (test or screenshot) +2. Update `PROGRESS.md` — mark task complete, add date +3. Check `IMPLEMENTATION-PLAN.md` for what's next + +## Code Standards +- TypeScript strict mode — no `any`, no unsafe `as` casts +- Pure functions for ECS systems +- Descriptive variable names (not `x`, `tmp`, `data`) +- Comments explain WHY, not WHAT diff --git a/.cursor/rules/chemistry-engine.mdc b/.cursor/rules/chemistry-engine.mdc new file mode 100644 index 0000000..404f771 --- /dev/null +++ b/.cursor/rules/chemistry-engine.mdc @@ -0,0 +1,40 @@ +--- +description: Chemistry engine — real science constraints and data patterns +globs: src/chemistry/**/*.ts +alwaysApply: false +--- + +# Chemistry Engine Rules + +## Scientific Accuracy +All chemical data must be **real** (simplified, never fabricated): +- Element properties: atomic number, mass, electronegativity, group, period +- Reactions: real products, real conditions (temperature, catalyst) +- Failed reactions return a **reason**: "Requires catalyst", "Insufficient energy" + +## Element Interface +```typescript +interface Element { + symbol: string; // "Na" + name: string; // "Sodium" + atomicNumber: number; // 11 + atomicMass: number; // 22.99 + electronegativity: number; + category: ElementCategory; + state: MatterState; // at room temperature + color: string; // hex for rendering +} +``` + +## Reaction Lookup +Reactions indexed by **sorted reactant key** for O(1) lookup: +```typescript +// "Cl+Na" → { products: [{ symbol: "NaCl", ... }], ... } +function reactionKey(symbols: string[]): string { + return [...symbols].sort().join('+'); +} +``` + +## Every Reaction Includes +- `description`: human-readable explanation of the science +- `failureReason`: if conditions not met, explain WHY (this is the teaching mechanism) diff --git a/.cursor/rules/data-files.mdc b/.cursor/rules/data-files.mdc new file mode 100644 index 0000000..737d873 --- /dev/null +++ b/.cursor/rules/data-files.mdc @@ -0,0 +1,36 @@ +--- +description: Game data JSON schema and validation conventions +globs: src/data/**/*.json +alwaysApply: false +--- + +# Data File Conventions + +## General Rules +- Every entity has a unique `id` field (string) +- Cross-references use `id` strings, never array indices +- Every entry has a `description` field (feeds the in-game Codex) +- Data must be valid JSON — no comments, no trailing commas + +## elements.json +Array of Element objects. Keyed by `symbol`. All values from real periodic table. +```json +{ + "symbol": "Na", + "name": "Sodium", + "nameRu": "Натрий", + "atomicNumber": 11, + "atomicMass": 22.99, + "electronegativity": 0.93, + "category": "alkali-metal", + "state": "solid", + "color": "#c8c8c8", + "description": "Мягкий щелочной металл. Бурно реагирует с водой." +} +``` + +## reactions.json +Array of Reaction objects. Include science explanation and failure reasons. + +## Validation +All JSON is loaded and type-checked at boot. Schema mismatches crash with clear errors — never silently ignore bad data. diff --git a/.cursor/rules/ecs-patterns.mdc b/.cursor/rules/ecs-patterns.mdc new file mode 100644 index 0000000..3f8e2e5 --- /dev/null +++ b/.cursor/rules/ecs-patterns.mdc @@ -0,0 +1,65 @@ +--- +description: bitECS 0.4 component and system conventions +globs: src/ecs/**/*.ts +alwaysApply: false +--- + +# ECS Patterns (bitECS 0.4) + +## Components — Plain Objects with Arrays +```typescript +// ✅ GOOD: plain object with number arrays +export const Position = { x: [] as number[], y: [] as number[] }; +export const Health = { current: [] as number[], max: [] as number[] }; + +// ❌ BAD: old API (defineComponent, Types) — removed in 0.4 +``` + +## Entity Lifecycle +```typescript +import { createWorld, addEntity, addComponent, removeEntity } from 'bitecs'; + +const world = createWorld(); +const eid = addEntity(world); +addComponent(world, eid, Position); +Position.x[eid] = 100; +Position.y[eid] = 200; +``` + +## Queries — Direct Function Calls +```typescript +import { query, Not } from 'bitecs'; + +// ✅ 0.4 API: query(world, components) +const entities = query(world, [Position, Velocity]); +for (const eid of entities) { + Position.x[eid] += Velocity.x[eid] * delta; +} + +// Operators: Not, And, Or, Any, None +const stationary = query(world, [Position, Not(Velocity)]); +``` + +## Observers (replace enterQuery/exitQuery) +```typescript +import { observe, onAdd, onRemove } from 'bitecs'; + +observe(world, onAdd(Position), (eid) => { /* entity gained Position */ }); +observe(world, onRemove(Position), (eid) => { /* entity lost Position */ }); +``` + +## Systems — Pure Functions +```typescript +export const movementSystem = (world: World, delta: number) => { + for (const eid of query(world, [Position, Velocity])) { + Position.x[eid] += Velocity.x[eid] * delta; + Position.y[eid] += Velocity.y[eid] * delta; + } +}; +``` + +## Rules +- Components = plain objects with `[] as number[]` fields +- Never store strings in components — use numeric IDs, look up in registry +- Systems must not create Phaser objects — only the sync bridge does that +- Parameter order: `addComponent(world, eid, component)` (world first, then eid) diff --git a/.cursor/rules/phaser-scenes.mdc b/.cursor/rules/phaser-scenes.mdc new file mode 100644 index 0000000..a8e11cb --- /dev/null +++ b/.cursor/rules/phaser-scenes.mdc @@ -0,0 +1,44 @@ +--- +description: Phaser scene lifecycle and conventions +globs: src/scenes/**/*.ts +alwaysApply: false +--- + +# Phaser Scene Conventions + +## Lifecycle +``` +constructor → init(data) → preload() → create() → update(time, delta) +``` +- `init`: receive data from scene transitions +- `preload`: load assets (prefer doing this only in BootScene) +- `create`: set up game objects, physics, input, cameras +- `update`: run ECS systems, sync bridge + +## Scene Structure +```typescript +export class GameScene extends Phaser.Scene { + constructor() { + super({ key: 'GameScene' }); + } + + create(): void { + // 1. Initialize ECS world + // 2. Create tilemap + // 3. Spawn entities + // 4. Set up camera + // 5. Launch UI overlay + this.scene.launch('UIScene'); + } + + update(time: number, delta: number): void { + // Run ECS systems in defined order + } +} +``` + +## Rules +- Full transition: `this.scene.start('Name', { data })` +- Overlay: `this.scene.launch('Name')` (for HUD, menus) +- Never put game logic in scenes — delegate to ECS systems +- Scenes manage Phaser objects; ECS manages game state diff --git a/.cursor/rules/project-context.mdc b/.cursor/rules/project-context.mdc new file mode 100644 index 0000000..6ee9dba --- /dev/null +++ b/.cursor/rules/project-context.mdc @@ -0,0 +1,28 @@ +--- +description: Core project context for Synthesis — scientific roguelike +alwaysApply: true +--- + +# Synthesis — Project Context + +## What Is This +2D scientific roguelike (TypeScript + Phaser 3 + bitECS + Vite). Real science replaces magic. Player combines real chemical elements, builds mechanisms using physics, tames creatures through biology, manipulates NPCs via cognitive biases. + +## Architecture +- **Phaser 3** — rendering, physics, input, audio, scenes +- **bitECS** — entity-component-system for game logic (TypedArrays, systems = pure functions) +- **Vite** — build + HMR +- Phaser handles rendering; bitECS handles logic. They sync via a bridge layer. + +## Key Files +- `synthesis-gdd.md` — Game Design Document (source of truth for ALL design decisions) +- `engine-analysis.md` — Technical stack rationale +- `IMPLEMENTATION-PLAN.md` — Phased development roadmap +- `PROGRESS.md` — Current status (**update after completing tasks**) + +## Design Principles +1. All science must be **real** (simplified, never wrong) +2. Every failed experiment teaches WHY it failed +3. Death is a feature, not punishment — it's a cycle phase +4. Cycles everywhere: gameplay, visuals, audio, narrative +5. Data-driven: game data in `src/data/*.json`, logic in TypeScript diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14ab128 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +.vite/ +*.local +.DS_Store diff --git a/IMPLEMENTATION-PLAN.md b/IMPLEMENTATION-PLAN.md new file mode 100644 index 0000000..647bdce --- /dev/null +++ b/IMPLEMENTATION-PLAN.md @@ -0,0 +1,248 @@ +# Synthesis — Implementation Plan + +## Overview + +**Target:** Vertical Slice — один полный ран через один биом (Каталитические Пустоши) +**Approach:** Каждая фаза даёт тестируемый, играбельный инкремент +**Method:** Полностью агентная разработка (AI пишет код, проверяет через Playwright и vitest) + +--- + +## Phase 0: Project Setup ✅ + +**Цель:** Собираемый, запускаемый проект с dev-инструментарием + +| # | Задача | Статус | +|---|--------|--------| +| 0.1 | Инициализация npm + зависимости (Phaser, bitECS, Vite, Vitest) | ✅ | +| 0.2 | TypeScript strict конфигурация | ✅ | +| 0.3 | Vite конфигурация (HMR, chunk splitting) | ✅ | +| 0.4 | Phaser game config (1280x720, pixel-art, arcade physics) | ✅ | +| 0.5 | BootScene с титульным экраном | ✅ | +| 0.6 | Cursor rules (6 правил под предметную область) | ✅ | +| 0.7 | Implementation plan + progress tracking | ✅ | + +**Критерий готовности:** +- `npm run dev` запускает dev-сервер +- Титульный экран виден на localhost:5173 +- TypeScript компилируется без ошибок + +--- + +## Phase 1: Chemistry Engine + +**Цель:** Ядро химической системы — элементы, реакции, соединения +**Зависимости:** Phase 0 +**Это чистая логика — идеальна для TDD (vitest)** + +| # | Задача | Описание | +|---|--------|----------| +| 1.1 | Типы и интерфейсы | `Element`, `Reaction`, `Compound`, `ReactionResult`, `ElementCategory`, `MatterState` | +| 1.2 | Данные: 20 элементов | H, He, C, N, O, Na, Mg, Al, Si, P, S, Cl, K, Ca, Fe, Cu, Zn, Sn, Hg, Au — из реальной таблицы Менделеева | +| 1.3 | Реестр элементов | Загрузка из JSON, lookup по symbol/atomicNumber, валидация | +| 1.4 | Движок реакций | Ввод: элементы + условия → вывод: продукт ИЛИ причина неудачи. Lookup по отсортированному ключу реагентов (O(1)) | +| 1.5 | Данные: 50 реакций | Базовые: горение, окисление, кислотно-щелочные, синтез солей, органика. Каждая с описанием науки | +| 1.6 | Соединения | Свойства: формула, название, игровой эффект, состояние, масса. Реестр с lookup | +| 1.7 | Тесты | Загрузка элементов, lookup, все 50 реакций (корректные продукты), невалидные реакции (корректные причины отказа) | + +**Критерий готовности:** +- `npm run test:run` — все тесты химии проходят +- Na + Cl → NaCl, H + H + O → H₂O, и т.д. +- Невалидная реакция возвращает человекочитаемую причину +- Все данные элементов соответствуют реальной таблице Менделеева + +--- + +## Phase 2: ECS Foundation + +**Цель:** Работающая ECS с мостом к Phaser-рендерингу +**Зависимости:** Phase 0 + +| # | Задача | Описание | +|---|--------|----------| +| 2.1 | World setup | bitECS world + контейнер времени (delta, elapsed, tick) | +| 2.2 | Core components | `Position`, `Velocity`, `SpriteRef`, `Health`, `ChemicalComposition` | +| 2.3 | Movement system | Обновляет Position по Velocity * delta | +| 2.4 | Phaser ↔ bitECS bridge | Синхронизация: ECS Position → Phaser sprite.x/y. Создание/удаление спрайтов по enterQuery/exitQuery | +| 2.5 | Entity factory | `createEntity(world, components)` → eid. Фабрика для типовых сущностей | +| 2.6 | Health system | Урон, лечение, смерть при health ≤ 0 | +| 2.7 | Визуальный тест | 10-20 цветных кругов двигаются по экрану — проверить скриншотом | + +**Критерий готовности:** +- Сущности рендерятся как спрайты/графика +- Движение работает (видно на скриншоте) +- Удаление сущности убирает спрайт с экрана + +--- + +## Phase 3: World Generation + +**Цель:** Процедурный тайлмап для Каталитических Пустошей +**Зависимости:** Phase 2 + +| # | Задача | Описание | +|---|--------|----------| +| 3.1 | Tilemap система | Phaser Tilemap из data-driven определений тайлов | +| 3.2 | Biome data | `biomes.json` — Каталитические Пустоши: типы тайлов, распределение, цвета | +| 3.3 | Noise генерация | simplex-noise для terrain. Высота → тип тайла. Seed-based для воспроизводимости | +| 3.4 | Типы тайлов | Выжженная земля, кислотные озёра, кристаллические формации, гейзеры, минеральные жилы | +| 3.5 | Размещение ресурсов | Руды/минералы на основе параметров биома и noise-карты | +| 3.6 | Камера | Следование за игроком, zoom (колесо мыши), clamp по границам карты | +| 3.7 | Миникарта | Упрощённая мини-карта в углу (цветные пиксели по тайлам) | + +**Критерий готовности:** +- Карта генерируется с разнообразным рельефом +- Разные тайлы визуально отличимы +- Камера плавно следует за «игроком» +- Каждый запуск даёт уникальную карту + +--- + +## Phase 4: Player Systems + +**Цель:** Играбельный персонаж: движение, сбор, крафт, бросок +**Зависимости:** Phase 2, Phase 1, Phase 3 + +| # | Задача | Описание | +|---|--------|----------| +| 4.1 | Player entity + контроллер | WASD движение, мышь — направление/взаимодействие | +| 4.2 | Инвентарь | Весовой (атомные массы!), слоты, stack одинаковых элементов | +| 4.3 | Сбор элементов | Взаимодействие с объектами мира (руда, озеро, растение) → элемент в инвентарь | +| 4.4 | Крафтинг | Выбрать элементы → химический движок → результат/ошибка → в инвентарь | +| 4.5 | Система броска | ПКМ — запуск элемента/соединения как снаряда. Arcade physics для полёта | +| 4.6 | Quick slots | Клавиши 1-2-3-4 для быстрого переключения подготовленных предметов | +| 4.7 | HUD | UIScene поверх GameScene: кольцо здоровья, полоска инвентаря, инфо об элементе при наведении | + +**Критерий готовности:** +- Игрок ходит по карте WASD +- Подбирает элементы с земли +- Комбинирует на верстаке — видит результат или причину неудачи +- Бросает натрий в воду — экзотермическая реакция +- HUD показывает здоровье, инвентарь, quick slots + +--- + +## Phase 5: Creatures & Ecology + +**Цель:** Живая экосистема в Каталитических Пустошах +**Зависимости:** Phase 2, Phase 3 + +| # | Задача | Описание | +|---|--------|----------| +| 5.1 | Данные существ | 3 типа: Кристаллиды (медленные, прочные), Ацидофилы (кислотные), Реагенты (парные, взрывоопасные) | +| 5.2 | AI поведение | FSM: idle → wander → feed → flee → attack. Переходы по условиям (голод, угроза, территория) | +| 5.3 | Метаболизм | Что ест, что выделяет. Ацидофилы едят минералы → выделяют кислоту | +| 5.4 | Популяционная динамика | Упрощённые Лотка-Вольтерра: рождаемость, смертность, хищничество | +| 5.5 | Жизненный цикл | Яйцо → юность → зрелость → старение → смерть → разложение (→ нутриенты) | +| 5.6 | Взаимодействие | Наблюдение (кодекс), атака, базовое приручение | +| 5.7 | Тест экосистемы | Запустить без игрока: популяции должны колебаться, не вымирать полностью | + +**Критерий готовности:** +- Существа спавнятся, двигаются, едят, размножаются, умирают +- Популяция без вмешательства достигает динамического равновесия +- Истребление хищников → перенаселение травоядных → коллапс (эмерджентно!) +- Существа реагируют на игрока + +--- + +## Phase 6: Run Cycle + +**Цель:** Полный рогалик-цикл: рождение → смерть → перерождение +**Зависимости:** Phase 3, Phase 4, Phase 1 + +| # | Задача | Описание | +|---|--------|----------| +| 6.1 | Споровая Колыбель | Сцена пробуждения: выбор школы (Алхимик для начала) | +| 6.2 | Триггер смерти | Health ≤ 0 → запуск death sequence | +| 6.3 | Анимация смерти | Тело Адепта распадается на элементы (реальный состав: 65% O, 18% C, 10% H...) | +| 6.4 | «Момент между» | WebGL fractal shader — психоделическая визуализация перерождения | +| 6.5 | Мета-прогрессия | Кодекс (открытые элементы/реакции), споры (валюта), persistence (IndexedDB) | +| 6.6 | Фазы рана | Пробуждение → Исследование → Эскалация → Кризис → Развязка. Таймер/прогрессия | +| 6.7 | Эскалация | Нарастающая энтропия: нестабильные реакции, агрессивные существа | +| 6.8 | Кризис: Химическая Чума | Цепная реакция отравляет атмосферу. Нужна химия для нейтрализации | +| 6.9 | Школа: Алхимик | Стартовый набор (H, O, C, Na, S, Fe), принцип (химическое равновесие) | + +**Критерий готовности:** +- Полный цикл: spawn → explore → gather → craft → die → fractal → respawn +- Кодекс сохраняется между ранами +- Споры начисляются за открытия +- Эскалация ощутимо меняет поведение мира +- Кризис запускается и решается через химию +- Fractal shader работает (даже простой вариант) + +--- + +## Phase 7: Mycelium + +**Цель:** Подземная сеть, связывающая раны +**Зависимости:** Phase 6 + +| # | Задача | Описание | +|---|--------|----------| +| 7.1 | Граф Мицелия | Структура данных (ngraph): узлы, рёбра, метаданные | +| 7.2 | Грибные узлы | Точки на карте, где Мицелий выходит на поверхность. Визуально — светящиеся споты | +| 7.3 | Запись знаний | Депозит открытий в Мицелий для будущих ранов | +| 7.4 | Извлечение знаний | «Вспышки» — фрагменты воспоминаний из прошлых ранов | +| 7.5 | Микоз | Визуальное искажение при долгом контакте с узлом. Открывает скрытую информацию | +| 7.6 | Интеграция с Колыбелью | Стартовые бонусы за споры | + +**Критерий готовности:** +- Узлы видны на карте +- Взаимодействие с узлом показывает память прошлого рана +- Знание из рана N появляется как подсказка в ране N+1 +- Микоз-эффект визуально работает +- Споры тратятся на бонусы при старте рана + +--- + +## Phase 8: First Archont — Ouroboros + +**Цель:** Первый босс-бой (головоломка, не рефлексы) +**Зависимости:** Phase 5, Phase 6 + +| # | Задача | Описание | +|---|--------|----------| +| 8.1 | Уроборос entity | Змей, поедающий хвост. Циклические фазы атаки | +| 8.2 | Паттерн-механика | Бой = петля с нарастающей сложностью. Игрок определяет паттерн и ломает его в нужный момент | +| 8.3 | Арена | Специальная генерируемая комната на подступах к Сердцу Синтеза | +| 8.4 | Множественные решения | Химическое, физическое, наблюдательное — минимум 3 пути к победе | +| 8.5 | Награда | Архонтова Память — лор-фрагмент в Кодексе, спорная щедрая награда | + +**Критерий готовности:** +- Бой имеет узнаваемый повторяющийся паттерн +- Паттерн усложняется с каждым циклом +- Минимум 2 валидных стратегии победы работают +- Победа добавляет лор в Кодекс + +--- + +## Vertical Slice = Phase 0–8 + +После Phase 8 игра содержит: +- ✅ Один полный биом (Каталитические Пустоши) +- ✅ Работающая химия (~20 элементов, ~50 реакций) +- ✅ Живая экосистема (3 вида существ с динамикой популяций) +- ✅ Полный цикл рана (рождение → смерть → перерождение) +- ✅ Мета-прогрессия между ранами (Кодекс, споры) +- ✅ Мицелий (память между ранами) +- ✅ Один босс (Уроборос) +- ✅ Фрактальная сцена смерти +- ✅ Одна школа (Алхимик) +- ✅ Один тип кризиса (Химическая Чума) + +Это — вертикальный срез, описанный в GDD Section X. + +--- + +## Beyond Vertical Slice (Post-MVP) + +| Фаза | Содержание | +|-------|-----------| +| Phase 9 | +2 биома (Кинетические Горы, Вердантовые Леса), +20 элементов, +100 реакций | +| Phase 10 | +3 школы (Механик, Натуралист, Навигатор), принципы физики и биологии | +| Phase 11 | Великий цикл (7 ранов): следы между ранами, Великое Обновление | +| Phase 12 | +3 Архонта (Спора Прайма, Энтропа, Когнитон) | +| Phase 13 | Оставшиеся биомы + школы + существа | +| Phase 14 | Сердце Синтеза (финальный регион), нарратив | +| Phase 15 | Аудио: лупы, эмбиент, циклические саундскейпы | +| Phase 16 | Полировка: шейдеры, частицы, UI/UX, accessibility | diff --git a/PROGRESS.md b/PROGRESS.md new file mode 100644 index 0000000..3dc67e0 --- /dev/null +++ b/PROGRESS.md @@ -0,0 +1,52 @@ +# Synthesis — Development Progress + +> **Last updated:** 2026-02-12 +> **Current phase:** Phase 0 ✅ → Ready for Phase 1 + +--- + +## Completed + +### Phase 0: Project Setup ✅ +- [x] Game Design Document (`synthesis-gdd.md`) +- [x] Engine analysis and tech stack selection (`engine-analysis.md`) +- [x] npm project + dependencies (Phaser 3, bitECS, TypeScript, Vite, Vitest) +- [x] TypeScript strict config +- [x] Vite config (HMR, chunk splitting) +- [x] Phaser config (1280x720, pixel-art, arcade physics, top-down) +- [x] BootScene with title screen +- [x] Cursor rules (6 rules: project context, agent workflow, ECS, chemistry, Phaser, data) +- [x] Implementation plan (`IMPLEMENTATION-PLAN.md`) +- [x] Progress tracking (this file) + +--- + +## In Progress + +_None — ready to begin Phase 1_ + +--- + +## Up Next: Phase 1 — Chemistry Engine + +- [ ] 1.1 Types and interfaces (`Element`, `Reaction`, `Compound`) +- [ ] 1.2 Element data — 20 real elements (JSON) +- [ ] 1.3 Element registry with lookup +- [ ] 1.4 Reaction engine core +- [ ] 1.5 Reaction data — 50 real reactions (JSON) +- [ ] 1.6 Compound properties +- [ ] 1.7 Unit tests (vitest) + +--- + +## Blockers + +None + +--- + +## Session Log + +| # | Date | Phase | Summary | +|---|------|-------|---------| +| 1 | 2026-02-12 | Phase 0 | Project setup complete: GDD, engine analysis, npm init, Phaser config, BootScene, cursor rules, implementation plan | diff --git a/engine-analysis.md b/engine-analysis.md new file mode 100644 index 0000000..3bedb21 --- /dev/null +++ b/engine-analysis.md @@ -0,0 +1,328 @@ +# Выбор движка для агентной разработки в Cursor + +## Критерии оценки + +Для **полностью агентной** разработки (AI-агент в Cursor пишет весь код) критически важны: + +| Критерий | Почему | +|----------|--------| +| Качество генерации кода | AI должен хорошо знать язык и фреймворк | +| Всё-в-коде | Никаких визуальных редакторов — агент работает только с текстовыми файлами | +| Сборка/запуск из терминала | Агент запускает через Shell tool | +| Визуальная проверка | Агент может УВИДЕТЬ результат (критично для отладки графики) | +| Скорость итерации | Hot-reload, быстрый цикл «изменил → увидел» | +| Экосистема библиотек | Для химии, математики, процедурной генерации | +| Возможности 2D-движка | Тайлмапы, физика, частицы, шейдеры, камера | + +--- + +## Кандидаты + +### 1. TypeScript + Phaser 3 + bitECS + Vite + +**Язык:** TypeScript — один из лучших для AI-генерации. Строгая типизация ловит ошибки до рантайма. Огромный объём обучающих данных у моделей. + +**Всё-в-коде:** Полностью. Ни один файл не требует визуального редактора. Конфиги, сцены, уровни — всё на TypeScript/JSON. + +**Сборка/запуск:** `npm run dev` → Vite поднимает сервер с HMR за <1 секунду. + +**Визуальная проверка:** **Killer feature** — Cursor имеет встроенные Playwright browser tools. Агент может: +- Открыть `localhost:5173` в браузере +- Сделать скриншот игры +- Прочитать console.log ошибки +- Кликнуть по элементам UI +- Проверить результат визуально + +Ни один другой стек не даёт агенту такой обратной связи. + +**Скорость итерации:** Vite HMR — изменения видны мгновенно, без перезапуска. Для игры это означает: поменял логику врага → сразу видишь в браузере. + +**Экосистема:** +- `bitecs` — высокопроизводительная ECS (~5kb, TypedArrays, SoA-layout, до десятков тысяч сущностей) +- `simplex-noise` — шум для процедурной генерации +- Любые npm-библиотеки для математики, графов, pathfinding + +**2D-возможности Phaser 3:** +- Спрайты, атласы, тайлмапы +- Arcade/Matter.js физика +- Система частиц +- Камеры, зум, следование +- Tweens и анимации +- WebGL-шейдеры (пайплайны) +- Ввод: клавиатура, мышь, тач, геймпад +- Аудио +- Scene-менеджер + +**Производительность:** WebGL-рендер, батчинг до ~2000 спрайтов/batch. bitECS для логики — Structure of Arrays, итерация по компонентам на скорости, близкой к нативной. Web Workers для тяжёлой симуляции (экология, химия) в отдельном потоке. + +``` +Агентопригодность: ██████████ 10/10 +Визуальная отладка: ██████████ 10/10 +Язык для AI: ██████████ 10/10 +Скорость итерации: ██████████ 10/10 +2D-возможности: ████████░░ 8/10 +Производительность: ███████░░░ 7/10 +Экосистема: █████████░ 9/10 +───────────────────────────────────── +ИТОГО: 64/70 +``` + +--- + +### 2. Godot 4 + GDScript + +**Язык:** GDScript — Python-подобный, AI знает его удовлетворительно, но значительно хуже TypeScript/Python. Меньше обучающих данных. Ошибки чаще. + +**Всё-в-коде:** Почти. `.tscn` и `.tres` файлы текстовые и генерируемые. Формат специфичен, но [godot-mcp](https://github.com/Coding-Solo/godot-mcp) (MCP-сервер для Godot, 1.8k stars) снимает эту проблему: создание сцен, добавление нод, загрузка спрайтов — через MCP API без ручного конструирования `.tscn`. + +**Сборка/запуск:** `godot --headless --path . --script test.gd` — работает. С godot-mcp — запуск/остановка проекта и захват debug output через MCP-инструменты. Логическая отладка решена. + +**Визуальная проверка:** Остаётся слабым местом. godot-mcp не поддерживает скриншоты viewport — агент видит только console output. Альтернатива — HTML5-экспорт + Playwright, но цикл итерации длинный: сохранение → экспорт (секунды) → перезагрузка браузера → скриншот (~15-30 сек vs. ~1-2 сек у Phaser + Vite HMR). Для логики — достаточно. Для визуала (шейдеры, фракталы, анимации) — сильно уступает Phaser. + +> **Watch list:** если godot-mcp добавит viewport capture (Godot технически умеет сохранять viewport в изображение через `get_viewport().get_texture().get_image().save_png()`), разрыв с Phaser сократится радикально. Следить за развитием проекта. + +**Скорость итерации:** Средняя. GDScript не компилируется, но перезапуск сцены — не мгновенный. С веб-экспортом цикл ещё длиннее. + +**2D-возможности:** Лучшие в классе. Встроенные тайлмапы, физика, анимация, частицы, шейдеры, сигналы, scene tree. + +**Производительность:** Отличная для 2D. GDNative/C# для критических участков. + +``` +Агентопригодность: ███████░░░ 7/10 (↑ благодаря godot-mcp) +Визуальная отладка: ████░░░░░░ 4/10 (HTML5-экспорт; viewport capture пока нет) +Язык для AI: ██████░░░░ 6/10 +Скорость итерации: ██████░░░░ 6/10 +2D-возможности: ██████████ 10/10 +Производительность: █████████░ 9/10 +Экосистема: ██████░░░░ 6/10 (↑ godot-mcp как часть экосистемы) +───────────────────────────────────── +ИТОГО: 48/70 +``` + +--- + +### 3. Godot 4 + C# + +Тот же Godot, но на C#. + +**Преимущество:** C# AI знает отлично. Строгая типизация. NuGet-пакеты. + +**Недостаток:** Компиляция медленнее GDScript. Требует .NET SDK. Визуальная проверка — через HTML5-экспорт (те же ограничения). godot-mcp работает и с C#-проектами. + +``` +Агентопригодность: ███████░░░ 7/10 (↑ благодаря godot-mcp) +Визуальная отладка: ████░░░░░░ 4/10 (HTML5-экспорт; viewport capture пока нет) +Язык для AI: ████████░░ 8/10 +Скорость итерации: █████░░░░░ 5/10 +2D-возможности: ██████████ 10/10 +Производительность: █████████░ 9/10 +Экосистема: ███████░░░ 7/10 +───────────────────────────────────── +ИТОГО: 50/70 +``` + +--- + +### 4. Python + Pygame-CE + +**Язык:** Python — абсолютный лидер для AI-генерации кода. Максимальное качество. + +**Всё-в-коде:** Полностью. + +**Визуальная проверка:** Нет. Pygame рисует в собственном окне, которое Playwright не видит. + +**2D-возможности:** Минимальные. Спрайты и рект — есть. Тайлмапы, физику, частицы, камеру — писать с нуля. + +**Экосистема:** Лучшая для научных вычислений (numpy, scipy). Химическая симуляция на numpy будет быстрой. + +**Производительность:** Потолок. Для тысяч сущностей без оптимизаций будет тормозить. Numpy помогает, но рендер — бутылочное горлышко. + +``` +Агентопригодность: ████████░░ 8/10 +Визуальная отладка: ██░░░░░░░░ 2/10 +Язык для AI: ██████████ 10/10 +Скорость итерации: ████████░░ 8/10 +2D-возможности: ████░░░░░░ 4/10 +Производительность: █████░░░░░ 5/10 +Экосистема: ██████████ 10/10 +───────────────────────────────────── +ИТОГО: 47/70 +``` + +--- + +## Вердикт + +### Победитель: TypeScript + Phaser 3 + bitECS + Vite + +Решающий фактор — **визуальная обратная связь через Playwright**. Агент в Cursor может: + +1. Написать код +2. HMR мгновенно применит изменения +3. Агент делает скриншот в браузере +4. Видит результат +5. Корректирует + +Это замыкает цикл разработки полностью внутри агента. Ни один другой стек не даёт такой обратной связи для 2D-игры. + +### Архитектура проекта + +``` +synthesis/ +├── package.json +├── tsconfig.json +├── vite.config.ts +├── index.html +├── src/ +│ ├── main.ts # Точка входа, инициализация Phaser +│ ├── config.ts # Конфигурация Phaser +│ │ +│ ├── ecs/ # Entity Component System (bitECS) +│ │ ├── world.ts # Мир ECS +│ │ ├── components/ # Компоненты +│ │ │ ├── position.ts +│ │ │ ├── health.ts +│ │ │ ├── chemistry.ts # Элементный состав сущности +│ │ │ ├── metabolism.ts # Метаболизм +│ │ │ ├── behavior.ts # Поведенческие параметры +│ │ │ └── lifecycle.ts # Фаза жизненного цикла +│ │ └── systems/ # Системы +│ │ ├── movement.ts +│ │ ├── ecology.ts # Популяционная динамика +│ │ ├── chemistry.ts # Химические реакции +│ │ ├── lifecycle.ts # Рождение → старение → смерть → разложение +│ │ └── ai.ts # Поведение существ +│ │ +│ ├── chemistry/ # Химический движок +│ │ ├── elements.ts # Таблица элементов +│ │ ├── reactions.ts # База реакций +│ │ ├── engine.ts # Движок комбинаций +│ │ └── compounds.ts # Справочник соединений +│ │ +│ ├── world/ # Генерация мира +│ │ ├── generator.ts # Процедурная генерация +│ │ ├── biomes.ts # Определения биомов +│ │ ├── tilemap.ts # Тайлмап-система +│ │ └── mycelium.ts # Система Мицелия +│ │ +│ ├── scenes/ # Phaser-сцены +│ │ ├── BootScene.ts +│ │ ├── GameScene.ts +│ │ ├── DeathScene.ts # «Момент между» — фракталы +│ │ ├── CodexScene.ts # Энциклопедия +│ │ └── UIScene.ts # HUD поверх игры +│ │ +│ ├── player/ # Всё про игрока +│ │ ├── controller.ts +│ │ ├── inventory.ts +│ │ ├── abilities.ts +│ │ └── schools.ts # Стартовые школы +│ │ +│ ├── meta/ # Мета-прогрессия (между ранами) +│ │ ├── codex.ts # Кодекс знаний +│ │ ├── spores.ts # Споры (валюта) +│ │ ├── persistence.ts # Сохранение в localStorage/IndexedDB +│ │ └── cycles.ts # Трекер малых/великих циклов +│ │ +│ ├── rendering/ # Визуальные эффекты +│ │ ├── shaders/ # WebGL-шейдеры +│ │ │ ├── fractal.glsl # Фрактальный шейдер для «Момента между» +│ │ │ ├── mycelium.glsl # Визуал Мицелия +│ │ │ └── distortion.glsl # Искажения при Микозе +│ │ └── particles/ # Конфиги частиц +│ │ +│ ├── audio/ # Звук +│ │ ├── loops.ts # Музыкальные лупы +│ │ └── ambient.ts # Эмбиент-система +│ │ +│ └── data/ # Статические данные +│ ├── elements.json # Периодическая таблица +│ ├── reactions.json # База химических реакций +│ ├── creatures.json # Определения существ +│ ├── biomes.json # Параметры биомов +│ └── lore/ # Тексты лора +│ ├── diary.json # Дневник Последнего Синтетика +│ └── codex-entries.json +│ +├── assets/ # Графика, звук +│ ├── sprites/ +│ ├── tilesets/ +│ ├── ui/ +│ └── audio/ +│ +├── tests/ # Тесты +│ ├── chemistry.test.ts # Тесты химического движка +│ ├── ecology.test.ts # Тесты экосимуляции +│ └── generation.test.ts # Тесты генерации мира +│ +└── docs/ + ├── synthesis-gdd.md # Game Design Document + └── engine-analysis.md # Этот файл +``` + +### Ключевые зависимости + +```json +{ + "dependencies": { + "phaser": "^3.80.0", + "bitecs": "^0.4.0" + }, + "devDependencies": { + "typescript": "^5.4.0", + "vite": "^5.4.0", + "vitest": "^2.0.0" + } +} +``` + +**Дополнительные зависимости** (подключаются по мере необходимости): + +```json +{ + "simplex-noise": "^4.0.0", // Процедурная генерация ландшафта + "ngraph.graph": "^20.0.0", // Граф Мицелия + "ngraph.path": "^1.4.0", // Pathfinding (A*, NBA*) + "rbush": "^4.0.0" // Spatial index (R-tree) для коллизий и culling +} +``` + +Минималистичный стек. Никаких лишних абстракций. Всё, что нужно для «Синтеза»: +- Phaser = рендер, физика, ввод, камера, сцены +- bitECS = высокопроизводительная симуляция сущностей +- Vite = мгновенная сборка +- Vitest = тесты для химического движка и экосимуляции +- ngraph = граф Мицелия + pathfinding +- rbush = пространственная индексация для производительности при тысячах сущностей + +### Почему не Godot? + +Godot — объективно лучший **2D-движок**. [godot-mcp](https://github.com/Coding-Solo/godot-mcp) существенно улучшил его агентопригодность: управление сценами, запуск проекта, захват debug output — всё через MCP. Но для «Синтеза» Godot проигрывает по двум параметрам: + +1. **Визуальная обратная связь.** godot-mcp не поддерживает скриншоты viewport. Агент видит console output, но не экран игры. Для проекта с фрактальными шейдерами, психоделическими визуалами и сложным UI — это блокирующий минус. Альтернатива (HTML5-экспорт + Playwright) даёт цикл итерации ~15-30 сек vs. ~1-2 сек у Phaser + Vite HMR. На тысячах итераций — дни потерянного времени. + +2. **Качество генерации кода.** GDScript агент знает хуже TypeScript. C# — хорошо, но компиляция добавляет задержку. godot-mcp снимает проблему `.tscn`-файлов, но не GDScript'а. + +**Точка пересмотра:** если godot-mcp добавит viewport capture (технически реализуемо через `Viewport.get_texture().get_image()`), разрыв сократится до одного фактора — скорости итерации (HMR vs. перезапуск). На этом этапе Godot + C# (50/70) приблизится к Phaser (64/70) настолько, что выбор станет неоднозначным. + +Сегодня — TypeScript + Phaser + browser tools = единственный стек с мгновенным замыканием цикла агентной разработки. + +### Почему не Pixi.js + bitECS? + +Pixi.js — более производительный 2D-рендерер, чем Phaser, с меньшим оверхедом. Раз ECS внешний (bitECS), встроенные системы Phaser частично избыточны. Однако: + +- Phaser даёт **из коробки:** физику (Arcade + Matter.js), камеру с зумом и следованием, систему ввода (клавиатура + мышь + геймпад), Scene Manager, загрузчик ассетов, аудио, tweens, систему частиц. С Pixi.js каждую из этих систем — реализовывать вручную или подбирать отдельную библиотеку. +- Для **агентной разработки** это критично: каждая дополнительная самописная система — источник багов, которые агент будет долго отлаживать. +- Производительность Phaser достаточна для вертикального среза и alpha. Если на этапе beta рендер станет бутылочным горлышком — миграция рендерера на Pixi.js возможна без переписывания ECS-логики (они уже разделены). + +**Вердикт:** Phaser = скорость разработки. Pixi.js = запасной вариант для оптимизации рендера. + +### Риски и план действий + +| Риск | Вероятность | Влияние | Митигация | +|------|-------------|---------|-----------| +| **Потолок производительности браузера** — тысячи сущностей с экосимуляцией + фрактальные шейдеры перегружают WebGL/JS | Средняя | Высокое | Web Workers для симуляции. Spatial partitioning. Culling невидимых сущностей. Если не хватит — вынос тяжёлой логики в WASM (Rust → wasm-pack) | +| **Phaser 3 — проект одного разработчика** (bus factor) | Низкая | Среднее | Phaser 3 стабилен и feature-complete. При необходимости — миграция рендера на Pixi.js, ECS-логика не затронута | +| **Веб-стек не годится для консолей** | Средняя | Среднее | Electron/Tauri для десктопа. Для Switch — потребуется порт (возможно, на этом этапе Godot с импортом логики) | +| **Масштаб химической системы** (2000 реакций) нагружает runtime | Низкая | Среднее | Реакции — табличный lookup (O(1)), не симуляция. База в JSON, индексация по реагентам. Узкое место — UI поиска, не вычисления | +| **IndexedDB/localStorage — потолок для мета-прогрессии** | Низкая | Низкое | IndexedDB вмещает сотни MB. Для «Синтеза» достаточно. При необходимости — экспорт в файл | diff --git a/index.html b/index.html new file mode 100644 index 0000000..9dc314d --- /dev/null +++ b/index.html @@ -0,0 +1,22 @@ + + + + + + Синтез + + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9256be1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1560 @@ +{ + "name": "synthesis", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "synthesis", + "version": "0.1.0", + "dependencies": { + "bitecs": "^0.4.0", + "phaser": "^3.80.0" + }, + "devDependencies": { + "happy-dom": "^20.6.1", + "typescript": "^5.4.0", + "vite": "^5.4.0", + "vitest": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bitecs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/bitecs/-/bitecs-0.4.0.tgz", + "integrity": "sha512-ho6Zop/L79DRTnBAfakPpGPuX7y0+lAjX06CpaAW+5tnAc7BH3L3RlSrWAXAqwnQGDZ10GsoxaxyTTsddlun3g==", + "license": "MPL-2.0" + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/happy-dom": { + "version": "20.6.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.6.1.tgz", + "integrity": "sha512-+0vhESXXhFwkdjZnJ5DlmJIfUYGgIEEjzIjB+aKJbFuqlvvKyOi+XkI1fYbgYR9QCxG5T08koxsQ6HrQfa5gCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "entities": "^6.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/phaser": { + "version": "3.90.0", + "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz", + "integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5e5eeb2 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "synthesis", + "version": "0.1.0", + "description": "Scientific roguelike where real science replaces magic", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "vitest", + "test:run": "vitest run" + }, + "dependencies": { + "bitecs": "^0.4.0", + "phaser": "^3.80.0" + }, + "devDependencies": { + "happy-dom": "^20.6.1", + "typescript": "^5.4.0", + "vite": "^5.4.0", + "vitest": "^2.0.0" + } +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..7bd9c07 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,29 @@ +import Phaser from 'phaser'; +import { BootScene } from './scenes/BootScene'; + +export const GAME_WIDTH = 1280; +export const GAME_HEIGHT = 720; + +export const gameConfig: Phaser.Types.Core.GameConfig = { + type: Phaser.AUTO, + width: GAME_WIDTH, + height: GAME_HEIGHT, + backgroundColor: '#0a0a0a', + parent: document.body, + scene: [BootScene], + physics: { + default: 'arcade', + arcade: { + gravity: { x: 0, y: 0 }, + debug: false, + }, + }, + scale: { + mode: Phaser.Scale.FIT, + autoCenter: Phaser.Scale.CENTER_BOTH, + }, + render: { + pixelArt: true, + antialias: false, + }, +}; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..a86b7c2 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,4 @@ +import Phaser from 'phaser'; +import { gameConfig } from './config'; + +new Phaser.Game(gameConfig); diff --git a/src/scenes/BootScene.ts b/src/scenes/BootScene.ts new file mode 100644 index 0000000..d5623aa --- /dev/null +++ b/src/scenes/BootScene.ts @@ -0,0 +1,58 @@ +import Phaser from 'phaser'; + +export class BootScene extends Phaser.Scene { + constructor() { + super({ key: 'BootScene' }); + } + + create(): void { + const cx = this.cameras.main.centerX; + const cy = this.cameras.main.centerY; + + // Title + this.add + .text(cx, cy - 40, 'СИНТЕЗ', { + fontSize: '64px', + color: '#00ff88', + fontFamily: 'monospace', + fontStyle: 'bold', + }) + .setOrigin(0.5); + + // Subtitle + this.add + .text(cx, cy + 30, 'Наука — это магия, которая работает', { + fontSize: '16px', + color: '#557755', + fontFamily: 'monospace', + }) + .setOrigin(0.5); + + // Version + this.add + .text(cx, cy + 80, 'v0.1.0 — Phase 0: Project Setup', { + fontSize: '12px', + color: '#333333', + fontFamily: 'monospace', + }) + .setOrigin(0.5); + + // Pulsing indicator + const dot = this.add + .text(cx, cy + 120, '◉', { + fontSize: '24px', + color: '#00ff88', + fontFamily: 'monospace', + }) + .setOrigin(0.5); + + this.tweens.add({ + targets: dot, + alpha: 0.2, + duration: 1500, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); + } +} diff --git a/synthesis-gdd.md b/synthesis-gdd.md new file mode 100644 index 0000000..0e8e764 --- /dev/null +++ b/synthesis-gdd.md @@ -0,0 +1,606 @@ +# «СИНТЕЗ» — Game Design Document + +--- + +## 0. ПАСПОРТ ПРОЕКТА + +| Параметр | Значение | +|----------|----------| +| **Рабочее название** | Синтез / Synthesis | +| **Жанр** | Scientific roguelike + open-world exploration sandbox | +| **Перспектива** | 2D top-down; side-view для вертикальных зон и подземелий | +| **Платформа** | PC (Steam) — основная; Nintendo Switch — целевой порт | +| **Режим** | Singleplayer | +| **Аудитория** | 14+, любители roguelike и sandbox-игр, любознательные игроки без страха перед наукой | +| **Длительность одного рана** | 45–90 минут | +| **Путь до финала нарратива** | ~49 ранов (~50–80 часов) | +| **Эндгейм** | Бесконечный (мета-циклы с новым контентом) | +| **Референсы** | Noita (физика как оружие), Outer Wilds (цикличный нарратив), Caves of Qud (глубина мира), Rain World (живая экосистема), Opus Magnum (химия как головоломка) | + +### Elevator Pitch + +Roguelike, в котором настоящая наука заменяет магию. Смешивай реальные химические элементы, строй механизмы по законам физики, приручай существ через биологию, манипулируй NPC через когнитивные искажения. Умирай, перерождайся, и с каждым циклом понимай мир — и игровой, и настоящий — глубже. + +--- + +## I. ФИЛОСОФСКОЕ ЯДРО: ВЕЛИКИЙ ЦИКЛ + +### Центральная метафора + +Всё во вселенной игры — цикл. Атом колеблется. Сердце бьётся. Существо рождается, живёт, умирает, становится почвой для нового. Звезда коллапсирует и из её праха собирается новая. Цивилизация возникает, познаёт мир, гибнет, и через тысячелетия другая цивилизация находит её руины и начинает сначала. + +Игрок проживает это **буквально**. Каждый ран — малый цикл. Каждые 7 ранов — великий цикл. А за гранью великих циклов — нечто, что можно только почувствовать в моменты «прозрения». + +Ощущение — как мухоморный трип: ты застрял в петле, но каждый виток чуть другой. Ты умираешь и рождаешься, умираешь и рождаешься. Постепенно приходит осознание: ты не застрял В цикле — ты И ЕСТЬ цикл. И единственный выход — не сломать его, а понять. + +### Три закона Арканы + +1. **Закон Возвращения** — всё, что было, будет снова, но никогда в точности так же +2. **Закон Следа** — ничто не исчезает бесследно; каждый цикл оставляет отпечаток в следующем +3. **Закон Синтеза** — когда все фрагменты знания соединяются, цикл не ломается — он трансцендирует + +--- + +## II. КОСМОЛОГИЯ: ЦИКЛЫ ВНУТРИ ЦИКЛОВ + +### Масштабы циклов (фрактальная структура) + +Одни и те же паттерны повторяются на каждом уровне масштаба. Это не декорация — это работающая механика. + +``` +НАНОЦИКЛ Атом колеблется ←→ Химическая реакция +МИКРОЦИКЛ Клетка делится → живёт → умирает → питает другие клетки +БИОЦИКЛ Существо: рождение → рост → размножение → смерть → разложение +ЭКОЦИКЛ Экосистема: зарождение → расцвет → кризис → коллапс → перерождение +ЦИВОЦИКЛ Цивилизация: открытие → расцвет → гордыня → падение → тёмный век → новое открытие +КОСМОЦИКЛ Звезда: туманность → протозвезда → горение → коллапс → сверхновая → туманность +МЕТАЦИКЛ Сама Аркана: Создание → Познание → Синтез → Катастрофа → Забвение → Создание +``` + +**Ключевой дизайн-принцип:** игрок может наблюдать все эти циклы в реальном времени. Увидеть, как делится клетка под самодельным микроскопом — и узнать тот же паттерн в расколе империи на карте мира. + +### Лор: Что произошло + +Древние Синтетики — не первая цивилизация и не последняя. Они лишь одна итерация. Копаясь в руинах, игрок находит доказательства цивилизаций ДО Синтетиков. А в глубочайших подземельях — намёки на то, что и сам игрок уже проходил через это бесконечное число раз. + +Катастрофа, уничтожившая Синтетиков, не была случайностью. Это **фаза цикла**. Как суперновая — не смерть звезды, а обязательный этап перед рождением новых. Синтетики достигли полноты знания, и это знание стало слишком концентрированным. Реальность не выдержала. Но осколки знания засеяли мир, как споры — почву. + +--- + +## III. МИЦЕЛИЙ: СВЯЗУЮЩАЯ ТКАНЬ ВСЕХ ЦИКЛОВ + +### Концепция + +Под поверхностью всего мира Арканы тянется **Мицелий** — грибная сеть невообразимого масштаба и древности. Мицелий — не просто растение. Это **память мира**. Каждый цикл, каждая смерть, каждое открытие — всё записано в его нитях. + +Мицелий — это: +- Механизм сохранения знаний между ранами (лор-объяснение мета-прогрессии) +- Подземная «интернет-сеть» мира — NPC-грибники подключаются к ней для связи +- Буквальный организм, с которым можно взаимодействовать +- Метафора коллективного бессознательного + +### Мицелий в геймплее + +**Грибные узлы** — точки на карте, где Мицелий выходит на поверхность. Здесь можно: +- Получить «вспышки» — фрагменты воспоминаний прошлых циклов (кат-сцены из предыдущих ранов, но искажённые, как сон) +- Передать знание — записать в Мицелий открытие, чтобы в будущих ранах оно проявилось как «интуиция» +- Общаться с Мицелием — он «говорит» паттернами: показывает циклические последовательности, которые игрок должен расшифровать (учит распознавание паттернов, основы теории информации) + +**Споры** — валюта мета-прогрессии. Каждый ран заканчивается тем, что знания Адепта впитываются Мицелием. Чем больше открыл — тем больше спор. Споры тратятся на стартовые бонусы и разблокировку новых дисциплин. + +**Микоз** — состояние, когда Адепт слишком глубоко погружается в Мицелий. Экран искажается, время петляет, реальность становится фрактальной. Это опасно, но в этом состоянии можно увидеть вещи, невидимые иначе: скрытые проходы, истинную природу сущностей, связи между циклами. Визуально — **прямая отсылка к психоделическому опыту**: фрактальные паттерны, зацикленные анимации, ощущение déjà vu. + +--- + +## IV. СТРУКТУРА РАНОВ: МАЛЫЙ И ВЕЛИКИЙ ЦИКЛЫ + +### Малый цикл (один ран) + +``` +ПРОБУЖДЕНИЕ → ИССЛЕДОВАНИЕ → ЭСКАЛАЦИЯ → КРИЗИС → РАЗВЯЗКА + ↑ | + └────────────────── СМЕРТЬ/ПЕРЕРОЖДЕНИЕ ─────────────┘ +``` + +**1. Пробуждение** +Адепт «просыпается» в Споровой Колыбели — полости внутри гигантского гриба. Не помнит ничего, кроме смутного ощущения «я уже был здесь». Выбирает школу (или получает случайную при разблокированном режиме хаоса). + +**2. Исследование** +Открытый мир. Свободный порядок регионов. Собираешь элементы, открываешь соединения, встречаешь сущности. Мир находится в фазе «расцвета» — относительно мирный, богатый ресурсами. + +**3. Эскалация** +Энтропия растёт. Это проявляется физически: температура колеблется сильнее, химические реакции становятся нестабильнее, существа агрессивнее, NPC — параноидальнее. Мир входит в фазу «кризиса» своего цикла. + +**4. Кризис** +Конкретная катастрофа, уникальная для этого рана: +- **Химическая чума** — цепная реакция отравляет атмосферу (нужна химия для нейтрализации) +- **Механический коллапс** — древние машины активируются и крушат всё (нужна инженерия) +- **Экологический каскад** — вымирание ключевого вида рушит всю пищевую цепь (нужна биология) +- **Социальный распад** — фракции начинают войну (нужна психология/дипломатия) +- **Энтропийный шторм** — сама физика начинает сбоить (нужна физика/математика) +- **Мицелиевое безумие** — Мицелий «вспоминает» слишком много и мир наполняется эхом прошлых циклов + +**5. Развязка** +Попытка достичь Сердца Синтеза и либо остановить катастрофу, либо... понять, что она — необходимая фаза цикла, и направить её во что-то продуктивное (как контролируемый пожар в лесоводстве). + +**Смерть** — не «game over», а фаза. Адепт растворяется в Мицелии. Короткая сцена: фрактальный тоннель, мелькают образы из прошедшего рана, из прошлых ранов, из ранов которых не было... Потом — тишина. Потом — пробуждение. + +### Великий цикл (каждые 7 ранов) + +После 7-го рана (и затем каждого 7-го) мир переживает **Великое Обновление**: +- Фундаментально меняется генерация (новые типы биомов, другие пропорции) +- Разблокируется новый «слой» лора +- Появляется новый тип сущностей — **Архонты** — существа, пережившие несколько великих циклов +- Мицелий «созревает» — открывает новые способности +- Предыдущие 7 ранов оставляют конкретные следы: руины твоих лагерей, последствия твоих решений, мутировавшие потомки существ, которых ты разводил + +**Великие циклы формируют нарратив:** +- Цикл 1 (раны 1-7): **Пробуждение** — узнаёшь мир, базовые механики +- Цикл 2 (раны 8-14): **Сомнение** — находишь следы прошлых Адептов (это были ты?) +- Цикл 3 (раны 15-21): **Осознание** — понимаешь природу циклов +- Цикл 4 (раны 22-28): **Попытка** — первые попытки трансцендировать цикл +- Цикл 5 (раны 29-35): **Принятие** — понимание, что цикл — не тюрьма, а форма бытия +- Цикл 6 (раны 36-42): **Синтез** — объединение всех знаний +- Цикл 7 (раны 43-49): **Трансценденция** — ??? + +После Цикла 7... игра не заканчивается. Начинается **Мета-Цикл**. Великие циклы начинают повторяться, но на новом уровне понимания. Новый контент, новые слои, бесконечно. + +### Секунда-к-секунде: основной цикл геймплея + +Макро-структура рана описана выше. Вот что происходит на микроуровне — это то, что игрок делает каждую минуту: + +``` +НАБЛЮДАЙ → СОБИРАЙ → ЭКСПЕРИМЕНТИРУЙ → ПРИМЕНЯЙ → ЗАПИСЫВАЙ + ↑ | + └────────────────────────────────────────────────────┘ +``` + +1. **Наблюдай** — мир наполнен подсказками: цвет минерала, поведение существа, следы на руинах. Активное наблюдение — главный навык. Курсор при наведении показывает базовые свойства объектов (у продвинутых игроков — расширенные). +2. **Собирай** — элементы, образцы, данные. Инвентарь ограничен весом (реалистичные атомные массы!), поэтому выбор, что нести с собой, — постоянное стратегическое решение. +3. **Экспериментируй** — на переносном верстаке (3 слота, базовые реакции) или в стационарной лаборатории (полный доступ). Соединяй элементы, проверяй гипотезы. Неудача ≠ потеря: система объясняет, ПОЧЕМУ реакция не пошла (недостаточно энергии, нет катализатора, неправильная пропорция). +4. **Применяй** — новое знание немедленно полезно: оружие, инструмент, лекарство, ключ к головоломке, торговый товар. +5. **Записывай** — Кодекс обновляется автоматически при открытии; Мицелий фиксирует знание для будущих ранов. + +**Управление:** клавиатура + мышь (геймпад как альтернатива). ЛКМ — взаимодействие/использование, ПКМ — бросок/применение активного элемента. Быстрый доступ к 4 «подготовленным» элементам/соединениям (1-2-3-4). Колесо мыши или Q/E — переключение. + +**Камера:** 2D top-down, плавное следование за Адептом с зуммированием при входе в здания/пещеры. Переключение в side-view для Кинетических Гор и подземных секций. + +--- + +## V. СИСТЕМА СПОСОБНОСТЕЙ: БЕСКОНЕЧНЫЕ КОМБИНАЦИИ + +### Уровень 1 — Элементы (Первоматерия) + +Базовые строительные блоки. Соответствуют реальной таблице Менделеева. В начале рана доступны 5-8 (зависит от школы), всего в игре ~70 (наиболее значимые реальные элементы). + +Каждый элемент — физический предмет в инвентаре. Его можно: +- Бросить (некоторые токсичны, горючи, взрывоопасны) +- Съесть (с последствиями — железо полезно в малых дозах, ртуть — нет) +- Соединить с другим элементом (если знаешь реакцию или экспериментируешь) +- Использовать как инструмент (медь проводит ток, кремний — полупроводник) + +**Как элементы учат:** игрок запоминает свойства реальных элементов через практику. Ртуть — жидкий металл, токсичный, но нужен для определённых соединений. Калий бурно реагирует с водой. Углерод — основа всей органики. Это не зубрёжка — это survival tool. + +### Уровень 2 — Соединения (Материя) + +Комбинация элементов по реальным (упрощённым) химическим правилам. + +**Примеры ключевых соединений:** + +| Формула | Название | Игровой эффект | Реальная наука | +|---------|----------|----------------|----------------| +| NaCl | Соль | Консервация, защита от слизней, торговля | Ионные связи, осмос | +| H₂O | Вода | Растворитель, охлаждение, гидравлика | Полярность молекул | +| C₂H₅OH | Этанол | Дезинфекция, горючее, опьянение NPC | Органическая химия | +| NaHCO₃ | Сода | Нейтрализация кислот, выпечка, огнетушитель | Кислотно-щелочной баланс | +| Fe₂O₃ | Ржавчина | Термит (с Al), индикатор старения металлов | Окисление | +| KNO₃ + S + C | Порох | Взрывчатка, ракеты, сигнальные ракеты | Горение, окислители | +| CaCO₃ | Известняк | Строительство, нейтрализация кислой почвы | Геология, агрохимия | +| C₆H₁₂O₆ | Глюкоза | Быстрая энергия, корм для микроорганизмов | Биохимия, метаболизм | +| CH₃COOH | Уксус | Растворитель, приманка, консервант | Ферментация | +| Ca(OH)₂ | Гашёная известь | Строительный раствор, дезинфекция | Стройматерилы | + +**Экспериментирование:** игрок может соединить ЛЮБЫЕ два элемента. Если реакция невозможна — система объясняет ПОЧЕМУ (энергия связи, электроотрицательность, условия). Это превращает каждую неудачу в микроурок. + +### Уровень 3 — Принципы (Законы) + +Физические, математические и биологические законы, применяемые к материи. + +**Физика:** +- **Рычаг/Момент силы** — усиление физических атак, создание простых механизмов +- **Давление** (P=F/S) — гидравлические прессы, подводные конструкции, вакуумные ловушки +- **Электричество** — цепи, двигатели, электролиз, освещение +- **Оптика** — линзы, зеркала, лазеры (концентрация света), волоконная связь +- **Термодинамика** — двигатели, холодильники, понимание энтропии (привязано к лору о циклах!) +- **Волны** — звуковые атаки, резонанс (разрушение конструкций), сонар + +**Биология:** +- **Генетика** — селекция растений/существ, понимание наследственности +- **Симбиоз** — создание взаимовыгодных связей с существами +- **Иммунология** — создание вакцин, борьба с болезнями +- **Нейробиология** — понимание поведения существ, создание приманок/репеллентов + +**Математика/Логика:** +- **Вероятность** — оценка рисков, оптимальные стратегии, азартные мини-игры +- **Геометрия** — проектирование конструкций, расчёт траекторий +- **Логика** — решение головоломок древних, программирование автоматонов +- **Теория графов** — оптимизация маршрутов, сетевой анализ (торговые пути, Мицелий) + +**Психология:** +- **Когнитивные искажения** — манипуляция NPC (эффект привязки в торговле, ошибка невозвратных затрат) +- **Иерархия Маслоу** — предсказание поведения NPC (голодный не будет рассуждать о философии) +- **Теория игр** — дипломатия, дилемма заключённого с фракциями +- **Мнемотехники** — реальные техники запоминания, которые помогают и в игре, и в жизни + +### Уровень 4 — Техники (Синтез) + +Уникальные применения, возникающие на пересечении дисциплин. + +**Примеры кросс-дисциплинарных техник:** + +- **Электрохимия:** гальваника (покрытие предметов металлом), электролиз (разложение воды на H₂ + O₂ для взрывчатки или дыхания) +- **Биофизика:** ультразвуковая эхолокация, биолюминесцентное освещение, пьезоэлектрические кристаллы из костей +- **Нейрохимия:** создание нейротрансмиттеров для усиления/ослабления существ, антидоты к ядам +- **Материаловедение:** композитные материалы (углеволокно = прочнее стали, легче алюминия), полупроводники для электроники +- **Агроэкология:** пермакультурные фермы, которые работают автономно и кормят между ранами +- **Судебная химия:** анализ следов, чтобы понять что произошло в руинах — хроматография, спектральный анализ + +--- + +## VI. ОТКРЫТЫЙ МИР: СЕМЬ РЕГИОНОВ + +### 1. Каталитические Пустоши (Химия) + +**Ландшафт:** выжженная равнина с кислотными озёрами, кристаллическими формациями, гейзерами. Почва богата минералами — тут добываются большинство редких элементов. + +**Цикл региона:** Пустоши проходят через химический цикл: зарождение реакций → каскад → насыщение → стагнация → новый катализатор. Игрок может ускорять/замедлять этот цикл. + +**Уникальные существа:** +- **Кристаллиды** — кремниевая жизнь, формирующая геометрически правильные тела. Растут медленно, но несокрушимы +- **Ацидофилы** — слизнеобразные существа, живущие в кислотных озёрах. Их тела — готовые кислоты/щёлочи +- **Реагенты** — парные существа, безопасные по отдельности, взрывоопасные при контакте + +**Что учит:** свойства элементов, типы реакций, pH, катализ, техника безопасности при работе с химикатами. + +### 2. Кинетические Горы (Физика/Механика) + +**Ландшафт:** вертикальный мир. Гигантские шестерни и маятники Синтетиков встроены в скалы. Гравитация аномальна в некоторых зонах. Лавины, оползни, тектоническая активность. + +**Цикл региона:** Механический: завод → работа → износ → поломка → ремонт/замена. Древние машины включаются и выключаются циклически — игрок учится предсказывать фазы. + +**Уникальные существа:** +- **Маятники** — летающие существа, чьё движение идеально периодично. Используй как часы или платформы +- **Механоиды** — полуживые автоматоны. Можно чинить, перепрограммировать, использовать запчасти +- **Резонаторы** — существа, вибрирующие на определённых частотах. Опасны, если частота совпадает с твоим снаряжением + +**Что учит:** механика, простые машины, энергия, волны, термодинамика, основы инженерии. + +### 3. Вердантовые Леса (Биология/Экология) + +**Ландшафт:** многослойный лес с пологом на разных высотах. Подземный уровень — царство грибов и корней (ближайший контакт с Мицелием). Невероятное биоразнообразие. + +**Цикл региона:** Экологический: сукцессия → климакс → катастрофа (пожар/болезнь) → первичная сукцессия. Игрок наблюдает за несколько игровых дней то, что в реальности занимает столетия. + +**Уникальные существа:** +- **Симбионты** — всегда парами. Один без другого погибает. Разлучить — жестоко, но иногда полезно +- **Мимикры** — маскируются под растения, предметы, даже NPC. Учат наблюдательности +- **Споровики** — грибные существа, аватары Мицелия. Мирные, но если обидишь — весь лес ополчится + +**Что учит:** экология, ботаника, зоология, грибоводство, пищевые цепи, эволюция, первая помощь (лекарственные растения). + +### 4. Ноосферный Город (Психология/Социология) + +**Ландшафт:** полуразрушенный мегаполис Синтетиков. Уцелевшие районы населены фракциями. Архитектура спроектирована по принципам нейроморфизма — город буквально выглядит как мозг. + +**Цикл региона:** Социальный: объединение → расцвет → расслоение → конфликт → распад → объединение. Фракции постоянно проходят через этот цикл. Игрок может вмешиваться. + +**Уникальные существа/NPC:** +- **Когниты** — ментальные паразиты, воплощающие когнитивные искажения. Когнит Подтверждения заставляет игнорировать контраргументы. Когнит Невозвратных Затрат — продолжать провальное дело +- **Эмпаты** — существа, отражающие эмоциональное состояние игрока. Агрессия порождает агрессию +- **Архетипы** — юнгианские архетипы как буквальные персонажи: Тень, Анима/Анимус, Мудрый Старец + +**Что учит:** психология, коммуникация, переговоры, когнитивные искажения, манипуляции (и защита от них), основы экономики, теория игр. + +### 5. Архипелаг Меркатор (Навигация/География/Астрономия) + +**Ландшафт:** цепь островов в бесконечном (процедурно генерируемом) океане. Карта не даётся — надо составлять самому. Ночное небо — настоящая карта звёзд. + +**Цикл региона:** Навигационный: отлив → прилив → шторм → штиль. Приливы открывают/закрывают проходы между островами. Штормы перестраивают маршруты. + +**Уникальные существа:** +- **Стеллариды** — летающие существа, повторяющие паттерны созвездий. По ним можно ориентироваться +- **Глубинные** — океанские левиафаны, чьё поведение привязано к лунным циклам +- **Картографы** — NPC-отшельники на изолированных островах, каждый знает фрагмент навигации + +**Что учит:** ориентирование, картография, астрономия, метеорология, океанология, геодезия. + +### 6. Руины Логоса (Математика/Логика) + +**Ландшафт:** невозможная геометрия. Эшеровские лестницы, фрактальные здания, пространства с неевклидовой топологией. Всё — гигантская головоломка. + +**Цикл региона:** Логический: тезис → антитезис → синтез → новый тезис. Головоломки эволюционируют: решённая задача порождает более сложную версию. + +**Уникальные существа:** +- **Аксиомы** — неуничтожимые сущности, задающие правила локации. Изменить правила — надо «опровергнуть» аксиому +- **Парадоксы** — существа, нарушающие внутреннюю логику мира. Опасны, непредсказуемы, но из их останков — ценные ресурсы +- **Итераторы** — самокопирующиеся существа. Один превращается в два, два в четыре... Если не остановить рост — экспоненциальная катастрофа + +**Что учит:** логика, комбинаторика, теория чисел, вероятность, основы программирования, криптография. + +### 7. Сердце Синтеза (Все дисциплины) + +**Ландшафт:** центр мира. Гигантская спираль, закручивающаяся внутрь — и чем глубже, тем больше дисциплин требуется одновременно. Визуально: все стили предыдущих регионов наслаиваются, переплетаются, создавая **психоделическую фрактальную мешанину**. + +**Цикл региона:** Мета-цикл. Само Сердце пульсирует, и с каждым «ударом» реальность внутри перестраивается. Одна фаза — физика доминирует, другая — биология, третья — все законы работают одновременно. + +**Финальное испытание:** не босс-файт. Нужно **синтезировать** решение, требующее знаний из ВСЕХ дисциплин. Каждый ран — новая задача. Нет единственного решения — есть класс решений. + +--- + +## VII. СУЩНОСТИ И ЭКОЛОГИЯ + +### Экосистемная модель + +Каждое существо в мире имеет: +- **Метаболизм** — что ест, что выделяет (реальная биохимия) +- **Популяционная динамика** — размножение, смертность, хищничество (уравнения Лотки-Вольтерры, упрощённые) +- **Поведенческая модель** — реакция на стимулы, обучение, память +- **Роль в цикле** — какую фазу экоцикла поддерживает + +Если игрок истребит хищников — травоядные размножатся → съедят всю растительность → массовый голод → коллапс экосистемы. Это не скриптовое событие — это эмерджентная симуляция. + +### Цикл жизни и смерти (в каждом существе) + +Существа проходят через стадии: +``` +Яйцо/Семя → Личинка/Росток → Юность → Зрелость → Старение → Смерть → Разложение → Нутриенты → Яйцо/Семя +``` + +Игрок может вмешиваться на любой стадии. Ускорить рост (удобрения, гормоны). Замедлить старение (антиоксиданты, теломераза — да, настоящая наука). Приручить на стадии юности. Получить редкие материалы только от старых особей. + +### Боссы — Архонты + +Архонты — существа, пережившие множество великих циклов. Они **помнят**. Каждый Архонт — воплощение определённой фазы цикла: + +1. **Уроборос** — змей, поедающий свой хвост. Воплощение бесконечного повторения. Бой с ним — петля: одни и те же фазы, но с нарастающей сложностью. Победить = понять паттерн и разорвать его в правильный момент +2. **Спора Прайма** — древнейший гриб, ядро Мицелия. Не враждебна, но подавляющая. Контакт = погружение в видения ВСЕХ прошлых циклов одновременно. Испытание рассудка и способности найти порядок в хаосе +3. **Энтропа** — воплощение второго закона термодинамики. Всё вокруг неё распадается. Нельзя победить силой (энергия рассеивается). Нужно создать термодинамически замкнутую систему +4. **Когнитон** — коллективный разум, составленный из всех когнитов. Бой — психологический: он использует против игрока его собственные когнитивные искажения, выявленные за все раны +5. **Фрактал** — существо, состоящее из уменьшенных копий себя. Убей одного — две копии. Убей копии — четыре. Нужен нестандартный подход: изменить масштаб атаки, нарушить самоподобие +6. **Нулевой Адепт** — ты из предыдущего мета-цикла. Имеет все способности, которые ты использовал в прошлых ранах. Зеркальный бой — победа = использование того, чего ты НИКОГДА не пробовал + +--- + +## VIII. РОГАЛИК-МЕХАНИКИ + +### Смерть как механика + +Смерть в «Синтезе» — не наказание, а **фаза цикла**. Визуально: + +1. Мир замедляется, звуки стихают +2. Тело Адепта распадается на элементы (показывает реальный элементный состав тела: 65% O, 18% C, 10% H...) +3. Элементы впитываются в почву → по ускоренным нитям бегут к Мицелию +4. **Момент между** — 10-30 секунд чистого фрактального визуала. Мириады циклов мелькают перед глазами. Зацикленные петли рождений и смертей, бесконечно вложенные друг в друга. Тихий фоновый гул, складывающийся в паттерн. Игрок может различить в этом хаосе фрагменты — каждый раз разные — из будущих ранов, из лора, из знаний, которые ещё не открыл +5. Пробуждение в Споровой Колыбели + +**Цель дизайна «Момента между»:** это не наказание за смерть и не скипаемый экран. Это **награда**. Чем больше знаний накопил — тем больше разглядишь в хаосе. Опытный игрок видит подсказки для следующего рана. Новичок — красивый калейдоскоп. + +### Мета-прогрессия + +**Сохраняется навсегда:** +- Кодекс (энциклопедия) +- Разблокированные школы/дисциплины +- Споры (валюта) +- Архонтова Память (побеждённые боссы добавляют лор-фрагменты) +- Статистика всех ранов + +**Сохраняется до конца великого цикла (7 ранов):** +- Следы в мире (руины лагерей, последствия решений) +- Прирученные существа (их потомки) +- Взаимоотношения с фракциями NPC (общая репутация) + +**Не сохраняется (обнуляется каждый ран):** +- Инвентарь +- Уровень здоровья +- Собранные элементы и соединения +- Позиция на карте +- Текущие квесты + +### Школы (стартовые классы) + +| Школа | Стартовые элементы | Стартовый принцип | Стиль игры | +|-------|--------------------|--------------------|------------| +| **Алхимик** | H, O, C, Na, S, Fe | Химическое равновесие | Зельеварение, взрывчатка, яды | +| **Механик** | Fe, Cu, Sn, Si, C | Рычаг и момент силы | Крафт механизмов, ловушки, автоматоны | +| **Натуралист** | C, N, O, P, K | Фотосинтез | Приручение, выращивание, экосистемный контроль | +| **Навигатор** | Si, Fe, C, H, O | Угловое измерение | Картография, разведка, мобильность | +| **Оратор** | (мало элементов) | Когнитивные искажения | Дипломатия, торговля, манипуляция | +| **Логик** | (мало элементов) | Булева алгебра | Головоломки, программирование, вероятность | +| **Лекарь** | O, C, H, N, Ca, Fe | Анатомия | Лечение, хирургия, нейрохимия | +| **Пустой** | (ничего) | (ничего) | Хардкор-режим. Всё с нуля. Открывается после 1-го великого цикла | + +> **Заметка по балансу:** Оратор и Логик компенсируют дефицит элементов мощными стартовыми Принципами (социальная манипуляция и автоматизация соответственно). Точный стартовый набор этих школ (2–3 элемента) — зона балансного тестирования на этапе alpha. Пустой — осознанный хардкор; его жизнеспособность зависит от объёма мета-прогрессии, накопленной игроком. + +### Боевая система + +Бой в «Синтезе» — не про рефлексы, а про подготовку и знание. Основной вопрос не «как быстро нажимаешь кнопки», а «что ты знаешь и что успел синтезировать». + +**Реактивный бой (real-time, неспешный темп):** + +- **Бросок элементов/соединений** — основная атака. Натрий во влажного врага = экзотермический взрыв. Кислота на известняковую броню = растворение. Эффекты следуют реальной химии — игрок учится предсказывать результат. +- **Устройства** — ловушки, электрические цепи, газовые облака, автоматоны-турели. Создаются заранее на верстаке или импровизируются на ходу из подручных элементов. +- **Среда** — заманить врага в кислотное озеро, обрушить нестабильную конструкцию резонансной частотой, поджечь горючий газ. Каждый биом даёт уникальные средовые тактики. +- **Существа-компаньоны** — прирученные существа действуют по поведенческим паттернам (агрессия / защита / поддержка), которые игрок настраивает через понимание нейробиологии. + +**Против Архонтов:** + +- Каждый Архонт — научная головоломка, замаскированная под босс-файт +- Грубая сила неэффективна или контрпродуктивна (Фрактал размножается, Энтропа рассеивает энергию) +- Нужно определить научный принцип, лежащий в основе Архонта, и применить контрпринцип +- Всегда есть несколько валидных решений (химическое, физическое, биологическое, социальное) — реиграбельность + +**Шкала угрозы (почему бой не доминирует):** + +Большинство существ не агрессивны по умолчанию. Агрессия — результат экосистемного давления (голод, территориальность, фаза Эскалации). Игрок, понимающий биологию, может избежать 70% боёв через знание поведенческих триггеров. Это осознанный дизайн: игра про знание, не про убийство. + +### Онбординг и адаптация сложности + +**Первый ран — обучающий, но не tutorial:** + +- Мир генерирует «мягкий» набор: меньше агрессивных существ, больше очевидных реакций, NPC-Споровик сопровождает первый час как ненавязчивый наставник +- Игрок не знает, что ран «облегчённый» — ощущение идентично обычному +- Смерть в первом ране наступает неизбежно (заложено дизайном) — чтобы нормализовать умирание и показать «Момент между» + +**Динамическая сложность (невидимая для игрока):** + +- 3+ смерти подряд на фазе «Исследование» → мир подбрасывает больше подсказок, безопасных зон, «удачных» находок +- Прохождение «Кризиса» без потерь → следующий ран усиливает вызов +- Система не ощущается как «помощь» — воспринимается как естественная вариативность мира + +### Система сохранения + +- **Авто-сохранение** при входе/выходе из регионов и при взаимодействии с грибными узлами Мицелия +- **Roguelike integrity:** нельзя «переиграть» момент — только продолжить с последнего автосейва или начать новый ран +- **Между ранами:** мета-прогрессия сохраняется автоматически и немедленно +- **Выход из игры:** текущее состояние рана сохраняется, можно вернуться в ту же точку + +--- + +## IX. ЦИКЛЫ РОЖДЕНИЯ И СМЕРТИ: ГЛУБОКАЯ ИНТЕГРАЦИЯ + +### Циклы в визуальном дизайне + +Всё в мире визуально зациклено: +- **Анимации** существ — плавные петли без начала и конца +- **Архитектура** — спирали, кольца, уроборосы, вложенные окружности +- **UI** — таблица Менделеева расположена по спирали; полоска жизни — не линейная, а кольцевая (когда «здоровье» кончается, кольцо заполняется заново, но другим цветом — «вторая жизнь», «третья жизнь», каждая короче) +- **Карта мира** — спиральная, закручивающаяся к Сердцу Синтеза +- **Шрифты и руны** Синтетиков — основаны на циклических символах + +### Циклы в звуковом дизайне + +- **Музыка** строится на лупах, которые медленно эволюционируют — каждый повтор чуть-чуть другой +- **Эмбиент** — природные циклические звуки: дыхание мира, пульс Мицелия, ритмичный стук древних машин +- **Звук смерти** — не тишина и не драматический удар, а замедление всех звуков до тех пор, пока не останется только одна нота — которая становится первой нотой музыки нового рана + +### Циклы в нарративе + +**Дневник Последнего Синтетика** — фрагменты текста, разбросанные по миру. Ключевые фразы: + +> «Я записываю это в сто сорок седьмой раз. Или в первый. Разница стирается.» + +> «Мицелий помнит то, что я забыл. А может, он помнит то, чего не было. Или то, что будет. Для грибов это одно и то же.» + +> «Цикл — не тюрьма. Тюрьма — это когда ты не знаешь, что ты в цикле. Когда знаешь — это спираль. А спираль ведёт куда-то.» + +> «Мы не сломали цикл. Мы стали его новым витком. И это не поражение.» + +> «Всякий раз, когда я умираю, вижу одно и то же: бесконечную вложенность. Я внутри существа, которое внутри мира, который внутри мысли, которая внутри меня. Это пугало, пока я не понял: так выглядит истина, если смотреть прямо.» + +### Циклы в геймплее: механика Déjà Vu + +Иногда (с нарастающей частотой) игра подбрасывает ситуации, **идентичные** прошлым ранам: +- Тот же NPC с тем же предложением в том же месте +- Та же ловушка, тот же расклад +- Игрок может «узнать» ситуацию и получить бонус за правильное решение + +Но иногда — **ложное** déjà vu. Ситуация ВЫГЛЯДИТ так же, но ключевая деталь изменена. Реагировать по шаблону = попасть в ловушку. Учит критическому мышлению: не доверяй паттернам слепо. + +### Циклы в обучении игрока + +Каждая научная концепция в игре преподаётся циклически: + +``` +ПЕРВЫЙ КОНТАКТ Игрок случайно обнаруживает явление (натрий + вода = БУМ) + ↓ +ЭКСПЕРИМЕНТ Пробует повторить, варьируя условия (сколько натрия? температура воды?) + ↓ +ПОНИМАНИЕ Кодекс записывает формулу и объяснение + ↓ +ПРИМЕНЕНИЕ Использует в бою/крафте/выживании + ↓ +УГЛУБЛЁННОЕ ОТКРЫТИЕ Находит неочевидное применение (электролиз NaOH для получения чистого натрия) + ↓ +ОБУЧЕНИЕ ДРУГИХ Может «научить» NPC, получая учеников и репутацию + ↓ +ПЕРЕОСМЫСЛЕНИЕ В следующем ране знакомое явление работает в новом контексте + ↓ +ПЕРВЫЙ КОНТАКТ (Нового уровня) — цикл повторяется на более глубоком уровне +``` + +--- + +## X. ТЕХНИЧЕСКИЕ ЗАМЕТКИ ПО РЕАЛИЗАЦИИ + +### Стек (рекомендации для 2D) + +- **Движок:** Godot 4 (открытый, отлично для 2D, GDScript/C#) +- **Генерация мира:** Wave Function Collapse + правила реальной геологии/экологии +- **Химическая система:** база данных реальных реакций (~2000 самых значимых), движок проверки валидности по упрощённым правилам +- **Экосимуляция:** агентная модель, уравнения Лотки-Вольтерры для популяций, конечные автоматы для поведения +- **Мицелий:** граф, обновляемый между ранами, хранящий «память» мира +- **Визуал:** пиксель-арт с шейдерными эффектами для «мицелиевых» искажений, фрактальная генерация для момента между циклами + +### Приоритеты прототипирования + +1. Химическая система комбинаций (ядро) +2. Один биом с рабочей экосистемой +3. Цикл рана (рождение → смерть → мета-прогрессия) +4. «Момент между» (визуал смерти/перерождения) +5. Мицелий как хранилище мета-данных + +### Управление скоупом: дорожная карта + +Проект амбициозен. Ниже — реалистичная разбивка по этапам, позволяющая выпускать играбельные билды на каждом шаге. + +**Вертикальный срез (6–9 месяцев):** +- 1 полный биом (Каталитические Пустоши) с рабочей экосистемой +- ~20 элементов, ~50 реакций +- Полный цикл одного рана (Пробуждение → Кризис → Смерть → Перерождение) +- Мицелий: базовая мета-прогрессия (споры, кодекс) +- «Момент между» (визуал смерти) +- 1 Архонт (Уроборос) +- 2 школы (Алхимик, Натуралист) + +**Alpha (12–18 месяцев):** +- 3 биома (+ Кинетические Горы, Вердантовые Леса) +- ~40 элементов, ~200 реакций +- Базовые Принципы (физика + химия + биология) +- Великий цикл (7 ранов) с работающими следами между ранами +- 4 школы + +**Beta (18–24 месяца):** +- Все 7 биомов +- Полная химическая система (~70 элементов, ~2000 реакций) +- Все Принципы и Техники +- Все 6 Архонтов +- Все 8 школ +- Полный нарративный путь (49 ранов) + +### Доступность + +- **Цветовая слепота:** элементы и реакции идентифицируются не только цветом, но уникальной формой значка и символом +- **Масштабируемый UI и текст** — особенно критично для химических формул и мелких обозначений +- **Настраиваемая скорость игры** (0.5x — 1.5x) — не влияет на баланс, но позволяет замедлить бой +- **Субтитры** для всех аудио-подсказок (пульс Мицелия, предупреждающие звуки существ) +- **Переназначение клавиш** — полное + +--- + +## XI. МОНЕТИЗАЦИЯ (этика) + +- **Premium:** единоразовая покупка. Никаких microtransactions +- **DLC:** новые дисциплины (Музыка/Акустика, Кулинария как отдельный глубокий модуль, Астрофизика) +- **Никогда:** pay-to-win, ускорение прогресса за деньги, продажа спор, лутбоксы + +Знание — единственная валюта, которую нельзя купить. Это и есть месседж игры. + +--- + +## XII. ЕДИНОЕ ВИДЕНИЕ + +> Игрок запускает «Синтез» ради геймплея. Умирает раз за разом, учится, пробует, ошибается. Через 10 ранов понимает, что запомнил таблицу валентностей. Через 30 — объясняет знакомому, почему небо голубое. Через 100 — разбирается в когнитивных искажениях лучше, чем после курса психологии. +> +> А в игре — продолжает умирать. И каждая смерть красива. Каждое перерождение — новая возможность. Цикл не кончается. Спираль раскручивается. +> +> Это и есть Синтез. diff --git a/tests/setup.test.ts b/tests/setup.test.ts new file mode 100644 index 0000000..aa8d66e --- /dev/null +++ b/tests/setup.test.ts @@ -0,0 +1,38 @@ +import { describe, it, expect } from 'vitest'; + +describe('Project Setup', () => { + it('should have working test infrastructure', () => { + expect(1 + 1).toBe(2); + }); + + // Phaser requires Canvas API — verified in browser via Playwright, not in unit tests + it.skip('should import Phaser types (browser-only)', async () => { + const Phaser = await import('phaser'); + expect(Phaser).toBeDefined(); + }); + + it('should import bitECS 0.4 API', async () => { + const { createWorld, addEntity, addComponent, query } = await import('bitecs'); + expect(createWorld).toBeTypeOf('function'); + expect(addEntity).toBeTypeOf('function'); + expect(addComponent).toBeTypeOf('function'); + expect(query).toBeTypeOf('function'); + }); + + it('should create ECS world and entities', async () => { + const { createWorld, addEntity, addComponent, query } = await import('bitecs'); + + const Position = { x: [] as number[], y: [] as number[] }; + const world = createWorld(); + const eid = addEntity(world); + + addComponent(world, eid, Position); + Position.x[eid] = 10; + Position.y[eid] = 20; + + const entities = query(world, [Position]); + expect(entities).toContain(eid); + expect(Position.x[eid]).toBe(10); + expect(Position.y[eid]).toBe(20); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..68fe550 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "lib": ["ESNext", "DOM", "DOM.Iterable"] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..a4f3629 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,23 @@ +/// +import { defineConfig } from 'vite'; + +export default defineConfig({ + base: './', + build: { + target: 'esnext', + rollupOptions: { + output: { + manualChunks: { + phaser: ['phaser'], + }, + }, + }, + }, + server: { + port: 5173, + open: false, + }, + test: { + environment: 'happy-dom', + }, +});