Files
Sprinter-SDCC/applications/SprPoP/docs/keys_plan.md
T
snark13 76f02e76db SprPoP: раскладка управления — фазы A и B плана keys_plan.md
Приводим клавиши к keys.txt (раскладка SDLPoP).  Работа делится надвое, и
здесь только то, что не трогает игровую логику: переназначения (A) и три
мелкие функции поверх готовых механизмов (B).  Читы, требующие правки
логики, и осмотр соседних комнат — фазы C и D, отложены.

ПЕРЕНАЗНАЧЕНИЯ (A)

  U            -> Shift+I   переворот экрана; U отдан «комнате сверху»
  Esc          -> Backspace меню; Esc остаётся дублёром, как у SDLPoP
  F7 / F8      -> - / +     ±минута, основной ряд и цифровой блок
  - / +        -> Ctrl+- / Ctrl++  обход комнат (наш отладочный телепорт)
  I            -> Ctrl+I    бессмертие
  P            -> Ctrl+P    режим скорости
  1 и 2        -> Ctrl+F    стоп-кадр, теперь одной клавишей
  F10 в игре   -> Ctrl+Q    вторая клавиша выхода; F10 ловится глобально

Плюс новое на готовых путях: Ctrl+A — рестарт уровня, Ctrl+V — версия
сборки (функция была, её показывал только старт), Ctrl+D — отладочная
строка (тот же тумблер, что в Settings; POP.CFG не пишем), Home / Page Up —
дублёры диагональных прыжков (у SDLPoP это не отдельное действие, а те же
Up+Left / Up+Right, поэтому просто добавляются к стрелкам).

Все наши сверхштатные клавиши ушли под Ctrl, чтобы не занимать голые буквы
из раскладки, и вписаны в keys.txt отдельным разделом.  Ctrl+B намеренно
не занята: keys.txt держит её под «вернуться в комнату Кида» (фаза D).

ФУНКЦИИ (B)

  Space  «сколько осталось» (seg000:612).  Не печатает сама: поднимает тот
         же pop_show_time, которым пользуется автоматическое объявление
         минут, и строку собирает time_msg() — «59 MINUTES LEFT» и «11
         SECONDS LEFT» остаются в одном месте.
  T      постоянный показ таймера.  Переиспользует поле DBG_F_TIME
         отладочной строки, своего рендера нет.  У верхней полосы теперь
         три состояния, и отслеживается РЕЖИМ (0 нет / 1 таймер / 2 всё),
         а не флаг: переход «таймер -> полоса» тоже перерисовывает всё.
  Ctrl+R возврат в заставку — тот же переход, что «Restart Game» в меню.

ДВЕ ЛОВУШКИ, найденные по дороге

  Модификатор обязан входить в САМО значение, а не в условие блока: с
  `if (ctrl) { nav = ...; nav_prev = nav; }` при отпускании Ctrl кромка
  застревала ненулевой и следующее нажатие глохло.

  Один скан-код на два чита: Shift+I и Ctrl+I — это 0x43 в обоих случаях.
  По той же причине ±минута требует ОТПУЩЕННОГО Ctrl (иначе сработает и
  время, и обход комнат), а T — отпущенных Shift и Ctrl (Shift+T отдан
  «добавить HP» в фазе C).

Обработчики положены в pop_frame_ui (банк 8), а не в резидент: там куча
всего 308 байт.  Проверено в MAME: Ctrl+D поднимает строку
«Level 1, Room 1, Speed: NORMAL...», T — один таймер 59:30 без подписей,
Space — watchpoint на pop_show_time ловит запись значения 2 (именно
обработчик клавиши, автообъявление пишет 1).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 15:22:21 +03:00

