Files
Binding-Fignyaac/docs/HOWTO.md
T
mayatnikovandClaude Opus 4.8 3bb46e2f6b feat(render): псевдо-3D, всегда видимые двери, ассеты+эффекты, русификация
- Рендер переведён в псевдо-3D: наклонная PerspectiveCamera, пол лежит плашмя,
  персонажи/враги — вертикальные спрайты-биллборды, стены с высотой. Починен
  баг: пол создавался без поворота и стоял вертикально → «вывернутая» перспектива.
- Двери видны всегда: закрыты (засов) во время боя, открытый проём после зачистки.
- render/assets.ts: процедурные текстуры (спрайты персонажей/врагов, пол, стены,
  двери, снаряд, тень) + эффекты оружия (вспышка из дула, искры, облачко гибели).
- Светлее палитра — исправлено «тёмное на тёмном».
- Игра переименована в «Биндим Фигняшку»; полная русификация UI
  (ДАЛЬНИЙ/БЛИЖНИЙ, ИГРА ОКОНЧЕНА, ПОБЕДА); клавиши-подсказки оставлены латиницей.
- Ввод по event.code → WASD/Q/R работают в любой раскладке (вкл. русскую).
- theme.ts урезан до реально используемых тинтов (bg/swing/flash).
- docs/ASSET_BRIEF.md — бриф и промпт на полную художку; доки синхронизированы.
- Ядро (src/core) не тронуто — раунд чисто render/UI. 27 тестов + типы зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 15:16:57 +03:00

10 KiB
Raw Blame History

HOWTO — рецепты доработки

Практические инструкции «как сделать X». Каждый рецепт перечисляет все файлы, которые нужно тронуть. После любой правки — bun run check и проверка в браузере (bun run dev). Общие правила — в CLAUDE.md.


1. Покрутить баланс (скорость, HP, урон, число врагов)

Всё в одном файле — src/config.ts. Например:

  • игрок быстрее: PLAYER.speed;
  • больше HP у босса: ENEMY_STATS.boss.hp;
  • больше врагов в комнате: SPAWN.normalMin / SPAWN.normalExtra;
  • крупнее данжен: MIN_ROOMS / EXTRA_ROOMS / MAP_RADIUS.

Код менять не нужно. Это и есть смысл config.ts.


2. Добавить новый тип врага (например, tank)

  1. src/core/types.ts — добавь в union: export type EnemyType = 'normal' | 'fast' | 'boss' | 'tank';
  2. src/config.ts — строка характеристик в ENEMY_STATS:
    tank: { size: 40, hp: 8, speed: 0.7, damage: 2 },
    
  3. src/core/systems/spawner.ts — реши, когда он спавнится (логика выбора типа в начале цикла). Напр. с шансом: rng.chance(0.15) ? 'tank' : rng.chance(ENEMY.fastChance) ? 'fast' : 'normal'.
  4. src/render/assets.ts — добавь ключ в SpriteKey и ветку в Assets.sprite() (рисуется через drawCharacter(...) с твоими цветами).
  5. src/render/ThreeRenderer.ts — добавь строку в enemyMatKey, сопоставив новый Enemy['type'] этому SpriteKey (без этого не пройдёт проверка типов).

ИИ, урон, отбрасывание, мигание при попадании — общие, их трогать не нужно. Добавь тест в tests/spawner.test.ts, если ввёл особое правило спавна.


3. Добавить новое поведение врага (особый ИИ)

Сейчас все враги ведут себя одинаково (преследование игрока) в Game.updateEnemies (src/core/Game.ts). Чтобы развести поведение:

  • по e.type внутри updateEnemies развилкой (просто, для 2–3 типов), или
  • вынеси ИИ в src/core/systems/ai.ts как функции update<Type>(enemy, player, room) и диспетчеризуй по типу (чище, когда типов много).

Держи это в core/ (без рендера) и старайся писать чистыми функциями — их легко покрыть тестом.


4. Добавить оружие / третий режим боя

Режимы — это MODE_RANGED = 0 / MODE_MELEE = 1 и тип CombatMode = 0 | 1. Для третьего режима:

  1. src/config.ts — константа MODE_X = 2 и блок баланса.
  2. src/core/types.ts — расширь CombatMode (0 | 1 | 2).
  3. src/core/Game.ts — в handleAttack добавь ветку создания нужного снаряда/ эффекта; в consumeActions смена оружия циклом по всем режимам.
  4. src/render/HudOverlay.ts — подпись/иконка режима.
  5. Если это новый вид снаряда — заведи сущность в src/core/entities/ и обновляй её в Game.step (по образцу Projectile/updateTears), а в ThreeRenderer добавь её отрисовку.

