From 08c6a8504fc66784cf95ff937a5de88db0635754 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Wed, 29 Jul 2026 11:58:06 +0300 Subject: [PATCH] =?UTF-8?q?PoP=20roomtest:=20=D0=BE=D1=82=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=BE=D0=B1=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=20=D0=BA=D0=BE=D0=BC=D0=BD=D0=B0=D1=82=20(+/-)=20+=20TOD?= =?UTF-8?q?O=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B4=D1=80=D0=BE=D1=8E=20?= =?UTF-8?q?=D0=BF=D0=B8=D0=BA=20=D0=B8=20idle-skip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOMNAV (одна строка #define в roomtest.c): '+'/'-' — следующая/предыдущая комната уровня ПО НОМЕРУ (1..24, с обёрткой), Kid ставится на первый пол, pop_trob_reset() возвращает пики/ворота в исходное. Нужен для обхода всех 24 комнат в поиске багов отрисовки, включая недостижимые обычным путём. Цена — 505 Б кода W1 (22816 -> 23321), они же вычитаются из кучи (4178 -> 3673 Б); W3 не тронут. Переводить приложение в huge ради этого не стали: смена модели памяти (банкованный W1 + трамплины) ради полукилобайта — плохой размен прямо перед прогоном комнат. Номер комнаты — ПОЛОСКАМИ в верхнем борте (слева десятки, справа единицы), а не outtextxy: текст тянет системный знакогенератор (_gfx_font_buf, 2 КБ статики) и сажает кучу до 576 Б. Цвет полосок 0x57, а не WHITE: палитра игровая, запись 15 в ней ЧЁРНАЯ (kid.pal 0x0F = 0,0,0) — из-за этого закомментированный ранее лейбл был бы невидим в любом случае. bug_list.md: раздел TODO (T-1 редрой пик по причине, T-2 idle-skip) + пустая таблица на 24 комнаты под результаты обхода. Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/bug_list.md | 88 +++++++++++++++++++++++++++ applications/PoP/roomtest/roomtest.c | 77 +++++++++++++++++++---- 2 files changed, 152 insertions(+), 13 deletions(-) diff --git a/applications/PoP/roomtest/bug_list.md b/applications/PoP/roomtest/bug_list.md index 4b8dd18..138f715 100644 --- a/applications/PoP/roomtest/bug_list.md +++ b/applications/PoP/roomtest/bug_list.md @@ -344,3 +344,91 @@ roomtest.c (enter_room/drawn_room/главный цикл), pop_kid.c (кадр Реализовано на 2026-07-22: S1 (kid_room + render_dx) + pop_leave_timer. S2/S3 отложены — баг закрыт odd-pixel поправкой (см. КОРЕНЬ выше). Память: [[pop_seam_room_model]], [[sdlpop_odd_pixel_char_x]]. + +--- + +# TODO: оптимизация отрисовки (записано 2026-07-29) + +Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к +перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый +кадр), а у нас каждая такая пометка превращается в реальный heal (копию из +ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем +безусловно» корректен, но дорог. + +## T-1. Пики: перерисовывать по причине, а не безусловно + +**Сейчас:** `pop_process_trobs` зовёт `pop_spike_redraw` каждый кадр для +каждой живой пики в комнате (порт `redraw_21h`, который `animate_spike` +вызывает вне всяких `if`). Это корректно, но лишнее для пик, до которых +Киду дела нет. + +**Надо:** перерисовывать тайл пики, только если +1. **сменился её видимый кадр** (шаг выдвижения/уборки), ЛИБО +2. **её кто-то стёр** — а стереть у нас может только heal, то есть тайл + попал в прямоугольник `kid_heal` этого кадра. + +Это и есть модель оригинала, просто выраженная флагами: `redraw_at_char` +(seg003:0576) каждый кадр помечает `set_redraw_fore` тайлы персонажа, причём +**объединение текущего и предыдущего** прямоугольника +(`MIN(char_top_row, prev_char_top_row)` и т.д.), а `animate_spike` помечает +свой тайл. Итог = {тайл сменил кадр} ∪ {тайлы Кида}. + +**Как:** слой Кида и так считает `cL..cR`/`rT..rB` в `pop_fore_over_kid` — +пусть публикует их (плюс предыдущие, как в оригинале), а цикл trob'ов +сравнивает `tilepos` с диапазоном целочисленно. Никаких пересечений +прямоугольников (см. память `manual_hints_over_auto_detect`). + +## T-2. Idle-skip: не перерисовывать Кида, когда ничего не происходит + +**Сейчас:** `kid_heal` → `kid_draw` → `pop_fore_over_kid` идут каждый кадр, +даже когда Kid стоит и в его тайлах ничего не меняется. Это ровно поведение +оригинала (`draw_game_frame`, seg000:917 — `draw_moving()` + `draw_tables()` +безусловно), но у него это дёшево, а у нас нет. + +**Надо:** пропускать heal+draw Кида, когда кадр/поза/координаты не менялись +и в его тайлах нет активной анимации. + +**Осторожно (дабл-буфер):** пропускать можно **не раньше второго подряд** +неизменного кадра — иначе одна из двух страниц останется со старым +содержимым. Условие «обе страницы уже получили это состояние». + +**Связь с T-1:** после T-1 idle-skip не требует ничего дополнительного для +пик: раз Кида не перерисовываем — heal'а нет — стирать пики нечем — редрой +не нужен, он отпадает сам. + +--- + +# Обход всех 24 комнат уровня 1 — поиск багов отрисовки + +Инструмент: `#define ROOMNAV` в `roomtest.c` — `+`/`-` (цифровой блок либо +`=`/`-` основного ряда) переключают комнату по номеру (1..24, с обёрткой), +Kid ставится на первый пол, `pop_trob_reset()` возвращает пики/ворота в +исходное. Номер комнаты — полосками в верхнем борте: слева десятки, справа +единицы (`||` `||||` = 24). Убирается закомментированием одной строки. + +| комната | статус | что не так | +|---------|--------|------------| +| 1 | | | +| 2 | | | +| 3 | | | +| 4 | | | +| 5 | | | +| 6 | | | +| 7 | | | +| 8 | | | +| 9 | | | +| 10 | | | +| 11 | | | +| 12 | | | +| 13 | | | +| 14 | | | +| 15 | | | +| 16 | | | +| 17 | | | +| 18 | | | +| 19 | | | +| 20 | | | +| 21 | | | +| 22 | | | +| 23 | | | +| 24 | | | diff --git a/applications/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c index e547d2e..967d085 100644 --- a/applications/PoP/roomtest/roomtest.c +++ b/applications/PoP/roomtest/roomtest.c @@ -26,12 +26,19 @@ __sfr __at (0xFE) io_border; /* Тест-старт для отладки пик (Фаза S): комната 6 = тестовая (пики в col3/col4 * row2, шахта col3). Kid на полу row0 col1, идёт вправо → падает в шахту → * пики. TODO: вернуть на pop_level_start_room (комната 1) после отладки. */ -#define START_ROOM 14 /* ОТЛАДКА: пики (2,7)/(2,8) — редрой каждый кадр */ -#define START_COL 9 -#define START_ROW 2 +#define START_ROOM 1 /* обход комнат: старт с 1-й, дальше +/- (ROOMNAV) */ +#define START_COL 3 +#define START_ROW 0 #define RESPAWN_DELAY 400 /* авто-респавн (фолбэк), если не нажали ↑ */ #define RESPAWN_SETTLE 20 /* тиков до приёма ↑ (труп успел лечь) */ +/* ОТЛАДОЧНЫЙ ОБХОД КОМНАТ: '+'/'-' (цифровой блок либо '='/'-' основного + * ряда) — следующая/предыдущая комната уровня ПО НОМЕРУ (1..24, с обёрткой), + * Kid ставится на первый попавшийся пол. Нужен, чтобы пройти все комнаты и + * собрать список багов отрисовки, включая недостижимые обычным путём. + * Убирается закомментированием ОДНОЙ строки ниже. */ +#define ROOMNAV 1 + /* Рабочая копия ТЕКУЩЕЙ комнаты (W2, мутабельная: loose→empty) + срезы * соседей для кромок. Заполняются pop_room_load из уровня. File-scope, * чтобы enter_room переключал комнату без длинного списка параметров. */ @@ -172,6 +179,9 @@ int main(void) * отладки багов рисования (edge по sp_prev, чтоб не дёргать). */ uint8_t front = 0, back, dbuf = 1, sp_prev = 0, frozen = 0; uint16_t dead_frames = 0; +#ifdef ROOMNAV + uint8_t nav_prev = 0; +#endif if (pop_bg_load() != 0) { /* атласы — ДО initgraph (снимает DSS-страницу W0) */ puts("pop_bg_load failed"); @@ -246,6 +256,37 @@ int main(void) continue; } +#ifdef ROOMNAV + /* Обход комнат по номеру: '+' (0x79 numpad / 0x55 '=') — вперёд, + * '-' (0x7B / 0x4E) — назад. Edge по nav_prev: одно нажатие = одна + * комната. pop_trob_reset ДО enter_room — каждая комната смотрится в + * исходном состоянии (пики убраны, ворота закрыты). */ + { + uint8_t nav = (uint8_t)((kbd_raw_down(0x79) || kbd_raw_down(0x55)) | + ((kbd_raw_down(0x7B) || kbd_raw_down(0x4E)) << 1)); + if (nav && !nav_prev) { + uint8_t r = cur_room, i, c, pos = 20, any = 0xFF; + r = (nav & 1) ? (uint8_t)(r == 24 ? 1 : r + 1) + : (uint8_t)(r == 1 ? 24 : r - 1); + pop_trob_reset(); + enter_room(r); + /* Предпочитаем обычный пол; иначе — любой tile_is_floor + * (seg001): всё, кроме пустоты/стены/верхов колонны и двери. */ + for (i = 0; i < 30; i++) { + c = (uint8_t)(room_fg[i] & 0x1F); + if (c == 1) { any = i; break; } + if (any == 0xFF && c && c != 9 && c != 12 && c != 20 && + !(c >= 26 && c <= 29)) any = i; + } + if (any != 0xFF) pos = any; + kid_init(SEQ_STAND, (int8_t)(pos % 10), (int8_t)(pos / 10), 0); + pop_kid_hp_reset(); + dead_frames = 0; + } + nav_prev = nav; + } +#endif + /* Рисуем в СКРЫТУЮ страницу (при dbuf); в однобуфере — в 0. */ back = dbuf ? (uint8_t)(front ^ 1) : 0; gfx_set_draw_page(back); @@ -360,18 +401,28 @@ int main(void) /* Отладка: номер отрисованной комнаты в верхнем борте — рисуем ТОЛЬКО * при её смене (change-driven, счётчик 2 = обе страницы дабл-буфера), * а не каждый кадр (обход sdcc_z80_cmp_store_a_bug: сначала сохранить). */ +#ifdef ROOMNAV { - uint8_t r = cur_room; -// if (r != room_label_sig) { room_label_sig = r; room_label_redraw = 2; } -// if (room_label_redraw) { -// char roomLabel[] = "Room ##"; -// roomLabel[5] = (char)(r / 10 + '0'); -// roomLabel[6] = (char)(r % 10 + '0'); -// setcolor(WHITE); -// outtextxy(100, 0, roomLabel); -// room_label_redraw--; -// } + uint8_t r = cur_room, prev = room_label_sig; + room_label_sig = r; /* СНАЧАЛА сохранить (sdcc_z80_cmp_store_a_bug) */ + if (r != prev) room_label_redraw = 2; + if (room_label_redraw) { + /* Номер ПАЛОЧКАМИ, а не outtextxy: текст тянет системный + * знакогенератор (_gfx_font_buf, 2 КБ статики в W2) и сажает + * кучу с 3867 до 576 Б. Слева десятки, справа единицы: + * || |||| = 24. bar/setfillstyle уже используются. */ + uint8_t i; + setfillstyle(SOLID_FILL, BLACK); + bar(100, 0, 175, 5); + /* 0x57, а НЕ WHITE: палитра игровая, запись 15 в ней чёрная + * (kid.pal: 0x0F = 0,0,0; чистый белый = 0x57). */ + setfillstyle(SOLID_FILL, 0x57); + for (i = 0; i < (uint8_t)(r / 10); i++) bar(100 + i * 5, 0, 101 + i * 5, 5); + for (i = 0; i < (uint8_t)(r % 10); i++) bar(130 + i * 5, 0, 131 + i * 5, 5); + room_label_redraw--; + } } +#endif gfx_wait_vsync(); gfx_wait_vsync();