L9-INVERT II.4: персонажи рисуются перевёрнутыми (зеркальные страницы атласов)

Спрайты персонажей хранятся column-major (ради бесплатного H-флипа), а
вертикальное зеркало у такой раскладки — разворот байтов ВНУТРИ колонки,
чего accel не умеет.  Поэтому pop_vflip.c готовит зеркальные КОПИИ страниц
атласов: копия побайтово повторяет раскладку .atl (заголовок, каталог,
смещения лент), развёрнуты только пиксели — значит вызывающему достаточно
подменить номер страницы, которую он мапит в W0.

* Кэш ленивый (страница зеркалится при первом обращении в перевёрнутом
  виде) и живёт до смены уровня — зелье переключает состояние туда-обратно,
  перезеркаливать по 16 КБ на каждый переворот незачем.  EMM хватает: 34
  страницы из ~215 свободных.
* pop_cdraw/pop_kdraw: страница + позиция (top = 191 - obj_y для персонажа,
  192 - top - h для клинка).  «Пропустить skip строк сверху» у зеркальной
  ленты = «срезать снизу» у оригинала, поэтому клипы считаются как обычно.
* Модуль резидентный: он маппит W3 (приёмник копии) — банку так нельзя.
* pop_vflip_reset на смене уровня: копии сделаны из старых страниц.

Проверено в MAME: чит U переворачивает всё разом, Кид и факелы вверх ногами,
полоса HP на месте, бег без следов на фоне (heal бьёт в те же координаты).

ОСТАЁТСЯ (L9-CLIPCHAR): clip_char в перевороте должен резать снизу, а не
сверху — пока при pop_upside не режем вовсе.  Тени/брызг это не касается.
This commit is contained in:
2026-08-12 22:25:18 +03:00
parent 055d6d7c89
commit 755190674c
8 changed files with 216 additions and 6 deletions
+26 -3
View File
@@ -20,7 +20,8 @@
#include <sprite.h>
#include "pop_guard.h"
#include "pop_kid.h"
#include "_pop_kdraw.h" /* меч + блит по id — сосед по банку 4 */ /* атласы/кадр Кида, pop_sword_draw, окна Char */
#include "_pop_kdraw.h"
#include "pop_vflip.h" /* зеркальные страницы атласов (зелье инверсии) */ /* меч + блит по id — сосед по банку 4 */ /* атласы/кадр Кида, pop_sword_draw, окна Char */
#include "pop_cdraw.h"
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char, pop_fore_set_clip */
#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */
@@ -521,7 +522,18 @@ void pop_char_draw(uint8_t who) __banked
idx = (uint8_t)(fr->image & 7);
if (page >= npages) return; /* страница набора не загружена */
img = (const uint8_t *)atlas_image(&pages[page], idx);
gfx_w0_map(pages[page].page);
/* ПЕРЕВОРОТ (зелье инверсии): тот же атлас, но страница с вертикально
* зеркальными пикселями — смещения лент и размеры кадров у копии те же,
* поэтому меняется ровно номер страницы (pop_vflip.h). Если EMM не
* дала страницу — рисуем обычной: неперевёрнутый кадр лучше дыры. */
{
uint8_t vpg = pages[page].page;
if (pop_upside) {
uint8_t f = pop_vflip_page(vpg);
if (f) vpg = f;
}
gfx_w0_map(vpg);
}
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
dp = gfx_get_draw_page() & 1;
@@ -534,13 +546,24 @@ void pop_char_draw(uint8_t who) __banked
bx = obj_x;
if (flip) bx -= (int)w;
top = obj_y - (int)h + 1;
/* Поле переворачивается вокруг своей середины: строка t уходит в
* (POP_PLAYFIELD_H - 1 - t), значит верх кадра [top, top+h) —
* в 192 - top - h, что при top = obj_y-h+1 даёт ровно 191 - obj_y.
* Пиксели уже зеркальны (страница выше), поэтому дальше всё
* считается как обычно: «пропустить skip строк сверху» у зеркальной
* ленты означает «срезать снизу» у оригинала — ровно то, что нужно. */
if (pop_upside) top = (POP_PLAYFIELD_H - 1) - obj_y;
vis_w = w;
lskip = 0;
skip = 0;
/* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё
* выше линии полосы своего ряда НЕ рисуется, иначе персонаж
* просвечивает сквозь щели кладки при спуске/висе. */
ct = pop_clip_char_top(fp_x, obj_y, w, h);
/* 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; /* обрезан целиком */