| 2026-09-07 |

perf: горячий путь 60 Гц без аллокаций (О-1)
...
Baseline после: fps покой 6 / бой 6 (было 5-6/3-6), heap 29 МБ — не хуже.
- Частицы: stepParticle — out-параметр визуала; Particles — компакт swap-pop
в один проход вместо двух filter за тик (~3400 объектов/сек раньше).
- Свет движка: upsertLight/upsertDarkSpot — апсейт def по полям вместо
{...def}; пульсы — итерация Map без спреда; lightFrame — out-кадр
(applyFrame reuse).
- Свет игры: дифф-наборы источников/пятна — два буфера со свопом (clear не
задевает активный), defsBuf/posBuf/lampDef — переиспользуемые.
- Тики: Tween/Updatables/shake — компакт на месте вместо filter каждый тик
(в Tween пограничный случай dt-в-delay сохранён — твин остаётся).
- Сцена: debug.setLines только при видимой панели; Atmosphere.updateHazard —
поиск зоны только при смене тайла героя.
- Бой: CombatFlow.updateRing — трогает Graphics только на переходах;
noiseFor — общий view вместо копии origin; EnemyBrain — переиспользуемые
INTENT_* константы + dirInto/moveIntentFrom (dirToward больше не аллоцирует).
- SceneRegistry.near — опциональный out-массив + reuse Set дедупликации.
- PlayerController: имя клипа ходьбы — кэш по facing.
- docs/engine/render.md: out-параметр stepParticle.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
13 hours ago
|

refactor: DialogueRunner -> модель + рантайм; LocationScene 890 -> 737
...
Движок:
- dialogue/graph.ts (новый): типы графа, VarCondition/DialogueEffects/... и
чистые предикаты hasConditions/evalConditions — их берут валидатор, dry-run
и квест-стадии; DialogueRunner.ts (459 -> 298) — только рантайм обхода.
Публичный API @rpg/engine не изменился; ui-and-dialogue.md дополнен.
Игра (сцена — оркестратор, механики — в systems/):
- systems/combat/CombatFlow.ts (новый): боевая обвязка героя — ввод атаки/
резонанс, реакции на урон (вспышка/шейк/отброс/смерть-респаун), hp-бар,
кольцо заряда, уровень угрозы для музыки, подписка combat:hurt.
- systems/LocationHud.ts (новый): название локации, подсказка, счётчик мотов
(одна вьюха в uiRoot вместо трёх полей сцены).
- makeExitGlow — маркер выхода интерьера уехал в InteractableViews.
Проверки: check:fast, agent:check 16/16, smoke-act1 (квест+кат-сцена),
final-smoke (3 поездки + реальный бой) — зелёные.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
14 hours ago
|
chore: ESLint (typescript-eslint, flat config) + npm run lint в check:fast
...
- eslint.config.js: recommended TS для src обоих пакетов + тулз; node/браузерные
глобали для .mjs-сценариев и редактора диалогов; no-undef выключен в TS
(глобали DOM/Vite проверяет tsc).
- Попутные находки (8): no-useless-assignment в waitFor-агентах (catch опирается
на init false), тернарник-выражение в StateMachine, причина ошибки в
AssetLoader ({ cause }), неиспользуемые consoleLogs/JSON_SPACE, let->const,
as const в тесте квестов.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
14 hours ago
|

