SprPoP: README для игрока и экран Controls в две колонки

README, который едет НА ДИСК рядом с программой, в двух версиях: README.TXT
(английский) и README.RUS (русский).  Краткое описание, запуск, полная
раскладка управления, читы, файлы игры, чего не хватает против оригинала,
благодарности.

Русский обязан быть в CP866 — DSS и местные просмотрщики читают именно её,
UTF-8 показался бы кракозябрами.  Исходники лежат в dist/ как UTF-8 (чтобы
читались в репозитории), перекодировка и CRLF делаются правилом Makefile.
ПОРЯДОК В КОНВЕЙЕРЕ ВАЖЕН: sed ставит CRLF ДО iconv — BSD sed в UTF-8
локали отказывается работать с байтами CP866 («RE error: illegal byte
sequence»), а с валидным UTF-8 работает.  iconv БЕЗ -c намеренно: потеря
символа должна ломать сборку, а не молча портить текст.

ЭКРАН CONTROLS переписан в две колонки.  В одну раскладка больше не
помещалась: после фаз A-C клавиш стало вдвое больше, а по высоте есть
только 200 точек вместе с заголовком.  Слева игра (бег, лазание, бой),
справа служебное и читы; высота блока считается по ДЛИННОЙ колонке.
Строка «IN A FIGHT» — заголовок, а не клавиша: ниже неё те же стрелки и
Shift означают другое, и без разделителя список читался бы противоречиво.

Заодно экран перестал врать: там до сих пор висели P, I, U и F7/F8 —
клавиши, переназначенные ещё в фазе A.

