| 2026-09-08 |
game: карта окрестностей — предмет map_scroll и оверлей области
...
Предмет «Карта окрестностей» (набросок Ирвина) выдаётся вместе с часами
в elder_first. Использование в сумке открывает MapScene (push поверх
сумки): схема области из renderMinimap с палитрой из арт-палитры,
маркеры переходов и NPC, мигающий маркер героя. Game.activeLocation
дополнен areaId/heroTile() (регистрация локации уже была).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat: B7 — амбиент: драматургия по времени суток + слой зон наката
...
Ночные варианты лупов трёх областей (_night): воздух ниже и плотнее,
дальняя машина и колокол (луга), вода громче (пруды), гул башни и глухие
удары (Звенец). Кроссфейд день↔ночь по кривой суток: LocationAudio
выбирает ключ с гистерезисом ±0.05, кроссфейд даёт Game.playAmbience;
стартовый амбиент выбирается по часам при входе. Новый слой
ambience/ashflow (шелест + глухой гул) в зонах наката: у AmbienceLayerDef
заведены явные positions (тайлы зон вынесены в константы HAZARD_*).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat: B6 — пепельная трава, летающий пепел, тёплые зоны наката
...
ashTile: стебли травы сквозь пепел (G0/G1/G2H, 4 шт на ромб, в редком
ash_2 — 2). Летающий пепел лугов: плотнее и с медленным падением (rate 9,
ветер + y 1..4). Тёмные пятна зон наката — тёплая пепельная мгла
(DARK_SPOT_TINT 0x3e3e48 → 0x4a4440).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat: B5 — дома: три фасада (шпальная крыша / ржавый лист / длинное однооконное)
...
houseTile параметризован: общие стены-щиты и фундамент, различаются крыша
и проёмы. Фасады подключены вариантами HOUSE (хэш-микс в руинах разъезда);
в Звенце три дома расставлены явно (вариантные id в тайлах), чтобы деревня
показывала всё разнообразие — хэш-микс для трёх домов не гарантирует его.
Для этого HOUSE_V1/V2 добавлены в BLOCKED, амбиент-слой «houses» теперь
матчит все три id. Карты перезаписаны, арт-библия обновлена.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat: B4 — деревья: три вида крон, качание, осыпающийся пепел
...
Движок: анимация тайлов сочетается с вариантами — tall-объект анимируется
по фактическому (вариантному) id клетки, у разных вариантов свой fps.
Игра: три вида деревьев (широкая крона с кабелем / редкая / молодая),
каждый с кадром качания (крона +1px по X), fps 1.5/1.7/1.3 — кроны
качаются рассинхронизировано. Варианты TREE_V1/V2 раздаются вариантами
карт по хэшу координат, карты перезаписаны. С крон изредка осыпается
пепел (ember-частицы, серые, нормальный блендинг).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat: B3 — замах и след удара (conePath в движке)
...
- engine: math/shapes.ts — conePath(from, dir, radius, halfAngle, segments):
полигон сектора для отрисовки (углы в экранных осях, как inCone); тесты
(точки дуги на радиусе, симметрия, вырожденный луч), экспорт через index.ts
- игра (CombatFlow): при удержании атаки — полупрозрачный сектор конуса
(перерисовка только на смене направления/заряда, позиция следует за героем
без перестройки полигона); заряжен резонанс — круг волны r=3 юнита
- след удара: вспышка-сектор (alpha 0.4, тает за 0.18 с через fx.fade)
- demo.md: math/shapes дополнен conePath
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): B2 — лампа героя ярче и шире, видимый фонарь на поясе
...
- HERO_LAMP: radius 1.6 → 2.0, intensity 0.85 → 1.0 (прорезает ночь заметнее)
- новый спрайт chars/hero_lamp.png (латунь B*, тёплое стекло F*) — ребёнок
view героя у пояса: свет читается «от фонаря», а не сам по себе
- центр круга уже на ногах героя (позиция = ноги) — визуальная проба
день/ночь/золотой час офсета не потребовала
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
fix(game): B1 — мот не возвращается, автосейв на переходах и закрытии, маркер сбоку
...
- вьюхи once-объектов с used-флагом не создаются при повторном входе в область
(мот/сундук больше не «возвращаются» на карте)
- сериализация прохождения вынесена в buildSaveData/autosave; автосейв пишется
и на переходах (позиция до fade), и при beforeunload/visibilitychange(hidden)
— закрыл игру в любой момент, прогресс на месте
- LocationScene регистрируется в Game.activeLocation (доступ из main.ts даже
когда поверх открыта сумка)
- маркер «можно говорить» у NPC сдвинут к плечу — не закрывает лицо
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
| 2026-09-07 |
fix: флип side-анимации, панель диалога сразу полной высоты, перенос шрифтов
...
- PlayerController: side-кадры генератора смотрят вправо — флип теперь при
facing left (был при right, анимация ходьбы казалась зеркальной).
- DialogueBox (движок): панель при typewriter сразу строится под полный
текст (измерение до печати), место под «далее ▸» резервируется всегда —
подложка больше не появляется после напечатавшегося текста.
- Перенос шрифтов: nameText диалога, метки Button, тосты (wordWrapWidth
440) — длинные строки не вылезают за панели и экран; тост сдвинут ниже
строки «Цель:» (LocationHud) — не накладываются.
- Доки: ui-and-dialogue.md (механика панели), practices.md (приём
«размечай по полному тексту»), art-style.md (side смотрит вправо).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): квестовая ясность — трекер цели, тост тонов, направления
...
- HUD: строка «Цель: <стадия (N/M)>» из activeStage (questTracker);
скрыта, когда активных квестов нет
- тост «Тон (N/3)» у деревьев-механизмов: InteractResponse.toast
(текст + прогресс по вару) вместо полного текста реакции
- эпилоги квестов получили doneFlag (act2_hook, новый machine_hook
в clint_repeat) — трекер корректно переходит между квестами
- журнал в сумке: текущие задачи с переносом по ширине панели,
взятия-стадия видна после принятия; панель 300×222
- направления в текстах стадий и диалогах (пруды — северо-запад
лугов, роща — восток Звенца); убран дубль ключа reply в clint_first
- фикс: сепарация тел не вталкивает врага в непроходимый тайл
(откат позиции; инвариант enemy-in-wall в resonator)
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): ветер лугов — воздушная полоса вместо «моря»
...
Причина «моря» в ambienceMeadows: полоса 100–600 Гц (рокот), строгая
4-секундная LFO-волна громкости (формула наката) и синус-гул 82 Гц.
- полоса поднята до 250–1800 Гц (как у эталонного слоя ambience/wind)
- «дыхание» — сумма нескольких медленных волн с целым числом циклов
на луп (без ровного прилива), порывы — тем же шумом, короче и случайнее
- тихий высокий свист (2–4 кГц) только на пике дыхания, гул 82 Гц убран
- npm run audio перегенерировал WAV; ключи те же, чеки audio/music не задеты
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): солнечный день, золотой час и тюнинг под прорези
...
- движок lightSim: dayNightAmbient — трёхопорный микс суток день → закат →
ночь (DUSK_PEAK 0.45, DEFAULT_DUSK_COLOR 0xe8c090), экспорт из index.ts
- locations.ts: дневные ambient к почти-белому (meadows 0xf7f4ea, ponds
0xf2f0e8, zvenets 0xf5f2ea, rust_grove 0xe9e6da, old_siding 0xe4e2d8),
ночи темнее под прорези (0x3e4658…); duskColor в LightingDef
- HERO_LAMP компактнее и ярче (радиус 1.6, интенсивность 0.85); радиусы
колокольчиков 1.4→1.0, окон 1.8→1.2, интенсивности выше
- палитра: ключевые цвета травы/пепла/воды подняты тоном (G*, P4–P5H, W1–W2),
арт перегенерирован; art-style.md синхронизирован
- чеки lighting/time под новые значения и трёхопорный микс; тесты золотого
часа (lightSim, GameLighting); docs/engine/render.md
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(agent): мост scene:walk/approach вместо tapTile, чеки и смоуки на кнопках
...
- SceneAgentView: команды scene:walk (A* + followPath) и scene:approach
(findPathToNeighbor, возврат тайла прибытия)
- GameAgent: walkTo через одну команду + waitFor тайла (без !moving —
step-переход срабатывает в moving), новый approach; tapTile удалён
- lib.mjs: t.interactAt/t.talkTo/t.useInteractable — координаты из снапшота
по id; fightNearest — подход, доворот facing коротким шагом к врагу
(клавиши — экранный вектор) и конус, до 3 попыток
- Чеки и смоуки переписаны с кликов на кнопки: interact, interact-world,
quest-bells, transitions, final, resonator, zoom, lighting, collision,
perf; smoke-act1 и final-smoke green (бой починен)
- Доки: agent.md (контракт моста), practices.md (доворот facing, две
линейки), plan.md (задача 15)
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): удаление клик-механики — движение и взаимодействие только кнопками
...
ClickRouting.ts -> InteractRouting.ts. Удалены: resolveClick/ClickProbe/
resolvePixelTile/pointerToWorld, handleWorldClick, таргет врагов (target/
updateTarget/enemyAt/attackTarget), pending-механизм (pendingInteraction/
resolvePending/request*/walkTo), PlayerController.onWorldClick + setWalkGrid.
Остались: кнопка действия (pickInteract/interactCandidates/interactTarget/
resolveInteract), step-переходы с дисармом, линейка радиуса.
Мышь больше не маршрутизируется в мир: движение — WASD/стрелки/геймпад/тач-
джойстик, взаимодействие — E/A в радиусе, бой — Space конусом/резонансом.
Побочные баги кликов ушли вместе с механизмом (потеря клика при пустом пути,
перебивание подхода к NPC таргетом врага, тихий no-op без соседей, pending
не сбрасывался stop/teleport).
Доки: demo.md, practices.md (секция роутинга), input.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): WASD-движение, кнопка действия E/A и подпись [E] Имя
...
Ходьба: WASD/стрелки/крестовина/аналоговый стик + тач-джойстик (приоритет)
через готовый PlayerController.moveFree; при нулевом векторе — обычный
update (moveFree со стопом рвал бы путь моста).
Кнопка действия: interact = KeyE / геймпад A — ближайшая цель в радиусе
1.5 юнита (NPC → click-переход/заперто → интерактив → цветок), резолв
через pickInteract (чистый, эпсилон-ничья 0.05 + приоритет kind).
Подпись «[E] Имя» над ближайшей целью заменяет per-object подписи;
NPC впервые получают подписи. Снапшот: interactHint (+ ключ анти-дрейфа).
Гвард wasBusy: клавиша, закрывшая диалог, не ударяет/не переоткрывает
разговор в тот же тик (Space/A мульти-биндинг advance+attack/interact).
Клик-движение пока живо (удаляется в следующем коммите).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
fix(game): радиус взаимодействия — истинная евклидова линейка
...
inCircleW (проекционная линейка, анизотропия 2:1) давал зоне взаимодействия
экранный радиус 48px = до 3-4 тайлов, вытянутый вдоль мировой диагонали:
NPC/интерактив срабатывал по клику издалека, до подхода героя. Константа
1.5 юнита теперь применяется честно (Math.hypot позиций, юнит = 1 тайл):
орто-сосед 1.0 ✓, диагональный сосед 1.414 ✓ (findPathToNeighbor), 2 тайла ✗.
practices.md: заметка про две линейки мира (проекционная для конусов/хитбоксов,
евклидова для радиусов взаимодействия).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): финал акта 2 — правильный звон Старой Машины (A4)
...
- интерактив old_machine в зале Машины: без настроенного резонатора —
отказ, с ним — custom lull_machine (setFlags machine_lulled);
- InteractResponse.custom: сюжетные эффекты интерактивов через sink.onCustom;
- кат-сцена финала в LocationScene: спуск камеры к устью + наезд, звон с
тряской, резонанс CombatWorld (тот же пульс, что у героя), угольки гаснут;
- validate: custom реакций сверяется с DIALOGUE_CUSTOM;
- чек checks/final.mjs: спуск через люк -> отказ -> звон -> Машина спит (5/5);
- docs/world.md: арка акта 2 (6а); docs/demo.md: сюжетная рамка + матрица.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): квест «Голос для резонатора» (акт 2, A3)
...
- квест resonator в quests.ts: стадии Клинт (clint_first -> сбор тонов ->
clint_tune -> эпилог clint_repeat), QUEST_TONE_TILES — достижимые WIRE-
деревья рощи (16,3)/(6,5)/(10,15);
- интерактивы rust_grove: три дерева-механизма (тон — варом через реакции,
свой флаг на дерево против повтора) и резонатор Клинта;
- ResonatorQuest (systems/quest): кат-сцена настройки — cameraMove +
cameraZoom (наезд на резонатор), звон с тряской, ответ деревьев вспышками;
- диалог clint_tune (custom tune_resonator), clint_first ставит флаг взятия;
- валидатор: NPC квеста ищется и среди размещений по флагам;
- чек checks/resonator.mjs: крючок -> Клинт -> три тона -> сдача (5/5).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): акт 2 — Клинт, размещения NPC по флагу
...
- механика NPC_PLACEMENTS/npcsForArea в npcs.ts: NPC появляется в области
при поднятом флаге (act2_hook от Милы), мёрж в LocationScene при загрузке;
- SceneObjects.addNpc: размещения попадают в клик-роутинг и кэш занятых
тайлов (иначе клик по Клинту резолвился как движение);
- диалоги clint_first/clint_repeat (крючок «пол-тона»), спрайт в атласе
npcs_sheet (6 кадров, clint_idle);
- validate: placements проверяются на bounds/walkable, def-ы — на ссылки
флагов и диалогов;
- interact.mjs: шаг «флаг -> Клинт в роще -> диалог -> met_clint».
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): акт 2 — карты Ржавой рощи, Старого разъезда и зала Машины (A1)
...
- Новые тайлы: WIRE (бухта кабеля, tall 0.6), RAIL (рельсы), STAIRS
(люк-ступени), MACHINE (печь-автоматон, tall 2.5) — текстуры в gen.mjs,
реестр sizes.mjs (wire), арт-библия дополнена.
- Карты: buildRustGroveMap (плотный мёртвый лес, тропа Звенец->разъезд,
поляна мастерской Клинта), buildOldSidingMap (рельсы, вагоны-времянки,
люк в подземелье), buildMachineHallMap (интерьер с Машиной в два тайла);
в Звенце расчистлен восточный выход.
- Области: rust_grove / old_siding / machine_hall (ambient темнее лугов,
гул Машины tower_hum у люка и в зале, накат у рельсов, враги с
патрулями) + переход Звенца на восток.
- Чек transitions: цепочка Звенец -> роща -> разъезд -> зал и обратно.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): зум в ClickRouting/cullMap/свет/GameAgent + чек zoom.mjs
...
Интеграция зума камеры в игру (Z3):
- ClickRouting: pointerToWorld/resolvePixelTile делят экранные px на zoom
(тела не трогаются — их getBounds уже экранные); deps.worldZoom.
- LocationScene: cullMap окно /z; debug-клавиша Z — уровни 1<->2;
walker'ы актёров тикаются всегда (исполнители until).
- Lighting/CombatFlow: радиусы источников и импульсов * zoomLevel
(lightRoot вне worldRoot, масштабируется вручную).
- GameAgent: tapTile переводит тайл в экранные px с учётом z;
публичный worldRootView() для чеков.
- Чек zoom.mjs: debugZoom -> scale=2, tapTile по NPC в Звенце открывает
диалог, walkTo доходит, возврат на 1.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): инвентарь со скроллом сумки, настройки без пересборки списка
...
Сумка — visibleRows: 6 (ломалось при 7+ предметах), журнал фиксированно
ниже сумки, подсказка/результат — статус-строка под заголовком. Настройки:
список собирается один раз, громкости обновляются setValue (value-бары
переехали в value-слот кнопки).
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(game): диалог и катсцена в одном тике + ходоки актёров
...
LocationScene.update больше не взаимоисключающие ветки: диалог тикается
и во время кат-сцены (паттерн «диалог посреди сцены» — call(talkTo) +
until(диалог закрыт)), геймплей на паузе пока активна хоть одна система.
CutsceneActors: ActorWalker ведёт вьюху NPC по A*-пути (тики сценой,
depth-слой по смене тайла), npcWalkSteps/npcMoveView — фабрики шагов.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|
feat(engine): шаг moveView + skip() раннера катсцен
...
moveView двигает вьюху (Movable — структурно, без Pixi-типов) в локальных
координатах родителя со своим ease. skip() дожимает сцену до конца:
движения — в конечные точки, until форсируются (скип конечен даже на
шаге-ожидании); scene:skipCutscene переведён на него.
Co-Authored-By: Claude Code <noreply@anthropic.com>
|

