План модуля «ui-controls» — раскладка тангажа «как у дельтапланериста»

Внутренний документ. Журнал — docs/archive/plan/ui-controls-progress.md, контракты — docs/contracts/ui-controls.md. Ветка feature/ui-controls, копия ~/deltaplan-ui-controls (от main 938d1ea).

Цель

По умолчанию в полёте вперёд/вверх = трапеция от себя (нос вверх, торможение): W, ↑, мышь от себя (в режиме bar), стик вперёд. Назад/вниз = на себя (нос вниз, разгон). Это раскладка дельтапланериста, не самолёта.

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

  • 01.10 — по умолчанию в полёте W = трапеция от себя, S = на себя; то же для ↑/↓ и для стика/мыши по умолчанию («вперёд/вверх = от себя», мышь вперёд = от себя), без инверсии «как в самолёте» по умолчанию. Настройка инверсии остаётся, но по умолчанию — дельтапланерная.

Что было на v1.0.0 (938d1ea)

  • configs/controls.json → keys: pitch_pull_in = [W, Up] (на себя), pitch_push_out = [S, Down] (от себя). InputController._update_air: pitch_dir = −(pull_in − push_out)·inv → W даёт ControlInput.pitch −1.
  • Мышь (bar): pitch = −offset.y — мышь от себя (курсор вверх) = от себя. Уже дельтапланерно.
  • Стик (_apply_gamepad): pitch = gy·inv, ось 1 вперёд < 0 → стик вперёд = на себя. Как в самолёте — менять.
  • На земле: W/S — шаг (walk_forward/walk_back), ↑/↓ — нос вверх/вниз (nose_up/nose_down). Не меняется.
  • configs/ui.json → controls_help: раздел «На земле» показывает клавиши шага через действия pitch_pull_in/pitch_push_out — после перестановки показал бы S/↓ для «идти». Привязать к walk_forward/walk_back.
  • Подсказка инверсии settings_invert_pitch_hint («W — от себя, S — на себя») описывала инверсию; теперь инверсия = «как в самолёте».

Задачи

UC-1. Раскладка и проверки (dp-engineer)

  • Скоуп: перестановка клавиш тангажа в configs/controls.json (одно место знака для клавиатуры); стик вперёд = от себя; инверсия по-прежнему меняет всё (клавиши, мышь, стик); экран «Управление» (configs/ui.json → controls_help), подсказки и локали (locale/ui.csv); *_doc в controls.json; комментарии autopilot.gd/тестов, где написано «W = pitch_pull_in» (автопилот и тесты жмут действия по имени — знак по имени не меняется); регрессионный тест У1.
  • Не трогать: scripts/flight/ground_run.gd, логику земли в input_controller.gd (работа feature/control-fix, CF-3). Правка _apply_gamepad (одна строка знака) — отдельным последним коммитом, вливается только после согласования с главной сессией (файл control-fix).
  • Приёмка (headless): тест tests/game/test_ui_controls_contracts.gd — У1: в полёте по умолчанию W и ↑ дают ControlInput.pitch > 0, S и ↓ — < 0; мышь от себя (bar) — > 0; стик вперёд — > 0 (после коммита стика); invert_pitch = true меняет знак всех; в полёте W увеличивает угол атаки крыла относительно трима (сцена игры или FlightModel), S уменьшает. --filter=contracts, --filter=game, --filter=ui, --filter=flight без новых падений.
  • Оценка: 1–2 ч.

UC-2. Тексты (dp-writer)

  • Скоуп: описания раскладки в docs/guide/game.md, docs/guide/flight.md (если есть), README.md, страницы сайта (site/content/…, где описано управление), без страниц версий и дневника прошлых дней.
  • Приёмка: все упоминания «W/↑ — на себя/разгон» заменены на дельтапланерную раскладку; список изменённых мест в отчёте; hugo собирает сайт (если есть в системе).
  • Оценка: 30–60 мин.

Пересечения с feature/control-fix

  • CF-3 переделывает землю (Shift на разбеге — W/S, стрелки, мышь «как в воздухе») и правит input_controller.gd, controls.json, autopilot.gd, экран «Управление», локали, docs/guide/game.md/flight.md, тесты.
  • Владелец знака клавиатуры — этот модуль: перестановка списков клавиш в controls.json → keys.pitch_*. CF-3 знак повторно не переставляет; «как в воздухе» = через действия pitch_push_out/pitch_pull_in. Двойную перестановку ловит тест У1.
  • Слияние в main: сначала control-fix, затем ui-controls (подтянуть feature/control-fix перед финалом, конфликты в controls.json, ui.json, locale/ui.csv, доках — разрешает координатор, тест У1 — арбитр).

Шлюзы

  • Правка файла control-fix (input_controller.gd → _apply_gamepad) — согласование с главной сессией.
  • Финал — список изменённых мест, тесты, как проверить пилоту, пересечения с control-fix; в main не вливаю.

Этап 2 — камера с клавиш при тангаже мышью

Запрос пользователя (02.10, дословно)

«если управление тангажем через мышь то WASD должны от 1го лица двигать камеру».

