Commit Graph

6 Commits

Author SHA1 Message Date
snark13 47a4b084c4 Меню, статус-строка и оболочка игры: title/intro/cutscene/HoF, POP.CFG, палитры
Эта сессия (меню + текст в служебных полосах):

* Меню: рамка выделения считается от силуэта текста (SEL_PAD сверху и
  снизу), а не «на глаз»; все экраны центрируются в игровых 200 строках
  (UI_CENTER_TOP/UI_CENTER_FIELD) и в них помещаются; Enter и Space —
  равноправные клавиши действия (ui_action_down).
* Settings: убраны SHOW SPRINTER SCREEN и BACK, добавлены DEBUG BAR и
  ABOUT.  About показывает тот же текст, что стартовый Sprinter screen,
  минус строка про клавиши — общий about_text(), чтобы экраны не
  разъехались.  CONTROLS собирается таблицей и центрируется по
  фактическому числу строк.
* GAME PAUSED переехала в нижнюю статус-строку, как в оригинале
  (SDLPoP rect_bottom_text = {193,70,202,250}): это состояние программы,
  а не пункт меню.  POP_HP_Y вынесен в pop_cdraw.h — полосу делят два
  модуля.
* pop_status.c (банк 9) — текст в обеих служебных полосах.  Нижняя:
  порт display_text_bottom + таймера (QUICKSAVE/QUICKLOAD/SOUND ON/OFF,
  24 тика).  Верхняя отладочная переведена с палочек на текст
  «Level ##, Room ##, Speed: …, Sound: …, Immortal #» малым шрифтом, с
  своим форматированием чисел (без printf и без деления).
  Заявка сообщения — запись одного байта pop_status_msg: резидент W1/W2
  не растёт, весь рендер в банке.  Бюджет после правок не изменился
  (_CODE 23981, куча 267 Б).
* Цена вывода: блит глифа ~4,6 тыс. тактов независимо от размера, поэтому
  всё change-driven, отладочная строка перерисовывается ПО ПОЛЯМ, пробелы
  не блитятся вовсе, а вход в комнату заливает только игровое поле
  (pop_screen_fill_field) — борта от комнаты к комнате не меняются.
* Интро: в PV-сцене зазвучали пропавшие эффекты оригинала — закрытие
  ворот (4) и открытие двери покоев (51), из которой входит Джафар.
* docs/status_line_text.md — полная инвентаризация ВСЕХ текстов SDLPoP в
  статус-строке: геометрия, семантика text_time_total как идентификатора
  сообщения, мигание, рестарт по истечении 36/288.

Вместе с этим выкладывается накопленная работа по оболочке полной игры:
автомат состояний (pop_app), title, intro/PV и cutscene, attract-demo,
Hall of Fame, глобальный таймер, настройки и POP.CFG, модуль палитр и
fade, звуковой набор, host-тесты на новые швы.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:58:36 +03:00
snark13 f4b4852d51 QuickSave F6/F9 в roomtest; bank_load_file/bank_save_file/gfx_w0_page_prepare; sprinter-cc: авто n_banks
roomtest:
- QuickSave/QuickLoad (F6/F9): снапшот 'POPQ' v3 в POP.SAV/POP.BAK на HDD,
  транзакционная запись (POP.NEW -> rename, откат при ошибке), XOR-контрольная
  сумма payload'а; сериализация всех игровых переменных через W0-примитивы
  pop_qs_*; pop_qsave_process() на границе кадра вне Char-окон
- pop_qsave_restore_room(): полная перезагрузка комнаты после загрузки
  (карта/края/швы, сброс bake-кэша, перерисовка обеих страниц, инвалидация
  кэшей спрайтов и HP)
- сериализаторы в pop_map/pop_loose_mob/pop_trob/pop_guard_ai
  (+ восстановление инвариантов: mobs_live, trob_drawn, redraw)
- immortal-чит 2 уровня: уровень 2 поглощает только малый урон Kid'а

libc/libbgi:
- bank_load_file()/bank_save_file() — резидентное файловое I/O в банк,
  без правила W3 (путь читается до переключения страницы)
- gfx_w0_page_prepare(page) — подготовка W0-окна (IRQ/NMI-стабы) одной
  функцией; atlas_load.c и roomtest переведены на новые примитивы;
  ручные ISR-стабы удалены

sprinter-cc / сборка:
- --bank N=FILE.c: автогенерация n_banks (_n_banks_auto.c), ручные
  const n_banks удалены из тестов
- roomtest/app.mk: ресурсы через stamp-файлы (.resource-stamps/) — один
  запуск упаковщика на группу вместо N под -B; HDD_PACK_ARGS
