12-03. Геймплей свободного полёта: управление, камеры, приборы, итог

Цель: закрыть «в работе» из TODO этапа 1/4 по сцене: разбег W+Shift, камеры, прибор в углу, пауза/рестарт. Контекст: REQUIREMENTS.md (FR-21, FR-26, FR-30…FR-33, FR-27), scripts/game/input_controller.gd, scripts/game/camera_rig.gd, scripts/game/game.gd, configs/controls.json, docs/guide/game.md. Владения: scripts/game/input_controller.gd, scripts/game/camera_rig.gd, scripts/game/game.gd, scenes/main.gd, configs/controls.json, configs/camera.json, новый tests/game/test_gameplay.gd, docs/guide/game.md. Шаги:

  1. Разбег (FR-30): W+Shift — бег, нос держится сам; отпустил Shift — шаг; клавиши, зажатые при отрыве, не переходят в полёт до отпускания (защёлка); A/D на земле — поворот корпуса.
  2. Камеры (FR-26): C переключает cockpit → chase → free; chase не проваливается в рельеф; free — WASD+мышь, не двигает крыло. Прибор в углу (FR-21) виден только во внешних камерах; в кабине — нет.
  3. Приборы: клавиши 1–5 листают планшет в любой камере; вариометр 90-х на стойке показывает то же, что звук.
  4. Пауза (Esc) останавливает физику и звук; «Заново» — на тот же старт; после итога «Продолжить» — ходьба по земле.
  5. Итог свободного полёта (без рекордов): в info — время, дистанция, след, набор, макс. высота MSL, оценка посадки (мягкая/жёсткая/авария) — формат описать в docs/guide/game.md для группы 11.
  6. Отметить в docs/screenshots/gameplay/README.md кадры chase/free/угловой прибор (по 1 на камеру). КРИТЕРИЙ ПРИЁМКИ: test_gameplay.gd ≥ 8 проверок (защёлка, шаг/бег, смена 3 камер, прибор в углу вкл/выкл по камере, страницы 1–5, пауза замораживает Telemetry.time_s, restart ставит на старт ±1 м, info содержит все поля п.5); старые tests/game зелёные; 3 скриншота с чек-листом; tools/check.sh зелёный. Зависимости: 12-01 (волна 2, те же файлы). Модель: opus. Размер: M.

Дополнения координатора (обязательно)

  • Подключить завершение полёта из FlightStats (ui/02, закоммичено 023456e): Game.tick() спрашивает stats.is_finished() / finish_reason() (“landed” / “takeoff_failed”) и только тогда показывает итог; случайные касания у старта полёт не завершают (FR-27b).
  • Баг: scripts/game/game.gd:411 и 443 — info.merge(stats.summary(...)) без overwrite → заниженное время полёта после reset_in_air (landing_flare.gd:88 кладёт свой flight_time_s). Исправить: merge(..., true) или итог целиком из FlightStats. Тест.
  • Итог: главная кнопка «В главное меню» → главное меню (сигнал menu), «Ещё раз» → рестарт с того же старта.