План модуля «control-fix» — регрессия управления на старте (1.0.0)

Внутренний документ. Контракты — docs/contracts/control-fix.md, журнал — docs/archive/plan/control-fix-progress.md. Ветка feature/control-fix, копия ~/deltaplan-control-fix (от main 3820bda = v1.0.0).

Цель

Жалоба пилота на 1.0.0 (по смыслу): «что-то с управлением изменилось. Когда включаю игру, он сам начинает полёт — разбежаться не получается, ручку подтянуть, исправить крен на старте не получается… заваливается в крыло, падает. Мышью не корректирует положение носа и крена».

Симптомы:

  1. На старте аппарат сам начинает движение/полёт без действия пилота (нет фазы стояния и разбега).
  2. Нельзя подтянуть ручку / выровнять крен на старте — крыло валится, срыв.
  3. Мышь не меняет тангаж и крен (на земле и/или в воздухе).

Найти причину (коммит, файл:строка) с доказательством, исправить у причины, добавить регрессионные тесты. В main не вливаем — шлюз главной сессии.

Решения пользователя (не обсуждаются)

Действуют решения модуля start-fixes (docs/archive/plan/start-fixes.md, «Решения пользователя», п. 3): стоя пилот вертикален, A/D на земле — только курс (крыло не кренят), крен держит «рука пилота» с ограниченной силой ∝ доле веса на ногах; опасность учит последствием, без подсказок. Если причина регрессии — в самих этих решениях, это шлюз (варианты пользователю), а не правка исполнителем.

Решения пользователя модуля (01.10, шлюз «управление крылом на земле»)

  1. Вариант В «гибрид»: стоя и шагом — как сейчас (W/S — шаг, A/D — поворот на месте), крылом (нос и крен) управляют стрелки, мышь, стик. С зажатым Shift (разбег) W/S, A/D, стрелки и мышь управляют носом и креном крыла как в воздухе; курс на бегу — от крена крыла (дуга). Защёлку клавиш при отрыве убрать. Это отменяет в start-fixes п. 3 «стоит — только поворот» и «A/D на бегу заворачивают по дуге» в части крыла: крыло на земле кренит пилот; «рука пилота» с ограниченной силой ∝ доле веса на ногах остаётся.
  2. Мышь по умолчанию — «трапеция» (bar); осмотреться — с зажатой правой кнопкой.
  3. Мышь и клавиши «трапеции» — те же действия ввода, что в полёте; знак клавиш задаёт только карта ввода (раскладка W — от себя — модуль feature/ui-controls).
  4. Никаких фиксированных времён старта и взлёта (01.10, по отзыву пилота «на взлёт дано 2 с»): отрыв — только физика (L ≥ W от воздушной скорости: бег + ветер); в штиль бежать можно сколько нужно; без бега и ветра отрыва нет. Все таймеры, задержки, мин./макс. длительности фаз «стоит → бег → отрыв → полёт», защита после отрыва, таймеры автопилота/ботов на земле, подготовка/таймауты старта — заменить физическими условиями (силы, скорость, контакт, геометрия). Технически нужное (антидребезг контакта в 1–2 шага физики) — с обоснованием. Тест: время отрыва разное на разных склонах и ветрах и определяется только физикой.

Что известно до запуска (координатор, 01.10)

  • С v0.8.0 (0ac895d) до v1.0.0 в полёте/земле менялись: SF-3 «рука пилота» (800b84e, 3384aa8, 5f8990e) — GroundRun._ground_bank заменён динамикой крена, A/D больше не кренят крыло, срыв crosswind проверяется и стоя (до разбега); SF-4 (3707069) — тангаж стоя от направления склона; паспорта крыльев (4200e0f — масса и др. в конфигах), новые крылья; модель воздуха AM-04…AM-09 (поле ветра у старта, рывки и обратный поток за гребнем — 0d1ecf2); отладочные слои F1/F2/F5/F6 и аддоны Debug Draw 3D / Debug Menu (fffc741, 67dc73a).
  • scripts/game/input_controller.gd с v0.8.0 не менялся. Мышь по умолчанию — режим look (голова), bar — запасной; на земле (_update_ground) мышь не используется вовсе. Ввод мыши — через _unhandled_input (может съедаться GUI с mouse_filter = STOP).

Задачи (одна волна, параллельно)

ЗадачаЧтоТипОценка
CF-1Старт с земли: «сам начинает полёт», крен валится — причина, исправление, регрессияdp-engineer0,5–1 дн
CF-2Мышь не управляет тангажом/креном — причина, исправление, регрессияdp-engineer0,5 дн