perf(assets): параллельная загрузка и ленивый амбиент
...
О-4. AssetLoader грузил ключи await-в-цикле (~45 последовательных запросов);
BootScene — 4 атласа и карты друг за другом; 2.3 МБ WAV амбиента всех областей
декодировались до меню, хотя играется в первую очередь луговой.
- AssetLoader.load: один Assets.load(массив URL, onProgress) — Pixi грузит
параллельно, onProgress честный по группе; пост-обработка nearest по всем
результатам. API не изменился.
- BootScene.loadAll: Promise.all по группам (текстуры / 4 атласа / карты
областей / шрифт+аудио) с общим взвешенным прогресс-баром (0.6/0.15/0.15/0.1).
- Ленивое амбиент-аудио: регистрируются все ключи (дёшево), декодируются
только лупы стартовой области (data/audio.ts areaAudioKeys); остальные —
фоновый game.preloadAudio при входе в область (LocationScene.enter).
playAmbience/playLoop и раньше умели декодировать на месте (BufferCache
дедуплицирует in-flight) — теперь до меню их никто не ждёт.
Perf (swiftshader): bootMs 2947-3191 -> 2781; newGame->location без изменений
(~200 мс, аудио и карты грузились в boot и раньше). agent:check 16/16,
включая аудио-чеки с ленивым декодом.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
2 days ago
|

