Files
airdef/docs/CODEX-HANDOFF.md
T
chexit d45629ae29
build / mod (push) Failing after 22s
Fix the UI-event fuel trap on a second large raid order
The Windows log for #122 showed the mod stopping in the UI event
(fuel budget 1e9, remaining 0) with known_units <- order_raid <-
launch_wave <- ui_select: order_raid recomputed the planner snapshot
known_units() for every drone, and the snapshot itself rescanned the
whole force and the dedup list per intel entry. A second big Smart
order cost drones x intel-log x force-size inside one event.

Reproduced in actual WASM with measured per-callback fuel through the
exact ui_select(BTN_LAUNCH) handler (diagnostic guest, trigger.patch):
before, a 111-drone second order at revealed=13172/known=144 exhausted
exactly 1e9 and stopped the mod (an 11-drone order at the same state:
247805865); after, the same scenario passes with max command fuel
32946177/1e9, failures=0, and /stats at tick 2602 byte-identical.
A stacked single-point layout dies to one warhead (known=0, cheap
order), so the scenario deploys spread clusters with a moved player.

Fix: one known_units snapshot per order_raid passed to radar_strikes
and choose_target; known_units is a single O(log + force) set pass;
price_grid_on reuses its snapshot. Emitter/network targeting and
carrier choice semantics are unchanged (native equivalence test
against the former predicate; WASM checkpoint counters equal).
Limits unchanged; HD/recording untouched; wave size unrestricted.

Committed regressions: native snapshot-equivalence and
second-smart-order tests; the heavy WASM fixture now orders two Smart
raids through the wave screen's launch function (airdef preset +
airdef raid) with EW and a combat intel log, four accepted orders
asserted; the light contract covers preset and the empty-wave refusal.

Core release 195 + 8 scenarios; adapter 65 + 1 boundary + 5 scenarios;
light WASM 30 steps; heavy WASM 283 steps (tick max 182413686/300M,
command max 6939810/1e9, failures 0). Windows GUI acceptance remains
external and is not claimed.

Co-Authored-By: Kimi Code (Moonshot AI) <noreply@moonshot.cn>
2026-11-06 10:29:36 +03:00

42 KiB
Raw Blame History

Передача разработки AirDef

Это журнал фактически сделанных изменений и ограничений проверки, а не список обещанных функций. Рабочая ветка kimi-work; issues не закрывать, публиковать комментарии с коммитом и проверками. Движок — отдельный chexit/horizon-airdef, ветка airdef, подключён точным gitlink horizon/.

Текущее состояние: 4 ноября 2026

Последнее продолжение пяти задач: CONTINUATION-2026-11-04.md. Движок 57f8b2853c4443f7e3ff5701600cbb262b53118f, API29. В этом файле ниже сохранён исторический журнал предыдущих серий; старые SHA и числа тестов не заменяют новую контрольную точку.

След #91 продолжен: catalog/specs.json — единственная копия числовых строк дронов, ракет и комплексов; python3 tools/gen_catalog.py генерирует specs_generated.rs и unit_specs_generated.rs, --check (CI) валит расхождение. Числа не менялись; сверка с литералами подтвердила 1.6 как фактический голос P1-SUN (недостижимый P1sun => 1.7 в старом match не в счёт); на это добавлена Rust-регрессия. Остаток: варианты комплексов и игровая приёмка.

Продолжение #122 (второй налёт из UI)

Точный лог Windows: stopped in UI event (fuel budget 1000000000, remaining Some(0)), backtrace known_units ← order_raid ← launch_wave ← ui_select — другой класс, чем тик 1f28f83. Причина: order_raid пересчитывал снимок известных комплексов на каждом дроне, а сам снимок был квадратичен по журналу и группировке. Исправление: один снимок на приказ (+ O(журнал+группировка) через множества, price_grid_on переиспользует свой снимок); семантика целеуказания и выбора носителей не менялась, сверка с прежним предикатом в нативном тесте, /stats в WASM совпал побайтово. Лимиты не трогали, HD/запись не отключали, размер волны не ограничивали.

Воспроизведение в настоящем WASM (измеренный fuel): до — второй приказ 111 дронов исчерпывает ровно 1e9 в событии (failures=1), тот же приказ 11 дронов — 247805865; после — тот же сценарий целиком: max command 32946177/1e9, tick 236581288/300M, ошибок 0. Диагностический гость (trigger.patch вызывает обработчик ui_select(BTN_LAUNCH)) и точный сценарий: issue-122-ui-fuel. Воспроизвести удалось только с разнесённой группировкой: стек в одной точке гибнет от одной боеголовки (known=0, приказ дёшев) — у пользователя силы дожили до второго приказа.