refactor(game): К3 — декомпозиция LocationScene, гигиена, валидация контента
...
Декомпозиция (1338 -> ~870 строк, сцена — оркестратор вьюх):
- systems/LocationAudio: слои/roomtone/шорохи/музыка угрозы (порядок тиков сохранён)
- systems/InteractableViews + data/assetSets: фабрики вьюх NPC/интерактивов,
enemyTextures из данных (префикс кадров из EnemyKindDef.frames)
- systems/Atmosphere: пепел/мотыли/дымка, накат, виньетка
- systems/quest/FlowerQuest: сбор/посадка/кат-сцена сдачи
- pixelTile -> чистый resolvePixelTile + pointerToWorld в ClickRouting (+ юнит-тесты)
Хардкоды сюжета -> data: QUEST_PLANT_TILES/QUEST_TOWER_TILE/QUEST_TOWER_GREEN,
harvestTile в AreaDef (правило «цветок только в ponds»)
Гигиена: HealthBar.setHp не перерисовывает при том же hp; eps-прибытие
вместо float-равенства (PlayerController, FaunaSystem) со снапом;
agentCommand — таблица Record вместо switch на 130 строк; удалён мёртвый код
(GameLighting.addLight/removeLight, Interactables.all, SceneObjects.byKind,
VolumeKey, questTaken/questDone/inventoryItems, реэкспорт MenuScene)
Валидация контента (тихие ошибки): патрули (границы+проходимость), хазарды,
источники света (дубли id, границы, пустые, whenFlag/notFlag), requiresItem
переходов/накатов, return-правило интерьеров, аудио-ключи (ambience/theme/
слои/sfx реакций и реплик/roomtone)
Проверено: check:fast (692 теста), agent:check 16/16, smoke-act1
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
15 hours ago
|
game: миграция эффектов на FxLayer-пресеты + починка нетикаемых мотылей
...
- LocationScene: два слоя (fx в мире, uiFx в uiRoot); тост — floatText
с readSeconds и clear() перед новым (раньше наслаивались); подбор
интерактива — fx.fade; флэш героя при уроне — fx.flash.
- CombatViews: кольцо резонанса, hitBurst/deathBurst и флэши врагов —
через fx.ring/fx.burst/fx.flash (ручной массив колец удалён);
трупы растворяются через FxFade (keep — вьюху чистит сцена).
- Баг: эмиттер мотылей над потревоженным пеплом добавлялся в мир без
engine.fx — не анимировался и не уничтожался; теперь fx.burst с
emberPreset, доигрывает и исчезает сам.
- docs/demo.md: строка матрицы про render/fx/.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
| 2026-09-06 |
Игра: AudioSystem — позиционный звук мира, бой на событийной шине
...
- systems/AudioSystem.ts: playAt (затухание 1.5 степени + pan по экранному
смещению от слушателя), playStep (джиттер rate), attach (подписки combat:*),
setLayer (локальный амбиент, тик-фейд через новый MusicHandle.setVolume)
- CombatWorld освобождён от deps.audio: 5 play-вызовов -> события
(новое combat:spit {pos}, combat:awake дополнен pos) — симуляция снова чистая
- combat:playerHit -> sfx/hurt подпиской; sink интерактивов через playAt
- MusicHandle.setVolume в движке (cancel+setValueAtTime — без борьбы с ramp)
- 11 тестов AudioSystem; agent:check зелёный
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Тела не проходят друг сквозь друга + карта коллизий агенту
...
Расталкивание через реестр сцены: герой выталкивает себя из NPC
(PlayerController), враг — из героя, NPC и своих (EnemySystem); каждый
актор двигает только себя (separateCircles). attackStop (0.85) больше
суммы радиусов — авто-атака не дрожит.
A* не ведёт героя сквозь тело: SceneObjects.walkGrid() считает тайлы
NPC занятыми (герой застревал «на носу» у Ирвина — звон в верёвку
не поднимал флаг). Проводка: клик по миру, авто-подход к цели,
scene:route моста.
Карта коллизий агенту: слой s.collision {width, height, blocked (0/1
с footprint пропов), props} + команды scene:walkable {x, y} и
scene:raycast {from, to} (прямая видимость, вода прозрачна). Новый
сценарий checks/collision.mjs: слой валиден, walkable/raycast на
конкретных тайлах, клик в дерево — герой в проходимом, телепорт в
тело спящего ползуна — враг выталкивает себя.
Доки: agent.md (слой + 2 команды), practices.md (ситуация «проверяю
коллизии/движение» + грабли телепорта на тайл перехода).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Движковые коллизии: круг поверх сетки + LOS по пропам
...
Новый чистый модуль map/collision.ts в движке: gridOf (Grid из
TileMapData с footprint пропов), точная проверка круга circleFits
(ближайшая точка квадрата Blocked-тайла — ловит угловой прокол по
диагонали), moveCircle со скольжением по осям, pushOutOfWalls для
телепортов, separateCircles для будущего расталкивания тел.
Дедупликация четырёх копий worldToTile+isWalkable: движение героя
(moveFree), шаги врагов, фауна и валидатор контента теперь на одних
функциях. IsometricTileMap.isPropBlocked публичен.
Бой против стен: конус удара требует прямой видимости (hasLineOfSight
по tall + пропам), снаряды гасятся о стены и дома (вода прозрачна —
плевок летит над прудом). Отброс героя — только если тело помещается
(circleFits): прищёлкивание к центру тайла после отброса меняло
динамику погонь у прудов и валило пробу interact-world.
14 юнит-тестов на коллизии (скольжение, угловой прокол, совместимость
с A*-маршрутом, push-out, separateCircles). Доки: maps.md (раздел
«Коллизии»), README.md, llms.txt.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Сцена на SceneRegistry: NPC/интерактивы/пропы/фауна/враги в одном реестре
...
- дефы получают юниты: NpcDef.pos + atlas?, InteractableDef.pos + view?
(tile вычисляется при загрузке модуля — сейвы/снапшот/валидатор без миграций)
- SceneObjects: фасад реестра области (npc/int/prop с footprint)
- клик-роутинг: npcAt/interactableAt/enemyAt через реестр; чистый
resolveClick и приоритеты не тронуты (пробы transitions/interact зелёные)
- шов врагов ECS: запись с pos по ссылке, move после шага, remove при смерти
- фауна регистрируется (kind 'fauna'), вьюхи NPC — def.atlas, интерактивов —
def.view.tex
- инвариант registry-consistent (реестр согласован с ECS)
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Анимация 4/4: шоукейс в игре — все акторы на SpriteAnimator, статика оживает
...
Унификация акторов на движковые аниматоры (три ручных таймера кадров больше не дублируются):
- PlayerController: SpriteAnimator с клипами walk_*/idle_* — ушёл «застыл на произвольном кадре ходьбы»;
- CombatViews: SpriteAnimator (спящим pause), hit/death-бёрсты — oneShot через engine.fx
(закрыт баг «частицы стоят на месте»), новые опции drag/spin/scaleOverLife/colors;
- FaunaSystem: SpriteAnimator (на ходу walk, в покое стоп-кадр).
Оживление статики в LocationScene (всё тикается engine.fx, без ручного update):
- вода — setTileAnimation (2 кадра, один таймлайн на все клетки);
- мигание героя — SpriteMotion blink с живым duty (вместо ручного alpha);
- NPC idle — кадры npcs_sheet (randomPhase по тайлу), атлас добавлен в BootScene,
отдельные PNG NPC из списка ассетов убраны;
- искры pickup-объектов — SpriteMotion pulse;
- тёплые мотыльки — эмиттер blend:'add' + wobble + fadeIn;
- кадры героя/фауны — через assets.animation() (анимации атласов из батча 3).
Ограничение: sway для цветов/башни не вводился — это тайлы карты (спрайты
внутри IsometricTileMap), SpriteMotion на них неприменим.
Движок, попутные фиксы:
- Updatables: разрушенные записи не тикаются (Pixi обнуляет transform —
тик разрушенного SpriteMotion ронял весь тик и молча убивал игру после
перехода между локациями) + тесты;
- Engine.stepTicks: setManual(false)/resetTiming в finally — исключение в шаге
больше не оставляет цикл в ручном режиме навсегда;
- EmitterOptions.velocity опционален (в oneShot с radialSpeed не нужен);
- SpriteAnimator/SpriteMotion: геттер destroyed — самозачистка из engine.fx.
Доки: core.md (fx), anim.md (самозачистка), render.md (velocity), agent.md +
practices.md (грабля «мост глотает throw шага» и приём диагностики).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Фабрика тестовых фикстур + fail-fast предикатов waitFor
...
- testing/ (только для *.test.ts, в бандл не попадает):
combatWorld.ts (fakeMap/fakeWorld/run/far), gameState.ts (fakeState),
maps.ts (mapWithWall). EnemyAI/los/Interactables тесты переведены —
бойлерплейт фейковых карт/стейтов выведен из трёх файлов.
- GameAgent.waitFor: top-level обращения s.<ключ> сверяются с
AGENT_SNAPSHOT_KEYS до компиляции предиката; неизвестный ключ ->
{ ok: false, error: 'неизвестный ключ s.X', ticks: 0 } — опечатка
больше не тихий false на весь таймаут. predKeys — чистая функция,
тестируется в snapshot.test.ts.
- Список ключей: ENGINE_SNAPSHOT_KEYS (движок) + сценические + слой игры;
анти-дрейф тесты держат список согласованным со слоями снапшота.
Проверки: typecheck, 307 тестов (+6), agent check 45/45 (ни один чек
валидация не отторгла), smoke-act1. Доки: agent.md (поведение waitFor),
practices.md (проверка сценариев).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Батч 4: ИИ врагов (LOS, шум, патруль, настороженность, отступление)
...
- los.ts: линия видимости по сетке (дискретизация 0.25 юнита), тайлы с
tall-объектами непрозрачны; кэш LOS раз в 3 тика в EnemySystem.
- EnemyBrain: состояния patrol/wary/flee/return поверх существующего цикла;
расширенные сенсоры (pos, canSee, noise, patrolPoints, home, hpFraction);
бег при отступлении (run в EnemyIntent.move); warySec-таймер.
- CombatWorld.noise(pos, level): шаги героя 0.35, удар конусом 1.0, плевок и
смерть 0.6; спящих будит только уровень >= 0.7, бодрых слышат от 0.35 в
hearRadius (окно слышимости 0.5 с).
- Патруль — per-spawn (AREAS[*].enemies patrol {points, pauseSec}): тяжеловес
и плевун на лугах бродят по маршрутам, ползуны спят в пепле как раньше.
- Поля ИИ в EnemyKindDef + withDefaults; инварианты enemy-state-legal и
enemy-in-bounds; команды scene:noise и scene:damageEnemy.
- Сценарий tools/checks/ai.mjs (7 проверок) в agent check; 14 новых юнит-
тестов (EnemyAI) + 6 (los); доки: agent.md, practices.md, demo.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
| 2026-09-05 |
Фаза 3: мир игры в мировых юнитах
...
PlayerController, CombatWorld, EnemyBrain, CombatViews и LocationScene
переведены с виртуальных пикселей на мировые юниты (1 юнит = 1 тайл):
- позиции/дистанции/скорости в юнитах (герой 1.75 юн/с, скорости и
радиусы врагов, knockback 0.75, INTERACT_RANGE 1.5);
- конус/окружности через inConeW/inCircleW (метрика проекции);
- вью-позиции — через worldToScreen, округление до px только на границе;
- гистерезис дистанции плевуна ±0.4 юнита, attackStop 0.85 юнита;
- тесты EnemyBrain/stats обновлены под юниты (185/185).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
2 days ago
|
игра: бой в реальном времени — сгустки на ECS, конус/резонанс, HUD, интеграция в LocationScene
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
2 days ago
|