| 2026-09-06 |
Движок: playBuffer/createBuffer (sfx из памяти); игра — вариативные шаги
...
D2 работы D. AudioManager: общий запуск голоса вынесен в startSfx,
playBuffer(key, buffer, opts) играет буфер из памяти в шину sfx,
createBuffer(Float32Array, rate=22050) строит AudioBuffer (null до unlock).
Чистые примитивы синтеза вынесены в @rpg/engine/tools/synth.mjs (без
node-импортов — wav.mjs тянет node:fs и в браузере не соберётся); wav.mjs
реэкспортирует их для тулз, WAV-файлы побайтно не изменились.
Игра: systems/StepVariants.ts — 3 процедурных варианта шага на поверхность
(сиды 253–256, полосы как в gen.mjs), AudioSystem.playStep(surface) вместо
ключа+джиттера rate, LocationScene отдаёт поверхность. До unlock — файловый
шаг. Гвард: @rpg/engine/tools/* — публичное API. Доки и план обновлены.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
23 hours ago
|
Движок: onPlayed для лупов, onEnded, duration(key); игра без ручного лога амбиента
...
- onPlayed зовётся и на стартах лупов (нормализованные опции) — DEV-лог игры
собирается целиком шпионом, ручной push в Game.playAmbience удалён
- onEnded(key): естественный конец sfx; заглушенные голосом (stop/вор/restart)
событие не поднимают
- duration(key): длительность декодированного буфера (null до preload)
- Тесты: +5 в AudioManager.test.ts (440 всего); доки в том же коммите
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
23 hours ago
|
Движок: SfxHandle из play(), опция restart, лимит полифонии maxVoices
...
- play() возвращает SfxHandle | null: setVolume/setPan/setRate в полёте + stop;
после stop и естественного конца хендл no-op (учёт голосов через onended)
- restart: true — прежние экземпляры ключа глушатся коротким фейдом
- maxVoices (3-й аргумент конструктора, дефолт 24): при переполнении
воруется самый тихий играющий голос
- Тесты: 6 новых в AudioManager.test.ts (436 всего); доки assets-audio-save.md,
practices.md — в том же коммите
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Игра: DEV-лог аудио, агентная проба audio, практики «Добавляю звук»
...
- DEV-шпион window.__gameAudioLog (кольцо 24): sfx через onPlayed, амбиент через playAmbience
- checks/audio.mjs: амбиент запущен один раз, шаги без боя, bell_hit, guard амбиента, кольцо;
зарегистрирована в agent.mjs (8 проб)
- Доки: practices.md — ситуация «добавляю звук»; assets-audio-save.md — шпион и проба
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Игра: 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>
|
Движок: AudioManager v2 — шина ambience, playLoop, опции pan/rate
...
- 4-я шина ambience → master (лупы областей и локальные слои отдельно от музыки)
- PlayOptions {volume, rate, pan}: rate 0.5..2, |pan|<0.01 — без StereoPanner;
число в play() — сахар для {volume} (30+ старых вызовов не мигрируем)
- playLoop — луп без слота (база локальных амбиент-слоёв); playMusic/playAmbience —
независимые слоты с кроссфейдом, негодный ключ не глушит текущий трек
- ctxFactory инъекцией (фейк в тестах), шпионский хук onPlayed
- Settings.ambience (дефолт 1, старые настройки накрываются спредом)
- 10 тестов на фейковом AudioContext; доки assets-audio-save.md + README
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Батч 7: декор-слой (микро-кусты, камни, проплешины)
...
- engine: DecorData + TileMapData.decor; decorLayer между землёй и объектами;
addDecor (возвращает спрайт, один на тайл, повторный заменяет) / clearDecor;
setTile декор не трогает
- engine: MapFileV1.decor + validateDecor (границы, целые, dx/dy — числа)
- game: DECOR-реестр (tuft/stone/bald, id 15-17), buildDecor на createRng(seed+7919)
(~10% наземных тайлов, только GRASS/ASH, смещение не по сетке);
mulberry32 заменён на движковый createRng
- art: decor_tuft/stone/bald 16×16 (рисунок в ромб 16×8, палитра G/P)
- фикс fresh-теста: Math.round -> -0 теряется в JSON, нормализация +0
- тесты: порядок слоёв, addDecor/clearDecor, границы, round-trip + валидация decor
- доки: maps.md (раздел «Декор»), art-style.md, practices.md (ситуация
«меняю генераторы арта или карт», грабля -0)
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Батч 6: варианты текстур тайлов (микс по хэшу координат)
...
- engine: hashTile(x,y,seed) — чистый хэш координат (math/rng.ts)
- engine: TileMapData.variants + setTileVariants + публичный variantOf;
анимация сильнее вариантов (setTileAnimation снимает их)
- engine: MapFileV1.variants (строковые ключи) + validateVariants в parseMap
- game: GRASS_V1/V2, ASH_V1, PATH_V1 (id 11-14), GROUND_VARIANTS в трёх
генераторах карт, новые текстуры в ASSET_KEYS и tileTextures()
- art: grass_2/grass_3/path_2/ash_2 (32×16, та же палитра, другой seed)
- тесты: hashTile (детерминизм/разброс), выбор варианта стабилен при setTile,
анимация vs варианты, round-trip + валидация variants
- доки: maps.md (раздел «Варианты текстур»), art-style.md, art-pipeline.md
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: реестр объектов сцены с индексом по тайлам
...
Жанронезависимый примитив «что-то стоит на карте»: позиция в юнитах,
радиус тела, footprint в тайлах, непрозрачная ссылка на контент игры.
Запросы at/atWorld/near/byKind без перебора сцены; дубликат id — throw.
Фундамент для унификации NPC/интерактивов/врагов и телесных коллизий.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Реестр размеров спрайтов: sizes.mjs + AssetLoader.frameSize
...
Единый источник номиналов для тулз (gen.mjs, lint-art.mjs), рантайм
читает размер кадра из атласного JSON. Захардкоженные 16×24/16×16
заменены ссылками на реестр; VIRTUAL-дубли 480/270 в сценах — на
Game.VIRTUAL_W/H; маркер NPC считается от реальной высоты кадра.
- tools/pixelart/sizes.mjs: SIZES (tile/tall/prop/char/fauna/clump/decor)
+ SLOTS для линтера; тест sizes.test.ts
- gen.mjs, lint-art.mjs: импорт из реестра вместо локальных констант
- AssetLoader.frameSize(key, frameName?) — sourceSize из атласного JSON
- IsoLayout как параметр игры — отложено (docs/plan.md)
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>
|

feat(tools): батч 3 — тулзы кадров + animations в атласах
...
- engine/tools/frames.mjs: производные кадры из опорных — shiftFrame,
bobFrame (парение), squashFrame (приседание), swingFrame (nearest-
поворот вокруг опоры), mirrorFrame, deriveCycle (дескрипторы)
- buildAtlas(..., animations) — Record<имя, number[]> → JSON.animations
(формат Pixi); assets.animation() оживает
- gen.mjs: вода двумя кадрами (tiles/water_1/2, блики сдвинуты на 1px),
атласы героя/фауны/сгустков собираются через buildAtlas с animations,
новый chars/npcs_sheet (idle-кадры NPC через bobFrame); сгустки в
ячейке 20×20 теперь по центру (было прижато влево — спрайты врагов
центрируются точнее)
- aiart/atlas.mjs: инлайн-бобинг → squashFrame (вывод идентичен),
animations из frameSets
- игра: ключи воды water_1/water_2 (старый tiles/water удалён)
- ассеты перегенерированы вместе с генераторами (npm run art, art:lint ok)
- доки: art-pipeline.md (frames.mjs, animations), assets-audio-save.md
- тесты: frames (8), buildAtlas animations (2) — итого 369
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

feat(engine): батч 2 — оживители статики + партиклы v2 + тайл-анимации
...
- anim/motion.ts: чистая sampleMotion — bob/sway/pulse/blink, композиция,
округление bob до целого px; anim/SpriteMotion.ts — Pixi-адаптер
(база снимается в конструкторе, живые params, detach, seed→фаза)
- render/particleSim.ts (breaking): stepParticle → ParticleVisual
(alpha/scale/rotation/tint); drag, wobble, fadeIn/fadeOut-хвост,
scaleOverLife, colorOverLife; mergeEmitterOptions — чистое слияние
(burst больше не мутирует опции); fix: rng.int инклюзивен — выход
за границы colors
- render/Particles.ts: текстурные частицы, colors/colorOverLife, spin,
size-диапазон, max-лимит + пул спрайтов, blend 'add', setOptions,
onFinish у oneShot
- IsoDepthLayer.addFx(view, tx, ty, bias=0.5) — эффекты поверх актёра
- IsometricTileMap.setTileAnimation(id, frames, fps) + update(dt) —
один общий таймлайн на все клетки id (вода)
- engine.fx (батч 1) — рецепт авто-тика в доке вместо ручного update
- доки: render.md (частицы, addFx), anim.md (SpriteMotion, тайлы),
recipes.md (ходьба/атмосфера/статика/вода/удар), maps.md
- тесты: motion (9), particleSim переписан под v2 (итого 360)
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
engine: ядро анимации — SpriteAnimator, клипы, реестр тика engine.fx
...
- anim/clip.ts: чистый степпер клипов (loop/once/pingpong, время в
шагах, speedScale, dt>периода проматывает).
- anim/SpriteAnimator.ts: именованные клипы, play/pause/showStep,
onFrame/onFinish, randomPhase (seed), текстура только при смене шага.
- anim/FrameAnimation.ts → @deprecated-обёртка, API байт-в-байт.
- core/Updatables.ts + Engine.fx: движок тикает эффекты сам (между
tween-ами и сценами), самозачистка по destroyed; clear() в stop().
- Тесты: clip (7), Updatables (5), FrameAnimation (4) — пробел покрытия
закрыт (было 0). Доки: новый docs/engine/anim.md, core.md, README.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
aiart: примерка tryon + бобинг walk-кадров; фикс startDevServer (cwd)
...
- tryon.mjs: примерка AI-сборки в игре до promote — подмена целевого
атласа, rename кадров <id>_ → hero_ (контракт LocationScene),
скриншот героя в локации, восстановление файлов в finally.
- Бобинг: при bob:true 2-й кадр = 1-й, сдвинутый вниз на 1px —
различимая ходьба из одинаковых поз стоя; манифест bob:true.
- agent-lib startDevServer: опция cwd (приложение передаёт каталог с
index.html — vite от корня репо отдавал 404), spawn detached +
kill группы (зомби-дети npx держали порт), детект выхода при занятом
порте; игровой lib.mjs по умолчанию cwd apps/game; docs/engine/agent.md.
- meta.image в buildAtlas — basename (Pixi резолвит относительно URL
JSON; путь с каталогом давал /chars/chars/… и 404).
- docs/plan.md: статус проекта — техно-демка движка.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(aiart): палитровый ремапер + линтер арта
...
recolor.mjs — перекраска квантованного спрайта по ключам палитры/hex (CLI +
поле remap в манифесте атласов: плащ звонаря приведён к каноническим C0/C1).
lint-art.mjs (npm run art:lint) — авто-чеклист арт-библии: палитра, слоты,
детект «мыла»; все 31 кодовых ассета и AI-атлас проходят, негативы проверены.
Тест ремапера (3). Доки plan.md, art-pipeline.md, CLAUDE.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(engine/tools): findFigures/resizeRect + buildAtlas — основа AI-пайплайна атласов
...
imaging.mjs: автодетект фигур на однотонном фоне (связные компоненты) и
nearest-даунскейл прямоугольника с фоном-прозрачностью. atlas.mjs: сборка
кадров в строку + Pixi Spritesheet JSON. 8 тестов. Дока art-pipeline.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Крупные объекты: props с footprint в движке, слоты арта, проба дома кодом
...
Работа C из docs/plan.md — подготовка к зданиям акта 2.
Движок:
- TileMapData.props: крупный объект с footprint w×h от СЗ-угла — коллизия
по всему прямоугольнику, земля под ним (ground), спрайт якорем низ-центр
footprint'а; без текстуры — колонна-плейсхолдер (общий с tall хелпер).
- IsoDepthLayer.addRect: глубина по диагонали юго-восточного угла
footprint'а — герой у южной стены перекрывается домом, перед южным
углом рисуется поверх.
- mapFormat: props в файловом формате + валидация (целые w/h ≥ 1,
footprint внутри карты, высота в юнитах).
- LocationScene вставляет map.propViews в общий depth-слой.
- Доки движка: maps.md (секция props), render.md (addRect).
Арт (проба C2):
- Арт-библия: слоты крупных спрайтов (дом 2×2 — 64×80, 3×3 — 96×96,
4×4 — 128×112); плотность пикселя как у тайлов.
- gen.mjs: tiles/house_big.png (64×80) — композит: модули щитов + крыша
из шпал + козырёк + окна. Сравнение с AI-апскейлом 24→48 —
tools/aiart/probe-c2.png: код-композит заметно чище, апскейл — мыло с
чужой плотностью пикселя. Вывод: крупные объекты — код-композит;
апскейл только как болванка (см. docs/plan.md, работа C).
Тесты: 318 (+9: footprint, depth-rect, валидация пропов).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Чистка тулз: пробы-сироты и дублирующие смоуки удалены, доки по новой раскладке
...
- Удалены одноразовые пробы, никем не запускаемые: cam-probe, far-test,
skin-info, skin-probe, walk-probe, font-probe.
- Удалены смоуки, дублирующие checks/: smoke-zvenets и smoke-ponds
(покрываются checks/transitions.mjs), smoke-quest (checks/interact.mjs).
Полный прогон акта 1 — smoke-act1.mjs, базовый — smoke.mjs.
- Доки: CLAUDE.md (правило «общее — в движок, сценарий — в игра»,
packages/engine/tools), docs/engine/{practices,art-pipeline,agent}.md,
docs/demo.md, docs/llms.txt, README.md.
- npm run audio — скрипт для и без того существующего генератора WAV.
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>
|

Рефакторинг: вынос клик-роутинга и агентного моста из LocationScene
...
LocationScene (1169 строк) — god-object: каждая фича трогала один файл.
Сцена остаётся оркестратором вьюх, логика переезжает в два модуля:
- systems/ClickRouting.ts: чистый резолвер приоритетов resolveClick
(NPC → клик-переход/заперто → интерактив → цветок → враг → движение)
с юнит-тестами без Pixi + рантайм InteractionRouter (пути, отложенные
взаимодействия, step-переходы с дисармом тайла входа). Сцена получает
его deps-объектом и колбэками (talkTo/collectFlower/useTransition и
др. остаются в сцене — они трогают вьюхи/катсцены/смену сцен).
- agent/SceneAgentView.ts: агентный фасад (снапшот/инварианты/команды),
слои снапшота собираются фабриками snapshot.ts — дублирование слоёв
невозможно. LocationScene implements SceneAgent → 3 однострочника.
Попутно: TransitionSnapshot получает типизированное поле trigger
('step' | 'click') — фактический объект его уже клал.
Сцена: 1169 → 823 строки. Проверки: typecheck, 301 тест, agent check
45/45, smoke-act1. Доки: CLAUDE.md, practices.md (ситуация «меняю
клик-роутинг / агентный мост сцены»).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Реестры флагов/варов + кросс-валидация + pre-commit хук
...
- data/ids.ts: FLAGS/VARS (id = значение, doc — семантика), типы
FlagId/VarId, usedFlag() для used:<id> (была конкатенация в 3 местах).
- Контент и сцена на константах: dialogues/interactables/quests/npcs,
LocationScene (vars), Interactables, GameAgent; QuestStage.effect —
union QuestEffect, doneFlag/flagKey/progressKey типизированы.
- validateReferences(): флаг/вар вне реестра — error, мёртвый ключ —
warn, битые ссылки квестов на NPC/диалоги — error (+3 теста).
- pre-commit хук .githooks/ (check:fast = typecheck+guard+тесты),
автоподключение через "prepare". Доки: CLAUDE.md, practices.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Архитектура: гвард границы движок/игра + перенос тулз в apps/game/tools
...
- Гвард границы (apps/game/tools/guards/boundary.mjs, npm run guard):
движок не импортирует игру и не выходит из пакета, игра импортирует
движок только через @rpg/engine и @rpg/engine/assets/*. Чистое ядро
покрыто тестами (8 кейсов); проба guard:boundary в agent check.
- tools/ -> apps/game/tools/ (git mv, история сохранена): правка
корне-относительных путей в генераторах и CLI, ROOT-константы
'../../..', npm-скрипты обновлены.
- Доки: CLAUDE.md, llms.txt, docs/engine/*, README — новые пути,
практика «меняю границу движок/игра» и грабля про ROOT тулз.
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>
|
Батч 3: взаимодействие с миром шире (знаки, моты, колокол)
...
- Предмет mote («Пепельный мот») — зачаток экономики по docs/world.md.
- InteractResponse.addVar: прибавление к вару (подбор мотов копит, не затирает).
- Контент: столбы у троп (луга, пруды, Звенец), три пепельных мота на лугах,
верёвка колокола башни (флаг rang_tower, повтор с другим текстом).
- Мила: повторный диалог trader_repeat с веткой по вару (whenVar motes >= 1);
чинён dialogueRepeat (был trader_first — полотно давалось повторно).
- Инвариант interact-used-consistent в мосте: флаг used:<id> <=> снапшот used.
- walkTo моста пропускает узлы-объекты на пути (клик в мот/цветок больше
не ломает маршрут); docs/agent.md и practices.md обновлены.
- Плейсхолдеры-вью по видам: столб/рычаг/кучка с искрой/ящик.
- Сценарий tools/checks/interact-world.mjs (7 проверок) в agent check.
- Тесты: addVar, ветка trader_repeat (dialogues.test.ts).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Батч 2: интерьеры и интерактивные объекты
...
- Интерьеры house_elder/shop: buildInteriorMap (тайлы FLOOR/WALL), двери
кликом внутрь, выход через проём (transition kind 'return' по returnTo).
- InteractableDef/INTERACTABLES: реакции по when (flag/notFlag/varAtLeast),
once -> флаг used:<id>, эффекты setFlags/clearFlags/setVar/gives/sound.
- Системы: resolveInteractable (чистый) + Interactables (InteractSink);
pendingInteraction в LocationScene, подписи объектов при близости.
- Сундук/очаг/записка (дом Ирвина), прилавок (лавка Милы).
- Снапшот interactables; GameAgent.waitFor ждёт по полному снапшоту игры.
- Валидатор: переходы в существующие области + объекты в границах.
- Сейвы v3 (returnTo), миграция v1/v2.
- Исправление: колодец (22,20) перенесён в buildMeadowsMap (индекс 582
за пределами 28x20 молча дорастал массив).
- Сценарий tools/checks/interact.mjs (7 проверок); тесты резолвера/рантайма.
- Доки: agent.md (waitFor по полному снапшоту, валидация), practices.md
(ситуация «интерьер/объект»), demo.md, CLAUDE.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
| 2026-09-05 |

Батч 1: единый механизм переходов + телепортация
...
- data/locations.ts: TransitionDef/TransitionTarget/AreaDef/AREAS/areaOf —
выходы между областями (step) и двери/колодцы (click) в одном механизме
- data/transitions.ts: чистый resolveTransition (триггер, условия
requiresFlag/requiresItem с lockedText, дисарм тайла входа) + 8 тестов
- LocationScene: checkExits -> checkTransitions, клик-переходы в
handleWorldClick (NPC -> переход -> остальное), returnTo для kind 'return',
тост «заперто» без спама, дисарм при входе через переход
- Сейвы v3: SaveData.area + returnTo, normalizeSave для v1/v2
- Новые тайлы FLOOR/WALL/WELL + арт (колодец — бронзовый обруч), WELL на
карте: парные колодцы Звенец (9,6) <-> луга (22,20), duration 0.2
- Валидатор: переходы вместо exits (существование области, entry проходим)
- tools/checks/transitions.mjs (step + клик туда/обратно + снапшот) в check
- docs: практики (мимо-картная запись в генераторе, async-шаг Checks),
дорожная карта, легенда колодцев в world.md
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

Батч 0: агентный набор инструментов (мост, валидация, CLI, доки)
...
Движок (packages/engine/src/agent/):
- types.ts: SceneAgent/AgentHost/Invariant/SnapshotLayer — каркас жанронезависим
- EngineAgent: снапшот+инварианты, step/waitFor, атомарные tapVirtual/press/key
- invariants.ts: checkFinite/checkRange/checkBounds/checkWalkable
- Engine.stepTick/stepTicks/tickCount; GameLoop.setManual/resetTiming
- InputManager.inject*: инъекция через те же структуры ввода
- DialogueRunner: nodeId/choices/waitingForChoice раскрыты для снапшота
Игра (apps/game):
- agent/GameAgent.ts: window.__agent (DEV): snapshot/step/waitFor/walkTo/runDialogue
- agent/snapshot.ts: чистые слои hero/enemies/npcs/dialogue/game
- data/validate.ts: runtime-валидация контента → Invariant[] (нашла 4 врага на воде)
- сцены реализуют SceneAgent: whitelist-команды, lastToast в снапшоте
Инструменты и доки:
- tools/agent-lib.mjs (openGame/AgentClient/Checks), tools/agent.mjs (CLI check/run/snapshot/screenshot/dev)
- tools/checks/agent-invariants.mjs; смоуки переписаны на мост; maps:fresh в gen.test.ts
- docs/llms.txt, docs/engine/agent.md, docs/engine/practices.md (живой документ)
- CLAUDE.md, docs/engine/README.md, docs/demo.md обновлены
Co-Authored-By: Claude Code <noreply@anthropic.com>
|