Проверено в MAME: Backspace открывает меню, Settings -> Show key bindings
показывает обе колонки целиком.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 15:50:59 +03:00
parent 659071838d
commit 6cc8611d03
6 changed files with 620 additions and 32 deletions
+67 -28
View File
@@ -90,13 +90,22 @@
#define SET_HEAD2 (SET_HEAD1 + 4 * SET_STEP + SET_HEAD_GAP)
#define SET_HEAD3 (SET_HEAD2 + 3 * SET_STEP + SET_HEAD_GAP)
/* CONTROLS: число строк зависит от того, включены ли читы, поэтому верх
* блока считается в рантайме. */
#define CTRL_TITLE_GAP 20
#define CTRL_N_BASE 7 /* общие клавиши */
#define CTRL_N_CHEAT 5 /* дополнительные строки читов */
#define CTRL_COL_KEY 45
#define CTRL_COL_DESC 145
/* CONTROLS — ДВЕ КОЛОНКИ. В одну колонку раскладка уже не помещалась:
* после приведения клавиш к keys.txt (docs/keys_plan.md) их стало вдвое
* больше, а по высоте есть только 200 точек на всё вместе с заголовком.
*
* Слева — игра (бег, лазание, бой), справа — служебное и читы. Число строк
* в колонках разное и зависит от того, включены ли читы, поэтому высота
* блока считается в рантайме по ДЛИННОЙ колонке. */
#define CTRL_TITLE_GAP 18
#define CTRL_N_LEFT 10 /* игровые клавиши */
#define CTRL_N_SYS 7 /* служебные */
#define CTRL_N_CHEAT 5 /* читы: показываются, только если включены */
#define CTRL_L_KEY 6
#define CTRL_L_DESC 62
#define CTRL_R_KEY 164
#define CTRL_R_DESC 220
#define CTRL_STEP 13
/* ABOUT = ровно тот же текст, что на стартовом Sprinter screen, минус
* строка про клавиши. ABOUT_H — и высота блока, и смещение ПОСЛЕДНЕЙ
@@ -317,39 +326,69 @@ static void settings_draw(uint8_t sel)
/* Две колонки «клавиша — что делает». Первые CTRL_N_BASE строк общие, за
* ними идут читовые: их показываем, только когда читы включены. */
static const char * const ctrl_key[CTRL_N_BASE + CTRL_N_CHEAT] = {
"ARROWS", "SHIFT", "ESC", "F6 / F9", "CTRL+S", "CTRL+M", "P",
"K", "I", "SHIFT+L", "U", "F7 / F8"
/* ЛЕВАЯ КОЛОНКА — игра. Строка «IN A FIGHT» здесь заголовок, а не клавиша:
* ниже неё те же стрелки и Shift означают уже другое, и без разделителя
* список читался бы как противоречивый. */
static const char * const ctrl_l_key[CTRL_N_LEFT] = {
"ARROWS", "UP", "DOWN", "SHIFT", "SH+ARROWS", "HOME PGUP",
"IN A FIGHT", "SHIFT", "UP", "DOWN"
};
static const char * const ctrl_l_desc[CTRL_N_LEFT] = {
"RUN / TURN", "JUMP, CLIMB", "CROUCH", "PICK UP", "CAREFUL STEP",
"JUMP L / R",
"", "STRIKE", "BLOCK", "SHEATHE"
};
static const char * const ctrl_desc[CTRL_N_BASE + CTRL_N_CHEAT] = {
"MOVE / JUMP", "ACTION", "PAUSE MENU", "SAVE / LOAD", "SOUND", "MUSIC",
"SPEED",
"KILL GUARD", "IMMORTAL", "NEXT LEVEL", "FLIP SCREEN", "TIME - / +"
/* ПРАВАЯ КОЛОНКА — служебное, ниже читы. */
static const char * const ctrl_r_key[CTRL_N_SYS + 1 + CTRL_N_CHEAT] = {
"BACKSPACE", "SPACE", "T", "F6 / F9", "CTRL+A", "CTRL+S M", "CTRL+Q F10",
"CHEATS",
"SHIFT+L", "K", "+ / -", "SHIFT+I", "SHIFT+S T"
};
static const char * const ctrl_r_desc[CTRL_N_SYS + 1 + CTRL_N_CHEAT] = {
"MENU", "TIME LEFT", "SHOW TIMER", "SAVE / LOAD", "RESTART LVL",
"SOUND / MUSIC", "QUIT",
"",
"NEXT LEVEL", "KILL GUARD", "TIME + / -", "FLIP SCREEN", "HEAL / MAX HP"
};
/* Одна строка колонки: слева клавиша, справа что она делает. Пустое
* описание — значит строка была заголовком секции, и клавишу мы печатаем
* как есть, без второго столбца. */
static void ctrl_row(int kx, int dx, int y, const char *key, const char *desc)
{
pop_text_draw_mapped(POP_TEXT_SMALL, kx, y, key);
if (desc[0]) pop_text_draw_mapped(POP_TEXT_SMALL, dx, y, desc);
}
static void controls_draw(void)
{
uint8_t back = ui_begin();
/* С выключенными читами вместо пяти строк идёт одна пояснительная —
* список короче, и блок обязан центрироваться по ФАКТИЧЕСКОЙ высоте. */
/* С выключенными читами правая колонка короче: вместо пяти строк идёт
* одна пояснительная. Блок центрируется по ДЛИННОЙ из двух колонок. */
uint8_t cheats = pop_settings.cheats_enabled;
uint8_t rows = (uint8_t)(cheats ? CTRL_N_BASE + CTRL_N_CHEAT
: CTRL_N_BASE + 1);
uint8_t rright = (uint8_t)(cheats ? CTRL_N_SYS + 1 + CTRL_N_CHEAT
: CTRL_N_SYS + 1);
uint8_t rows = (uint8_t)(rright > CTRL_N_LEFT ? rright : CTRL_N_LEFT);
uint8_t i;
int top = UI_CENTER_TOP(POP_FONT_BIG_ASCENT + CTRL_TITLE_GAP +
(int)(rows - 1) * SET_STEP);
int y = top + POP_FONT_BIG_ASCENT;
(int)(rows - 1) * CTRL_STEP);
int y0 = top + POP_FONT_BIG_ASCENT;
int y;
pop_text_map();
pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, y, "CONTROLS");
y += CTRL_TITLE_GAP;
for (i = 0; i < rows; i++) {
const char *key = ctrl_key[i], *desc = ctrl_desc[i];
if (!cheats && i == CTRL_N_BASE) { key = "CHEATS"; desc = "DISABLED"; }
pop_text_draw_mapped(POP_TEXT_SMALL, CTRL_COL_KEY, y, key);
pop_text_draw_mapped(POP_TEXT_SMALL, CTRL_COL_DESC, y, desc);
y += SET_STEP;
pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, y0, "CONTROLS");
y = y0 + CTRL_TITLE_GAP;
for (i = 0; i < CTRL_N_LEFT; i++, y += CTRL_STEP)
ctrl_row(CTRL_L_KEY, CTRL_L_DESC, y, ctrl_l_key[i], ctrl_l_desc[i]);
y = y0 + CTRL_TITLE_GAP;
for (i = 0; i < rright; i++, y += CTRL_STEP) {
const char *key = ctrl_r_key[i], *desc = ctrl_r_desc[i];
/* Заголовок секции читов: с выключенными читами он же и объясняет,
* почему список ниже пуст. */
if (i == CTRL_N_SYS && !cheats) desc = "DISABLED";
ctrl_row(CTRL_R_KEY, CTRL_R_DESC, y, key, desc);
}
pop_text_unmap();
ui_end(back);