| 2026-09-07 |
docs(practices): сценарии «добавляю меню-сцену» и «предмет/лимит сумки»
...
Новые ситуации в practices.md: каркас MenuSceneBase + карта действий +
пауза под push + disabled-пункты; модель Inventory (лимиты, add → факт,
реестр и формат строки — в игре, onChange для HUD).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
5 hours ago
|
game(scenes): 4 сцены меню на движковом каркасе MenuSceneBase
...
- MenuScene/SettingsScene/SaveSlotsScene/InventoryScene — наследники
MenuSceneBase: скопированная навигация (up/down/advance/menu + гард
перехода) уехала в движок; у каждой сцены только build() и хуки.
- SaveSlotsScene: слоты из data/saves.ts, подписи из slotMeta с фолбэком
на полный load (старые сейвы), пустые слоты disabled (курсор их
пропускает), autosave защищён canDelete.
- Меню сейвов пишет мету (title/savedAt/version/extras.area).
- MenuList.setItems сохраняет фокус при пересборке (громкость больше не
сбрасывает курсор на первую строку).
- Агентный мост не тронут: снапшот из inventory.all, команды те же.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
5 hours ago
|
game: формат сейва — в saveData.ts, слоты конфигом, дефолт хазарда в контент
...
- scenes/saveData.ts: SAVE_VERSION/ReturnToData/SaveData/normalizeSave вынесены
из MenuScene (формат сейва больше не живёт в файле меню); MenuScene
реэкспортирует для совместимости.
- data/saves.ts: SAVE_SLOTS/slotTitle/canDelete вместо хардкода
['autosave','slot1'...] в SaveSlotsScene (дважды).
- HazardDef.requiresItem стал обязательным — контент уже задаёт 'cloth',
мёртвый фолбэк ?? 'cloth' в LocationScene снят (дефолт — в контенте).
- Тест: слоты/подписи/canDelete (510 всего).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
5 hours ago
|
engine(save): метаданные слотов сейва
...
- SaveSlotMeta { title, savedAt, version, extras } в параллельном ключе
meta:<slot> — формат сейвов не меняется, старые остаются читаемыми
(slotMeta -> null -> игра падает на полный load).
- save(slot, data, meta?), slotMeta(), list(); delete чистит оба ключа;
listSlots() не включает meta:* (главный подводный камень).
- Тесты 3 -> 6; доки assets-audio-save.md (мета + оси версий).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
5 hours ago
|
engine(ui): каркас меню-сцен — menuInput, MenuSceneBase, disabled-пункты
...
- Чистая readMenuInput (MenuActionMap/MenuNavEvent): порядок cancel → move →
(adjust XOR confirm) → extra; left/right подавляют confirm (Enter в
настройках не даёт двойной шаг).
- MenuSceneBase: build() из enter(), хуки onMove/onAdjust/onConfirm/
onCancel/onAction — заменяет скопированную в 4 сценах игры навигацию.
- MenuList.MenuItem.disabled + ListCursor с предикатом выбираемости:
курсор пропускает недоступные пункты, активация/клик игнорируются.
- Тесты: menuInput (5), listCursor +3 (505 всего); доки ui-and-dialogue.md
(«Меню-сцены», осознанный отказ от updateBelow).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
5 hours ago
|
engine(inventory): лимиты, богатые события, генерик id
...
- InventoryOptions { maxSlots, maxPerStack }: add обрезает и возвращает,
сколько влезло; spaceFor/size/slotLimit/stackLimit; load клампит.
- InventoryChange { kind, id, count, after } вместо () => void
(подписчиков в игре нет — миграции не нужны).
- Inventory<TId extends string> — задел типобезопасности; мёртвый
InventoryState удалён; формат serialize/load не меняется (сейвы v3).
- Тесты 6 → 11; новый док docs/engine/inventory.md, карта модулей
README.md, llms.txt.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
5 hours ago
|
UX-пачка: подсказки, клик-зоны, моты, коллизия домов, сундук, тихая вода
...
- Тосты держатся по длине текста (до 6 с) + подсказка сбора колокольчиков
при первом входе в пруды (флаг hint_bells).
- Клик по «телу» объекта: RouterDeps.pixelTile как фолбэк на голом клике —
bbox тел NPC/интерактивов (без подписей) + ремап на высокий тайл (дома).
- Моты: заметная насыпь с пульсирующей искрой, HUD-счётчик, вьюха
растворяется с искрами при поднятии (InteractSink.onConsumed).
- Движок: TallSpec.shadow — тайл (tx-1,ty-1) за домом блокируется
(shadowTilesOf в collision.ts + isWalkable карты, тесты).
- Интерьеры: бронзовый контур проёма + подпись «выход».
- Спрайт сундука (tiles/chest.png) вместо ящика-плейсхолдера.
- Вода прудов: radius 2.5, volume 0.22 — только вплотную и тихо.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
16 hours ago
|
Фикс десинка вью героя (SpriteMotion не двигает blink-view) + звук: ветер, шорохи, тише вода
...
- Движок: SpriteMotion перезаписывает позицию/поворот только при bob/sway —
view с чистым blink/pulse можно двигать извне (герой прибивало к точке
спавна, ходьба выглядела камерным скроллом, по прибытии спрайт
«возвращался»); detach симметричен; тесты SpriteMotion
- PlayerController: syncView на точном прибытии в waypoint (кадр не отстаёт)
- Звук: ambience/wind — глобальный слой (AmbienceLayerDef.radius опционален,
нет at/tiles — константная громкость) на лугах и прудах; вода прудов
тише (0.6→0.35, радиус 6→5); sfx/rustle — редкие дальние шорохи пепла
из тика сцены (rng сцены, случайные pan/rate). Птиц в мире нет по лору
- Доки: anim.md (правило позиционирования), practices.md (глобальные
слои, шорохи, «птицы молчат»)
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
16 hours ago
|
Аудио: стерео-музыка, динамические темы (бой/настороженность), room tone интерьеров
...
- Движок: renderMusicStereo (interleaved L,R, equal-power pan треков,
нормализация по общему пику каналов), createBuffer(channels 1|2);
моно renderMusic бит-в-бит прежним сэмплам
- Игра: темы боя и tension-стем в data/music.ts; threat-тик
LocationScene.updateMusic — chase → тема боя, wary → тихий стем
(setVolume с флагом stemPending против шторма перезапусков)
- Room tone интерьеров: ROOM_TONES (hum-спеки, шина ambience),
запуск в enter, стоп в exit, validate-тест ключей
- checks/music.mjs: инвариант «тема лугов не перезапускается»
(динамические темы/стем — легитимная реакция на врага)
- Доки: assets-audio-save.md (стерео), practices.md (динамическая
музыка, pending-флаг), plan.md (Работа G — сделано)
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
| 2026-09-06 |
audio: палитра спеков (tone/wobble/sweep/layers) — все sfx на спек-синтезе (E4)
...
- SoundSpec: kind 'tone' (чистый тон + свип freqTo), параметры w/attack/
fadeTo/wobbleHz|Depth|Base|Phase, доп. голоса layers (SpecVoice с delay);
без модификаторов бит-в-бит прежним формулам
- data/sfxSpecs.ts: 16 спеков — все бывшие файловые sfx, включая шаги
(StepVariants: варианты renderSpec с сидами base+i·10, fallback — базовый
спек); ui_click во всех сценах через playSfx
- gen.mjs остался только амбиенту; assets/audio/sfx/ удалён; формулы
бывших sfx экспортированы для парити-теста (bit-в-бит / eps 1e-5)
- мост: scene:synthesize принимает kind tone; доки: agent.md,
assets-audio-save.md, practices.md, plan.md (E4 ✅)
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
audio: миграция bell_hit/whoosh на спек-синтез (E3), мёртвый door.wav удалён
...
- SoundSpec: флаг swell (огибающая «0 → 1 → 0» у hit/scrape), у chime 4-й
партиал и константная амплитуда клика — формулы совпали с gen.mjs дословно
- data/sfxSpecs.ts: реестр SPEC_SFX + playSfx — единая точка проигрывания
sfx (AudioSystem, useTransition, collectFlower); ключи в AUDIO_KEYS
- gen.mjs: CLI-гвард (WAV при импорте не пишутся), формулы bellHit/whoosh
экспортированы для парити-теста sfxSpecs.test.ts (бит-в-бит равенство)
- доки: plan.md (E1–E3 ✅, F/G ✅), practices.md, assets-audio-save.md
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
Движок: секвенсор renderMusic + playLoopBuffer; игра — тема лугов (работа G)
...
Партитура MusicSpec (bpm, beats, партии-стемы: pluck/bell/pad) чисто
компилируется в луп; playLoopBuffer играет буфер памяти на любой шине.
Тема лугов — правило-базовый генератор в data/music.ts (сид, ля-минорная
пентатоника), компиляция в рантайме — WAV для музыки не нужен; у области
поле theme, у Game — playTheme с guard-ом и кроссфейдом. Проба
checks/music.mjs в agent:check. Секвенция тем (бой) — до боевой темы.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
Движок: auto-mute по visibilitychange, bus в PlayOptions, exponential-фейд
...
Свернул таб — ctx.suspend, вернулся — resume (опция pauseOnHide, без
DOM listener не ставится). Разовый sfx можно направить в любую шину
(bus, по умолчанию sfx). Затухания лупов — экспоненциальные (линейный
спад слышен ступенькой); нарастание осталось линейным — экспонента не
определена от нуля. Доки и план (работа F ✅) в том же коммите.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
Мост: scene:synthesize — звук по спеку из агентных проверок (работа E2)
...
Агент без слуха описывает звук параметрами (SoundSpec), движок
компилирует его в буфер (playSpec), факт запуска наблюдаем в
__gameAudioLog. Проба checks/synth.mjs в agent:check: удар/звон/
скрип/гул по спекам, громкость доезжает до голоса, повторный синтез
играет, негодные спеки возвращают null, гул не глушит амбиент.
Формы спеков — в docs/engine/agent.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
Движок: playSpec — спек-синтез звука по описанию (работа E1)
...
SoundSpec (kind hit/chime/scrape/hum + параметры) детерминированно
компилируется renderSpec в сэмплы; AudioManager.playSpec кэширует
буфер по ключу. Путь «звук по запросу» для ИИ-агента без слуха.
Типы synth.mjs — декларацией рядом (synth.d.mts): TS резолвит
specifier к реальному файлу и игнорирует ambient declare module.
План работ E/F/G (спек-синтез, хвосты аудио-инфраструктуры, музыка)
— в docs/plan.md; доки движка обновлены в том же коммите.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
17 hours ago
|
Движок: лок кадров (maxFps) и FPS-счётчик по F3
...
GameLoop: опция maxFps — срезанные rAF-кадры не делают ни шагов, ни рендера,
но время копится в аккумулятор (шаги идут пачкой на отрисованном кадре).
Engine: опция maxFps, FpsMeter — дебаг-плашка FPS в углу (среднее за ~0.5 с),
скрыта по умолчанию, переключается по F3 (движок слушает сам, preventDefault
против поиска Firefox; в игре F3 уже занята дебаг-оверлеем сцены — toggl'ятся
вместе). Игра: maxFps 30. Доки core.md/getting-started.md обновлены.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 hours ago
|
Движок: 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
18 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
18 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>
root
committed
18 hours ago
|
План: работа D — возможности аудио-движка (sfx-хендл, полифония, наблюдаемость, буферы)
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 hours ago
|
Игра: 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>
root
committed
18 hours ago
|
Игра: шаги по поверхностям, звуки предметов, локальные амбиент-слои
...
- Шаги: sfx/step_grass|ash|water|floor по тайлу под ногами (stepKey), старый sfx/step удалён
- Предметы: моты → mote_pickup, сундук → chest_open, знаки/записка → note, whoosh на переходах
- Фауна: событие fauna:startle (FaunaSystem слышит combat:attack) → sfx/deer_startle
- AudioSystem.setLayers: несколько амбиент-слоёв сразу, синхронный дедуп, тик-фейд громкости
- AreaDef.ambienceLayers: вода прудов, гул башни (точка), скрипы домов (тайлы)
- validate-тест покрывает новые ключи через AUDIO_KEYS
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 hours ago
|
Игра: 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>
root
committed
19 hours ago
|
Игра: амбиент на шину ambience вместо music, слайдер «Амбиент»
...
- gen.mjs: ключи ambience/meadows|ponds|zvenets -> assets/audio/ambience/ (music/ удалён)
- AUDIO_KEYS вынесен в data/audio.ts (валидатор в node-тестах без ?url-импортов Game)
- Game.playAmbience(key, 0.7) c guard ключа; '' — stopAmbience; шина ambience в settings
- AreaDef: визуальное ambience -> atmosphere, аудио ambience?: string (интерьеры — тишина)
- SettingsScene: Общая / Амбиент / Звуки (ползунок music не показываем — треков нет)
- validate-тест: area.ambience и Interactables.responses[].sound ∈ AUDIO_KEYS
- хвосты music/ в доках вычищены; agent:check зелёный
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 hours ago
|
Движок: 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>
root
committed
19 hours ago
|
Батч 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>
root
committed
19 hours ago
|
Батч 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>
root
committed
19 hours ago
|

Тела не проходят друг сквозь друга + карта коллизий агенту
...
Расталкивание через реестр сцены: герой выталкивает себя из 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>
root
committed
19 hours ago
|

Движковые коллизии: круг поверх сетки + 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>
root
committed
20 hours ago
|
Сцена на 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>
root
committed
20 hours ago
|