165 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: перевод управления на раскладку `keys.txt`
Целевая раскладка — [`keys.txt`](keys.txt), она же раскладка SDLPoP. Здесь
разобрано, что у нас уже есть, что стоит не на той клавише и каких функций
нет вовсе, плюс порядок работ. Кода этот документ не меняет.
## 1. Принципы
**Раскладка — не самоцель.** Часть строк `keys.txt` — это функции SDLPoP,
которых у нас нет вообще (воскрешение, замедленное падение, осмотр соседних
комнат). Поэтому работа делится на две разные по цене части: *переназначить
клавишу* (минуты) и *реализовать функцию* (от часа до дня). Смешивать их в
одной фазе нельзя — иначе простые переименования застрянут за сложным.
**Механику сверять с SDLPoP.** Для каждой новой функции — сначала найти её в
`assets/orig/SDLPoP/src/`, потом писать (правило проекта, memory
`pop_check_sdlpop_first`). Особенно для читов: `Shift+S`/`Shift+T` — это не
«прибавить HP», а конкретные `add_life`/`set_health_life`.
**Кромка против удержания.** Игровые клавиши (стрелки, Shift) читаются как
удержание — это held-state, ради которого и заводился `<kbd_raw.h>`. Все
служебные и читерские — как НОВОЕ нажатие (edge), иначе одно касание
сработает столько раз, сколько кадров клавиша была внизу. В коде уже есть
оба образца: `read_user_control()` для первого, `intro_skip_requested()` и
`hof_*` для второго.
**Где живёт обработчик.** Служебный слой — `pop_frame_ui()` в
`sprpop_cold.c` (банк 8), читы — `pop_cheat.h` + вызовы из главного цикла.
После переноса `pop_config`/`pop_hof` в банк 10 в банке 9 свободно 4436
байт, в банке 8 — 1786; новые обработчики лучше класть в 8-й или 10-й, а не
в 9-й.
**Ctrl-комбинации уже поддержаны:** `Ctrl+S`/`Ctrl+M` проверяют
`KBD_LCTRL || KBD_RCTRL` вместе с буквой (`sprpop_cold.c:518`). Тот же
приём годится для `Ctrl+A`, `Ctrl+R`, `Ctrl+V`, `Ctrl+Q`, `Ctrl+B`.
## 2. Что есть сейчас
### Движение и бой — совпадает, кроме диагоналей
| `keys.txt` | у нас | статус |
|---|---|---|
| Left / Right / Up / Down | стрелки, `read_input()` | **есть** |
| Down+Left/Right — прыжок на месте | комбинация стрелок | **есть** |
| Shift — поднять предмет | `KBD_LSHIFT`/`KBD_RSHIFT` | **есть** |
| Shift+Left/Right — осторожный шаг | комбинация | **есть** |
| Up+Left / Up+Right — прыжок в сторону | комбинация | **есть** |
| **Home / Page Up** — то же самое | — | **НЕТ**: читаются только стрелки |
| Up на бегу — прыжок с разбега | | **есть** |
| Shift в падении — зацеп | | **есть** |
| Бой: Left/Right/Shift/Up | | **есть** |
| Бой: Down — убрать меч | | **проверить отдельно** |
### Служебные
| `keys.txt` | у нас | статус |
|---|---|---|
| `Esc` — пауза/меню | `KBD_MENU = 0x76` | **есть** |
| `Backspace` — меню | — | **НЕТ** (в оригинале это основная клавиша меню, Esc — дублёр) |
| `Space` — показать остаток времени | — | **НЕТ** (`Space` занят только в меню и вводе имени) |
| `Ctrl+A` — рестарт уровня | только пунктом меню | **НЕТ клавиши** |
| `Ctrl+R` — вернуться в заставку | — | **НЕТ** |
| `Ctrl+S` — звук вкл/выкл | `Ctrl+S` | **есть, совпадает** |
| `Ctrl+M` — музыка вкл/выкл | `Ctrl+M` | **есть, совпадает** |
| `Ctrl+V` — версия | `pop_build_info_show()` есть, зовётся на старте | **НЕТ клавиши** |
| `Ctrl+Q` / `F10` — выход | `F10` в двух местах | **половина** (см. §3) |
| `F6` / `F9` — quicksave/load | совпадает | **есть** |
| `F12` — скриншот | — | помечено опциональным, **не делаем** |
### Читы
| `keys.txt` | у нас | статус |
|---|---|---|
| `Shift+L` — следующий уровень | совпадает | **есть** |
| `K` — убить стража | совпадает | **есть** |
| `[` / `]` — сдвинуть Кида на пиксель | совпадает | **есть** |
| `+` / `` — минута времени | у нас это **F7/F8**, а `+`/`` заняты обходом комнат | **конфликт, см. §3** |
| `Shift+I` — перевернуть экран | у нас это **`U`** | **переназначить** |
| `R` — воскресить Кида | — | **НЕТ функции** |
| `Shift+W` — замедленное падение | — | **НЕТ функции** |
| `Shift+S` — вернуть одну единицу HP | — | **НЕТ функции** |
| `Shift+T` — добавить HP | — | **НЕТ функции** |
| `T` — показывать таймер | — | **НЕТ функции** |
### Осмотр комнат — нет целиком
`H` / `J` / `U` / `N` (соседняя комната слева/справа/сверху/снизу) и
`Ctrl+B` (вернуться к Кида) не реализованы. У нас есть отладочный **обход
комнат по номеру** (`+`/``, `ROOMNAV` в `sprpop.c`) — это другое: он
ТЕЛЕПОРТИРУЕТ Кида, а `H/J/U/N` только смотрят, не двигая его.
### Наше сверх `keys.txt`
`I` — бессмертие, `P` — режим скорости, `1`/`2` — стоп-кадр, `+`/``
обход комнат. Всё это отладочное и в оригинале отсутствует; выбросить или
оставить — решение пользователя (§6).
## 3. Конфликты, которые надо решить до кодинга
**К1. `+`/`−`: время или комнаты.** `keys.txt` отдаёт их под ±минуту, у нас
на них обход комнат, а время висит на F7/F8. В `pop_cheat.h` это записано
как осознанный выбор («Numpad +/- уже принадлежат ROOMNAV»). Варианты:
вернуть `+`/`` времени и увести ROOMNAV на другие клавиши; оставить как
есть и разойтись с `keys.txt`; либо развести по контексту (ROOMNAV — только
в отладочной сборке). Развилка требует решения.
**К2. `U` занят.** Сейчас `U` — переворот экрана, а `keys.txt` хочет `U` под
«посмотреть комнату сверху», переворот — на `Shift+I`. Разрешается само,
если делать К5 и К7 одной фазой.
**К3. `S` без Ctrl.** Звук у нас требует Ctrl, и это правильно, но
`keys.txt` хочет ещё и `Shift+S` (вернуть HP). Проверка обязана различать
`Ctrl+S`, `Shift+S` и голое `S`, иначе они будут срабатывать вместе. То же
для `T`: `T` (таймер) против `Shift+T` (добавить HP).
**К4. Два выхода.** `F10` живёт в `pop_frame_ui()` (только в игре) и, с
коммита 808c2a5, в `kbd_idle()` (везде). Дублирование безвредное, но лишнее:
проверку в `pop_frame_ui` стоит убрать, оставив одну точку. Заодно повесить
туда же `Ctrl+Q`.
## 4. Порядок работ
**Фаза A — переназначения, функции уже есть.** Дёшево и не трогает логику.
- `Shift+I` вместо `U` для переворота экрана (К2).
- `Backspace` как основная клавиша меню, `Esc` оставить дублёром.
- `Ctrl+V` на существующий `pop_build_info_show()`.
- `Ctrl+Q` рядом с `F10`, убрать дубль `F10` из `pop_frame_ui` (К4).
- `Ctrl+A` на существующий путь `POP_MENU_RESTART_LEVEL`.
- Решение по К1 и правка `+`/``/F7/F8.
**Фаза B — мелкие новые функции.**
- `Space` — показать остаток времени. Механика есть (`pop_timer`, статус-
строка); нужен показ на несколько секунд, как `text_time_total` оригинала.
- `T` — постоянный показ таймера (минуты:секунды) в статус-строке.
- `Ctrl+R` — возврат в заставку. Автомат это умеет (`POP_APP_EV_RESTART_INTRO`),
нужен только вход с клавиши.
**Фаза C — читы, требующие правки игровой логики.** Каждый — сверка с
SDLPoP до кодинга.
- `Shift+S` / `Shift+T` — HP: аналоги малого и большого красного зелья.
- `R` — воскрешение Кида: сложнее прочих, поднимает мёртвого персонажа и
должен согласоваться с уже существующим гейтом «мёртв» (memory
`pop_level_restart_scope`).
- `Shift+W` — замедленное падение.
**Фаза D — осмотр соседних комнат (`H`/`J`/`U`/`N` + `Ctrl+B`).** Самое
крупное: нужен показ комнаты, в которой Кида нет, с возвратом. У нас уже
разобрана модель `kid_room ≠ drawn_room` (memory `pop_seam_room_model`,
[`room_model_plan.md`](room_model_plan.md)) — это её прямое применение, и
именно поэтому фаза идёт последней.
**Фаза E — `F12` скриншот.** Помечен опциональным; предлагаю не делать
вовсе: файл на 80 КБ и запись на диск посреди кадра.
## 5. Открытые вопросы
1. **К1** — кому отдать `+`/``: времени (как `keys.txt`) или обходу комнат?
2. Что делать с нашими сверхштатными клавишами (`I` бессмертие, `P` скорость,
`1`/`2` стоп-кадр): оставить как есть, увести под отладочную сборку или
выбросить?
3. Нужны ли `Home`/`Page Up` как дублёры диагональных прыжков — на IBM-клавиатуре
Sprinter они есть, но комбинация `Up+Left` уже работает.
4. Фазы C и D — делать ли их вообще: это читы и отладочный осмотр, к
прохождению игры отношения не имеющие.