perf(render): culling тайлов карты, пустые оверлеи света, setDepth без пересортировки
...
О-2 рендера. На лугах ~900+ спрайтов карты рисовались целиком при ~250
видимых; пустые fullscreen-оверлеи света (ambient/vignette/пульс) рисовались
каждый кадр даже днём; ходьба взводила sortDirty каждый тик.
- IsometricTileMap.cullToRect: тайловое окно из экранных углов (screenToIso),
margin ±1 по X и вверх на высоту самого высокого tall; пересчёт только при
сдвиге окна; isTileVisible — для тестов/отладки; вызов из сцены
(updateCameraFollow). null — вернуть всё.
- Lighting: ambient.visible=false при белом тинте, vignette/pulseAmb — при
нулевой альфе (днём минус 2-3 fullscreen-квада).
- IsoDepthLayer.setDepth/setDepthRect: ранний выход при неизменном zIndex
(пиксельная сортировка детей не перезапускается каждый тик ходьбы).
- agent.mjs perf: возврат 0 (объект отчёта ломал process.exit).
Perf (swiftshader headless, до → после): fps бой 3-6 → 7-8, покой 5-6 → 5-7
(rasterizer swiftshader глушит выигрыш — он раскроется на целевом iGPU);
heap в пределах шума (28-31 МБ). Скриншот смоука не изменился, тайлы у краёв
камеры на месте.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
2 days ago
|

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
2 days ago
|
perf: реальный fps в агентном мосту + чек perf (baseline О-0)
...
- Engine.get fps() через FpsMeter (было 1/fixedStep = 60 всегда); AgentHost.fps.
- checks/perf.mjs: старт (страница+ассеты, newGame→location), A* (scene:route),
walkTo через луга, fps/heap в покое и в бою; agent.mjs perf (не в check —
бенчмарк, не гейт); lib.mjs open() с сырыми опциями openGame.
- eslint: performance: readonly для node-тулз.
Baseline (swiftshader headless, 960x540):
- старт: ассеты→меню ~3.0 с (raw 4.5 с с фиксированными 1.5 с сна моста),
newGame→location ~150-175 мс;
- A* путь через луга: 2-4 мс (10 узлов), walkTo: ~0.5 с;
- fps: 5-6 в покое, 3-6 в бою; heap: 28-31 МБ.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
2 days 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
2 days ago
|
refactor: типизация agent-слоя — снапшот без as unknown as
...
- Снапшот-типы (HeroSnapshot и пр.) — type alias'ы вместо interface: у alias'ов
есть неявная индексная подпись, поэтому они структурно совместимы с
JsonValue/SnapshotLayer без кастов.
- Фабрики слоёв (heroLayer/enemiesLayer/npcsLayer/dialogueLayer/lightingLayer/
collisionLayer) возвращают SnapshotLayer & { <ключ>: <тип> } — рассинхрон
слоя и типа теперь ошибка компиляции.
- SceneAgentView.agentSnapshot(): LocationSnapshot собирается из слоёв
напрямую и возвращается как есть (7 кастов убрано); GameAgent.waitFor
и invariants пользуются типизированным GameSnapshot (ещё 4).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
2 days ago
|