5. Добавить тип комнаты (например, shop)

  1. src/core/types.ts — добавь в RoomType.
  2. src/core/world/RoomMap.ts — правило назначения типа в generate().
  3. src/core/systems/spawner.ts — сколько врагов (часто 0).
  4. src/core/Game.ts — если врагов 0, комната уже авто-зачищается при входе (см. enterRoom); особая логика комнаты — здесь же.
  5. src/render/HudOverlay.ts — цвет на миникарте и подпись (drawMinimap/drawHud).

6. Поменять управление

src/input/KeyboardController.ts — метод poll() (маппинг клавиш на InputState) и набор PREVENT (клавиши, у которых гасим поведение браузера). Логику игры это не затрагивает — она читает только InputState.

Геймпад/тач: сделай новый контроллер с тем же poll(): InputState и подставь его в main.ts.


7. Добавить уровень (пресет правил) в меню

src/core/rules.ts — добавь объект в массив PRESETS. Он сразу появится кнопкой в стартовом меню (меню строится из PRESETS). Например:

{
  id: 'swarm',
  name: 'Орда',
  description: 'Очень много слабых быстрых врагов.',
  map: { minRooms: 10, extraRooms: 4, mapRadius: 4 },
  player: { maxHp: 6, speed: 3.2 },
  enemies: { densityMul: 2.5, fastChance: 0.9, hpMul: 0.5, speedMul: 1.2, bossHpMul: 1 },
},
  • seed (необязательный) фиксирует данжен — одинаковый забег каждый раз.
  • Нужен новый «рычаг» (например, шанс сокровищниц)? Добавь поле в LevelRules и читай его там, где раньше брал константу из config.ts (генерация — RoomMap, спавн — spawner). Тест на влияние правил — в tests/game.test.ts.

8. Кастомные ассеты (спрайты, текстуры, тема)

Графика (спрайты персонажей/врагов, пол, стены, двери, снаряд, эффекты, тень) рисуется процедурно в src/render/assets.ts (класс Assets, кэширует текстуры three.js).

  • Свои PNG вместо процедурных: замени тело нужного drawX() на загрузку картинки new THREE.TextureLoader().load(url) и верни её из соответствующего геттера — рендер берёт текстуры по ключам, больше ничего менять не нужно. Полный список нужных ассетов с размерами и готовый промпт для дизайн-ИИ — в docs/ASSET_BRIEF.md.

  • Цвета/тинты/фон (не текстуры) — в src/render/theme.ts (Theme + DEFAULT_THEME).

  • Своя палитра: сделай ещё один объект Theme и передай его в new ThreeRenderer(canvas, myTheme) в main.ts. Сейчас рендер создаётся один раз при старте с темой по умолчанию и переиспользуется между забегами, поэтому чтобы выбирать тему по правилам уровня, нужно либо перенести new ThreeRenderer(...) внутрь startGame(rules)dispose() предыдущий), либо добавить рендеру метод setTheme(theme).

  • Перейти на спрайты/текстуры: расширь Theme полями с путями к картинкам (см. комментарий-задел в theme.ts), загрузи их через THREE.TextureLoader в ThreeRenderer и положи текстуру в material.map соответствующих мешей вместо (или вместе с) color. Логика игры при этом НЕ меняется — это чисто рендер.


9. Поменять или нарастить рендер (вплоть до 3D)

Рендер изолирован за интерфейсом src/render/Renderer.ts (render(game, alpha)

  • dispose()). Варианты:
  • Доработать вид (спрайты, текстуры, частицы) — внутри ThreeRenderer (камера уже наклонная PerspectiveCamera, сущности — биллборд-спрайты).
  • Усилить 3D — для настоящего объёма замени MeshBasicMaterial (он без света) на MeshStandardMaterial, добавь источники света и объёмные меши вместо плоскостей. Мир рисуется по тем же координатам сущностей из Game — логику менять не нужно.
  • Другой рендер целиком (например, Canvas2D для отладки) — реализуй Renderer и подставь в main.ts. Ядро не трогается вообще.

Помни про управление ресурсами (правило 6 в CLAUDE.md).


10. Отладка

  • В консоли браузера доступен game — текущий Game. Примеры:
    game.player.hp = 99            // бессмертие на тест
    game.curRoom.enemies.length    // сколько врагов в комнате
    game.reset()                   // новая карта
    
  • Детерминизм: чтобы воспроизвести конкретную карту, задай seed правилам — проще всего поставить seed: 42 нужному пресету в core/rules.ts (см. пресет daily). Тот же seed — та же генерация и спавн, в т.ч. после рестарта. В тестах можно передать свой ГПСЧ вторым аргументом: new Game(rules, new Rng(42)) (первый аргумент — правила, не ГПСЧ).
  • Тесты ядра гоняются мгновенно и без браузера: bun test. Логику отлаживай тестом, а не кликами.