Разбор кода (ветка модуля от main ce5ea58)

  • Мышь в режиме bar (controls.json → mouse.mode, по умолчанию; пункт настроек «Мышь: Обзор / Трапеция») ведёт трапецию по обеим осям (тангаж и крен), пока мышь захвачена (InputController.mouse_captured, клавиша M; захват при старте — mouse.capture_on_start). Клавиши при этом суммируются с мышью (_update_bar: _key_pitch + m_pitch) — WASD и стрелки дублируют мышь.
  • Обзор головой в кабине — CameraRig._head (рыскание/тангаж, пределы camera.json → cockpit.head: ±120°, вверх 60°, вниз 70°); в bar — только с зажатой правой кнопкой; отпустил — голова остаётся; V/средняя — плавно вперёд (recenter_time_s 0,25 с); Q — держать взгляд на прибор. Смещения точки глаз с клавиш в коде нет (точка глаз = маркер PilotHead + доля смещения тела).
  • Камера сзади (chase) — без обзора/орбиты. Свободная (free) — WASD/E/Q двигают её, крыло без рук (hands_off), при автопилоте клавиши ей не отдаются.
  • Земля (С2 v2 control-fix, CF-3): стоя/шагом W/S — шаг, A/D — поворот; Shift (разбег) — WASD, стрелки, мышь — нос и крен крыла «как в полёте».
  • Автопилот жмёт действия по имени (Input.action_press, без физических клавиш) — исключение физических клавиш (как _bar_strength(only_bar) на земле) его не задевает.
  • Контрактный тест У1 водит клавиши в режиме мыши look — остаётся верным.

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

  • 02.10 — WASD в режиме тангажа мышью — поворот головы (обзор камеры кабины), не смещение точки обзора.
  • 02.10 — «UI не переделывай, только камеру»: экран «Управление» не перерабатывать (строки трапеции по-прежнему показывают «W / ↑», оговорка — в строке controls_mouse_bar_wasd).

Решения координатора (02.10; дешёво поменять — сказать)

  1. Условие «мышь — трапеция» = mouse.mode == "bar" и мышь захвачена (M отпустил мышь — WASD снова трапеция, как раньше).
  2. Отменено решением пользователя 02.10 (этап 3): Разбег (Shift) — «как в полёте» (принцип CF-3): WASD — обзор головой, нос и крен — мышь и стрелки; поэтому на отрыве вклад клавиш в трапецию не меняется (скачка нет). Стоя и шагом — без изменений: W/S — шаг, A/D — поворот.
  3. Камера сзади: WASD ничего не делают (трапеция — мышь и стрелки); свободная — как раньше (WASD двигают её).
  4. Голова с клавиш — camera.json → cockpit.head.key_rate_deg_s = 90°/с в тех же пределах (±120°, вверх 60°, вниз 70°); отпустил — остаётся (как обзор мышью); V/средняя кнопка — вперёд.
  5. Стрелки — трапеция всегда; X, Q, режим мыши look, геймпад, автопилот (жмёт действия по имени) — без изменений.

Контракт — У2 v1 (docs/contracts/ui-controls.md), тест — tests/game/test_ui_controls_contracts.gd (test_u2_*, красные до UC-3).

Задачи этапа 2

UC-3. Обзор с WASD при трапеции мышью (dp-engineer)

  • Скоуп: действия look_up/look_down/look_left/look_right = W/S/A/D в controls.json → keys (+ _doc); InputController: при «мышь — трапеция» (bar и мышь захвачена) клавиши обзора не двигают трапецию (в полёте и на разбеге), действия без физической клавиши (автопилот, тесты) — считаются; CameraRig: в кабине при том же условии клавиши обзора поворачивают голову (key_rate_deg_s), в chase — ничего, free — без изменений; camera.json → cockpit.head.key_rate_deg_s; контрактный тест У2.
  • Не трогать: тексты (UC-4), ground_run.gd, физику.
  • Приёмка (headless): tests/game/test_ui_controls_contracts.gd — У2 (ниже) зелёный; У1 без изменений (режим look); --filter=ui_controls, contracts, game, ui, flight без новых падений; скриншоты кабины: взгляд вперёд и после 1 с A / W — в build/screenshots/UC-3/.
  • Оценка: 2–3 ч.

UC-4. Тексты (dp-writer)

  • Скоуп: экран «Управление» (configs/ui.json → controls_help: строка «мышь — трапеция: WASD — осмотреться, трапеция — мышь и стрелки»), locale/ui.csv (ru/en), подсказка в настройках режима мыши (если есть), docs/guide/game.md, site/content/mechanics/flight.md (управление), CHANGELOG «В работе».
  • Приёмка: все места, где сказано «W/S, A/D — трапеция», уточнены для режима «мышь — трапеция»; --filter=ui без новых падений; hugo собирает сайт.
  • Оценка: 30–60 мин.

