Статус-строка: время, отсчёт ##:## в отладочной полосе, смерть как в оригинале

* Порт show_time() целиком: «N MINUTES LEFT», «N SECONDS LEFT» (у
  единственной секунды свой total=12, как у оригинала), «TIME HAS
  EXPIRED!».  Флаг pop_show_time — это is_show_time: взводит и само ядро
  таймера (круглые пятёрки, каждая секунда последней минуты), и старт
  уровня, и читы времени.  Значение 2 = «перебить текущую строку», как
  оригинал делает в последнюю минуту и после читов.
* Отладочная полоса показывает оставшееся время ##:## у правого края.
  На табло уходит minutes-1: rem_min у оригинала — НОМЕР идущей минуты, а
  не остаток целых (старт 60 при rem_tick 719 = «почти 60:00»).  Секунды
  считаются делением раз в 12 кадров, а не каждый кадр — это единственное
  деление на кадровом пути.
* Смерть Кида по образцу оригинала: строка «Press Button to Continue» с
  7-го кадра (Kid.alive > 6), 288 тиков = 24 секунды, последние 72 тика
  мигание с периодом 12 и звуком 38 на появлении.  Промолчал — start_game
  (title), нажал Enter/Shift — рестарт уровня.  Авто-респавна по таймеру
  больше нет.  Отпускания клавиш оригинал не ждёт, а мы ждём: Shift у нас
  клавиша действия — см. impl_diff.md.
* POP_APP_PLAYING принимает EV_RESTART_INTRO: start_game зовётся прямо из
  геймплея, раньше это событие принимал только pause menu.
* expired() загейчен уровнем 13 (seg000:910).  Время на уровне Джафара
  идёт до открытия двери, но КОНЧИТЬСЯ игра там уже не может — у нас
  этого гейта не было, и партия завершалась.

Бюджет: _CODE 23979, куча 263 Б.  Host-тесты: 15 наборов.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 15:06:17 +03:00
parent 5756101d29
commit ffb59b3470
8 changed files with 320 additions and 27 deletions
+13 -5
View File
@@ -28,6 +28,7 @@
#include "roomtest_cold.h" /* холодная половина цикла — в банке */
#include "pop_qsave.h"
#include "pop_status.h"
#include "pop_timer.h"
#include "pop_menu.h" /* pause menu — банк 9, команды наружу */
#include "pop_settings.h" /* единая модель скорости/звука/читов */
#include "pop_app.h" /* автомат приложения: игровой цикл = PLAYING */
@@ -132,8 +133,6 @@ static void kbd_idle(void)
#define KBD_QUIT 0x09 /* F10 — отладочный немедленный выход (был на Esc,
* пока меню не готово рисовать — см. план MS4) */
#define RESPAWN_DELAY 400 /* авто-респавн (фолбэк), если не нажали ↑ */
#define RESPAWN_SETTLE 20 /* тиков до приёма ↑ (труп успел лечь) */
/* ОТЛАДОЧНЫЙ ОБХОД КОМНАТ: '+'/'-' (цифровой блок либо '='/'-' основного
* ряда) — следующая/предыдущая комната уровня ПО НОМЕРУ (1..24, с обёрткой),
@@ -581,7 +580,12 @@ int main(void)
if (!is_demo && pop_timer_tick(pop_settings.time_limit_enabled,
(uint8_t)(Kid.alive < 0 &&
(pop_current_level < 13 ||
(pop_current_level == 13 && pop_leveldoor_open == 0))))) {
(pop_current_level == 13 && pop_leveldoor_open == 0)))) &&
pop_current_level < 13) {
/* Время ИДЁТ и на уровне Джафара (до открытия двери), но
* КОНЧИТЬСЯ игра там уже не может: expired() у оригинала стоит
* под отдельным `current_level < 13` (seg000:910). На 13-м
* истечение оставляет только строку «TIME HAS EXPIRED!». */
if (!pop_app_dispatch(POP_APP_EV_TIME_EXPIRED)) failed = 1;
break;
}
@@ -811,8 +815,12 @@ int main(void)
}
if (!is_demo && pop_kid_dead) { /* demo после смерти завершается, без respawn */
dead_frames++;
if (dead_frames > RESPAWN_SETTLE &&
(kbd_raw_down(KBD_UP) || dead_frames > RESPAWN_DELAY)) {
/* Просьба нажать кнопку, мигание и таймаут — в банке
* (pop_status.h). Место возрождения выбирает pop_start_level:
* на части уровней это НЕ старт, а уже пройденный чекпойнт. */
uint8_t dr = pop_dead_prompt(dead_frames);
if (dr == 2) break; /* 288 тиков молчания -> title */
if (dr) {
dead_frames = 0;
pop_start_level();
gfx_set_draw_page(back);