CF-1. Старт с земли

  • Скоуп: воспроизвести headless в условиях игры по умолчанию (FlightSettings.defaults(): локация, крыло, ветер «встречный на старте», прогноз; поле воздуха как в игре — AirRuntime/WindField у старта, а не штиль), без ввода: что происходит (фаза, скорость, feet_load, wind_moment_nm, hold_limit_nm, крен, α, причина срыва/отрыва) по времени. Сравнить с v0.8.0 и промежуточными коммитами (bisect: 800b84e, 3707069, 5f8990e, 4200e0f, 0d1ecf2 и т. п.). Найти коммит и строку. Исправить у причины, физически обоснованно.
  • Ключевые файлы: scripts/flight/ground_run.gd, scripts/flight/flight_model.gd (стык GROUND→AIR), configs/flight.json, configs/pilot.json, конфиги крыльев (если причина в паспортах), scripts/game/game.gd (спавн, только при доказанной причине), tests/flight/.
  • Не трогать: scripts/game/input_controller.gd, UI, отладочные слои (CF-2) — нужна правка там → через координатора.
  • Приёмка (headless):
    1. Отчёт о причине: коммит, файл:строка, числа «до/после» (таблица по времени для v0.8.0, v1.0.0 и исправления).
    2. Тест: на старте по умолчанию (локация, крыло, ветер и поле воздуха как в игре) без ввода 10 с — аппарат стоит: не отрывается (TOOK_OFF нет), не срывается (FAILED нет), |крен| < 3°, смещение < 1 м.
    3. Тест: разбег (run) с нейтральной, подтянутой и отданной трапецией на том же старте — нейтральная отрывается; на себя/от себя — разное время/исход как по физике (числа в отчёте); крен на разбеге при встречном ветре < 3°.
    4. Условия приёмки SF-3 (docs/archive/plan/start-fixes.md, SF-3 п. 1–6) не сломаны.
    5. --filter=flight, --filter=game, --filter=start_, --filter=contracts — зелёные (известные падения под нагрузкой и test_headwind_launch atlas — назвать отдельно).
  • Шлюз: если устранение причины противоречит решениям пользователя start-fixes (A/D не кренят, предел руки, срыв стоя) — остановиться, вернуть варианты координатору.

CF-2. Мышь

  • Скоуп: проследить путь мыши в игре (сцена полёта, не только InputController в тесте): захват курсора при старте (capture_on_start), режим (look/bar) по умолчанию и из user://configs/controls.json, доставка InputEventMouseMotion в _unhandled_input (кто съедает: новые Control отладочных слоёв, аддон Debug Menu, объекты мира, меню), фазы земля/воздух. Сравнить с v0.8.0. Воспроизвести headless (Input.parse_input_event в загруженной сцене полёта), исправить у причины.
  • Ключевые файлы: scripts/game/input_controller.gd, scripts/game/debug_overlays.gd, scripts/game/game.gd (захват мыши), scripts/ui/*, addons/ (только настройка, не код аддона), configs/controls.json, tests/game/.
  • Не трогать: scripts/flight/*, configs/flight.json, configs/pilot.json (CF-1).
  • Приёмка (headless):
    1. Отчёт о причине: коммит, файл:строка (или «регрессии в коде нет» с доказательством — тогда что видит пилот и почему).
    2. Тест: в сцене полёта с захваченной мышью в режиме bar движение мыши меняет ControlInput.pitch/roll и тангаж/крен модели в воздухе (знак по контракту С1); события мыши доходят до InputController при всех слоях интерфейса полёта по умолчанию.
    3. --filter=game, --filter=ui, --filter=contracts — зелёные.
  • Шлюз: если мышь на земле (нос/крен) никогда не работала и её надо добавить — это новое поведение, развилка для пользователя через координатора.

CF-3. Управление крылом на земле (вариант В) — после CF-1

  • Скоуп: реализовать С1 v2, С2 v2, К3 v3 (docs/contracts/control-fix.md): поле ControlInput.turn; на земле pitch — нос крыла, roll — заданный крен крыла «рукой пилота» (стоя, шагом, на бегу); стоя/шагом — крыло стрелками, мышью (bar), стиком, W/S — шаг, A/D — поворот на месте (turn); Shift — разбег (без W), на бегу W/S, A/D, стрелки, мышь — нос и крен как в воздухе, курс — от крена крыла; убрать защёлку отрыва; мышь по умолчанию bar. Боты/автопилот/сеть — перевести на новый смысл полей. Физика: заданный крен, предел руки, дуга от крена — с обоснованием в docs/guide/flight.md; экран «Управление» и подсказки клавиш — по новой раскладке.
  • Ключевые файлы: scripts/core/control_input.gd, scripts/game/input_controller.gd, scripts/flight/ground_run.gd, configs/controls.json, configs/pilot.json, configs/flight.json → ground_bank, автопилот/боты (autopilot.gd, bot_pilot.gd), scripts/ui/controls_screen.gd, локали, docs/guide/flight.md, docs/guide/game.md, тесты.
  • Приёмка (headless):
    1. Стоя (штиль и 6 м/с встречный, «Славутич» и «спорт»): ↑/↓, мышь — нос меняется на полный ход (α числами), ←/→, мышь — крен крыла меняется к заданному (числа), W/S — шаг, A/D — поворот на месте без крена крыла.
    2. На бегу: W/S/стрелки/мышь — нос, A/D/стрелки/мышь — крен; курс — дугой от крена, |боковое ускорение| ≤ turn_accel_max_ms2; без ввода крена — прямо.
    3. Отрыв без скачка трапеции и крена (|Δpitch|, |Δroll| за шаг ≈ 0), защёлки нет.
    4. Разбег 0/3/6/10 м/с × советское/учебное/спортивное крыло с разумным вводом — исход и длина разбега (таблица); 6 м/с «Славутич» — взлёт.
    5. Контрактный тест С1 v2/С2 v2/К3 v3, --filter=flight, game, ui, start_, contracts, боты — зелёные.

Риски

  • Причина в поле ветра у старта (модель воздуха) — тогда исправление вне модуля; шлюз.
  • Профиль пилота user:// недоступен: что у него в настройках (режим мыши), неизвестно — вопрос пилоту через главную сессию.