2026-08-22 11:53:10 +03:00
snark13 c40ae3f8f5 Страж возвращался в комнату не туда: колонку несёт guards_x, а не тайл
Уровень 8 комната 24: после первого же выхода Кида страж телепортировался
в колонку 0, а ряд 0 там пустой в колонках 1..3 — страж падал с ряда 0 на
ряд 2 и разбивался.

leave_guard (seg002:02F5) кладёт в guards_tile get_tilepos(0, row), то есть
обнуляет колонку НАМЕРЕННО: позицию по горизонтали несёт guards_x, туда же
она и пишется.  enter_guard берёт x оттуда всегда.  Мы же считали x из
tile % 10 (то есть из нуля), а запомненную брали только у трупа.

Заодно портирован pos_guards (seg003:0913): при загрузке уровня guards_x
пересчитывается из колонки тайла, а файловое значение выбрасывается — в
комнате 24 уровня 8 там 255.

Проверено в MAME: три входа подряд дают одну и ту же позицию (x 156, ряд 0),
страж стоит на полу.  Host-тесты 5106 проверок без расхождений.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 16:20:40 +03:00
snark13 6feab5d2f2 Смерть на кнопке ломает её насовсем (died_on_button, seg007:776)
Уровень 7 начинается падением; разбившись на кнопке открытия решётки
(комната 3, тайл 2,1), Кид в оригинале открывает решётку НАСОВСЕМ, а у нас
она закрывалась обратно.  Чинить пришлось три места.

1. pop_phys_tick выходил по pop_kid_dead, то есть check_press до трупа не
   доходил вовсе.  У оригинала play_kid_frame гейтится только Char.room
   != 0.  Кнопка получала одно нажатие — в кадре смерти, потому что флаг
   ставит land() уже внутри цепочки.  «Труп не шевелится» держит внутренний
   выход в kid_phys, он остался.

2. Портирован died_on_button: открывалка → пол + связь дёргается типом
   «щебень» (открыть насовсем), прочие кнопки → TILE_STUCK.  Ветки по
   Char.alive в check_press не было вовсе.

3. Рестарт уровня восстанавливал только foretable, а died_on_button
   оставляет таймер связи нажатым (trob кнопки умирает сразу — тайл уже
   пол).  Остаток переживал респавн, и кнопка рисовалась нажатой с первого
   кадра.  pop_level_reset_tiles теперь возвращает и LINKMAP.

Проверено в MAME по всему циклу: смерть → FF (открыта навсегда), респавн →
кнопка цела и не нажата, второе падение → снова ломается.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:27:36 +03:00
snark13 055d6d7c89 Фикс: чтение файлов нельзя выносить в банк — банковый код сам живёт в W3
Первый запуск после разгрузки встал в halt по мусору в 0xC40E.  Причина:
pop_level_read_file и pop_kid_data_load маппят страницу данных в W3 на время
read() (файл читается по 0xC000), а модули банка исполняются ИЗ ЭТОГО ЖЕ
окна — после sprinter_page_w3() следующая инструкция приходит уже из чужой
страницы.  Обе функции вернулись в резидент; в банке остался код, который
ходит через W0 (gfx_w0_map) или окон не трогает вовсе.

Правило записано в _pop_level.h и _pop_kid.h: банковый модуль НЕ маппит W3
(через W0 — можно).  Поэтому же в резиденте живёт весь libbgi.

Куча 6095 Б (было 902 до разгрузки).  Проверено в MAME: игра стартует,
комната рисуется, Кид управляем.  tests-host 6/6.
2026-08-12 22:17:18 +03:00
snark13 5d31ed086f Резидент: pop_redraw + холодная половина pop_level в банки (902 -> 3525 Б кучи)
MEM-COLD3, шаг 1-2 из плана разгрузки резидента:

* pop_redraw.c целиком -> банк 7 (к pop_room.c: он и есть единственный
  потребитель разбора пометок).  Два модуля в одном банке линкуются в один
  сегмент — проверено на .map (BANK7 7928 -> 8556).
* pop_level.c расколот по частоте вызова: горячая половина (чтение тайлов,
  связи, дверные таблицы, живые стражи — зовут все банки, местами на каждый
  тайл) осталась в резиденте, холодная (чтение файла уровня, разбор комнаты,
  рестарт, потабличные различия) уехала в pop_level_cold.c -> банк 8.
  Общее состояние объявлено в _pop_level.h: данные банковых модулей всё
  равно линкуются в общий _DATA, в банк уехал только КОД.

Побочно найден баг кодогена SDCC: `return 1;` из ветки не кладёт 1 в A —
до __banked он маскировался случайным ненулевым остатком в A.  Репро и
разбор — docs/bugs/sdcc-z80-ret-const-lost/, стаб t_char починен одним
выходом через переменную.  Аудит всех .asm roomtest: других мест нет.

_CODE 25628 -> 23005, куча 902 -> 3525 Б.  tests-host 6/6.
2026-08-12 21:53:08 +03:00