Закреплённые регрессии: нативные «эквивалентность снимка» и «второй умный приказ с толстым журналом принят и запущен»; тяжёлый WASM fixture расширен двумя умными приказами через airdef preset + airdef raid (функция кнопки ПУСК) — 4 принятых приказа, SHA fixture обновлён; лёгкий контракт 30 шагов с preset и отказом пустой волны. Новая команда airdef preset <n> — script-эквивалент кнопки готового состава.

Последний общий прогон: core release 195 + 8 сценариев / 7 ignored; адаптер 65 + 1 boundary + 5 сценариев; лёгкий WASM 30 шагов; тяжёлый WASM 283 шага (tick max 182413686/300M, command max 6939810/1e9, failures=0), отчёт flat-wasm-raid-budget; 6 тестов отчёта budget и build-тест прошли. Ни одно issue не закрыто; пуш не выполнялся.

Контрольная точка 3 ноября

Код предыдущих четырёх задач интегрирован до 6e2d661. Продолжение #102/обратного вывоза опубликовано до aab3e78; последнее продолжение расследования налётов — 2932364. Текущий движок — f95f03c08ec2916a0e5bd3b2d4a2a790e7937b47, API 27. README описывает доступное поведение; IMPLEMENTATION-STATUS.md отделяет реализацию от приёмки. Ни одно issue не закрыто. Участники: Codex (интеграция, #119, ревью, документация) и три GPT-6.1 Terra (энергетика, типизированная доставка, излучение). Новые числа — только игровые.

Issue Опубликованные контрольные коммиты
#119 c96676f: укрытия, read-only geometry, четыре проверки
#118 c775f31, 952a10f: доставка ТЗМ, возврат снятой ракеты ПВО, реальное движение и save/restore
#121 26b258f, 0df225a, 88f9405, 9e1e0f5, b377a5d, ce76f20: излучение, UI/save/replay, наблюдения, last-known SEAD
#120 143d9b2 → e38ca0d, затем a7b8cb9: конечное горение, coast, swept impact, диагностика, звук, исправленное ветвление фаз
WASM 6e2d661: проверка On/Off и запрета управления отсутствующим радаром, 24 шага

Для обновления существующей установки сначала git switch kimi-work, затем git pull --ff-only, python3 tools/airdef.py all, python3 tools/airdef.py play. all обновляет точный сабмодуль, собирает игру/мод и устанавливает пакет. На Windows обычно используется python вместо python3.

Продолжение #102 и обратного вывоза RM48U

  • d3378b3: fork() синхронизирует URL из .gitmodules даже на fast path совпавшего pin. Реальная Git-регрессия воспроизводит старый upstream URL и новый коммит, отсутствующий в локальном clone. Это исправляет наблюдавшееся на Windows upload-pack: not our ref 86b1014…; пользователь подтвердил ручное исправление через git submodule sync --recursive до публикации автоматизации.
  • bb931ab, e9fbaba, b0085cb: ground-unload вдали от склада резервирует одну RM48U и вызывает ТЗМ. ground-cancel отменяет ещё ожидающий вывоз/доставку; после забора груз продолжает путь на склад. Проверены физическое движение и полный save/restore до забора и после забора, без телепортации запаса.
  • 910f02c: потеря зарезервированной ракеты вместе с ячейками сбрасывает заявку на вывоз. Иначе можно было создать сохранение, которое loader сам отвергал как несовместимое состояние.
  • Двигатель: f4c1131d3, затем c89c78c47 опубликованы в horizon-airdef:airdef до обновления gitlink. API27 добавляет acoustics-status и capture-owner-status, scoped к запуску мода и поколению мира. Остановка capture проверяет владельца; смена мира снимает незапущенный запрос или посылает cancel.
  • 910f02c: команда /airdef capabilities; дальний звуковой fallback сохраняется до подготовки акустического пути, экспорт принимает только своё подтверждённое состояние; Arming ждёт до 600 тиков. prepared — milestone хоста, не GPU fence, не результат каждой акустической трассы и не готовый MP4.
  • 4dddea2, 8d5d0d1: opt-in test-budgets, отдельный ручной workflow и JSON provenance; тесты ошибок сборки, stale identity и ненулевого runtime exit. Native success не заменяет actual WASM.
  • aab3e78: repo-local packer использует target-cache закреплённого движка; CARGO_TARGET_DIR не переопределяется для сборки самого мода и не уводит .hmod в чужое дерево.