Этап 3 — мышь всегда крыло, W/S/A/D — пилот и голова (02.10)

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

  • 02.10 — «WASD камера нужна только в полёте, на земле не нужна». Отменяет «Решение координатора» п.2 этапа 2 (разбег — WASD обзор).
  • 02.10 (поправка) — на земле (стоя, шагом, разбег) W/S/A/D — движение пилота, крыло — мышь (и стрелки).
  • 02.10 — «управление крылом через WASD в полёте не нужно вообще, можно убрать настройку — пилоты этим не пользуются». Целевая схема без вариантов:
    • мышь — всегда крыло (трапеция); настройка режима мыши («Мышь: Обзор / Трапеция», controls.json → mouse.mode, режим look) — убрать целиком (код, конфиг, пункт настроек, локали, тексты, тесты);
    • в полёте W/S/A/D — только поворот головы, в трапецию не идут никогда (независимо от захвата мыши);
    • на земле (стоя, шагом, разбег) W/S/A/D — движение пилота, крыло — мышь;
    • стрелки и геймпад — крыло, как были;
    • камера сзади / свободная: W/S/A/D в полёте в трапецию не идут; свободная — как была;
    • экран «Управление» — только строки/тексты, чтобы не врали, без переделки экрана.

Решения координатора (02.10; дешёво поменять — сказать)

  1. Клавиши трапеции — только стрелки: pitch_push_out = [Up], pitch_pull_in = [Down], roll_left = [Left], roll_right = [Right]. W/S/A/D больше не назначены ни на одно действие трапеции — фильтры «клавиша занята шагом/обзором» (GROUND_MOVE_ACTIONS/LOOK_ACTIONS в _bar_strength) не нужны.
  2. Разбег (Shift) — как в CF-3: Shift сам бежит вперёд, курс на бегу — от крена крыла (ground_run._turn, input.turn на бегу физикой не используется). На бегу W/S/A/D ничего не делают (walk/turn = 0); физику бега не трогаем.
  3. Захват мыши (M): без захвата мышь крыло не двигает (курсор свободен для меню/окон), трапеция держит последнее положение мыши — как и было в режиме bar; захват при старте (mouse.capture_on_start) — как было. Правая кнопка (при захвате) — осмотреться мышью в кабине, как было.
  4. Обзор головой с клавиш (keys_look()) — в полёте, ввод включён, руки на трапеции (не свободная камера); от мыши не зависит. На земле — нет.
  5. Переход на отрыве/касании: W/S/A/D в трапецию не идут ни на земле, ни в полёте — скачка трапеции нет; зажатая на отрыве W сразу крутит голову, на касании — шаг; защёлок не делаем.

Контракты — У1 v3, У2 v2 (docs/contracts/ui-controls.md), тесты — tests/game/test_ui_controls_contracts.gd, test_gameplay.gd::test_ground_bar_keys.

Задачи этапа 3

UC-5. Мышь — всегда крыло, W/S/A/D — пилот/голова (dp-engineer)

  • Скоуп: configs/controls.json — клавиши трапеции только стрелки, убрать mouse.mode (+ _doc везде, где упомянуты W/S/A/D как трапеция или режим мыши); InputController — убрать mouse_mode() и ветки режима look, keys_look() по У2 v2, упростить _bar_strength (фильтры не нужны); CameraRig — убрать ветку режима look (обзор мышью — правой кнопкой, как в bar), keys_look_fn как было; settings_panel.gd — убрать пункт «Мышь: Обзор / Трапеция»; controls_screen.gd и configs/ui.json → controls_help — убрать поле mouse_mode и строки режима look (код фильтра и строки, без переделки экрана); locale/ui.csv — удалить ключи пункта режима мыши и строк режима look, тексты строк экрана «Управление» и подсказок (ru/en) привести к схеме этапа 3 (крыло — мышь и стрелки; на земле W/S/A/D — шаг/поворот; в полёте — осмотреться); всё прочее, что опирается на mouse.mode/«W = трапеция» (автопилот жмёт действия по имени — проверить; tools/ и тесты, жмущие физические W/S/A/D ради трапеции — на стрелки).
  • Не трогать: ground_run.gd, физику; docs/, сайт, CHANGELOG (UC-6); контрактный тест и test_ground_bar_keys (правит координатор — если нужен другой интерфейс, к координатору).
  • Приёмка (headless): test_ui_controls_contracts.gd (У1 v3, У2 v2) зелёный; test_gameplay.gd::test_ground_bar_keys зелёный; --filter=ui_controls, contracts, game, ui, flight без новых падений; grep -rn "mouse.mode\|mouse_mode" scripts configs tests tools — пусто (кроме Input.mouse_mode).
  • Оценка: 1–2 ч.

UC-6. Тексты этапа 3 (dp-writer)

  • Скоуп (параллельно с UC-5; строки экрана и локали — в UC-5): docs/guide/game.md, site/content/mechanics/flight.md, CHANGELOG «В работе».
  • Приёмка: нигде не сказано, что W/S/A/D двигают крыло или что есть режим мыши «Обзор»; везде: крыло — мышь (и стрелки, стик); на земле W/S — шаг, A/D — поворот, Shift — разбег; в полёте W/S/A/D — осмотреться (в кабине); hugo собирает сайт.
  • Оценка: 30–60 мин.