Переворот: перерисовка вместо отражения, переключение на границе кадра, clip_char

Три правки по следам прогона пользователя на зелье инверсии.

1. ЛИШНИЕ ЯЗЫКИ ПЛАМЕНИ.  pop_flip_screen отражал уже нарисованное копией
   акселератора, а пламя факела ЗАПЕКАЕТСЯ в ОЗУ-копию (pop_torch_draw: heal'а
   у него нет, следующий кадр непрозрачно накрывает предыдущий).  Отражённое
   вместе с фоном, оно оказывалось в зеркальной позиции, где накрывать его
   некому — и оставалось навсегда (уходило только после входа в комнату,
   который строит фон с нуля).  Теперь переворот ПЕРЕРИСОВЫВАЕТ комнату тем же
   приёмом, что вход (ENTER-ROOM-FAST).  Это совпадает с оригиналом: SDLPoP на
   need_redraw_because_flipped зовёт redraw_screen(0), а не отражает картинку
   (seg000.c:928).

2. МОМЕНТ ПЕРЕКЛЮЧЕНИЯ.  Зелье выпивается из play_seq, в середине кадра, и
   pop_upside переключался прямо там — остаток кадра рисовался зеркально
   поверх ещё неперевёрнутого фона.  Разделены pop_upside_want (пишут зелье,
   смерть, чит U) и pop_upside (читают слои); переключение — одно место, начало
   кадра, вместе с перерисовкой.  Оригиналу этого не нужно: он всегда рисует в
   offscreen неперевёрнутым и зеркалит только на выводе — расхождение записано
   в docs/impl_diff.md.

3. CLIP_CHAR В ПЕРЕВОРОТЕ.  Граница clip_char приходит в ЛОГИЧЕСКИХ
   координатах (y_clip), сравнивать её с уже отражённым top нельзя: то, что
   логически выше линии, на экране ниже неё.  Теперь тот же срез применяется
   с другого конца — укорачивает кадр снизу (bcut), верх остаётся на месте;
   строки ленты не сдвигаются, потому что зеркальная лента отдаёт их в
   обратном порядке.  Раньше в перевороте клип был отключён совсем, и висящий
   Кид рисовался поверх плиты.

Проверено в MAME: переворот чистый, фон без остатков.  tests-host 6/6.
This commit is contained in:
2026-08-12 23:33:01 +03:00
parent 5b6664ffce
commit 948d8f08f2
6 changed files with 113 additions and 28 deletions
+27 -10
View File
@@ -487,6 +487,7 @@ void pop_char_draw(uint8_t who) __banked
uint8_t npages, page, idx, dp, flip, hurt, lskip;
uint16_t w, h, vis_w;
int obj_x, obj_y, fp_x, fwd, top, bx, skip, rows, ct, cr;
int bcut; /* срез СНИЗУ (clip_char в перевёрнутом виде) */
/* Страховка пропуска (DRAW-COST): главный цикл мог решить, что слот
* «тихий», и heal не звать — а тик после этого персонажа всё-таки
@@ -581,18 +582,34 @@ void pop_char_draw(uint8_t who) __banked
vis_w = w;
lskip = 0;
skip = 0;
bcut = 0;
/* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё
* выше линии полосы своего ряда НЕ рисуется, иначе персонаж
* просвечивает сквозь щели кладки при спуске/висе. */
/* clip_char в перевёрнутом виде должен резать СНИЗУ, а не сверху
* (тайл над головой стал тайлом под ногами) — отдельная задача
* L9-CLIPCHAR; пока в перевороте не режем вовсе: артефакт мельче,
* чем срезанный не с той стороны персонаж. */
ct = pop_upside ? -1000 : pop_clip_char_top(fp_x, obj_y, w, h);
if (ct > top) {
skip = ct - top;
if (skip > (int)h) skip = (int)h; /* обрезан целиком */
top = ct;
/* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё
* выше линии полосы своего ряда НЕ рисуется. Граница приходит в
* ЛОГИЧЕСКИХ координатах поля (y_clip), и при перевороте её нельзя
* сравнивать с уже отражённым top: то, что логически «выше линии»,
* на экране оказывается НИЖЕ неё. Поэтому в перевороте тот же
* срез применяется с другого конца — укорачивает кадр снизу
* (bcut), а верх (top) остаётся на месте. Строки ленты при этом
* не сдвигаются: зеркальная лента отдаёт их в обратном порядке,
* так что «убрать последние bcut строк вывода» = «не рисовать
* первые bcut строк оригинала», а это ровно то, что просит
* clip_char. */
ct = pop_clip_char_top(fp_x, obj_y, w, h);
if (!pop_upside) {
if (ct > top) {
skip = ct - top;
if (skip > (int)h) skip = (int)h; /* обрезан целиком */
top = ct;
}
} else {
int top_log = obj_y - (int)h + 1; /* верх кадра ДО отражения */
if (ct > top_log) {
bcut = ct - top_log;
if (bcut > (int)h) bcut = (int)h;
}
}
/* Вторая половина clip_char: правый косяк двери уровня. Дверь
* рисуется ПОД персонажем (вся в backtable), поэтому спрятать
@@ -619,7 +636,7 @@ void pop_char_draw(uint8_t who) __banked
* obj_clip_top = 0, obj_clip_bottom = 192). Персонаж, вышедший за
* него (скелет, падающий в пропасть), обязан обрезаться: ниже поля
* идёт полоса HP, и без клипа спрайт лез прямо на неё. */
rows = (int)h - skip;
rows = (int)h - skip - bcut;
if (top < 0) { skip += -top; rows += top; top = 0; }
if (top + rows > 192) rows = 192 - top;
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, без теневой копии */