Проверки объединённого состояния: core release 190 + 4 сценария, 7 ignored; адаптер 60 + 1 boundary + 4 сценария; лёгкий WASM 26 шагов, тяжёлый 69 шагов; Git-регрессия и три теста failure report прошли. Исходный профиль запрашивал 64 ПВО + 160 целей, 602 tick / 300 frame; JSON фиксирует clean aab3e78, pin API27 и совпадающий build identity. Полные данные: budget report. Исправление оценки покрытия: второй запрос исходного 69-шагового сценария отклонялся во время подготовки первого. Его успешный exit не подтверждает 160 запущенных или одновременно летящих целей. В архивный JSON добавлена поправка; исправленный сценарий проверяет ответы. Для настоящего stress также нужны ответы render-world: прежний sandbox их не давал и часто проверял лишь ожидание земли.

Дополнительно два свежих профиля игры на Metal/no-audio: smoke off не ready, HD smoke ready; при HORIZON_ACOUSTICS=1 + HORIZON_NO_AUDIO=1 акустика enabled/request=true, prepared=false, awaiting-backend. Выход через quit подтверждён. Это не прослушивание, не benchmark и не новый MP4. Правила API, пределы проверок и команды: ENGINE.md.

Двойные налёты и плотность следов

Перед продолжением читать RAID-STABILITY.md: два соответствующих класса отключения теперь воспроизведены и исправлены — тик (1f28f83) и событие UI на втором приказе (#122, order_raid/known_units); приёмка конкретного Windows-сеанса остаётся. Текущий исправленный WASM stress проверяет две принятые волны, обслуживаемую плоскую геометрию и 184 воздушные цели в конкретный момент; новый JSON заменяет прежнее недостаточное доказательство покрытия. Лимиты fuel/memory не увеличивались. 86e1d272e в движке улучшает callback/fuel/error-chain diagnostics; ed7615d36 добавляет только необязательную flat-world среду тестов и отказывает неожиданным ошибкам команд в Script. Основная клиентская логика сцены этим test helper не заменяется.

692ae77: HD-треки ракет оптически плотнее на 12% через общий rocket() и cruise style. SAM/RM48U, крылатые/сверхзвуковые/баллистические треки покрыты; сегментов, радиусов и времени не добавлено. Пожары и дымка винтовых дронов не менялись. Обычные ванильные fallback-частицы этим множителем не меняются. Пять существующих smoke-тестов прошли; прирост FPS и приёмка новой плотности на Windows не заявлены.

Последний общий прогон: core release190 + 6 сценариев, 7 ignored; адаптер60 + 1 boundary + 4 сценария; лёгкий WASM26; тяжёлый WASM271; 5 тестов отчёта. Нативные overlap-тесты — airdef-core/tests/scenarios.rs: два стандартных массовых пресета и два налёта с разделением «Орешников», оба со встроенной записью.

Продолжение по пользовательскому HD-отключению: 1f28f83 заменяет полный known.retain при каждом взрыве на удаление прежнего квадрата; семантическая эквивалентность проверена. В диагностическом WASM max-menu ×2 раньше был trap tick1835 на 300M; после фикса полный 3602-tick прогон прошёл, max135273690. Гость отличается от baseline только временным trigger hook состава меню и исправлением. Это измерение fuel, не прирост FPS. Артефакты/ограничения — в RAID-STABILITY. cc48302 добавляет публичный native max-menu regression; 3b470dd закрепляет опциональный engine test --fuel-profile (не новый SDK API и не увеличение лимитов).

cbd6067 исправляет сообщение о пустом соседнем складе, показывает типизированные запасы на M, уточняет подсказку разведки и убирает устаревшую строку README о якобы отсутствующей доставке. GROUND-STRIKES.md — инструкция с различием номеров своего С-400/склада и вражеской ПУ. Default RM48U=0; ground-stock задаёт остаток, ground-load грузит одну; поздний трек не даёт fix. ab737e7: actual WASM теперь 27 шагов с проверкой ошибки пустого соседнего склада.

После фикса полный core: 192 passed + 6 сценариев / 7 ignored; новый max-menu сценарий отдельно прошёл седьмым. Адаптер60 + 1 boundary + 4 сценария; light WASM27; engine /spin fuel-profile test прошёл.

Исходная контрольная точка этой серии

Исходное состояние этой серии: 2689d93; движок закреплён на 86b1014a9218e68041444cbf52db02a85107c815, уровень API 26. Рабочее дерево было чистым, коммиты опубликованы. Текущий точный pin всегда смотреть в git ls-files --stage horizon, а происхождение собранного пакета — в airdef-mod/target/horizon-mod/build-identity.json.

Четыре задачи были заказаны владельцем отдельно:

Issue Заказанный остаток
#120 Конечная работа двигателя перехватчика, coast, энергия манёвра, согласованные эффекты
#118 Типизированная доставка RM48U через ТЗМ; не создавать неподтверждённые носители
#121 Переключение излучения, сохранение разрешённого внешнего целеуказания, разведка излучателей
#119 Ослабление непрямого урона известным укрытием, ограниченная политика Unknown

Исполнители работали в отдельных worktree; их проверенные коммиты перенесены в kimi-work. В ветках codex/issue-* сохранены промежуточные версии; продолжать нужно от kimi-work, где разрешены конфликты общего формата сохранений и сигнатур боевых функций.

Что уже сделано в предыдущей серии Codex

  • airdef-core компилирует исходники airdef-mod/src/sim/mod.rs через #[path]. Это единый источник симуляции, но разделение домена и представления ещё не завершено (#87).
  • Исправлены остаточные терминальные коллизии ответных ударов и скачок после world-query (#106/#117). Восстановление ответов мира не даёт права телепортироваться к плановой точке.
  • Доработаны запись ответных ударов, launch one-shot и live-след (#109), lifecycle показа и часы отложенного звука (#88/#89). Полная унификация всех потребителей сцены ещё не закончена.
  • Движок вынесен в отдельную репу; сборщик берётся из закреплённого форка, а не случайного PATH. Есть build identity, настоящий WASM contract и handshake доступности дыма API26 (#102/#104).
  • Обновлён upstream Horizon 0.2.97, исправления очереди/инвалидации акустики сохранены. Измерительные fixtures, notices и лицензии добавлены; прослушивание и массовый CPU-тест ещё нужны (#96).
  • Pipeline поддерживает authored overrides, manifest и экспорт итоговых габаритов в sim/generated_bounds.rs. Навигация учитывает footprint. Generated output вручную не редактировать.
  • Улучшены модели наземной техники; степень сходства и материалы ещё промежуточные (#115). Галерея: MODEL-PILOTS.md, VEHICLE-MODELS.md; это не приёмка всех летательных аппаратов.
  • 966b52f: раздельные ПУ С-400/С-500, роли Radar Search/Engagement/Height/Multi. Чужая подсказка разрешает огневому радару уточнение, но не подменяет собственное измерение.
  • 966b52f: резерв конкретной ячейки, отдельные крышки ТПК, выброс до включения двигателя. RM48U — условный игровой профиль С-400; AA и ground rounds делят четыре физические ячейки. Боеприпас списывается при выбросе, не при выдаче отложенного приказа; отмена не расходует его.
  • Конфигурация, маска использованных ячеек и положение крышек включены в save/replay. Smoke начинается в точке зажигания. С-500 ограничен двумя ракетами по числу ТПК; старое сохранение с четырьмя загруженными ракетами при загрузке ограничивается двумя.
  • 2689d93: документированы источники архитектуры батареи, команды, скриншоты и границы визуальной проверки.

Проверки и воспроизведение

На 966b52f: 169 тестов ядра + 4 сценария прошли, 7 ignored; адаптер — 53 теста, 1 boundary и 4 сценария прошли; WASM contract — 21 шаг. Это исходная контрольная точка, не результат новых четырёх задач. На 2689d93 был собран чистый пакет (airdef_dirty=false).

На итоговом объединённом коде 6e2d661 прошли:

  • airdef-core --release: 187 passed, 7 ignored, плюс 4 сценария.
  • airdef-mod: 56 passed, 1 boundary, 4 сценария.
  • Настоящий WASM-пакет: 24 шага, включая команды излучения и typed ammunition.
  • gen_models.py --check: различий нет; в этой серии геометрия ассетов не менялась.

Полный core здесь запускался с --release; это не прогон всех семи ignored-тестов. Отдельно выполнен сравнительный ignored-тест баланса (см. ниже).

cargo test --release --manifest-path airdef-core/Cargo.toml
cargo test --manifest-path airdef-mod/Cargo.toml
python3 tools/airdef.py test-wasm
python3 tools/gen_models.py --check
python3 tools/test_gen_assets.py
git diff --check

На этой macOS cargo находится в ~/.cargo/bin/cargo. GUI-проверки запускать с отдельным HORIZON_HOME и рабочим каталогом тестового мира; не использовать реальные сохранения владельца. Metal-проверки не подтверждают Windows/Linux, HDR всех платформ или прослушивание звука. Скриншоты крышек подтверждают только их позу: полного видео выброса/зажигания пока нет.

Технические детали новой серии

#120: энергетика и единственный исход перехвата

MissileKind::burn_ticks задаёт конечное время горения: простой профиль равен 2/3 игрового spec.life; для электрического P1-SUN используется весь срок жизни. Это не отдельная модель топлива и не реальные кривые тяги. Missile::engine_lit — общий источник состояния мотора.

Три ветки скорости разделены явно: cold (age <= cold, множитель 0.97 с прежним минимумом 0.4), powered (+accel, ограничение spec.speed), coast (множитель 0.995 без холодного минимума). После выгорания доступный поворот масштабируется текущей скоростью; при падении ниже 25% игрового максимума перехват заканчивается диагностикой MotorEnergyLost. Срок жизни остаётся отдельным ограничением. Полноценной аэродинамики, гравитационной баллистики и расхода топлива по тяге нет.

Swept fuse учитывает движение обоих объектов за тик; Impact получает точку closest approach и относительную скорость того же проверенного отрезка, включая удержание на Unknown. Геометрию не заменяет отдельный бонус «встречная/догонная». Один остаточный pk зависит от фактической скорости перехватчика и существующего состояния носителя/дальности. Повторный штраф по stealth и старый штраф по абсолютной скорости цели удалены из этого броска; обнаружение/сопровождение по-прежнему учитывают stealth в своих местах.

MotorBurnout и MotorEnergyLost записываются в scene journal; tally отличает потерю энергии. Live и replay прекращают факел и новый дым по одному состоянию. Out::engine_stops немедленно снимает голос мотора на burnout, не ждёт обычной трёхтиковой grace пропавшего источника; при переходе на replay/export live-команды остановки отбрасываются вместе с live-выводом. Перемотка восстанавливает моторное состояние из записи, включая возврат до выгорания.

При ревью был найден и исправлен недостижимый coast: ранний прототип проверял !lit, что перехватывало и cold, и coast. Не переносить один 143d9b2 без последующих исправлений. a7b8cb9 содержит отдельный тест точных правил скорости всех трёх фаз.

Сравнительная выборка только изменения #120, отдельно от #119/#118/#121: baseline 2689d93, агентская итоговая ветка 508f9a0 (её исправление перенесено как a7b8cb9). 12 одинаковых seed, SAMS=20 SIZE=12: 204 цели в каждой версии; baseline — 202 сбитых, 2 прорыва, 367 ракет; новое — 203, 1, 355. Это небольшой регрессионный ориентир, не доказательство баланса всех типов/дальностей. Данные каждого seed: CSV.

Расширенная сетка остатка #120 (ветка agent/issue-120-balance, коммит см. в git): baseline 2689d93 против текущего e6a7000 (то есть с #118/#119/#121 и сменами движка, не только #120), 6 вариантов × 8 одинаковых seed, рейд SIZE=36 (46 целей: Gerbera 10, Shahed 13, Geran3 5, Х-101 5, «Калибр» 4, Х-22 2, «Циркон» 1, «Искандер» 4, «Кинжаль» 2; «Орешник» harness исключает, как в one_raid): batt10/20/50 (10/20/50 ЗРК, дистанция 500), near200 (20 ЗРК, пуск в 200), far1500 (20 ЗРК, пуск в 1500), hills20 (20 ЗРК, холмы, 500). Кластер тот же MIX из 10 систем: SAMS=10 — по одному комплексу каждого типа. Итого 2208 целей на сторону, 96 строк: CSV. Команда (игнорируемый тест sim::balance::issue_120_grid, только harness-поля в Result, поведение симуляции не менялось):

SEEDS=1-8 cargo test --release --manifest-path airdef-core/Cargo.toml issue_120_grid -- --ignored --nocapture

Факты сетки (суммы по 8 seed каждого варианта, baseline → текущий): сбитые 297→301 (batt10), 331→334 (batt20), 357→363 (batt50), 313→318 (near200), 328→338 (far1500), 328→334 (hills20); прорывы 70→66, 37→34, 11→5, 53→48, 40→30, 39→33. Промахов по взрывателю заметно меньше (например far1500: 70→38). Расход ракет близок (516→504 … 597→583). На холмах потери у рельефа: разбито 14 и 14, самоуничтожений у хребта 6→2. Прорывы во всех вариантах — преимущественно баллистические и быстрые классы («Искандер», «Кинжаль», Х-22, «Циркон»); у медленных Shahed/Gerbera прорывов почти нет (0–2 на вариант), у ложных Gerbera — ноль ни разу. MotorEnergyLost не сработал ни в одном из 48 сценариев текущего кода, expired ≤ 1 на вариант: coast существует, но бои на этих дистанциях заканчиваются раньше, чем сопротивление 0.995/тик опустит скорость ниже порога 25% (и раньше выработки ресурса). Это измеренное свойство профиля, не подтверждённый дефект, поэтому параметры не менялись. Путь потери энергии покрыт юнит-тестом регресса (regress.rs), а не этой сеткой.

SAMS=20 SIZE=12 cargo test --release --manifest-path airdef-core/Cargo.toml sim::balance::one_raid -- --ignored --nocapture

#118: один физический груз и восстанавливаемая заявка

ground_request у С-400 — заявка на одну RM48U. Рядом со складом остаётся прямая передача; вне радиуса склада та же команда ground-load создаёт заявку, если есть запас и живая ТЗМ. ТЗМ забирает одну ракету из конечного запаса, едет к исправному развёрнутому носителю и передаёт её в общие ячейки. Если вытеснена ракета ПВО, ТЗМ везёт её обратно на склад. Generic reload не создаёт RM48U. Ударная заявка имеет приоритет перед generic reload.

Поля ground_stock / air_48n6_stock на складе — остатки, на ТЗМ — физический груз двух типов. Заявка, груз и марш сохраняются; оперативный job реконструируется после загрузки. Передача запрещена во время движения, подготовки пуска, перезарядки, отказа ПУ и при отсутствии вместимости. Разрушение очищает заявку/груз; repair не восстанавливает уничтоженный typed stock. Проверены настоящее движение Sim.step по плоскому WorldSource, передача/возврат, истощение, прерывание, несовместимость, заполненные ячейки, unload и полный state_text → apply_state с продолжением маршрута. GUI-доставка на сложном рельефе ещё не проверена.

Новых носителей вроде Бука не добавляли: один публичный игровой профиль не даёт основания приписывать его всем ЗРК. Обратный вывоз из удалённого носителя автоматизирован в продолжении выше. Новая команда ground-cancel отменяет ожидающую заявку, но не возвращает уже забранный груз в ПУ. ТЗМ вмещает два типизированных места RM48U/48Н6; прежние абстрактные generic reload-пакеты учитываются отдельно. Вывезенная ракета возвращается в конечный запас; ПВО восстанавливается при заборе только из реально имеющейся у ТЗМ ракеты 48Н6 и свободной ячейки. Резерв одной RM48U учитывается при выборе носителя удара.

#121: излучение отдельно от сети

radar_on — сохраняемая настройка, default=true для старых миров. Команда /airdef radar <номер> on|off и кнопка карты меняют её; у launcher-only нет своего активного радара. radar_up учитывает этот флаг. Off запрещает новые own measurements, но старые треки стареют обычно и разрешённый сетевой приём сохраняется. Это не выключение корпуса/связи/ГСН ракеты. Огневой радар, обязательный для отдельной ПУ, в Off не авторизует новые пуски.

Условный наблюдатель-противник раз в 20 тиков обнаруживает работающие радары в 1600 блоках. Это упрощённый игровой датчик по расстоянию, без LOS/радиопеленгации и ошибки оценки позиции. Контакт хранится как последняя наблюдённая точка и стареет за 6000 тиков. Off не стирает контакт, не телепортирует его вслед за носителем и не предотвращает раскрытие пуском/РЭБ.

В raid::radar_strikes удалён отдельный обход: раньше SEAD выбирал любую Radar и её настоящий pos, даже если общий planner уже работал по наблюдениям. Теперь SEAD берёт те же last-known контакты и их возраст. Явный sandbox-режим Aim::ObjectsAndSams по-прежнему выбирает расстановку напрямую; не путать его с умным противником. Проверки покрывают необнаруженную молчащую РЛС, смену позиции без наблюдения, повторное раскрытие, старение, relay freshness и seek.

Совместимость текстового состояния

Сохраняем строки sam расширением справа; индексы ниже включают слово sam как поле 0.

Индекс Значение
22–24 ground_rounds, ground_stock, air_48n6_stock
25–27 mode, radar_role, spent_cells
28 ground_request: 0/1, только С-400
29 radar_on: true/false, отсутствие означает true
30 ground_return: true/false, резерв вывоза одной RM48U из С-400
31 return_cargo: true/false, возвращаемая RM48U на ТЗМ

Две агентские ветки сначала заняли поле 28 одновременно; при интеграции излучение перенесено в 29, опубликованная совместимость заявки сохранена. Проверка полного roundtrip одновременно содержит ожидающую доставку и выключенный радар. Новые значения входят в durable mark; настройка излучения также в SamKey и ghost replay. Не переиспользовать эти позиции для нового поля.

#119: непрямой взрыв и укрытия

WorldCache::blast_cover читает текущую occupancy без запросов мира и без загрузки чанков. Три луча к репрезентативным точкам корпуса дают общий множитель для корпуса и узлов; для отмеченного объекта считается своя тройка. Известное укрытие оставляет 15% игрового воздействия по перекрытому лучу, открытый — 100%; края/проёмы дают промежуточное значение. Все лучи этого вызова читаются до отправки разрушения в движок. Исходный воксель пропускается, чтобы контакт на поверхности стены не закрывал взрыв сразу во все стороны.

Unknown, stale и слишком старые секции считаются неразрешёнными. Ожидание равно нулю: для них сохраняется прежний неослабленный урон как консервативная игровая политика, а не утверждение, что там воздух. После ответа мира этот взрыв не пересчитывается. Нет очереди отложенного урона и повторного применения на world reply. Частичные блоки на этом этапе считаются занятым кубом; тонкая collision shape для blast отдельно не запрашивается. Прямой вооружённый контакт не ослабляется этим множителем.

Проверки sim::damage::cover_tests: стена/проём для корпуса, узлов и объекта; Unknown и поздний ответ без повторного урона/запросов; прямое попадание; устаревшая геометрия; пропуск исходного блока. Все четыре прошли в объединённом наборе.

Что следующему агенту проверить в первую очередь

  1. На Windows: обновление сабмодуля через all, сохранение/перезаход, рабочий цикл игры.
  2. В GUI: полный холодный пуск крупным планом; карточка On/Off; маршрут ТЗМ по сложной местности; разрушение стены одним взрывом и защита от того же взрыва по исходной геометрии.
  3. Прослушать прекращение голоса двигателя при burnout и seek/экспорт вокруг этой границы.
  4. Расширенная балансировка #120 (батареи/дальности/классы) записана в measurements/issue-120-grid.csv; остаются прослушивание голоса мотора и платформенная приёмка.
  5. Продолжить оставшиеся архитектурные #87/#88/#89/#91/#102 и художественный #115 по статусной таблице.

Это незавершённая платформенная/визуальная приёмка, а не отсутствие перечисленного выше кода. Прогон четырёх задач не запускал GUI/MP4. В продолжении API27 есть два коротких игровых smoke, но новый MP4, прослушивание и платформенная приёмка Windows/Linux по-прежнему не выполнены.

Правила продолжения

  • Не закрывать issue даже после исправления; сообщать проверенный объём и остаток в комментарии.
  • Не переписывать gitlink на плавающую ветку. Изменения движка сначала публикуются отдельно.
  • Не путать наблюдённый трек с настоящим положением цели и own_tick с relay.
  • Не менять live-мир при seek; replay воспроизводит последствия, не применяет урон повторно.
  • Не считать Unknown свободным пространством или подтверждённой стеной без явной игровой политики.
  • Новые числа — игровой масштаб, не реальные характеристики вооружения.
  • Токен Gitea читать из локального файла во время запроса; не коммитить и не печатать.